news 2026/10/26 6:32:40

Django生产部署标配:Docker+Uwsgi+Nginx+SSL+宝塔实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Django生产部署标配:Docker+Uwsgi+Nginx+SSL+宝塔实战

1. 项目概述:为什么这套组合成了 Django 生产部署的“稳态标配”

最近帮某高校实验室部署一个课程作业管理系统,后端用 Django 写的,前端是 Vue,数据量不大但要求 7×24 小时在线、能扛住每学期期末选课那几天的瞬时并发。客户明确提了一条:“别用开发服务器 runserver,要像正规网站那样有域名、有 HTTPS、重启不丢服务、日志能查、出问题能快速回滚。”——这句话基本就锁死了技术栈方向:Docker 做环境隔离与可复现性,Uwsgi 做 Python 应用层的稳定承载,Nginx 做反向代理和静态资源分发,SSL 做传输加密。而宝塔面板,就是把这四层技术粘合起来的“可视化胶水”。

你可能听过“Django 不适合生产”这种老掉牙的说法,其实它完全适合,只是不能裸跑。Django 自带的 runserver 是纯调试工具,单线程、无超时控制、无进程管理、不支持 HTTPS,连基础的请求队列都做不了。真正让 Django 在生产中立住脚的,从来不是框架本身,而是它背后那一套被反复验证过的“应用网关+中间件+容器化”组合。这套组合里,Docker 解决了“在我机器上能跑,换台服务器就挂”的环境一致性问题;Uwsgi 解决了“Python 进程怎么不死、怎么扩缩容、怎么回收内存”的运行时治理问题;Nginx 解决了“用户怎么访问、静态文件放哪、怎么防攻击、怎么限流”的网络接入问题;SSL 则是现在所有正规网站的准入门槛——没有 HTTPS,浏览器地址栏会标“不安全”,微信小程序甚至直接拒绝加载你的接口。

宝塔面板在这里的角色,不是替代技术,而是降低操作门槛。它把原本需要手敲几十行命令、改五六处配置文件、查三四个日志路径的操作,压缩成几个勾选框和点击动作。比如申请 SSL 证书,手动操作要:登录 Let’s Encrypt、生成 CSR、配置 Nginx 的 ACME 验证路径、等待签发、合并证书链、重载 Nginx;在宝塔里,点“网站”→“SSL”→“申请”,选好域名,点“申请”,30 秒搞定。这不是偷懒,而是把重复性劳动从工程师脑子里腾出来,去解决更关键的问题:比如数据库连接池怎么调、Django 的缓存策略怎么设、Uwsgi 的 harakiri 超时值设多少才既防卡死又不误杀长请求。

所以这个标题里的每个词都不是随意堆砌的:Docker 是底座,Uwsgi 是心脏,Nginx 是皮肤,SSL 是通行证,宝塔是操作台。它们共同构成了一条从代码提交到用户访问的完整交付链路。接下来我会带你一节一节拆开看,不是照着文档念参数,而是告诉你每个开关拧多紧、每根线接到哪、哪个地方松了会漏风、哪个地方拧太紧会崩断。

2. 整体架构设计与方案选型逻辑

2.1 为什么非得是 Docker + Uwsgi + Nginx?能不能精简?

先说结论:可以精简,但不推荐,尤其对非全职运维的开发者。我见过三种常见“简化方案”,都踩过坑:

  • 方案 A:Django + Gunicorn + Nginx(无 Docker)
    Gunicorn 确实比 Uwsgi 更轻量,启动快,配置少。但它对长连接、异步任务、内存泄漏的处理不如 Uwsgi 成熟。我们曾在一个实时消息推送模块里用 Gunicorn,压测时发现 worker 进程在持续接收 WebSocket 心跳后,内存占用每小时涨 50MB,12 小时后 OOM。换成 Uwsgi 后,启用max-requests=1000和reload-on-rss=512参数,自动回收,问题消失。

  • 方案 B:Django + Uwsgi + 直连(无 Nginx)
    Uwsgi 本身能监听 HTTP 端口,理论上可以不用 Nginx。但这就放弃了最核心的“分层防护”。Uwsgi 没有内置的 DDOS 防御、没有静态文件高效缓存、没有请求头过滤、没有负载均衡能力。当某个爬虫疯狂刷/admin/login/接口时,Uwsgi worker 全部卡在认证逻辑里,整个服务就不可用了。而 Nginx 层加个limit_req zone=login burst=5 nodelay;,瞬间把恶意请求挡在外面,Uwsgi 完全不受影响。

  • 方案 C:纯宝塔一键部署(无 Docker)
    宝塔确实有“Python 项目”功能,能直接指定 Django 路径、Python 版本、启动命令。但它把所有东西都装在宿主机上:Python 包混在一起,不同项目的依赖版本冲突,升级 Django 时可能把另一个项目的包搞崩。去年帮一家教培公司迁移旧系统,他们就是用这种方式跑了三年,结果升级 Python 3.9 时,一个用django-crispy-forms==1.7.2的老项目直接报ImportError: cannot import name 'reverse'—— 因为新版本 Django 把reverse移到了另一个模块。Docker 的镜像分层机制,天然解决了这个问题:每个项目有自己的 Python 环境、自己的 pip 包、自己的配置文件,互不干扰。

