Docker中Django无法连接PostgreSQL及GitLab CI/CD测试连接失败问题
嘿,看来你碰到了两个和Django数据库连接相关的棘手问题——Docker容器里的应用连不上PostgreSQL,还有GitLab CI跑测试时也触发了连接拒绝的错误。我来帮你一步步拆解排查,给出具体的解决办法:
这个问题大概率是容器网络隔离导致的,毕竟容器里的localhost指的是容器自身,不是宿主机或者其他容器。给你几个核心排查方向:
用Docker Compose编排服务,统一网络
单独启动的容器默认不在同一个网络,没法直接用localhost通信。建议用Docker Compose把Django和PostgreSQL放在同一个自定义网络里,这样可以用服务名作为数据库主机名。比如写一个docker-compose.yml:version: '3.8' services: web: build: . command: python manage.py runserver 0.0.0.0:8000 ports: - "8000:8000" depends_on: - db environment: - DATABASE_HOST=db - POSTGRES_USER=django_user - POSTGRES_PASSWORD=django_pass - POSTGRES_DB=django_db db: image: postgres:14-alpine environment: - POSTGRES_USER=django_user - POSTGRES_PASSWORD=django_pass - POSTGRES_DB=django_db volumes: - postgres_data:/var/lib/postgresql/data/ volumes: postgres_data:然后在Django的
settings.py里,把数据库HOST改成os.getenv('DATABASE_HOST', 'localhost'),这样就能通过服务名db访问PostgreSQL容器了。确保PostgreSQL容器配置正确
检查PostgreSQL容器是否暴露了5432端口(如果不用Compose,需要加-p 5432:5432参数),同时环境变量POSTGRES_USER、POSTGRES_PASSWORD、POSTGRES_DB要和Django配置完全匹配。等待PostgreSQL完全启动
有时候Django容器启动速度比PostgreSQL快,数据库还没准备好就发起连接,会导致失败。可以给Django的启动脚本加个等待逻辑,比如用wait-for-it工具:wait-for-it db:5432 -- python manage.py runserver 0.0.0.0:8000
这个报错的核心是:GitLab Runner的运行环境里默认没有启动PostgreSQL服务,而且你配置里用了localhost,自然连不上。解决步骤如下:
在.gitlab-ci.yml中添加PostgreSQL服务
GitLab CI支持通过services字段引入数据库服务,这样Runner会自动启动一个PostgreSQL容器,和你的测试容器共享网络。比如修改你的.gitlab-ci.yml:stages: - test test: stage: test image: python:3.9-slim services: - name: postgres:14-alpine alias: postgres # 别名,方便Django用这个主机名连接 variables: # 给PostgreSQL服务设置环境变量 POSTGRES_USER: test_user POSTGRES_PASSWORD: test_pass POSTGRES_DB: test_db # 给Django设置数据库连接参数 DATABASE_HOST: postgres DATABASE_PORT: 5432 before_script: - apt-get update && apt-get install -y postgresql-client # 可选,用来测试连接 - pip install --upgrade pip - pip install -r requirements.txt # 可选:测试数据库是否可达 - psql -h $DATABASE_HOST -U $POSTGRES_USER -d $POSTGRES_DB -c "SELECT 1;" script: - python manage.py migrate - python manage.py test动态修改Django的数据库配置
在Django的settings.py里,不要硬编码localhost,而是通过环境变量读取主机名,这样在CI环境下自动切换到postgres:import os DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': os.getenv('POSTGRES_DB', 'default_test_db'), 'USER': os.getenv('POSTGRES_USER', 'test_user'), 'PASSWORD': os.getenv('POSTGRES_PASSWORD', 'test_pass'), 'HOST': os.getenv('DATABASE_HOST', 'localhost'), 'PORT': os.getenv('DATABASE_PORT', '5432'), } }检查Runner的执行器类型
如果你的GitLab Runner用的是shell执行器(直接在EC2实例上跑命令),那services字段不会生效,你需要在EC2实例上手动安装并启动PostgreSQL服务,然后修改Django配置连接到EC2的localhost。不过更推荐用docker执行器,这样配合services更灵活,也不会污染EC2实例环境。
内容的提问来源于stack exchange,提问作者Manish Gupta

