【问题标题】:C# + DockerCompose - Wait for MS SQL Server Docker container to be up before trying to connectC# + DockerCompose - 在尝试连接之前等待 MS SQL Server Docker 容器启动
【发布时间】:2018-11-25 10:04:55
【问题描述】:

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

我仍在研究如何启动和播种数据库。 目前我有一个带有FluentDocker v2.2.15 和DbUp 4.1.0 的.NET Core 2.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 Server 足够的时间启动时,例如在调试时,我可以成功运行它。但是,如果我全速运行它,那么 DbUp 会在它尚未准备好时尝试连接到 SQL Server。 FluentDocker 有一个 WaitForPort 方法,但它似乎不适用于 DockerCompose API。

我想知道是否有办法在运行脚本之前等待 SQL Server 的端口 1433 响应(不包括诸如await Task.Delay 之类的非确定性策略),或者是否有其他库可以让我拥有这种控制。

谢谢

【问题讨论】:

  • 等待端口 1433 不一定足够——此时 SQL Server 可能仍在启动数据库的过程中(尽管所有系统数据库都应该启动,所以 CREATE DATABASE 需要一个新的无论如何都应该工作)。即便如此,更好的方法是等待一个简单的查询成功(如SELECT TOP(0) * FROM master.sys.tables)。在“本机” docker-compose 中,这可以通过 depends_on 运行状况检查来完成。对于 FluentDocker,没有任何线索。
  • 您应该能够使用 FluentDocker 对 compose 文件执行 depends_on,因为它将使用 docker-compose 本机二进制文件来驱动 compose 文件。

标签: c# sql-server docker-compose dbup fluent-docker


【解决方案1】:

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

给定 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 web 正在运行的using 子句需要使用 HTTP 来确定。您可以使用 Fluent API 来实例化并等待服务像这样启动。

  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 连接 并在继续之前查询表。 高于零的返回值是等待下一次测试的时间。零及以下将成功结束等待。异常将终止等待(并失败)。

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

干杯, 马里奥

【讨论】:

    猜你喜欢
    • 2015-10-23
    • 1970-01-01
    • 2021-11-03
    • 2022-01-04
    • 2019-03-28
    • 1970-01-01
    • 1970-01-01
    • 2023-03-05
    相关资源
    最近更新 更多