M1 Mac下Docker run与Compose的Postgres-API连接差异排查
我在M1 Mac上通过Docker运行Postgres 14.2镜像,端口映射为5432:5432,主机端应用可通过localhost:5432连接数据库。现在尝试将API应用部署到Docker中,出现异常情况:
可行的docker run命令
使用以下命令可成功连接数据库:
docker run --name api --add-host host.docker.internal:host-gateway -e DB_HOST=host.docker.internal -p 8000:8000
第一种docker-compose配置(连接失败)
复制相同配置到docker-compose却无法连接:
services: postgres: image: postgres:14.2 ports: - "5432:5432" networks: - my-network api: image: api environment: DB_HOST: host.docker.internal extra_hosts: - "host.docker.internal:host-gateway"
报错信息:
failed to connect to
host=host.docker.internal user=postgres database=postgres: failed to receive message (unexpected EOF)
第二种docker-compose配置(连接失败)
尝试将API容器加入Postgres所在的my-network网络,将DB_HOST改为容器名:
api: image: api environment: DB_HOST: postgres networks: - my-network
报错信息:
failed to connect to
host=postgres user=postgres database=postgres: dial error (dial tcp 192.168.192.2:5432: connect: connection refused)
已知数据库监听地址为IPv4 address "0.0.0.0", port 5432和IPv6 address "::", port 5432,为何docker run命令可行,而另外两种docker-compose配置却无法正常连接?
1. docker run可行的核心原因
docker run启动的API容器默认使用Docker默认的bridge网络,通过--add-host host.docker.internal:host-gateway配置后,host.docker.internal会指向宿主机网关地址。此时API容器通过宿主机的端口映射5432:5432访问Postgres,和主机端应用的连接逻辑完全一致,因此可以成功建立连接。
2. 第一种docker-compose配置失败的原因
你的Postgres容器加入了自定义网络my-network,但API容器未指定网络,默认会使用docker-compose自动创建的项目名_default网络。此时API容器的host.docker.internal虽指向宿主机,但受M1 Mac的Docker网络特性限制,或Postgres的pg_hba.conf访问控制规则影响,通过宿主机转发连接时出现EOF错误。
修复方案:给API容器也指定my-network网络,确保网络环境一致:
services: postgres: image: postgres:14.2 ports: - "5432:5432" networks: - my-network api: image: api environment: DB_HOST: host.docker.internal extra_hosts: - "host.docker.internal:host-gateway" networks: - my-network # 添加该行,让API容器和Postgres在同一网络
3. 第二种docker-compose配置失败的原因
这种容器间直接通信的方式报错connection refused,通常由两个问题导致:
- Postgres未完全就绪:API容器启动速度快于Postgres,尝试连接时数据库服务还未初始化完成。
- Postgres访问控制限制:虽然监听地址设为
0.0.0.0,但pg_hba.conf可能仅允许本地(127.0.0.1)或宿主机IP段连接,拒绝了Docker自定义网络的IP段(如192.168.192.0/24)。
修复方案:
方案A:添加健康检查与启动依赖
确保Postgres完全就绪后再启动API:
services: postgres: image: postgres:14.2 ports: - "5432:5432" networks: - my-network environment: POSTGRES_PASSWORD: your_db_password # 必须设置密码,否则Postgres可能拒绝连接 healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 5s timeout: 5s retries: 5 api: image: api environment: DB_HOST: postgres DB_PASSWORD: your_db_password # 和Postgres配置的密码一致 networks: - my-network depends_on: postgres: condition: service_healthy # 等待Postgres健康检查通过后再启动
方案B:修改Postgres访问控制规则
在pg_hba.conf中添加允许Docker自定义网络连接的规则:
- 本地创建
pg_hba.conf文件,添加一行:
host all all 192.168.0.0/16 scram-sha-256
- 在docker-compose中挂载该配置文件:
services: postgres: image: postgres:14.2 ports: - "5432:5432" networks: - my-network environment: POSTGRES_PASSWORD: your_db_password volumes: - ./pg_hba.conf:/var/lib/postgresql/data/pg_hba.conf # 挂载自定义配置
内容的提问来源于stack exchange,提问作者BeetleJuice

