1. 项目概述为什么 Django Postgres Nginx Gunicorn 是生产环境的黄金组合你刚用django-admin startproject mysite跑通了本地开发服务器python manage.py runserver一敲浏览器里跳出 “The install worked successfully!”——那一刻很爽。但当你把代码推上服务器用python manage.py runserver 0.0.0.0:8000对外暴露端口不到两小时就发现页面加载变慢、并发稍高就 502、数据库连接数爆满、日志里全是Connection refused、连 admin 后台登录都卡三秒……这不是你的代码有问题而是你正在用“厨房里的烤箱”去干“食品工厂流水线”的活。这个标题——Set Up Django with Postgres, Nginx, and Gunicorn on Ubuntu——不是一份安装清单它是一套经过十年以上互联网中后台系统验证的生产级部署契约。Django 本身不负责高并发、不处理静态资源、不管理进程生命周期、也不做反向代理Postgres 不是 SQLite 的放大版它的连接池、事务隔离、WAL 日志、角色权限体系必须被真正启用Nginx 不只是“转发一下请求”它是第一道流量闸门、SSL 终结者、静态文件 CDN、健康检查哨兵Gunicorn 更不是runserver的马甲它是基于预叉pre-fork模型的 WSGI 应用服务器靠 worker 进程隔离、超时熔断、优雅重启来扛住真实用户请求。而 Ubuntu尤其是 22.04 LTS是这四者协同运转最稳定、文档最全、社区支持最及时的 Linux 发行版底座——不是因为“它最好”而是因为“它最不坑人”。我从 2013 年在创业公司用 Ubuntu 12.04 部署第一个 Django 1.4 项目开始踩过所有你能想到的坑Postgres 密码策略导致psql: error: connection to server on socket /var/run/postgresql/.s.PGSQL.5432 failedGunicorn worker timeout 和 Django 数据库连接泄漏叠加引发的雪崩式 503Nginx 配置里少写一个proxy_buffering off导致长轮询接口永远不返回甚至因为/etc/profile里export PGPASSWORD写错位置让 cron 任务连不上数据库却查不出错因。这些都不是理论问题是凌晨三点告警电话打进来时你必须在 90 秒内定位并修复的真实战场。所以这篇内容不讲“Django 是什么”不教“怎么写视图”只聚焦一件事如何让一套 Django Web 应用在 Ubuntu 服务器上像自来水一样稳定、可监控、可伸缩、可回滚地持续对外服务。适合正在准备上线第一个项目的 Python 初学者、刚接手运维职责的后端开发者、或是需要快速搭建内部管理系统的中小团队技术负责人。2. 整体架构设计与选型逻辑为什么是这四块拼图缺一不可2.1 四层分工各司其职拒绝越界很多人初学部署时总想“精简”能不能不用 Nginx能不能直接用 Django 自带服务器能不能把 Postgres 换成 SQLite答案很明确在生产环境不能。这不是教条主义而是由每一层的核心职责决定的。我们拆开看Django 层应用逻辑层只做业务。它接收 HTTP 请求、调用模型、执行业务规则、渲染模板或序列化 JSON。它不该管“这个请求该不该缓存”、“这个静态文件该走哪条 CDN 路径”、“这个连接要不要复用”、“这个进程挂了谁来拉起”。Django 的runserver是调试工具不是服务器——它单线程、无超时控制、无连接池、无日志分级启动时还会警告You have unapplied migrations. Your project may not work properly until you apply the migrations.这种提示在生产环境毫无意义。Gunicorn 层WSGI 网关层只做协议转换和进程管理。它把 HTTP 请求解析成 WSGI 标准格式喂给 Django再把 Django 返回的 WSGI 响应打包成 HTTP 响应发回去。更重要的是它用--workers 3 --worker-class sync --timeout 30 --keep-alive 5这类参数硬性约束每个 worker 的内存占用、响应时限、空闲连接保持时间。当某个视图因数据库锁死卡住 60 秒Gunicorn 会在--timeout设定的 30 秒后强制 kill 掉该 worker并用新进程顶上避免整个服务雪崩。这是runserver完全不具备的生存能力。Nginx 层反向代理与边缘网关层只做网络调度。它监听 80/443 端口把用户请求分发给 Gunicorn通常走 Unix socket 或 127.0.0.1:8000它把/static/和/media/开头的请求直接由自己读取磁盘返回绝不转发给 Django它用gzip on; gzip_types text/plain application/json;压缩响应体它用limit_req zoneapi burst10 nodelay;限流防刷它用ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;终结 HTTPS。如果让 Django 直接监听 443 端口等于把加密解密、证书管理、HTTP/2 协商这些重活全塞给 Python 进程CPU 会瞬间飙到 90%。Postgres 层数据持久层只做数据强一致存储。它用 MVCC 实现高并发读写不锁表用pg_hba.conf的host all all 127.0.0.1/32 md5规则精确控制本地连接认证方式用shared_buffers 256MB和work_mem 4MB这类参数为 Ubuntu 4GB 内存服务器定制内存分配用pg_dump -Fc -v -f backup.dump mydb生成可压缩、可并行恢复的二进制备份。而 SQLite 是文件锁级别多进程写入极易报database is locked根本无法支撑日活千人以上的系统。提示网上常有人问 “dbeaver 的 postgres 表在哪里”这暴露了一个根本误解——DBeaver 是客户端 GUI 工具它连接的是 Postgres 服务进程表结构和数据实际存在/var/lib/postgresql/14/main/base/下的二进制文件里但你永远不该手动修改这些文件。就像你不会用记事本打开 MySQL 的.ibd文件去改数据一样。所有操作必须通过 SQL 或psql命令行完成。2.2 Ubuntu 22.04 LTS为什么不是 CentOS、Debian 或 DockerUbuntu 22.04代号 Jammy Jellyfish是当前 LTS长期支持版本提供 5 年安全更新至 2027 年且包管理器apt对 Python 生态极其友好。对比其他选项CentOS Stream / Rocky Linux虽然稳定但默认仓库的python3-dev、libpq-dev版本老旧装psycopg2时经常报fatal error: Python.h: No such file or directory需额外编译安装新手极易卡在第一步。Debian 12 (Bookworm)更轻量但部分 Django 第三方包如django-compressor对 Debian 的libjpeg-dev依赖路径有兼容性问题报错信息晦涩难查。Docker热词里有 “ubuntu安装docker”但 Docker 是另一套复杂体系。对新手而言先搞懂原生部署再迁移到容器才是正向学习路径。强行一步到位遇到gunicorn: command not found时你分不清是镜像没装对还是 PATH 没配好还是 volume 挂载错了——问题面太广debug 成本翻倍。WSL / VMware 虚拟机热词里高频出现 “wsl安装ubuntu”、“vmware安装ubuntu”这说明大量开发者在本地 Windows/Mac 上用虚拟环境练手。完全正确但要注意WSL2 的网络栈和原生 Ubuntu 有差异localhost在 WSL2 里指向的是虚拟机 IP不是宿主机VMware 的 NAT 模式下Ubuntu 的ifconfig显示的eth0IP 是内网地址需额外配置端口转发才能从宿主机访问。这些细节不提前知道你会以为“部署成功了”结果浏览器打不开。所以我们的基准环境锁定为物理服务器或云主机阿里云/腾讯云上的 Ubuntu 22.04 Server 版最小配置 2 核 4GB 内存磁盘建议 40GB SSD。所有命令、路径、配置均以此为准不加任何“兼容旧版”的妥协。2.3 关键参数决策为什么是这些数字而不是别的部署不是填空题每个参数背后都有血泪教训。我们挑三个最常被乱写的参数说透Gunicorn worker 数量常见错误是--workers 10。实测下来对于 2 核 CPU--workers 32×CPU1最稳。为什么因为每个 worker 是独立 Python 进程会复制整个 Django 应用内存镜像。一个中等规模 Django 项目含 DRF、Celery、Redis client单 worker 内存占用约 80–120MB。10 个 worker 就是 1GB 内存加上 Postgres 的shared_buffers、Nginx 的 worker 进程4GB 内存很快耗尽触发 OOM Killer 杀进程。--workers 3在保证并发能力每 worker 可处理 4–5 个请求的同时留出足够内存给系统和其他服务。Postgres 连接池大小Django 默认CONN_MAX_AGE 0不复用连接但 Gunicorn worker 是长驻进程若每个请求都新建/关闭连接Postgres 的max_connections默认 100很快被占满。正确做法是在settings.py中设CONN_MAX_AGE 60并在DATABASES配置里加OPTIONS: {MAX_CONNS: 20}需django-db-geventpool支持或更简单——用pgbouncer做连接池代理。但对新手先确保CONN_MAX_AGE 0就能缓解 80% 的连接数问题。Nginx client_max_body_size默认是 1MB意味着用户上传超过 1MB 的图片就会 413 Request Entity Too Large。电商后台、CMS 系统必须调大。但也不能设成0无限制否则恶意用户发一个 10GB 的垃圾文件会把磁盘撑爆。经验是根据业务定图片上传设10M视频上传设500M并在location /upload/块里单独配置不影响其他接口。这些不是玄学是我在 7 个不同客户项目中用htop、pg_stat_activity、nginx -t、gunicorn --check-config逐项压测、监控、调整后沉淀下来的数字。它们经得起真实流量考验。3. 核心细节解析与实操要点从系统初始化到服务自启3.1 Ubuntu 系统初始化绕过所有新手陷阱别急着装软件。先让系统干净、安全、可维护。以下步骤必须按顺序执行跳过任意一步后面都会埋雷。第一步创建非 root 部署用户绝对禁止用root用户跑 Django 或 Gunicorn。创建专用用户webappsudo adduser webapp sudo usermod -aG sudo webapp # 切换过去确认身份 sudo su - webapp注意adduser带 u是交互式命令会自动建 home 目录、设密码useradd不带 u是底层命令不建目录新手易踩坑。-aG sudo是关键让webapp有sudo权限执行systemctl但又不等于root。第二步更新源并安装基础工具Ubuntu 22.04 默认源在国内可能慢换清华源安全非第三方# 备份原文件 sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak # 替换为清华源 sudo sed -i s/archive.ubuntu.com/mirrors.tuna.tsinghua.edu.cn/g /etc/apt/sources.list sudo sed -i s/security.ubuntu.com/mirrors.tuna.tsinghua.edu.cn/g /etc/apt/sources.list sudo apt update sudo apt upgrade -y # 安装必备工具 sudo apt install -y python3-pip python3-dev libpq-dev nginx curl git vim提示“ubuntu安装教程”里常漏掉libpq-dev。这是psycopg2编译时必需的 PostgreSQL C 语言头文件。没有它pip install psycopg2必报错pg_config executable not found然后新手会去搜 “ubuntu安装postgres”试图重装数据库——其实只需一行apt install libpq-dev。第三步配置防火墙UFWUbuntu 默认禁用 UFW但生产环境必须开sudo ufw allow OpenSSH sudo ufw allow Nginx Full # 开放 80/443 sudo ufw enable # 验证 sudo ufw status verbose注意“nginx安装”后很多人忘了开防火墙结果从外网 ping 得通但curl http://your-server-ip超时。UFW 是 Linux 内核 netfilter 的前端比云厂商的安全组更底层必须配。第四步设置时区与 localeDjango 时间字段依赖系统时区。sudo timedatectl set-timezone Asia/Shanghai同时确保locale支持 UTF-8sudo locale-gen en_US.UTF-8 sudo update-locale LANGen_US.UTF-8 # 验证 locale # 输出应含 LANGen_US.UTF-8提示热词里有 “ubuntu中文输入法怎么设置”但服务器不需要输入法locale错会导致 Djangodatetime.now()返回 UTC 时间而非本地时间timezone.now()也失效所有时间字段存错。3.2 Postgres 安装与安全配置不止是apt install postgresqlUbuntu 22.04 默认安装 Postgres 14无需手动编译。但默认配置极度不安全必须改第一步切换到 postgres 用户进入 psqlsudo su - postgres psql第二步创建专用数据库与用户非 postgres 超级用户-- 创建数据库名字即项目名如 mysite_db CREATE DATABASE mysite_db OWNER mysite_user; -- 创建用户设强密码用 12 位以上含大小写字母数字 CREATE USER mysite_user WITH PASSWORD StrongPass123!; -- 授予数据库连接权限 GRANT CONNECT ON DATABASE mysite_db TO mysite_user; -- 切换到该数据库授予权限 \c mysite_db GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO mysite_user; GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO mysite_user;重点热词里有 “insufficient privilege: 7 error: must be able to set role postgres”这正是因为用postgres用户连接但 Django settings 里写了USER: postgres而 Postgres 14 默认禁止普通用户SET ROLE postgres。解决方案就是——永远不要用 postgres 用户跑应用用上面创建的mysite_user。第三步配置 pg_hba.conf仅允本地连接退出 psql编辑配置文件sudo nano /etc/postgresql/*/main/pg_hba.conf在文件末尾添加一行# TYPE DATABASE USER ADDRESS METHOD host mysite_db mysite_user 127.0.0.1/32 md5然后重启服务sudo systemctl restart postgresql提示“postgres bash免输入密码 /etc/profile pg_password” 是危险操作export PGPASSWORD会把密码明文写进 shell history 和进程环境变量ps aux | grep postgres就能看见。正确做法是用.pgpass文件echo localhost:5432:mysite_db:mysite_user:StrongPass123! ~/.pgpass chmod 600 ~/.pgpass这样psql -U mysite_user -d mysite_db就不用输密码且密码不泄露。3.3 Django 项目结构与环境隔离为什么不用系统 Python把 Django 项目放在/home/webapp/mysite结构如下mysite/ ├── manage.py ├── mysite/ │ ├── __init__.py │ ├── settings.py # 生产环境专用 │ ├── urls.py │ └── wsgi.py # Gunicorn 加载入口 ├── static/ # collectstatic 输出目录 ├── media/ # 用户上传文件目录 └── requirements.txt关键动作用 venv 创建隔离环境cd /home/webapp python3 -m venv mysite_env source mysite_env/bin/activate # 升级 pip避免旧版 bug pip install --upgrade pip # 安装生产依赖注意requirements.txt 里要区分 dev 和 prod pip install -r mysite/requirements.txt注意“python3” 和 “python” 在 Ubuntu 22.04 中是两个命令。python默认未安装为避免 Python 2/3 混淆必须用python3。venv是标准库模块无需pip install virtualenv。Django settings.py 生产配置要点在mysite/settings.py末尾加一个if DEBUG False:分支import os from pathlib import Path # ... 原有配置 ... # 生产环境开关 DEBUG False ALLOWED_HOSTS [your-domain.com, 123.45.67.89] # 必须写全不能 [*] # 静态文件 STATIC_URL /static/ STATIC_ROOT os.path.join(BASE_DIR, static) # 媒体文件 MEDIA_URL /media/ MEDIA_ROOT os.path.join(BASE_DIR, media) # 数据库用上面创建的用户 DATABASES { default: { ENGINE: django.db.backends.postgresql, NAME: mysite_db, USER: mysite_user, PASSWORD: StrongPass123!, HOST: localhost, PORT: 5432, CONN_MAX_AGE: 60, # 关键复用连接 } } # 安全相关生产必须 SECRET_KEY os.environ.get(DJANGO_SECRET_KEY, your-secret-key-here) # 从环境变量读 SECURE_BROWSER_XSS_FILTER True SECURE_CONTENT_TYPE_NOSNIFF True SECURE_HSTS_SECONDS 31536000 SECURE_HSTS_INCLUDE_SUBDOMAINS True SECURE_HSTS_PRELOAD True SESSION_COOKIE_SECURE True CSRF_COOKIE_SECURE True提示“django 4.2 django rest framework如何安装使用” 是另一个话题但 DRF 的DEFAULT_AUTHENTICATION_CLASSES在生产环境要禁用SessionAuthentication改用TokenAuthentication或 JWT避免 session 存服务器内存。3.4 Gunicorn 配置不只是gunicorn mysite.wsgi:applicationGunicorn 需要 systemd 服务文件实现开机自启、崩溃自拉起、日志归集。创建/etc/systemd/system/gunicorn.service[Unit] DescriptionGunicorn daemon for mysite Requiresgunicorn.socket Afternetwork.target [Service] Typenotify Userwebapp Groupwww-data WorkingDirectory/home/webapp/mysite ExecStart/home/webapp/mysite_env/bin/gunicorn \ --access-logfile /home/webapp/mysite/logs/gunicorn_access.log \ --error-logfile /home/webapp/mysite/logs/gunicorn_error.log \ --log-level debug \ --bind unix:/run/gunicorn.sock \ --bind 127.0.0.1:8000 \ --workers 3 \ --worker-class sync \ --timeout 30 \ --keep-alive 5 \ --max-requests 1000 \ --max-requests-jitter 100 \ --preload \ mysite.wsgi:application [Install] WantedBymulti-user.target解析关键参数--bind unix:/run/gunicorn.sockUnix socket 比 TCP 快 20%Nginx 直接读这个文件。--preload在 fork worker 前先加载 Django 应用节省内存避免每个 worker 重复 import。--max-requests 1000每个 worker 处理 1000 个请求后自动重启防止内存泄漏累积。Typenotify要求 Gunicorn 启动后发READY1信号给 systemd确保服务状态准确。创建日志目录并赋权sudo mkdir -p /home/webapp/mysite/logs sudo chown -R webapp:www-data /home/webapp/mysite/logs3.5 Nginx 配置从裸奔到企业级防护Nginx 主配置/etc/nginx/sites-available/mysiteupstream django_app { server unix:/run/gunicorn.sock fail_timeout0; # server 127.0.0.1:8000 fail_timeout0; # 备用 TCP 方式 } server { listen 80; server_name your-domain.com www.your-domain.com; return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; server_name your-domain.com www.your-domain.com; # SSL 证书用 Lets Encrypt ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem; include /etc/letsencrypt/options-ssl-nginx.conf; ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # 静态文件由 Nginx 直接服务 location /static/ { alias /home/webapp/mysite/static/; expires 1y; add_header Cache-Control public, immutable; } location /media/ { alias /home/webapp/mysite/media/; expires 1y; } # 其他请求转发给 Gunicorn location / { include proxy_params; proxy_pass http://django_app; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_buffering off; # 关键避免长响应卡住 proxy_read_timeout 60; } # 防爬虫基础规则 if ($http_user_agent ~* sqlmap|nikto|wget|curl) { return 403; } }提示“nginx配置文件详解” 里常忽略proxy_buffering off。Django 流式响应如 CSV 导出、SSE需要实时推送若开启 bufferingNginx 会攒够 4KB 才发导致前端一直等待。proxy_read_timeout 60是给 Gunicorn 的响应时间上限必须 Gunicorn 的--timeout。启用站点并测试sudo ln -sf /etc/nginx/sites-available/mysite /etc/nginx/sites-enabled/mysite sudo nginx -t # 必须通过 sudo systemctl restart nginx4. 实操过程与核心环节实现从零到上线的完整链路4.1 初始化 Django 项目与数据库迁移以webapp用户身份操作cd /home/webapp # 拉取代码假设用 Git git clone https://github.com/yourname/mysite.git cd mysite # 激活虚拟环境 source ../mysite_env/bin/activate # 安装依赖确保 requirements.txt 包含 gunicorn, psycopg2-binary pip install -r requirements.txt # 创建 logs 目录 mkdir -p logs # 运行迁移首次 python manage.py migrate # 创建超级用户用于 admin python manage.py createsuperuser # 收集静态文件Nginx 要用 python manage.py collectstatic --noinput注意“怎么运行django框架项目” 的答案不是runserver而是gunicorn mysite.wsgi:application --bind 0.0.0.0:8000。但此时不要直接运行要先配好 systemd。4.2 启动 Gunicorn 服务并验证启用并启动 Gunicornsudo systemctl daemon-reload sudo systemctl enable gunicorn sudo systemctl start gunicorn # 查看状态 sudo systemctl status gunicorn # 查看日志实时 sudo journalctl -u gunicorn -f正常输出应含Started Gunicorn daemon for mysite. Listening at: unix:/run/gunicorn.sock Booting workers with pid: 1234验证 socket 文件是否存在ls -l /run/gunicorn.sock # 应显示srw-rw---- 1 webapp www-data 0 ...提示如果status显示failed90% 是权限问题。检查/run/gunicorn.sock所属用户是否为webapp:www-data/home/webapp/mysite目录权限是否为755manage.py是否可执行chmod x manage.py。4.3 配置 Lets Encrypt 免费 SSL 证书用 Certbot 自动获取 HTTPSsudo apt install -y certbot python3-certbot-nginx sudo certbot --nginx -d your-domain.com -d www.your-domain.comCertbot 会自动修改 Nginx 配置添加 SSL 相关指令并设置自动续期定时任务。验证sudo certbot renew --dry-run # 应输出Congratulations, all simulated renewals succeeded.提示“nginx反向代理” 和 “nginx安装配置 linux” 是基础但 SSL 是生产环境底线。没有 HTTPS现代浏览器会标 “不安全”Chrome 甚至会阻止混合内容HTTP 资源在 HTTPS 页面加载。4.4 全链路压力测试与监控基线部署完不是终点要验证是否真能扛住流量。用abApache Bench模拟并发# 测试首页100 并发1000 次请求 ab -n 1000 -c 100 http://your-domain.com/ # 关键看Requests per second应 100、Time per request应 100ms、Failed requests应为 0同时监控资源# 实时看 CPU、内存 htop # 看 Postgres 连接数 sudo -u postgres psql -c SELECT * FROM pg_stat_activity WHERE state active; # 看 Nginx 请求速率 sudo tail -f /var/log/nginx/access.log | awk {print $1} | sort | uniq -c | sort -nr | head -20实操心得我第一次上线时ab测试显示Failed requests: 120。排查发现是gunicorn --timeout 30太短而某个报表视图查询慢。解决方案不是加 timeout而是优化 SQL 加索引。部署是手段性能是目标不能本末倒置。4.5 “修改 py 代码自动重启” 的正确姿势热词里高频问 “gunicorn 修改py代码自动重启”但生产环境严禁用--reload因为--reload会监控所有.py文件包括settings.py一旦改错配置Gunicorn 会反复崩溃重启。它用inotify在高 IO 服务器上可能漏事件导致不重启。它不支持 graceful shutdown正在处理的请求会被中断。正确流程是修改代码git commit -m fix buggit pull拉取最新代码python manage.py migrate如有新 migrationpython manage.py collectstatic --noinputsudo systemctl restart gunicornsystemd 会发送SIGTERM给主进程Gunicorn 先停止接受新请求等所有 worker 处理完当前请求后再退出全程无请求丢失。提示可以写一个一键部署脚本deploy.sh把步骤 2–5 封装起来每次上线只需./deploy.sh。这才是工程师该有的效率。5. 常见问题与排查技巧实录那些凌晨三点的告警电话5.1 典型问题速查表现象可能原因快速验证命令解决方案浏览器打不开显示This site can’t be reachedNginx 未运行或防火墙拦截sudo systemctl status nginxsudo ufw statussudo systemctl start nginxsudo ufw allow Nginx Full打开页面显示502 Bad GatewayGunicorn 未运行或 socket 文件权限错误sudo systemctl status gunicornls -l /run/gunicorn.socksudo systemctl start gunicornsudo chown webapp:www-data /run/gunicorn.sockAdmin 登录后 500日志报relation auth_user does not exist数据库未迁移sudo -u webapp python /home/webapp/mysite/manage.py migrate运行迁移命令静态文件 404但collectstatic已执行Nginxalias路径错误或STATIC_ROOT配置错ls /home/webapp/mysite/static/admin/检查settings.py中STATIC_ROOT确保alias指向STATIC_ROOT目录且目录存在psql: error: connection to server on socket /var/run/postgresql/.s.PGSQL.5432 failedPostgres 服务未启动或pg_hba.conf拒绝连接sudo systemctl status postgresqlsudo cat /etc/postgresql/*/main/pg_hba.conf | grep mysite_usersudo systemctl start postgresql确认pg_hba.conf有host mysite_db mysite_user 127.0.0.1/32 md55.2 独家避坑技巧书本里不会写的实战经验技巧一用strace抓 Gunicorn 启动失败的根因有时systemctl status gunicorn只显示failed日志为空。这时用stracesudo strace -f -e traceexecve -s 1000 /home/webapp/mysite_env/bin/gunicorn --bind unix:/run/gunicorn.sock mysite.wsgi:application它会打印出 Gunicorn 尝试执行的每一个系统调用。如果看到execve(/home/webapp/mysite_env/bin/python, ...)后报No such file or directory说明虚拟环境损坏需重装venv。技巧二Nginx 502 时用netstat确认 socket 是否被监听sudo netstat -tulpn \| grep :8000 # 看 TCP 端口 sudo netstat -tulpn \| grep gunicorn.sock # 看 Unix socket如果netstat没输出证明 Gunicorn 根本没 bind 成功不是 Nginx 配置问题。技巧三Postgres 连接数爆满用pg_terminate_backend()紧急清理当psql -c SELECT * FROM pg_stat_activity;显示上百个idle in transaction状态时执行SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE state idle in transaction AND now() - state_change interval 5 minutes;这会杀掉空闲超 5 分钟的事务连接立竿见影释放连接数。技巧四Django 日志不输出到文件检查LOGGING配置和权限在settings.py中确保LOGGING { version: 1, disable_existing_loggers: False, handlers: { file: { level: INFO, class: logging.FileHandler, filename: /home/webapp/mysite/logs/django.log, }, }, loggers: { django: { handlers: [file],