添加PostgreSQL后Docker中Gunicorn无法启动的问题求助
问题概述
已成功实现Django+Gunicorn+Nginx的Docker部署,添加PostgreSQL后出现异常:Postgres容器可正常启动,但Gunicorn容器无法启动,日志显示连接127.0.0.1:5432被拒绝。修改settings中的PostgreSQL主机IP后仅改变错误信息,问题未解决。
错误日志
C:\Users\stephane.bernardelli>docker logs testapp-django_gunicorn-1 Traceback (most recent call last): File "/usr/local/lib/python3.10/site-packages/django/db/backends/base/base.py", line 244, in ensure_connection self.connect() File "/usr/local/lib/python3.10/site-packages/django/utils/asyncio.py", line 26, in inner return func(*args, **kwargs) File "/usr/local/lib/python3.10/site-packages/django/db/backends/base/base.py", line 225, in connect self.connection = self.get_new_connection(conn_params) File "/usr/local/lib/python3.10/site-packages/django/utils/asyncio.py", line 26, in inner return func(*args, **kwargs) File "/usr/local/lib/python3.10/site-packages/django/db/backends/postgresql/base.py", line 203, in get_new_connection connection = Database.connect(**conn_params) File "/usr/local/lib/python3.10/site-packages/psycopg2/__init__.py", line 122, in connect conn = _connect(dsn, connection_factory=connection_factory, **kwasync) psycopg2.OperationalError: connection to server at "127.0.0.1", port 5432 failed: Connection refused Is the server running on that host and accepting TCP/IP connections? The above exception was the direct cause of the following exception: Traceback (most recent call last): File "/app/manage.py", line 22, in <module> main() File "/app/manage.py", line 18, in main execute_from_command_line(sys.argv) File "/usr/local/lib/python3.10/site-packages/django/core/management/__init__.py", line 446, in execute_from_command_line utility.execute() File "/usr/local/lib/python3.10/site-packages/django/core/management/__init__.py", line 440, in execute self.fetch_command(subcommand).run_from_argv(self.argv) File "/usr/local/lib/python3.10/site-packages/django/core/management/base.py", line 414, in run_from_argv self.execute(*args, **cmd_options) File "/usr/local/lib/python3.10/site-packages/django/core/management/base.py", line 460, in execute output = self.handle(*args, **options) File "/usr/local/lib/python3.10/site-packages/django/core/management/base.py", line 98, in wrapped res = handle_func(*args, **kwargs) File "/usr/local/lib/python3.10/site-packages/django/core/management/commands/migrate.py", line 91, in handle self.check(databases=[database]) File "/usr/local/lib/python3.10/site-packages/django/core/management/base.py", line 487, in check all_issues = checks.run_checks( File "/usr/local/lib/python3.10/site-packages/django/core/checks/registry.py", line 88, in run_checks new_errors = check(app_configs=app_configs, databases=databases) File "/usr/local/lib/python3.10/site-packages/django/core/checks/model_checks.py", line 36, in check_all_models errors.extend(model.check(**kwargs)) File "/usr/local/lib/python3.10/site-packages/django/db/models/base.py", line 1461, in check *cls._check_indexes(databases), File "/usr/local/lib/python3.10/site-packages/django/db/models/base.py", line 1864, in _check_indexes connection.features.supports_covering_indexes File "/usr/local/lib/python3.10/site-packages/django/utils/functional.py", line 49, in __get__ res = instance.__dict__[self.name] = self.func(instance) File "/usr/local/lib/python3.10/site-packages/django/db/backends/postgresql/features.py", line 84, in is_postgresql_11 return self.connection.pg_version >= 110000 File "/usr/local/lib/python3.10/site-packages/django/utils/functional.py", line 49, in __get__ res = instance.__dict__[self.name] = self.func(instance) File "/usr/local/lib/python3.10/site-packages/django/db/backends/postgresql/base.py", line 354, in pg_version with self.temporary_connection(): File "/usr/local/lib/python3.10/contextlib.py", line 135, in __enter__ return next(self.gen) File "/usr/local/lib/python3.10/site-packages/django/db/backends/base/base.py", line 639, in temporary_connection with self.cursor() as cursor: File "/usr/local/lib/python3.10/site-packages/django/utils/asyncio.py", line 26, in inner return func(*args, **kwargs) File "/usr/local/lib/python3.10/site-packages/django/db/backends/base/base.py", line 284, in cursor return self._cursor() File "/usr/local/lib/python3.10/site-packages/django/db/backends/base/base.py", line 260, in _cursor self.ensure_connection() File "/usr/local/lib/python3.10/site-packages/django/utils/asyncio.py", line 26, in inner return func(*args, **kwargs) File "/usr/local/lib/python3.10/site-packages/django/db/backends/base/base.py", line 243, in ensure_connection with self.wrap_database_errors: File "/usr/local/lib/python3.10/site-packages/django/db/utils.py", line 91, in __exit__ raise dj_exc_value.with_traceback(traceback) from exc_value File "/usr/local/lib/python3.10/site-packages/django/db/backends/base/base.py", line 244, in ensure_connection self.connect() File "/usr/local/lib/python3.10/site-packages/django/utils/asyncio.py", line 26, in inner return func(*args, **kwargs) File "/usr/local/lib/python3.10/site-packages/django/db/backends/base/base.py", line 225, in connect self.connection = self.get_new_connection(conn_params) File "/usr/local/lib/python3.10/site-packages/django/utils/asyncio.py", line 26, in inner return func(*args, **kwargs) File "/usr/local/lib/python3.10/site-packages/django/db/backends/postgresql/base.py", line 203, in get_new_connection connection = Database.connect(**conn_params) File "/usr/local/lib/python3.10/site-packages/psycopg2/__init__.py", line 122, in connect conn = _connect(dsn, connection_factory=connection_factory, **kwasync) django.db.utils.OperationalError: connection to server at "127.0.0.1", port 5432 failed: Connection refused Is the server running on that host and accepting TCP/IP connections?
配置文件
docker-compose.yml
version: '3.8' services: rabbitmq3: image: rabbitmq:3-alpine ports: - 5672:5672 networks: - main postgres: container_name: postgres hostname: postgres image: postgres:latest environment: - POSTGRES_USER=postgres - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} - POSTGRES_DB=Scripts Application networks: - main ports: - "5432:5432" restart: on-failure volumes: - postgresql-data:/var/lib/postgresql/data django_gunicorn: volumes: - static:/static - media:/media env_file: - env build: context: . ports: - "8000:8000" command: sh -c "python manage.py migrate && python manage.py collectstatic --no-input && gunicorn main.wsgi:application --bind 0.0.0.0:8000" depends_on: - postgres networks: - main nginx: build: ./nginx volumes: - static:/static - media:/media ports: - "80:80" depends_on: - django_gunicorn networks: - main celery: restart: always build: context: . command: celery -A main worker -P eventlet -c 100 -l INFO env_file: - env depends_on: - rabbitmq3 - django_gunicorn - postgres networks: - main networks: main: volumes: postgresql-data: static: media:
Dockerfile
FROM python:3.10.5-alpine ENV PYTHONUNBEFFERED = 1 RUN pip install --upgrade pip COPY ./requirements.txt . RUN \ apk add --no-cache postgresql-libs && \ apk add --no-cache --virtual .build-deps gcc musl-dev postgresql-dev && \ python3 -m pip install -r requirements.txt --no-cache-dir && \ apk --purge del .build-deps COPY ./src /app WORKDIR /app
排查与解决步骤
1. 修正Django数据库连接的HOST配置
在Docker Compose的同一网络中,容器之间可以通过服务名直接通信。你的PostgreSQL服务名为postgres,因此Django的数据库配置中HOST必须设置为postgres,而非127.0.0.1或其他IP地址。
修改settings.py中的数据库配置:
import os DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': os.environ.get('POSTGRES_DB', 'Scripts Application'), 'USER': os.environ.get('POSTGRES_USER', 'postgres'), 'PASSWORD': os.environ.get('POSTGRES_PASSWORD'), 'HOST': 'postgres', # 关键:使用服务名作为主机 'PORT': '5432', } }
建议通过环境变量读取配置,保持与docker-compose.yml的一致性,避免硬编码。
2. 解决容器启动顺序与数据库就绪问题
depends_on仅保证Postgres容器先启动,但不保证数据库服务已完全就绪(比如初始化完成)。Gunicorn容器启动后立即执行migrate,此时Postgres可能还未准备好接受连接,导致失败。
方案:添加等待脚本
在django_gunicorn的启动命令中加入等待逻辑,直到Postgres服务就绪再执行后续操作:
- 首先在Dockerfile中安装PostgreSQL客户端工具(用于
pg_isready命令):FROM python:3.10.5-alpine ENV PYTHONUNBUFFERED=1 RUN pip install --upgrade pip COPY ./requirements.txt . RUN \ apk add --no-cache postgresql-libs postgresql-client && \ # 添加postgresql-client apk add --no-cache --virtual .build-deps gcc musl-dev postgresql-dev && \ python3 -m pip install -r requirements.txt --no-cache-dir && \ apk --purge del .build-deps COPY ./src /app WORKDIR /app - 修改
docker-compose.yml中django_gunicorn的command:command: sh -c "until pg_isready -h postgres -p 5432 -U postgres; do sleep 2; done; python manage.py migrate && python manage.py collectstatic --no-input && gunicorn main.wsgi:application --bind 0.0.0.0:8000"
这段命令会每隔2秒检查一次Postgres是否就绪,直到连通后再执行迁移和启动Gunicorn。
3. 检查数据库名称的空格问题
你的POSTGRES_DB设置为Scripts Application(包含空格),虽然PostgreSQL支持带空格的数据库名,但可能在某些场景下引发解析问题。建议将其修改为下划线分隔的格式:
- 在
docker-compose.yml中修改:environment: - POSTGRES_USER=postgres - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} - POSTGRES_DB=Scripts_Application - 同步修改
settings.py中的NAME配置,或通过环境变量统一设置。
4. 验证容器网络连通性
如果以上步骤仍未解决问题,可手动验证Gunicorn容器与Postgres容器的网络连通性:
- 进入Gunicorn容器:
docker exec -it testapp-django_gunicorn-1 sh - 尝试Ping Postgres服务:
如果能Ping通,说明网络连接正常;如果不通,检查Docker网络配置是否正确(确保两个容器都在ping postgresmain网络中)。 - 测试端口连通性:
如果连接成功,说明Postgres端口开放正常;如果失败,检查Postgres容器是否正常监听5432端口。telnet postgres 5432
内容的提问来源于stack exchange,提问作者Balizok