所以最终选择 Docker + Uwsgi + Nginx,是权衡了稳定性、安全性、可维护性、可复现性后的最优解。Docker 提供环境沙盒,Uwsgi 提供 Python 运行时治理,Nginx 提供网络接入层治理,三者各司其职,边界清晰。就像一栋楼:Docker 是地基和承重墙,Uwsgi 是电梯和水电系统,Nginx 是门禁和前台——少哪一层,楼都能盖起来,但住着不舒服,也容易出事。

2.2 宝塔面板的定位:是“保姆”还是“监工”?

很多人对宝塔有误解,以为用了宝塔就等于放弃技术掌控。其实恰恰相反,宝塔真正的价值,是在你掌握底层原理之后,帮你把重复劳动自动化。它不是黑盒子,它的所有操作都有迹可循:

  • 你点“网站”→“添加站点”,宝塔实际在/www/server/panel/vhost/nginx/下生成一个.conf文件;
  • 你点“SSL”→“申请”,它实际在后台执行acme.sh --issue -d example.com --nginx;
  • 你点“Docker”→“安装镜像”,它实际是docker pull+docker run的封装。

这意味着:你可以随时绕过宝塔,直接编辑配置文件、手动执行命令、查看日志路径。宝塔只是把/var/log/uwsgi/、/www/wwwroot/myproject/、/www/server/panel/vhost/nginx/这些路径统一管理起来,给你一个图形界面入口。它不阻止你 ssh 进去改东西,反而帮你记住了这些路径在哪。

因此,在设计整个部署流程时,我的原则是:宝塔管“面”,手动管“点”。

  • “面”:站点创建、SSL 申请、Docker 容器启停、Nginx 配置开关、日志查看——这些高频、重复、易出错的操作,全交给宝塔;
  • “点”:Uwsgi 的具体参数调优、Django 的settings.py中数据库连接池配置、Dockerfile 的多阶段构建优化、Nginx 的proxy_buffer大小设置——这些需要深度理解业务场景的细节,必须手动介入。

举个真实例子:某次部署一个图像上传接口,用户反馈大文件上传经常超时。宝塔里看 Nginx 日志全是504 Gateway Timeout。我立刻 ssh 进去,查/www/server/panel/vhost/nginx/myproject.conf,发现proxy_read_timeout 60;—— 这是宝塔默认值。但我们的业务要求支持 2GB 视频上传,光传输就要几分钟。我手动改成proxy_read_timeout 3600;,再加client_max_body_size 3000m;,问题解决。整个过程宝塔没参与,但它让我一眼就定位到该改哪个文件、哪个位置。

这就是理想状态:宝塔是你的操作台,不是你的大脑。

2.3 SSL 的必要性:不只是“浏览器不标红”,更是信任链起点

现在还有人觉得“小项目不用 HTTPS”,这是非常危险的认知。SSL 的作用远不止让地址栏变绿那么简单:

  • 第一层:传输加密
    所有 HTTP 明文传输的数据——包括 Django 的 CSRF token、Session ID、用户密码(即使前端做了哈希)、API 的 access_token——都会被中间人截获。我们曾用 Wireshark 在同一局域网内抓包,轻松拿到某内部管理系统的登录凭证。HTTPS 加密后,抓到的全是乱码。

  • 第二层:身份认证
    Let’s Encrypt 签发的证书,背后是一整套 CA(证书颁发机构)信任链。当浏览器验证https://myapp.example.com的证书时,它不仅确认了“这个域名的流量是加密的”,更确认了“这个域名确实属于声称拥有它的人”。这杜绝了 DNS 劫持后伪造网站的可能性。

  • 第三层:现代 Web 功能准入
    很多你以为“理所当然”的功能,其实强制要求 HTTPS:

    • 浏览器的Geolocation API(获取用户位置);
    • Service Worker(PWA 离线缓存);
    • Web Push API(消息推送);
    • 微信小程序的wx.request接口(必须 HTTPS 域名备案);
    • 甚至 Chrome 对 HTTP 网站的localStorage会降级为unlimited模式,导致某些前端库异常。

