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



我正在开发一些使用testcontainers与Spring-Boot的测试用例,以获得一个MS-SQL dockerized数据库。这是一个巨大的数据库,在docker运行进程中恢复大约需要40分钟。

我处理这个图像的步骤是:

  1. 构建Dockerfile, schema和数据脚本标记为"db"
  2. 运行容器,等待约40分钟恢复数据库。
  3. 使用"db-ready"标签。

我期望的行为是测试用例尝试从"db-ready"运行一个容器如果失败,直接从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 Profiles来启用它):

@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();
}

这种方法的潜在缺陷

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

阅读更多

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

关于这个话题的博文已经写完了,会在直播时更新这个答案

最新更新