1. 项目概述:为什么一个 Django 应用必须认真对待“尺寸”与“防护”
你写完一个 Django 项目,本地 python manage.py runserver 跑得飞快,API 返回秒级响应,前端页面加载丝滑——这很美好。但当你把它扔进生产环境,面对真实用户、真实流量、真实攻击者时,这种“美好”往往在三分钟内崩塌:CPU 突然飙到 98%,Nginx 返回 502 Bad Gateway,Let’s Encrypt 自动续期失败导致全站 HTTPS 中断,Docker 容器反复重启,日志里满屏 Connection refused 和 timeout 。这不是玄学,是典型的“未维度化 + 未防护”组合拳打出来的结果。
所谓 dimensionner (法语,意为“确定规模、设定规格”),在 Django 生产部署中,绝不是简单地选台 4 核 8G 的云服务器就完事。它是一套系统性工程判断:你的应用每秒要处理多少并发请求?每个请求平均消耗多少内存?数据库连接池该开多大?Docker 容器的 CPU 限额和内存限制设多少才既防 OOM 又不浪费资源?静态文件缓存策略怎么配才能让 Nginx 真正扛住流量?这些参数不是拍脑袋定的,而是基于压测数据、资源监控曲线和业务增长模型推演出来的。我见过太多团队,把开发机配置直接照搬到生产,结果上线三天就扩容两次,每次都是半夜被告警电话叫醒。
而 sécuriser (防护),更不是加个 SECURE_SSL_REDIRECT = True 就高枕无忧。它是一道立体防线:Docker 层面要禁用特权模式、挂载只读文件系统、使用非 root 用户运行;Nginx 层面要过滤恶意 User-Agent、限制请求频率、隐藏版本号、强制 HSTS;Let’s Encrypt 不只是“装上证书”,更要确保 ACME 协议通信走专用端口、私钥权限严格为 600 、续期脚本有失败回滚机制;Django 层面则要关闭调试模式、设置 ALLOWED_HOSTS 为精确域名列表、启用 X-Content-Type-Options 等安全头。任何一环松动,都可能成为攻击入口。
这个标题直指现代 Web 应用交付的核心矛盾: 功能交付速度 vs. 系统稳定性边界 。它面向的不是刚学完 pip install django 的新手,而是已经能写出完整 CRUD、正准备把第一个真实项目推向用户的中级开发者,或是负责技术选型、需要向运维/架构团队解释方案合理性的后端负责人。你不需要从零造轮子,但必须清楚每个组件在整条链路上承担什么职责、暴露什么风险、又如何协同加固。接下来的内容,就是我过去三年在电商后台、SaaS 平台、教育管理系统等十余个 Django 生产项目中,踩过坑、调过参、熬过夜后沉淀下来的实操手册——没有理论堆砌,只有可抄、可改、可验证的具体步骤和参数依据。
2. 整体架构设计与核心组件选型逻辑
2.1 为什么是 Docker + Nginx + Let’s Encrypt 这个铁三角?
先说结论:这不是为了“时髦”,而是当前 Linux 服务器环境下, 成本、安全、可维护性、自动化程度四者平衡后的最优解 。我们来逐层拆解这个选择背后的硬逻辑。
Docker 的核心价值,在于 环境隔离与部署一致性 。Django 项目依赖 Python 版本、特定 C 库(如 libpq 用于 PostgreSQL)、编译型依赖(如 Pillow 的 libjpeg )。在 Ubuntu 22.04 上 pip install 成功的包,在 CentOS 7 上可能因 glibc 版本差异直接编译失败。Docker 通过镜像固化整个运行时环境,让 docker build 产出的镜像,在开发机、测试机、生产机上行为完全一致。更重要的是,它天然支持 资源限制 :你可以用 --memory=512m --cpus=1.5 精确控制容器能使用的最大内存和 CPU 时间片,这是传统虚拟机或裸机部署无法低成本实现的。我曾用 docker stats 监控一个 Django API 容器,发现其内存占用在 320MB~480MB 波动,于是将 --memory 设为 512m ,再配合 --memory-swap=512m (禁止使用 swap),彻底杜绝了因内存溢出触发 OOM Killer 杀死进程的风险。
Nginx 在这里扮演 反向代理与边缘网关 的双重角色。很多人误以为它只是“转发请求”,其实它的关键能力在于: 连接管理 。Django 的 runserver 是单线程阻塞式,Gunicorn/uWSGI 是多进程/多线程模型,但它们都受限于 Python GIL 和同步 I/O,难以高效处理成千上万的长连接(如 WebSocket、SSE)。Nginx 基于事件驱动(epoll/kqueue),用极小的内存开销就能维持数万并发连接。它把海量客户端连接“接住”,再以可控的并发数(通过 upstream 的 max_conns 和 keepalive 参数)转发给后端 Django 应用。同时,Nginx 天然承担了 静态文件服务、SSL 终结、请求过滤、负载均衡(未来扩展) 等职责,让 Django 专注业务逻辑,不必再为 collectstatic 后的文件分发、HTTPS 握手耗时等问题分心。
Let’s Encrypt 则解决了 HTTPS 普及化的最后一公里障碍 。过去自签名证书或商业 CA 证书,要么不被浏览器信任,要么年费高昂、流程繁琐。Let’s Encrypt 提供免费、自动化、可信的 X.509 证书,其 ACME 协议设计精巧:客户端(如 Certbot)只需证明你对域名拥有控制权(通过 HTTP-01 或 DNS-01 挑战),即可自动签发和续期。关键在于, 它与 Nginx 的集成是开箱即用的 。Certbot 能自动修改 Nginx 配置,添加临时 location 块来响应 ACME 挑战,并在签发成功后无缝切换到 HTTPS 配置。这使得“全站 HTTPS”从一个需要专人维护的复杂任务,变成了一个 certbot --nginx -d example.com 命令就能完成的标准化操作。
提示:这个组合的“不可替代性”体现在故障隔离上。当 Django 应用因代码 bug 崩溃时,Nginx 会返回 502,但自身依然健壮,用户看到的是友好的错误页而非空白;当 Let’s Encrypt 续期失败,Nginx 仍可用旧证书提供 HTTPS 服务,给你留出修复时间;Docker 容器崩溃,
docker restart一条命令即可恢复,无需登录服务器手动启停进程。三者各司其职,互为备份。
2.2 架构拓扑图与数据流向详解
虽然不能画 Mermaid 图,但我用文字精准描述这个生产环境的标准拓扑:
Internet
↓ (HTTPS:443 / HTTP:80)
[Public IP] → [Nginx (Host Network)]
↓ (HTTP:8000, via Docker bridge network 'webnet')
[Django App Container (Gunicorn)] ←→ [PostgreSQL Container]
↓ (HTTP:8000, same network)
[Static Files: /static/ → Nginx volume mount]
↓ (HTTP:8000, same network)
[Media Files: /media/ → Nginx volume mount, with auth if needed]
- 网络层面 :Nginx 运行在宿主机的
host网络模式,直接监听0.0.0.0:80和0.0.0.0:443。这是必须的,因为 Let’s Encrypt 的 ACME 挑战需要从公网直接访问 Nginx 的 80 端口。而 Django 容器、PostgreSQL 容器则运行在一个名为webnet的自定义 Docker



被折叠的 条评论
为什么被折叠?