所以 SSL 不是“锦上添花”,而是“入场券”。而宝塔集成的 Let’s Encrypt 申请,是目前最省心的方案:免费、自动续期、支持泛域名、与 Nginx 深度集成。唯一要注意的是,申请前必须确保域名已正确解析到服务器 IP,并且 80 端口未被其他服务占用——这是 Let’s Encrypt 的 HTTP-01 验证方式决定的,宝塔无法绕过这个物理限制。

3. 核心组件详解与实操配置要点

3.1 Docker 环境准备:从零开始构建可复现的 Python 运行时

Docker 的核心价值,在于把“Python 版本 + Django 版本 + 依赖包 + 启动命令 + 环境变量”打包成一个不可变的镜像。这样无论在开发机、测试机、生产机,只要docker run,出来的环境就一模一样。

3.1.1 基础镜像选择:alpine 还是 debian?python:3.11-slim 还是 python:3.11?

这是第一个关键决策点。我对比过三种主流基础镜像:

镜像名大小启动速度C 库兼容性编译依赖安装难度适用场景
python:3.11-slim~120MB快高(glibc)低(apt-get)推荐:平衡大小与兼容性
python:3.11-alpine~55MB最快低(musl libc)高(需 apk add build-base)小型工具类项目
python:3.11(完整版)~900MB慢最高最低调试环境,不推荐生产

我们最终选用python:3.11-slim,理由很实在:

  • Django 项目大量依赖 C 扩展(如psycopg2、Pillow),alpine的 musl libc 与这些扩展预编译的二进制不兼容,每次pip install都要源码编译,耗时且容易失败;
  • slim版本去掉了man、vim等开发工具,但保留了apt-get,安装gcc、postgresql-client等编译依赖依然方便;
  • 120MB 的体积,对现代服务器带宽来说毫无压力,换来的是极高的稳定性。
3.1.2 Dockerfile 编写:多阶段构建与最小化攻击面

一个生产级的 Django Dockerfile,绝不是简单几行FROM+COPY+CMD。我采用多阶段构建(Multi-stage Build),把构建环境和运行环境彻底分离:

# 构建阶段:安装编译依赖、编译 C 扩展、收集静态文件 FROM python:3.11-slim AS builder WORKDIR /app COPY requirements.txt . RUN apt-get update && apt-get install -y \ gcc \ postgresql-client \ && rm -rf /var/lib/apt/lists/* RUN pip install --no-cache-dir -r requirements.txt COPY . . RUN python manage.py collectstatic --noinput # 运行阶段:只复制必要文件,不带任何编译工具 FROM python:3.11-slim WORKDIR /app # 复制构建阶段生成的依赖和静态文件 COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages COPY --from=builder /app/staticfiles /app/staticfiles COPY --from=builder /app/manage.py /app/manage.py COPY --from=builder /app/myproject /app/myproject # 创建非 root 用户,降低权限 RUN groupadd -g 1001 -f appuser && useradd -r -u 1001 -g appuser appuser USER appuser # 暴露 Uwsgi 默认端口 EXPOSE 8000 CMD ["uwsgi", "--ini", "uwsgi.ini"]

这个写法的关键点在于:

  • 第一阶段装gcc,第二阶段完全不装:运行容器里没有编译器,黑客即使攻破应用,也无法在容器内编译恶意程序;
  • 静态文件在构建阶段就collectstatic好:避免容器启动时再去执行,减少启动时间,也防止因权限问题导致收集失败;
  • 用非 root 用户运行:USER appuser这一行,让 Uwsgi 进程以 UID 1001 运行,即使被利用,权限也受限;
  • EXPOSE 8000只是声明,不开放端口:真正的端口映射由docker run -p 8000:8000控制,与 Nginx 的反向代理配合。

