测试容器,在构建 Dockerfile 之前尝试从本地注册表加载

Testcontainers, try to load from local registry before build a Dockerfile

我正在使用带有 Spring-Boot 的 Tescontainer 开发一些测试用例,以便启动 MS-SQL dockerized 数据库。这是一个巨大的数据库,需要大约 40 分钟才能在 docker 运行 进程中恢复。

我处理这张图片的步骤是:

  1. 使用模式和数据脚本将其标记为“db”构建 Dockerfile。
  2. 运行 容器并等待大约 40 分钟以恢复数据库。
  3. 提交带有“db-ready”标签的容器。

我期望的行为是测试用例尝试 运行 来自“db-ready”图像的 cointainer,如果失败,则直接从 Dockerfile 构建图像。我试过的代码看起来像:

public static CustomMSSqlContainer getInstance() {
    if (container == null) {
        try {
            container = new CustomMSSqlContainer("myproject:db-ready");
        }catch(Exception ex) {              
            container = new CustomMSSqlContainer(new ImageFromDockerfile().withFileFromClasspath("Dockerfile", "docker/Dockerfile")
                        .withFileFromClasspath("schema.sql", "docker/schema.sql")
                        .withFileFromClasspath("entrypoint.sh", "docker/entrypoint.sh")
                        .withFileFromClasspath("data.sql", "docker/data.sql")
                        .withFileFromClasspath("data-init.sql", "docker/data-init.sql")
                        .withFileFromClasspath("start.sh", "docker/start.sh"));
        }
            
        container.waitingFor(Wait.forLogMessage("Database ready\n", 1)
                    .withStartupTimeout(Duration.ofHours(1)))
                    .withExposedPorts(1433);
    }
        
    return (CustomMSSqlContainer)container;
}

当然,这段代码并不像我预期的那样工作。

有什么建议吗?

我们是如何解决这个问题的

我们这样做的方法是仅在 Main/Dev 分支 上构建自定义图像。这样:

  • 我们不需要 try-catch;
  • 我们只在实际需要时构建一个新容器(在合并请求批准更改并合并到主分支之后);
  • 此容器的构建仅在 CI 管道上完成(因此人们不必也不能随机推送到容器注册表)

这是一个使用 JUnit 测试的示例(在此示例中禁用,但您可以使用 Spring 配置文件来启用它):

@Test
@Disabled("Should be run only with certain profiles ;)")
public void pushNewImage() throws InterruptedException {
    // Startup the container before this point, using @Container or just container.start().
    // That should run all your scripts and wait for the waiter 

    // Get the DockerClient used by the TestContainer library (you can also use your own if they every make that private).
    final DockerClient dockerClient = customMSSqlContainer.getDockerClient();

    // Commit docker container changes into new image
    dockerClient.commitCmd(customMSSqlContainer.getContainerId())
            .withRepository("myproject")
            .withTag("db-ready")
            .exec();

    // Push new image. Logger is used for visual feedback.
    dockerClient.pushImageCmd("myproject:db-ready")
            .exec(new ResultCallback.Adapter<>() {
                @Override
                public void onNext(PushResponseItem object) {
                    log.info(object.toString()); // just log the push to the repo
                }
            }).awaitCompletion();
}

这种方法的潜在缺陷

docker commit 不会保存任何已保存到卷中的内容。 这是一个问题,因为大多数数据库图像实际上会创建一个卷。我看不到你的 Dockerfile,但请确保你保存的所有数据都没有保存到卷中!

阅读更多

我最近在 JFokus 会议上与 TestContainers 核心团队的一个人在我的房间里简短地谈到了这个问题:https://youtu.be/pxxMnvu52K8?t=1922

关于此主题的博客 post 即将完成,将在发布时更新此答案