C# + DockerCompose - 在尝试连接之前等待 MS SQL 服务器 Docker 容器启动

C# + DockerCompose - Wait for MS SQL Server Docker container to be up before trying to connect

我正在尝试为我的微服务创建类似于 Spotify's approach 的集成测试。

我仍在研究如何启动和播种数据库。 目前我有一个 .NET Core 2.0 项目 FluentDocker v2.2.15 and DbUp 4.1.0.

我使用 FluentDocker 调用 DockerCompose 并启动我的服务,包括 SQL Server container

var hosts = new Hosts().Discover();
var dockerHost = hosts.FirstOrDefault(x => x.IsNative) ?? hosts.FirstOrDefault(x => x.Name == "default");

if (dockerHost == null)
{
    return;
}

var composeFile = Args["composeFile"];

var result = dockerHost.Host.ComposeUp(composeFile: composeFile);

然后我使用 DbUp 运行 我的脚本并为数据库播种。

var connectionString = Args["connectionString"];
var scriptsPath = Args["scriptsPath"];

EnsureDatabase.For.SqlDatabase(connectionString);

var upgradeEngine = DeployChanges.To.SqlDatabase(connectionString).WithScriptsFromFileSystem(scriptsPath).Build();

var result = upgradeEngine.PerformUpgrade();

当我给 SQL 服务器足够的时间启动时,我可以 运行 成功,例如,在调试时。但是,如果我 运行 全速执行此操作,则 DbUp 会在尚未准备好时尝试连接到 SQL 服务器。 FluentDocker 有一个 WaitForPort 方法,但它似乎不适用于 DockerCompose API.

我想知道是否有办法在 运行 脚本之前等待 SQL 服务器的端口 1433 响应(不包括非确定性策略,例如 await Task.Delay) 或者是否有其他库允许我进行这种控制。

谢谢

您可以在 FluentDocker v2 中使用 WaitForPortWaitForProcessWaitForHttp 或自定义 lambda Wait 函数.6.2.例如:

给定 docker-compose 文件:

version: '3.3'
services:
  db:
    image: mysql:5.7
    volumes:
    - db_data:/var/lib/mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: somewordpress
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: wordpress

  wordpress:
    depends_on:
    - db
    image: wordpress:latest
    ports:
    - "8000:80"
    restart: always
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: wordpress
volumes:
  db_data:

文件指定wordpress依赖db,因此先启动db,再实例化wordpress容器。但要确保在 wordpress 网络是 运行 的 using 子句,您需要使用 HTTP 来确定这一点。可以使用FluentAPI来实例化,这样等待服务启动。

  var file = Path.Combine(Directory.GetCurrentDirectory(),
    (TemplateString) "Resources/ComposeTests/WordPress/docker-compose.yml");

using (new Builder()
            .UseContainer()
            .UseCompose()
            .FromFile(file)
            .RemoveOrphans()
            .Wait("wordpress", (service, cnt) => {
                if (cnt > 60) throw new FluentDockerException("Failed to wait for wordpress service");

                var res = HttpExtensions.DoRequest("http://localhost:8000/wp-admin/install.php").Result;            
                return (res.Code == HttpStatusCode.OK && 
                        res.Body.IndexOf("https://wordpress.org/", StringComparison.Ordinal) != -1) ? 0 : 500;
              })
            .Build().Start())
  {
    // Since we have waited - this shall now always work.       
    var installPage = await "http://localhost:8000/wp-admin/install.php".Wget();
    Assert.IsTrue(installPage.IndexOf("https://wordpress.org/", StringComparison.Ordinal) != -1);
  }

(上面的示例比较繁琐WaitForHttp但是使用自定义lambda来说明这一点)。

通过这种方式,您甚至可以使用 db 连接 并在继续之前查询 table。 Return 大于零的值是等待下一次测试的时间。零及以下将成功结束等待。异常将终止等待(并失败)。

上面的例子使用了FluentAPI语法,但是你可以手动添加一个钩子到compose容器上,然后自己使用扩展。

干杯, 马里奥