使用Test Containers连接PostgreSQL容器失败问题排查
问题:Test Containers启动自定义PostgreSQL镜像超时
我在项目中用Test Containers搭建测试环境,先通过自定义Dockerfile构建PostgreSQL镜像,再用Scala代码配置Test Containers,但启动测试时抛出ContainerLaunchException,日志显示PostgreSQL已就绪,但Test Containers检测超时。
自定义Dockerfile
# To build this Dockerfile run the folloing command # docker build -t custom-postgres-initdb --build-arg SQL_SCRIPT_PATH=/path/to/your/sql-script.sql . # Use the official PostgreSQL image as the base image FROM postgres:latest # Create a directory to store your SQL scripts and set it as the working directory WORKDIR /docker-entrypoint-initdb.d # Copy all SQL scripts from the specified directory into the container COPY *.sql ./ # Set the environment variable for the initial password ENV POSTGRES_PASSWORD=my_pass # Optionally, you can set other environment variables like POSTGRES_USER and POSTGRES_DB ENV POSTGRES_USER=my_user ENV POSTGRES_DB=my_db # You can also specify the port to expose (default is 5432) EXPOSE 5432 # Any additional configuration or scripts can be added here if needed # Start PostgreSQL CMD ["postgres"]
Scala配置代码
override val container: PostgreSQLContainer = PostgreSQLContainer().configure { psqlContainer => psqlContainer.setDockerImageName("my-postgres-test-container:latest") psqlContainer.withDatabaseName(s"${dbConfig.dbName}") psqlContainer.withUsername(s"${dbConfig.user}") psqlContainer.withPassword(s"${dbConfig.pass}") psqlContainer.withExposedPorts(5432) psqlContainer.withCreateContainerCmdModifier(cmd => cmd.withHostConfig( new HostConfig().withPortBindings( new PortBinding(Ports.Binding.bindPort(dbConfig.port), new ExposedPort(32852)) ) ) ) //psqlContainer.waitingFor(Wait.forLogMessage(".*database system is ready to accept connections.*", 2)) } override def beforeAll(): Unit = { super.beforeAll() container.start() someProvisioningRepoImpl.map(_ => new ProvisioningRepoImpl[IO](getDBConfigForTestContainer)) }
解决方案
1. 恢复并调整日志等待规则
你注释掉的waitingFor配置是核心修复点,Test Containers默认检测逻辑可能和自定义镜像的日志输出不匹配。取消注释并调整参数:
psqlContainer.waitingFor(Wait.forLogMessage(".*database system is ready to accept connections.*", 1).withStartupTimeout(Duration.ofMinutes(2)))
- 把匹配次数从2改为1:部分PostgreSQL版本仅输出一次就绪日志
- 延长启动超时:如果初始化SQL脚本较大,需要给足执行时间,默认超时可能不够
2. 移除冲突的手动端口绑定
代码中手动绑定主机端口到容器的32852,但容器暴露的是5432,这会导致Test Containers无法正确检测端口可用性。直接删除这段端口绑定代码:
// 移除以下代码块 psqlContainer.withCreateContainerCmdModifier(cmd => cmd.withHostConfig( new HostConfig().withPortBindings( new PortBinding(Ports.Binding.bindPort(dbConfig.port), new ExposedPort(32852)) ) ) )
Test Containers会自动分配随机可用端口,你可以通过container.getJdbcUrl()获取正确的连接地址,无需手动指定端口绑定。
3. 确保初始化脚本执行完成
如果SQL初始化脚本执行时间长,PostgreSQL虽输出就绪日志,但实际仍在执行初始化,此时Test Containers尝试连接会失败。可以修改等待逻辑,结合端口监听和连接检测:
psqlContainer.waitingFor(Wait.all( Wait.forLogMessage(".*database system is ready to accept connections.*", 1), Wait.forListeningPort() ).withStartupTimeout(Duration.ofMinutes(2)))
或者直接使用健康检查:
psqlContainer.waitingFor(Wait.forHealthcheck())
4. 消除环境变量冲突
自定义镜像中已设置POSTGRES_USER、POSTGRES_DB、POSTGRES_PASSWORD,但代码中又通过withDatabaseName等方法重复设置,可能导致配置冲突。二选一即可:
- 去掉代码中的重复配置,直接使用镜像预设的参数
- 保留代码配置,删除镜像中的
ENV环境变量设置,确保两者参数一致
内容的提问来源于stack exchange,提问作者joesan
相关产品推荐
相关产品推荐

