Spring Boot+Docker部署Postgres遇连接超时及容器自动关闭问题
环境与配置
- 系统:Ubuntu
- Spring Boot版本:3.3.0
- Docker版本:26.1.3 (build b72abbb)
- 使用
spring-boot-docker-compose实现启动时自动拉起Docker镜像
核心配置文件
pom.xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-docker-compose</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency>
Dockerfile
# Use the Amazon Corretto 21 base image FROM amazoncorretto:21.0.3 # Create a volume for temporary files VOLUME /tmp # Arguments for the JAR file to be copied ARG JAR_FILE=target/*.jar EXPOSE 8080 EXPOSE 5432 # Copy the JAR file into the Docker image COPY ${JAR_FILE} /app.jar # Set the entry point for the container to run the application ENTRYPOINT ["java","-jar","/app.jar"]
docker-compose.yml
version: '3.8' services: app: build: context: . dockerfile: Dockerfile ports: - "8081:8080" environment: SPRING_PROFILES_ACTIVE: dev network_mode: host postgres: image: postgres:latest restart: on-failure environment: - POSTGRES_DB=expiry_service_db - POSTGRES_PASSWORD=postgres - POSTGRES_USER=postgres ports: - "5431:5432" network_mode: host
application.properties
spring.application.name=expiry-service server.port=8080 # expiry_service datasource spring.datasource.driver-class-name=org.postgresql.Driver spring.datasource.url=jdbc:postgresql://localhost:5432/expiry_service_db spring.datasource.username=postgres spring.datasource.password=postgres #spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect spring.jpa.database-platform=postgres spring.jpa.hibernate.ddl-auto=update
问题现象
启动Spring Boot应用后,Tomcat在8080端口启动,Docker Desktop显示app和postgres容器已运行,但等待约2分钟后,两个容器均停止。
容器日志
postgres-1 | 2024-06-07 12:05:29.558 UTC [1] LOG: received fast shutdown request postgres-1 | 2024-06-07 12:05:29.566 UTC [39] FATAL: terminating connection due to administrator command postgres-1 | 2024-06-07 12:05:29.566 UTC [1] LOG: background worker "logical replication launcher" (PID 32) exited with exit code 1 postgres-1 | 2024-06-07 12:05:29.569 UTC [27] LOG: shutting down postgres-1 | 2024-06-07 12:05:29.572 UTC [27] LOG: checkpoint starting: shutdown immediate app-1 | 2024-06-07T12:05:29.576Z INFO 1 --- [expiry-service] [ionShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default' postgres-1 | 2024-06-07 12:05:29.600 UTC [1] LOG: database system is shut down postgres-1 exited with code 0
Spring Boot日志
org.springframework.boot.docker.compose.lifecycle.ReadinessTimeoutException: Readiness timeout of PT2M reached while waiting for services [expiry-service-app-1] at org.springframework.boot.docker.compose.lifecycle.ServiceReadinessChecks.waitUntilReady(ServiceReadinessChecks.java:83) ~[spring-boot-docker-compose-3.3.0.jar:3.3.0] Suppressed: org.springframework.boot.docker.compose.lifecycle.ServiceNotReadyException: IOException while connecting to port 8080 Caused by: java.net.ConnectException: Connection refused
问题解答
1. 能否通过POSTGRES_DB参数自动创建数据库?
可以。Postgres官方镜像原生支持通过POSTGRES_DB、POSTGRES_USER、POSTGRES_PASSWORD三个参数的组合,在容器首次启动时自动创建指定名称的数据库,并将该数据库设为POSTGRES_USER的默认数据库。你当前的docker-compose.yml已完整配置这三个参数,所以expiry_service_db应该会被自动创建,这个环节不存在问题。
2. 数据库为何自动关闭?
容器自动关闭的核心原因是Spring Boot Docker Compose的就绪检查超时,触发了所有关联容器的自动停止,而就绪检查失败的根源是应用服务(app)未能正常启动并监听8080端口,具体问题出在以下配置冲突:
network_mode: host导致的端口与网络问题
host模式下容器直接复用主机网络栈,ports配置会被忽略:- 对于postgres:你配置的
5431:5432端口映射无效,容器会直接占用主机的5432端口。如果主机5432端口被其他进程占用,postgres启动会失败;即使启动成功,若postgres启动速度慢于应用,应用会因无法连接数据库而启动失败。 - 对于app:容器的8080端口直接使用主机8080端口,若主机该端口已被占用,应用无法启动,导致Spring Boot Docker Compose的就绪检查(默认监听8080端口)无法连接,触发2分钟超时后关闭所有容器。
- 对于postgres:你配置的
缺少服务依赖与健康检查
未配置app服务等待postgres就绪的逻辑,应用会在postgres完全启动前尝试连接,导致连接失败进而启动失败,端口无法监听,最终触发就绪检查超时。
修复建议
移除
network_mode: host
使用Docker默认桥接网络,服务间可通过服务名(如postgres)访问,同时修改application.properties的数据库URL:spring.datasource.url=jdbc:postgresql://postgres:5432/expiry_service_db配置服务依赖与健康检查
在docker-compose.yml中添加依赖关系与postgres健康检查,确保postgres就绪后再启动app:version: '3.8' services: app: build: context: . dockerfile: Dockerfile ports: - "8081:8080" environment: SPRING_PROFILES_ACTIVE: dev depends_on: postgres: condition: service_healthy postgres: image: postgres:latest restart: on-failure environment: - POSTGRES_DB=expiry_service_db - POSTGRES_PASSWORD=postgres - POSTGRES_USER=postgres ports: - "5431:5432" healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres -d expiry_service_db"] interval: 5s timeout: 5s retries: 5调整数据库连接超时配置
在application.properties中增加连接池超时设置,给数据库足够的启动时间:spring.datasource.hikari.connection-timeout=30000 spring.datasource.hikari.maximum-pool-size=10
内容的提问来源于stack exchange,提问作者Richard

