
本文详解 docker compose 环境下 python 应用(如使用 psycopg2)无法连接 postgresql 容器的根本原因:服务间通信应使用容器内网端口(5432),而非宿主机映射端口(6544),并提供可立即生效的修复方案。
本文详解 docker compose 环境下 python 应用(如使用 psycopg2)无法连接 postgresql 容器的根本原因:服务间通信应使用容器内网端口(5432),而非宿主机映射端口(6544),并提供可立即生效的修复方案。
在 Docker Compose 构建的多容器应用中,一个高频但易被忽视的网络误区是:混淆“宿主机端口映射”与“容器间通信端口”。你的 docker-compose.yml 中已正确定义了 PostgreSQL 服务:
postgres: image: postgres:latest environment: POSTGRES_USER: admin POSTGRES_PASSWORD: admin1234 POSTGRES_DB: db ports: - "6544:5432" # ← 宿主机 6544 → 容器内部 5432 networks: - my-network hostname: postgres
此处 ports: [“6544:5432”] 的含义是:将宿主机的 6544 端口转发到 PostgreSQL 容器监听的默认端口 5432。该映射仅对宿主机外部(如本地 pgAdmin、浏览器、curl 或本机运行的 Python 脚本)有效;而同一 my-network 网络内的其他容器(如 python-service)必须直接访问 postgres:5432 —— 因为它们通过 Docker 内置 DNS 解析 postgres 主机名,并直连容器 IP 的 5432 端口,不经过宿主机端口转发层。
因此,你原始代码中硬编码的连接配置:
con_db = psycopg2.connect( dbname="db", user="admin", password="admin1234", host="localhost", # ❌ 错误:在 python-service 容器内,localhost 指向自身,非 PostgreSQL port="6544" # ❌ 错误:6544 在容器内未监听,PostgreSQL 只监听 5432 )
存在两个关键问题:
立即学习“Python免费学习笔记(深入)”;
- host=”localhost”:在容器内,localhost 解析为 127.0.0.1(即 python-service 自身),而非 postgres 服务;
- port=”6544″:6544 是宿主机端口,PostgreSQL 容器内部实际监听的是 5432,该端口在容器网络中才可达。
✅ 正确做法是:
- 使用服务名作为 host(Docker DNS 自动解析);
- 使用容器内实际端口 5432;
- 通过环境变量注入配置,提升可维护性与安全性。
✅ 修复步骤
第一步:更新 docker-compose.yml 中 python-service 的环境变量
python-service: build: context: ./python-translate-api dockerfile: Dockerfile ports: - "5000:5000" depends_on: postgres: condition: service_completed_successfully environment: DB_HOST: postgres # ✅ 服务名,Docker 自动解析 DB_PORT: "5432" # ✅ 容器内真实端口 DB_NAME: db DB_USER: admin DB_PASSWORD: admin1234 networks: - my-network
第二步:重构 Python 连接逻辑,读取环境变量
import os import psycopg2 from psycopg2 import sql # 从环境变量安全读取配置(带默认值兜底,便于本地调试) DB_HOST = os.getenv("DB_HOST", "localhost") DB_PORT = os.getenv("DB_PORT", "5432") DB_NAME = os.getenv("DB_NAME", "db") DB_USER = os.getenv("DB_USER", "admin") DB_PASSWORD = os.getenv("DB_PASSWORD", "admin1234") try: con_db = psycopg2.connect( host=DB_HOST, port=DB_PORT, dbname=DB_NAME, user=DB_USER, password=DB_PASSWORD ) print(f"✅ 成功连接 PostgreSQL ({DB_HOST}:{DB_PORT})") except psycopg2.OperationalError as e: print(f"❌ 数据库连接失败: {e}") raise
? 提示:depends_on 仅确保容器启动顺序,不保证 PostgreSQL 服务已就绪。生产环境建议添加连接重试逻辑(例如使用 tenacity 库)或健康检查。
⚠️ 注意事项与最佳实践
- 禁止在容器内使用 localhost 访问同网络其他服务:localhost 永远指向当前容器,跨服务通信必须使用 service-name(如 postgres);
- 端口映射 ≠ 容器内端口:ports 字段仅影响宿主机访问,容器间通信永远使用 EXPOSE 或镜像默认端口(PostgreSQL 为 5432);
- 环境变量优先于硬编码:避免敏感信息泄露,也便于多环境切换;
- 验证网络连通性(调试时):进入 python-service 容器执行 ping postgres 和 nc -zv postgres 5432,确认 DNS 与端口可达;
- Java 能连而 Python 不能? 很可能 Java 服务也配置了 host=postgres + port=5432,或其 Dockerfile/启动脚本已正确适配容器网络。
✅ 总结
根本症结在于网络作用域理解偏差:6544 是给宿主机用的“入口”,而 5432 才是容器网络中的“真实地址”。修正 host 为服务名、port 为容器内端口,并配合环境变量管理,即可彻底解决 psycopg2 连接拒绝(Connection refused)问题。这一原则同样适用于 redis、rabbitmq 等所有 Docker 化中间件的客户端连接配置。