Docker Compose部署Django+Postgres时SQLAlchemy连接数据库被拒如何解决
所有连接报错的根源是三个配置偏差,任意一个都能直接触发连接拒绝的错误:
- 容器网络场景下回环地址的使用逻辑错误
- 自定义SQLAlchemy引擎的连接配置没有和Django的数据库配置同步适配容器网络
- 服务启动逻辑没有等待数据库完全就绪,应用抢跑发起连接
主机地址配置完全适配容器网络模型
容器内部的网络语境下,127.0.0.1代表的是web应用容器自身的回环网卡,根本不是运行Postgres的db容器。docker-compose启动时会自动创建桥接网络,同一compose下的服务可以直接用服务名作为域名互相解析,db服务在web容器内可正常访问的主机名就是db,不需要写127.0.0.1。
你之前单独修改Django配置没解决问题,核心原因就是手写的自定义SQLAlchemy引擎是独立创建的连接逻辑,完全不读取Django的数据库配置参数,这部分连接串里的主机地址还是保留了本地运行时用的127.0.0.1,刚好是报错栈定位到的代码行,必须同步修改。
同时要注意:原配置里带空格的用户名my user、数据库名my database,在URL连接串中如果不做编码非常容易出现解析截断,建议改成不带空格的命名,比如myuser、mydatabase,和docker-compose里的环境变量保持一致。
修改前后的配置对比:
原SQLAlchemy引擎配置:engine = create_engine("postgresql://my user:my password@127.0.0.1:5432/my database")
修正后配置:engine = create_engine("postgresql://myuser:mypassword@db:5432/mydatabase")
对应的Django settings.py里的数据库配置,同步把HOST从127.0.0.1改成db,把带空格的NAME、USER值改成和compose环境变量一致的不带空格版本。修正web服务的监听绑定地址
原web服务启动命令写的是python3 manage.py runserver 127.0.0.1:8080,这个绑定规则在容器内代表服务只接收来自自身回环网卡的请求,完全不监听网桥的外部流量。哪怕compose里配置了8080:8080的端口映射,宿主机或者其他容器的请求也根本无法触达web服务。
把启动命令里的绑定地址改成0.0.0.0:8080,让服务监听容器内所有网卡的8080端口即可。补全服务启动的就绪校验逻辑
原compose里的depends_on: - db,只是保证compose会优先拉起db容器,根本不会等待Postgres完成初始化、可以正常接收连接再启动web服务。很容易出现web容器已经发起数据库连接的时候,db容器还在执行初始化流程,直接抛出连接拒绝的错误。
给db服务加健康检查规则,同时给web的依赖配置加上就绪等待条件,确保db完全可用再启动web,修正后的compose服务段参考:services: db: image: postgres:9.6 environment: - POSTGRES_DB=mydatabase - POSTGRES_USER=myuser - POSTGRES_PASSWORD=mypassword healthcheck: test: ["CMD-SHELL", "pg_isready -U myuser -d mydatabase"] interval: 5s timeout: 5s retries: 5 web: build: . command: python3 manage.py runserver 0.0.0.0:8080 volumes: - .:/code ports: - "8080:8080" depends_on: db: condition: service_healthy可选调试配置
如果你需要从宿主机的数据库客户端直接连接容器内的Postgres调试,可以给db服务加端口映射ports: - "5432:5432"。注意这个映射仅面向宿主机使用,web容器内部连接db时不需要走这个映射,直接用服务名db加内部5432端口就可以正常连通。
内容的提问来源于stack exchange,提问作者Sanewa

