多Docker服务下Django数据库跨连接问题及自动恢复情况
当前进展
Django现已可正确解析主机名db_service1,消除了数据库连接的歧义,但暂未定位到最初解析失效的原因。
问题场景
本地运行多个独立服务,每个服务都启动专属的Postgres Docker容器;另有一个"admin services"仓库,通过Docker Compose部署pgAdmin等管理工具,可连接所有运行中的服务,各服务已配置外部端口映射以支持本地同时运行。
为让admin services正常访问所有服务,我们给需要被访问的服务都加入了common网络。pgAdmin可以通过容器名db_service1、db_service2正常连接对应数据库,但Django的DATABASES["default"]["HOST"]配置最初只能识别Compose文件中的服务ID(所有服务的数据库服务ID均为db),无法直接使用容器名。
出现的错误
由于所有数据库容器在common网络内使用内部IP,且内部端口都是5432,启动service2后,service1出现数据库连接错误:
relation "django_session" does not exist
LINE 1: ...ession_data", "django_session"."expire_date" FROM "django_se...
推测是因为同处common网络,service1的Django应用意外连接到了service2的数据库。
已尝试的方案
- 修改DATABASES配置,通过
localhost及容器外部端口连接 - 将
DATABASES["default"]["HOST"]改为db_service1而非db(最初失效,当前已正常)
可行解决方案(无需修改服务名)
1. 为Django容器配置extra_hosts
在每个服务的Docker Compose文件中,给Django所在的web容器添加extra_hosts映射,将目标容器名指向当前Compose内的db服务:
services: web: # 其他配置项 extra_hosts: - "db_service1:db"
这样Django配置HOST: "db_service1"时,会被解析到当前服务专属的db容器,避免跨服务连接。
2. 给数据库服务添加网络别名
在每个数据库服务的Compose配置中,为common网络设置唯一别名,保留原服务名db的同时增加专属标识:
services: db: # 其他配置项 networks: default: common: aliases: - db_service1
配置后,数据库容器在common网络中同时拥有db和db_service1两个可访问的名称,Django直接配置HOST: "db_service1"即可精准连接到对应数据库。
3. 用环境变量动态注入数据库主机
在每个服务的Compose文件中设置专属环境变量,再在Django配置中读取该变量:
services: web: # 其他配置项 environment: - DB_HOST=db_service1
在settings.py中读取环境变量:
import os DATABASES = { "default": { # 其他数据库配置 "ENGINE": "django.db.backends.postgresql", "NAME": os.getenv("DB_NAME"), "USER": os.getenv("DB_USER"), "PASSWORD": os.getenv("DB_PASSWORD"), "HOST": os.getenv("DB_HOST", "db"), "PORT": os.getenv("DB_PORT", "5432"), } }
这种方式无需修改核心配置文件,每个服务可独立配置对应的数据库主机,灵活性更高。
内容的提问来源于stack exchange,提问作者michjnich