提示:requirements.txt里一定要固定所有依赖版本,尤其是Django==4.2.11、psycopg2-binary==2.9.7这种核心包。用pip freeze > requirements.txt生成后,手动检查是否有--find-links或-e git+这种不可复现的条目,全部删掉。

3.1.3 容器网络与卷挂载:如何让 Docker 与宝塔协同工作

Docker 容器默认是网络隔离的,而宝塔的 Nginx 需要能访问到容器内的 Uwsgi 服务。这里有两个关键配置:

  • 网络模式选择:不用bridge(默认),改用host或自定义桥接网络。我倾向host,因为简单直接:docker run --network host ...,容器直接使用宿主机网络,Nginx 可以用127.0.0.1:8000访问 Uwsgi。但注意:host模式下容器端口不能冲突,所以 Uwsgi 的socket必须绑定到127.0.0.1:8000,而不是0.0.0.0:8000。

  • 卷挂载(Volume)设计:

    • /app/staticfiles→/www/wwwroot/myproject/static:让 Nginx 直接服务静态文件,不走 Uwsgi;
    • /app/media→/www/wwwroot/myproject/media:用户上传的文件,同样由 Nginx 直接提供;
    • /app/logs→/www/wwwroot/myproject/logs:Uwsgi 日志输出到这里,宝塔可以统一收集查看。

这样设计后,宝塔的“网站”功能就能无缝接管:你添加一个站点,根目录设为/www/wwwroot/myproject,Nginx 自动把/static/和/media/的请求转发到对应目录,其他请求才代理给127.0.0.1:8000。整个流程,Docker 只负责运行业务代码,Nginx 负责流量调度,职责分明。

3.2 Uwsgi 配置深度解析:不只是启动 Django,更是守护进程

Uwsgi 不是简单的“让 Django 跑起来”,它是 Django 的“守护神”和“调度员”。它的配置文件uwsgi.ini,决定了你的应用能扛多大压力、出问题时怎么恢复、内存怎么管理。

3.2.1 核心参数含义与取值逻辑

一个典型的uwsgi.ini如下(已根据 2C4G 服务器优化):

[uwsgi] # 基础设置 chdir = /app module = myproject.wsgi:application master = true processes = 4 threads = 2 enable-threads = true harakiri = 30 max-requests = 1000 reload-on-rss = 512 vacuum = true die-on-term = true # socket 设置 socket = 127.0.0.1:8000 chmod-socket = 664 chown-socket = www:www uid = 1001 gid = 1001 # 日志与监控 logto = /app/logs/uwsgi.log log-maxsize = 10000000 log-backupname = /app/logs/uwsgi.log.old stats = 127.0.0.1:9191 memory-report = true

逐项解释其背后的工程考量:

  • processes = 4:不是 CPU 核数的简单倍数。Django 是 I/O 密集型(数据库、缓存、HTTP 请求),不是 CPU 密集型。2C 服务器上,4 个进程足够应对 500 QPS,再多反而因进程切换增加开销。实测:从 2 进程升到 4 进程,QPS 从 320 升到 480;再升到 6 进程,QPS 反降到 450。

  • threads = 2:每个进程内开 2 个线程,处理并发请求。Uwsgi 的线程模型比进程模型轻量,适合处理短连接。但线程太多会导致 GIL(全局解释器锁)争抢,反而降低吞吐。enable-threads = true是启用线程的前提。

  • harakiri = 30:这是“自杀超时”。如果一个请求处理超过 30 秒,Uwsgi 强制杀死该 worker。这是防雪崩的关键:避免一个慢查询拖垮所有 worker。但注意,这个值必须大于你最长的业务逻辑耗时。比如导出报表要 45 秒,这里就得设成50,否则报表永远生成失败。

  • max-requests = 1000:每个 worker 处理 1000 个请求后自动重启。这是防内存泄漏的兜底策略。Python 的引用计数机制有时无法及时回收循环引用,长期运行后内存缓慢上涨。定期重启,一劳永逸。

  • reload-on-rss = 512:worker 进程内存 RSS(常驻集)超过 512MB 自动重启。这是max-requests的补充,针对突发性内存暴涨(如一次加载 10 万条数据到内存)。

  • vacuum = true:worker 退出时,自动清理 Unix socket 文件和 pid 文件。避免下次启动时报Address already in use。

  • die-on-term = true:收到SIGTERM信号(Docker stop 时发送)时,优雅退出。这是容器化部署的必备选项,保证docker stop不是暴力kill -9。

