我正在开发一些使用testcontainers与Spring-Boot的测试用例,以获得一个MS-SQL dockerized数据库。这是一个巨大的数据库,在docker运行进程中恢复大约需要40分钟。
我处理这个图像的步骤是:
- 构建Dockerfile, schema和数据脚本标记为"db"
- 运行容器,等待约40分钟恢复数据库。
- 使用"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
关于这个话题的博文已经写完了,会在直播时更新这个答案