Docker镜像构建时连接PostgreSQL生成JOOQ对象失败求助
我正在开发一个副业项目,想用docker-compose完成本地部署,预期流程是:
- 部署带有init.sql文件的PostgreSQL容器;
- 在镜像构建阶段生成应用所需的JOOQ代码;
- 将生成的代码复制到镜像并运行应用。
但不管怎么配置,构建容器始终连不上PostgreSQL容器来生成代码,试过links、网络配置都没用。
当前配置
Docker Compose 配置
services: database: container_name: database hostname: database image: postgres:latest ports: - "5432:5432" environment: - PGUSER=user - POSTGRES_USER=user - POSTGRES_PASSWORD=password - POSTGRES_DB=db volumes: - ./init.sql:/docker-entrypoint-initdb.d/init.sql healthcheck: test: [ "CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}" ] interval: 2s timeout: 5s retries: 5 networks: - mynet server: build: context: . dockerfile: Dockerfile depends_on: database: condition: service_healthy ports: - "8080:8080" networks: - mynet networks: mynet: driver: bridge
Dockerfile 配置
FROM maven:3.9.7-amazoncorretto-17 AS build COPY models models RUN mvn clean package -f models/pom.xml -Plocal
pom.xml 配置
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.abl.rjmdb</groupId> <artifactId>models</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <jooq.version>3.19.8</jooq.version> <postgresql.version>42.7.3</postgresql.version> </properties> <dependencies> <dependency> <groupId>org.jooq</groupId> <artifactId>jooq</artifactId> <version>${jooq.version}</version> </dependency> <dependency> <groupId>org.jooq</groupId> <artifactId>jooq-meta</artifactId> <version>${jooq.version}</version> </dependency> <dependency> <groupId>org.jooq</groupId> <artifactId>jooq-codegen</artifactId> <version>${jooq.version}</version> </dependency> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <version>${postgresql.version}</version> </dependency> </dependencies> <profiles> <profile> <id>local</id> <properties> <database.driver>org.postgresql.Driver</database.driver> <database.url>jdbc:postgresql://database:5432/db</database.url> <database.user>user</database.user> <database.password>password</database.password> <database.schema>public</database.schema> </properties> <build> <plugins> <plugin> <groupId>org.jooq</groupId> <artifactId>jooq-codegen-maven</artifactId> <version>${jooq.version}</version> <executions> <execution> <phase>generate-sources</phase> <goals> <goal>generate</goal> </goals> </execution> </executions> <configuration> <jdbc> <driver>${database.driver}</driver> <url>${database.url}</url> <user>${database.user}</user> <password>${database.password}</password> </jdbc> <generator> <database> <includes>.*</includes> <inputSchema>${database.schema}</inputSchema> </database> <target> <packageName>com.abl.rjmdb.model.jooq</packageName> <directory>${project.build.directory}/generated-sources/jooq</directory> </target> </generator> </configuration> </plugin> </plugins> </build> </profile> </profiles> </project>
遇到的错误
- 用
database作为主机时:
11.96 [ERROR] Failed to execute goal org.jooq:jooq-codegen-maven:3.19.8:generate (default) on project models: Error running jOOQ code generation tool: The connection attempt failed. Unknown host database -> [Help 1]
- 用
127.0.0.1作为主机时:
Failed to execute goal org.jooq:jooq-codegen-maven:3.19.8:generate (default) on project models: Error running jOOQ code generation tool: Connection to 127.0.0.1:5432 refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections. Connection refused
- 用
docker inspect <container name>获取的IP地址时:
Failed to execute goal org.jooq:jooq-codegen-maven:3.19.8:generate (default) on project models: Error running jOOQ code generation tool: Connection to 172.17.0.2:5432 refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections. Connection refused
我已经确认PostgreSQL容器正常运行,而且从主机可以连接上。想请教两个问题:
- 该如何解决这个连接问题?
- 在Docker镜像内生成JOOQ代码是不是不属于最佳实践?
解决方案
问题根源
Docker Compose的depends_on只会控制服务启动顺序,但镜像构建阶段(docker build)是在Compose网络之外执行的,所以构建容器根本访问不到PostgreSQL服务所在的Compose网络,这就是为什么用database主机名找不到、用localhost或容器IP也连不上的原因。
解决方法
方法1:使用主机网络进行构建(本地开发场景)
修改Docker Compose中server的构建配置,让构建过程使用主机网络,这样就能直接访问本地已启动的PostgreSQL容器:
server: build: context: . dockerfile: Dockerfile network: host # 新增配置,让构建容器使用主机网络 depends_on: database: condition: service_healthy ports: - "8080:8080" networks: - mynet
同时修改pom.xml中的数据库URL为主机能访问的地址:
<database.url>jdbc:postgresql://localhost:5432/db</database.url>
这样构建时,Maven容器会使用主机网络,直接访问主机映射的5432端口,就能连接到PostgreSQL容器。
方法2:多阶段构建+临时数据库(通用隔离方案)
如果不想依赖外部数据库,可以在Dockerfile的构建阶段临时启动一个PostgreSQL容器来生成JOOQ代码,构建过程完全自给自足:
FROM maven:3.9.7-amazoncorretto-17 AS build COPY models models COPY init.sql /tmp/init.sql # 安装PostgreSQL服务和客户端 RUN apt-get update && apt-get install -y postgresql postgresql-client # 临时启动数据库、执行初始化脚本、生成JOOQ代码 RUN service postgresql start && \ su postgres -c "createdb db" && \ su postgres -c "createuser user -s" && \ su postgres -c "psql -d db -f /tmp/init.sql" && \ su postgres -c "psql -d db -c \"ALTER USER user WITH PASSWORD 'password';\"" && \ mvn clean package -f models/pom.xml -Plocal -Ddatabase.url=jdbc:postgresql://localhost:5432/db -Ddatabase.user=user -Ddatabase.password=password && \ service postgresql stop
这种方式不需要依赖外部服务,适合CI/CD环境使用。
关于镜像内生成JOOQ代码的最佳实践
在镜像构建阶段生成JOOQ代码不是最佳实践,原因如下:
- 构建效率低:每次构建都要连接数据库、生成代码,增加构建时间;
- 构建依赖外部服务:容易出现像你遇到的网络连接问题,构建过程不具备隔离性;
- 可重复性差:如果数据库结构变更,没有版本控制的话,生成的代码可能不一致。
推荐的最佳实践:
- 在本地或者CI/CD环境中,先连接数据库生成JOOQ代码,把生成的代码提交到版本控制系统;
- 镜像构建时直接使用已生成的代码,不需要再连接数据库,这样构建过程更快、更可靠,也不会依赖外部服务。
内容的提问来源于stack exchange,提问作者AmitBL