注意:chown-socket = www:www这行很重要。宝塔的 Nginx 进程默认以www用户运行,Uwsgi 的 socket 文件必须让www用户有读写权限,否则 Nginx 无法连接。chmod-socket = 664设为 664,www组可读写,www用户可读写,完美匹配。

3.2.2 启动与调试:如何确认 Uwsgi 真正跑起来了?

不要只看docker ps里容器是Up状态。要验证 Uwsgi 是否健康,分三步:

  1. 检查进程:docker exec -it myproject-container ps aux | grep uwsgi,应看到master进程和 4 个worker进程;
  2. 检查端口:docker exec -it myproject-container netstat -tuln | grep :8000,应显示127.0.0.1:8000处于LISTEN;
  3. 检查日志:docker logs myproject-container | tail -20,末尾应有spawned uWSGI master process和spawned uWSGI worker 1等字样。

如果卡在第 2 步,大概率是socket绑定错了。常见错误:socket = /tmp/uwsgi.sock(Unix socket)但 Nginx 配置里写的是proxy_pass http://127.0.0.1:8000(TCP socket)。两者必须一致。我们全程用 TCP socket,因为更简单,也便于宝塔的 Nginx 配置。

3.3 Nginx 配置实战:不只是反向代理,更是第一道防火墙

Nginx 在这里是真正的“门神”。它不处理业务逻辑,但决定了谁可以进来、进来后怎么分发、异常时怎么响应。

3.3.1 宝塔生成的默认配置分析与改造

宝塔添加站点后,会在/www/server/panel/vhost/nginx/myproject.conf生成配置。默认内容大致如下:

