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

pef*_*cio 5 c# sql-server docker-compose dbup fluent-docker

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

I am still working on how to spin up and seed the database. Currently I have a .NET Core 2.0 project with FluentDocker v2.2.15 and DbUp 4.1.0.

I use FluentDocker to call DockerCompose and start my services, including the 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);
Run Code Online (Sandbox Code Playgroud)

and then I use DbUp to run my scripts and seed the database.

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();
Run Code Online (Sandbox Code Playgroud)

I can run this successfully when I give SQL Server enough time to start, for example, when debugging. However, if I run this at full speed, then DbUp tries to connect to SQL Server when it isn't ready yet. FluentDocker has a WaitForPort method but it doesn't seem to work with DockerCompose API.

I would like to know if there is a way to wait for SQL Server's port 1433 to be responsive before running the scripts (excluding non-deterministic tactics such as await Task.Delay) or if there are alternative libraries that allow me to have this kind of control.

Thanks

Mar*_*fia 2

您可以在FluentDocker v2.6.2 中的 compose 上使用WaitForPortWaitForProcessWaitForHttp或自定义 lambdaWait函数。例如:

给定 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:
Run Code Online (Sandbox Code Playgroud)

该文件指定wordpress依赖于db,因此先启动db,然后实例化wordpress容器。然而,为了确保在using子句中wordpress web 正在运行,您需要使用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);
  }
Run Code Online (Sandbox Code Playgroud)

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

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

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

干杯,马里奥