You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 14:16:15