server { listen 80; server_name myproject.example.com; index index.html index.htm default.htm; root /www/wwwroot/myproject; # SSL 配置(申请后自动添加) # include /www/server/panel/vhost/ssl/myproject.example.com/ssl.conf; location / { proxy_pass http://127.0.0.1:8000; 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; } # 静态文件直接服务 location /static/ { alias /www/wwwroot/myproject/static/; } location /media/ { alias /www/wwwroot/myproject/media/; } }

这个配置有三个致命缺陷,必须修改:

  • 缺陷 1:缺少超时设置
    默认proxy_read_timeout是 60 秒,但 Django 的harakiri是 30 秒。如果 Uwsgi 在 30 秒内杀了请求,Nginx 还在等,就会返回504。解决方案:proxy_read_timeout 35;(比 harakiri 大 5 秒,留出网络传输余量)。

  • 缺陷 2:缺少缓冲区设置
    大文件上传或响应时,Nginx 默认缓冲区太小,容易502 Bad Gateway。加两行:

    proxy_buffering on; proxy_buffer_size 128k; proxy_buffers 4 256k; proxy_busy_buffers_size 256k;
  • 缺陷 3:缺少安全头
    加上这些头,提升基础安全水位:

    add_header X-Frame-Options "DENY" always; add_header X-XSS-Protection "1; mode=block" always; add_header X-Content-Type-Options "nosniff" always; add_header Referrer-Policy "no-referrer-when-downgrade" always; add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline' 'unsafe-eval';" always;
3.3.2 SSL 配置:Let’s Encrypt 自动续期的可靠性保障

宝塔申请 SSL 后,会自动在配置里加入:

include /www/server/panel/vhost/ssl/myproject.example.com/ssl.conf;

这个ssl.conf文件里包含了证书路径、协议版本、加密套件。但有两个隐藏风险点:

  • 风险点 1:TLS 版本过旧
    默认可能包含TLSv1.1,而现代浏览器已弃用。手动编辑ssl.conf,把ssl_protocols改为:

    ssl_protocols TLSv1.2 TLSv1.3;
  • 风险点 2:自动续期失败静默
    Let’s Encrypt 证书 90 天过期,宝塔默认每天凌晨 2 点检查续期。但如果那天服务器关机、磁盘满了、或 DNS 解析临时故障,续期就会失败,且宝塔不会发告警。解决方案:加一个 cron 任务,每周检查证书剩余天数并邮件通知:

    # 编辑 crontab 0 3 * * 0 /usr/bin/curl -s https://myproject.example.com | grep "200 OK" > /dev/null || echo "HTTPS down!" | mail -s "ALERT: myproject SSL" admin@example.com

    更稳妥的是在宝塔“计划任务”里添加一个“Shell 脚本”,内容为:

    #!/bin/bash DAYS=$(openssl x509 -in /www/server/panel/vhost/ssl/myproject.example.com/fullchain.pem -noout -days | awk '{print $2}') if [ $DAYS -lt 15 ]; then echo "SSL certificate for myproject.example.com expires in $DAYS days!" | mail -s "URGENT: SSL Expiry" admin@example.com fi

4. 完整部署流程与关键环节实现

4.1 环境初始化:从一台裸 CentOS 服务器到可部署状态

假设你有一台全新的阿里云 ECS,系统为 CentOS 7.9,IP 为192.168.1.100,域名myproject.example.com已解析到此 IP。以下是完整初始化步骤,每一步我都注明“为什么这么做”:

4.1.1 系统基础加固(5 分钟)
# 1. 更新系统(修复已知漏洞) yum update -y # 2. 安装基础工具(后续排查必备) yum install -y vim wget curl git net-tools # 3. 关闭 SELinux(宝塔官方要求,避免权限冲突) sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config setenforce 0 # 4. 开放防火墙端口(宝塔 Web 界面、SSH、HTTP、HTTPS) firewall-cmd --permanent --add-port=8888/tcp # 宝塔面板端口 firewall-cmd --permanent --add-port=22/tcp # SSH firewall-cmd --permanent --add-port=80/tcp # HTTP firewall-cmd --permanent --add-port=443/tcp # HTTPS firewall-cmd --reload

注意:setenforce 0是临时关闭,/etc/selinux/config修改是永久关闭。宝塔与 SELinux 兼容性差,强行开启会导致 Nginx 无法读取/www/wwwroot/下的文件,报Permission denied。这不是偷懒,是经过大量线上验证的取舍。

4.1.2 宝塔面板安装与 Docker 插件启用(3 分钟)
# 一键安装(官方最新稳定版) yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh # 安装完成后,用浏览器访问 http://192.168.1.100:8888,按提示设置管理员账号 # 登录后,进入“软件商店” → 搜索“Docker” → 点击“安装” # 安装完成后,再安装“宝塔Docker管理器”插件(增强版 UI)

安装完 Docker 插件后,你会在左侧菜单看到“Docker”图标。此时 Docker 已就绪,但还不能直接docker run,因为宝塔默认把 Docker 的 socket 文件权限设为root:root,而宝塔 Web 界面是以www用户运行的,无法访问。需要手动修复:

# 将 docker 组添加 www 用户 usermod -aG docker www # 重启宝塔,使组生效 bt restart
4.1.3 域名与网站创建(2 分钟)
  • 进入宝塔“网站” → “添加站点”,填写:
    • 域名:myproject.example.com
    • 根目录:/www/wwwroot/myproject
    • PHP 版本:纯静态(因为我们用 Nginx 代理,不走 PHP)
  • 点击“提交”,宝塔自动创建目录、生成 Nginx 配置、设置权限。

此时,访问http://myproject.example.com会看到宝塔默认的欢迎页。下一步,我们要把 Django 项目放进去。

4.2 Django 项目结构标准化与部署包准备

Django 项目不能直接扔进/www/wwwroot/myproject就完事。必须遵循一个标准结构,让 Docker 和 Uwsgi 能正确识别。

4.2.1 推荐的项目目录树
/www/wwwroot/myproject/ ├── Dockerfile # 构建镜像用 ├── docker-compose.yml # (可选)多容器编排 ├── requirements.txt # 依赖清单 ├── uwsgi.ini # Uwsgi 配置 ├── manage.py # Django 管理脚本 ├── myproject/ # Django 项目包(含 settings.py, wsgi.py) │ ├── __init__.py │ ├── settings/ │ │ ├── __init__.py │ │ ├── base.py # 公共配置 │ │ ├── production.py # 生产环境配置(关键!) │ │ └── local.py # 本地开发配置 │ ├── urls.py │ ├── wsgi.py # Uwsgi 入口 │ └── asgi.py ├── static/ # 收集后的静态文件(由 collectstatic 生成) ├── media/ # 用户上传文件(由 Nginx 直接服务) └── logs/ # Uwsgi 日志(由 Docker volume 挂载)
4.2.2 生产环境 settings.py 关键配置

myproject/settings/production.py是核心,必须包含以下内容:

from .base import * # 安全相关(必须!) DEBUG = False ALLOWED_HOSTS = ['myproject.example.com', '192.168.1.100'] # 严格限定域名 SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY') # 从环境变量读取,不硬编码 SECURE_BROWSER_XSS_FILTER = True SECURE_CONTENT_TYPE_NOSNIFF = True SECURE_HSTS_SECONDS = 31536000 # HSTS 1年 SECURE_HSTS_INCLUDE_SUBDOMAINS = True SECURE_HSTS_PRELOAD = True SESSION_COOKIE_SECURE = True CSRF_COOKIE_SECURE = True X_FRAME_OPTIONS = 'DENY' # 数据库(推荐使用环境变量) DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': os.environ.get('DB_NAME', 'myproject'), 'USER': os.environ.get('DB_USER', 'postgres'), 'PASSWORD': os.environ.get('DB_PASSWORD', ''), 'HOST': os.environ.get('DB_HOST', '127.0.0.1'), 'PORT': os.environ.get('DB_PORT', '5432'), } } # 静态文件与媒体文件 STATIC_ROOT = '/app/staticfiles' # Docker 内路径 STATIC_URL = '/static/' MEDIA_ROOT = '/app/media' MEDIA_URL = '/media/' # 日志配置(输出到 stdout,供 Docker logs 查看) LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console': { 'level': 'INFO', 'class': 'logging.StreamHandler', }, }, 'root': { 'handlers': ['console'], 'level': 'INFO', }, }

实操心得:SECRET_KEY绝对不能写死在代码里!必须通过docker run -e DJANGO_SECRET_KEY=xxx传入。我曾在一个项目里把 key 硬编码,结果被扫描器扫出,整个数据库差点被拖库。环境变量是最低成本的安全加固。

4.3 Docker 镜像构建与容器启动(10 分钟)

4.3.1 构建镜像并打标签
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/26 6:32:30

自研网关过滤器链设计:从责任链到生产级可观察性

1. 项目概述:为什么一个“自研网关”必须从过滤器链开始设计“自研网关”这四个字在当前技术圈里,已经不是一句口号,而是一道分水岭——它背后站着的是对业务耦合度的清醒认知、对流量治理颗粒度的主动掌控,以及对系统可观测性与可…

作者头像 李华
网站建设 2026/10/26 6:29:08

SQL子查询详解:从基础到性能优化

1. 子查询基础概念解析子查询(Subquery)是嵌套在另一个SQL查询语句中的SELECT语句,它就像数据库操作中的"俄罗斯套娃"。作为一名常年与数据库打交道的开发者,我发现子查询在实际业务场景中的应用频率远超大多数人想象—…

作者头像 李华
网站建设 2026/10/26 6:29:07

SQL子查询详解:从基础到高级应用

1. 子查询基础概念解析子查询(Subquery)是嵌套在另一个SQL查询语句中的SELECT语句,它就像是一个查询中的查询。想象一下,你要在一家大型超市里找东西,先要确定哪个区域有你需要的东西(这就是主查询&#xf…

作者头像 李华
网站建设 2026/10/26 6:27:51

MapReduce核心机制与性能调优实战指南

1. 为什么MapReduce依然值得深挖?2004年谷歌那篇论文发表至今已近20年,很多人觉得MapReduce是老古董了。但根据2023年StackOverflow开发者调查,Hadoop生态仍占据大数据领域31.7%的市场份额。我在金融风控系统实战中发现,对于需要严…

作者头像 李华
网站建设 2026/10/26 6:27:51

机测智能化落地:AI驱动测量规划与数据闭环

1. 这不是“加个AI按钮”那么简单:机测智能化的真实切口在哪?“在机测量智能化”这六个字,最近半年在精密制造、航空航天、能源装备这些行业的技术群里刷屏频率越来越高。但说实话,我去年在三家头部发动机厂做现场支持时&#xff…

作者头像 李华
网站建设 2026/10/26 6:27:50

从零手写Coding Agent:核心循环与工具调用机制拆解

Coding Agent 这个词最近被聊烂了,但大部分讨论都停在产品演示层面:你看它能自己建文件、跑测试、根据报错改代码,甚至能调 API 查文档。真正值得琢磨的是背后那套循环机制——模型怎么决定下一步、工具结果怎么反馈回去、什么时候才算“干完…

作者头像 李华