Docker部署的Django应用启动后HTTP请求响应缓慢求助
问题描述
本地运行Docker化的Django+Postgres应用,启动后首次访问任何localhost URL都需要3-4分钟才能响应,之后恢复正常(响应时间100-200ms),特征如下:
- 无重型进程运行,所有URL(admin、swagger等)均出现该情况
- 卡顿期间CPU负载明显升高,新旧机器都存在,排除硬件问题
- 部署环境无此问题
- 卡顿期间
docker-compose exec或run命令仍正常工作
注:可提供线程转储或其他日志,目前无明确排查方向
docker-compose配置(local.yml)
version: '3' volumes: backend_local_postgres_data: {} backend_local_postgres_data_backups: {} services: django: &django build: context: . dockerfile: ./compose/local/django/Dockerfile image: backend_local_django container_name: backend_local_django depends_on: - postgres volumes: - .:/app:z env_file: - ./.envs/.local/.django - ./.envs/.local/.postgres ports: - "8000:8000" command: /start postgres: build: context: . dockerfile: ./compose/production/postgres/Dockerfile image: backend_production_postgres container_name: backend_local_postgres volumes: - backend_local_postgres_data:/var/lib/postgresql/data:Z - backend_local_postgres_data_backups:/backups:z env_file: - ./.envs/.local/.postgres
应用启动日志
(venv) docker-compose -f local.yml up [+] Running 1/1 - Network backend_default Created 0.6s [+] Running 3/34T19:42:41+01:00" level=warning msg="mount of type `volume` should not define `bind` option" - Network backed_default Created 0.6s - Container backend_local_postgres Created 0.1s - Container backend_local_django Created 0.1s Attaching to backend_local_django, backend_local_postgres | | PostgreSQL Database directory appears to contain a database; Skipping initialization | | 2023-02-04 18:42:42.783 UTC [1] LOG: starting PostgreSQL 14.6 (Debian 14.6-1.pgdg110+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 10.2.1-6) 10.2.1 20210110, 64-bit | 2023-02-04 18:42:42.783 UTC [1] LOG: listening on IPv4 address "0.0.0.0", port 5432 | 2023-02-04 18:42:42.783 UTC [1] LOG: listening on IPv6 address "::", port 5432 | 2023-02-04 18:42:42.792 UTC [1] LOG: listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432" | 2023-02-04 18:42:42.804 UTC [27] LOG: database system was shut down at 2023-02-01 11:55:22 UTC | 2023-02-04 18:42:42.814 UTC [1] LOG: database system is ready to accept connections | PostgreSQL is available | Operations to perform: | Apply all migrations: account, admin, auth, authtoken, constructor, contenttypes, django_celery_beat, sessions, sites, socialaccount, users | Running migrations: | No migrations to apply. | WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. | * Running on all addresses (0.0.0.0) | * Running on http://127.0.0.1:8000 | * Running on http://172.18.0.3:8000 | Press CTRL+C to quit | * Restarting with watchdog (inotify) | Performing system checks... | | System check identified no issues (0 silenced). | | Django version 4.0.8, using settings 'config.settings.local' | Development server is running at http://0.0.0.0:8000/ | Using the Werkzeug debugger (http://werkzeug.pocoo.org/) | Quit the server with CONTROL-C. | * Debugger is active! | * Debugger PIN: 130-235-671
依赖包配置(requirements.txt)
base.txt
pytz==2022.6 python-slugify==7.0.0 Pillow==9.3.0 argon2-cffi==21.3.0 redis==4.3.5 hiredis==2.0.0 celery==5.2.7 django-celery-beat==2.4.0 flower==1.2.0 # Django django==4.0.8 django-environ==0.9.0 django-model-utils==4.3.1 django-allauth==0.51.0 django-crispy-forms==1.14.0 crispy-bootstrap5==0.7 django-redis==5.2.0 # Django REST Framework djangorestframework==3.14.0 django-cors-headers==3.13.0 djangorestframework-simplejwt==5.2.2 drf-yasg==1.21.4
local.txt
-r base.txt Werkzeug[watchdog]==2.2.2 ipdb==0.13.9 psycopg2-binary==2.9.5 watchfiles==0.18.1 # Testing mypy==0.982 django-stubs==1.12.0 pytest==7.2.0 pytest-sugar==0.9.6 djangorestframework-stubs==1.7.0 # Documentation sphinx==5.3.0 sphinx-autobuild==2021.3.14 # Code quality flake8==5.0.4 flake8-isort==5.0.3 coverage==6.5.0 black==22.10.0 pylint-django==2.5.3 pylint-celery==0.3 pre-commit==2.20.0 # Django factory-boy==3.2.1 django-debug-toolbar==3.7.0 django-extensions==3.2.1 django-coverage-plugin==2.0.4 pytest-django==4.5.2
排查与解决方案
结合症状,重点排查以下几个方向:
1. Django首次启动的代码预热与模块加载
首次请求时Django会懒加载所有相关模块,若项目依赖多、模块复杂,可能导致首次加载耗时过长,CPU升高。
- 解决办法:
- 在启动脚本中添加预热逻辑,比如启动后立即发送一个HEAD请求到首页或admin页面,触发模块预加载
- 优化settings中的
INSTALLED_APPS,移除本地开发不需要的应用 - 检查是否有自定义中间件或信号在首次请求时执行了耗时操作
2. Docker文件系统绑定挂载的性能问题
本地开发中使用.:/app绑定挂载,Docker在同步大量文件时可能产生性能瓶颈,尤其是首次访问时需要读取大量静态文件或模块文件。
- 解决办法:
- 添加
.dockerignore文件,排除不必要的文件(如__pycache__、.git、日志文件等),减少文件同步量 - 尝试使用Docker卷代替绑定挂载,或调整Docker文件共享设置(如Docker Desktop中启用高性能文件共享选项)
- 添加
3. PostgreSQL连接与初始化延迟
虽然日志显示PostgreSQL已就绪,但首次连接时可能存在隐式的数据库初始化或连接池建立过程。
- 解决办法:
- 检查Django数据库配置中的
CONN_MAX_AGE,设置为合理值(如60),避免每次请求重新建立连接 - 在启动脚本中使用
wait-for-it等工具,等待PostgreSQL完全就绪后再启动Django,而不仅仅依赖depends_on
- 检查Django数据库配置中的
4. Werkzeug调试服务器的性能问题
本地开发使用的Werkzeug调试服务器并非生产级服务器,首次请求时会执行额外的调试检查、代码重载准备等操作。
- 解决办法:
- 替换为
gunicorn作为本地开发服务器,测试是否还存在首次卡顿问题 - 在启动命令中添加
--noreload,关闭Werkzeug的自动重载功能
- 替换为
5. 依赖包的初始化开销
部分依赖包在首次导入时会执行耗时操作(如redis连接初始化、celery配置加载等)。
- 解决办法:
- 检查
settings.py中是否有在模块级别执行的耗时操作(如提前初始化客户端、加载大量数据),将这些逻辑延迟到首次请求或异步执行 - 暂时注释掉非必要的依赖包(如celery、flower),测试是否卡顿消失,逐步排查定位问题包
- 检查
排查工具建议
- 使用
docker stats查看卡顿期间容器的CPU、内存使用情况,确认是Django容器还是PostgreSQL容器导致CPU升高 - 在Django中添加日志,记录首次请求各阶段的耗时(如中间件处理、视图渲染、数据库查询等)
- 使用
py-spy生成线程转储,查看卡顿期间Django进程正在执行的函数
内容的提问来源于stack exchange,提问作者Lev Slinsen
相关产品推荐
相关产品推荐

