Python Web生产部署:uWSGI+Nginx实战指南

Python Web生产部署:uWSGI+Nginx实战指南

1. 项目概述:为什么一个Python Web应用非得绕过开发服务器直面uWSGI + Nginx?

你写完一个Django或Flask应用,python manage.py runserverflask run一敲,本地浏览器里页面唰一下就出来了——这感觉太爽了。但只要项目要上线、哪怕只是给同事演示个内部系统,这个命令立刻从“救星”变成“定时炸弹”。它不是设计来扛真实流量的:单线程、无超时控制、不处理静态文件、不防DDoS、连基本的请求队列都靠Python解释器硬撑。我见过太多团队在测试环境用runserver跑得好好的,一上预发环境,3个并发请求就让整个服务卡死,日志里全是OSError: [Errno 24] Too many open files。这不是代码问题,是架构选型没跨过那道坎。

uWSGI + Nginx这套组合,就是Python Web生产环境的“标准答案”。它不是某个厂商的私有方案,而是经过十年以上千万级站点验证的工业级流水线:uWSGI负责把Python代码编译成可被Web服务器理解的字节码,管理进程生命周期、内存回收、热重载;Nginx则像一个经验老到的交通警察,负责接收所有HTTP请求、分发给后端uWSGI进程、缓存静态资源、抵御慢速攻击、做SSL终止、甚至能根据URL路径把API请求和前端资源请求分流到不同后端。它们之间通过Unix Socket或TCP协议通信,数据不经过磁盘,全程在内存中流转,吞吐量比单线程开发服务器高出两个数量级。

关键词里反复出现的“windows 安装uwsgi”、“nginx配置”、“centos7 nginx uwsgi django drf vite vue docker mysql redis生产环境”,恰恰说明这不是一个孤立的技术点,而是一整套基础设施能力。你在Windows上装uWSGI,本质上是在模拟Linux生产环境的最小闭环;你调nginx.conf里的client_max_body_size,解决的是前端上传大文件报413错误的现实痛点;你研究if using custom web server, verify that web server is sending .br files with,背后是现代Web对Brotli压缩的强制要求——这些都不是教科书里的理论,是每天在服务器日志里跳出来的血淋淋的告警。这篇文章不讲“什么是WSGI”,而是直接带你把一台空服务器变成能扛住真实业务流量的Python Web工厂。无论你是刚学完print("Hello World")的零基础新手,还是正在为线上500错误焦头烂额的运维老手,接下来的内容,每一步都能复制粘贴执行,每一个参数都有它的脾气和来历。

2. 核心技术栈拆解:WSGI、uWSGI、Nginx三者如何各司其职又无缝咬合?

2.1 WSGI:Python Web世界的“普通话”,不是软件,是契约

很多人第一次看到“WSGI”就以为是个要下载安装的程序,其实它连一行代码都不是。WSGI(Web Server Gateway Interface)是Python社区在2003年定下的一个接口规范,就像USB接口标准——它不规定U盘长什么样,只规定U盘插进电脑时,必须用哪几根针脚传输数据、电压多少、握手信号怎么发。对Python Web而言,WSGI规范强制约定了两件事:

  1. Web服务器(如Nginx)必须提供一个可调用对象(callable),这个对象接收两个参数:environ(一个包含所有HTTP请求头、方法、路径、客户端IP等信息的字典)和start_response(一个回调函数,用于设置HTTP状态码和响应头);
  2. Python应用(如Django的wsgi.py)必须返回一个可迭代对象(通常是列表或生成器),里面是HTTP响应体的字节流。

提示:你可以把WSGI想象成餐厅里的“传菜口”。厨房(Python应用)把做好的菜(响应体)打包好,放在传菜口(WSGI callable);服务员(Web服务器)从传菜口取走菜品,加上菜单(状态码、Content-Type头),端给客人(浏览器)。厨房和餐厅不用知道对方怎么运作,只要遵守传菜口的尺寸和流程就行。

Django、Flask、FastAPI等框架都内置了符合WSGI规范的入口模块。比如Django项目根目录下的wsgi.py文件,核心就这一行:

application = get_wsgi_application()

这个application变量,就是WSGI规范要求的那个“可调用对象”。它不是你写的业务逻辑,而是框架为你准备好的、严格遵循WSGI契约的“翻译官”。

2.2 uWSGI:Python应用的“职业经理人”,专治各种不服

如果WSGI是契约,uWSGI就是那个拿着契约去监督执行、管理团队、应对突发状况的职业经理人。它是一个独立的、用C语言编写的应用服务器(Application Server),核心使命是:把Python应用的WSGI接口,包装成Web服务器(如Nginx)能高效、安全、稳定调用的服务。它干的活远超“翻译”:

  • 进程/线程管理runserver是单进程单线程,uWSGI可以开4个master进程,每个下面管8个worker子进程,每个worker还能开4个线程——总共128个并发处理单元,且能根据CPU负载自动伸缩。
  • 内存与资源隔离:一个worker进程崩溃,其他worker照常工作,不会导致整个应用雪崩。它还内置了max-requests=1000参数,强制worker处理1000个请求后自动重启,防止内存泄漏累积。
  • 优雅重启与热重载uwsgi --reload myapp.pid命令能让uWSGI在不中断服务的情况下,平滑加载新代码,用户完全感知不到。
  • 协议桥接:它原生支持HTTP、FastCGI、SCGI,但最常用的是与Nginx通信的uwsgi协议——一种比HTTP更轻量、专为进程间通信优化的二进制协议。

注意:uWSGI不是Web服务器!它不直接监听80端口,也不处理SSL证书。它的定位非常清晰:只专注运行Python代码,把网络层的事,彻底交给更专业的Nginx。这就是“专业分工”的威力。

2.3 Nginx:Web流量的“中央调度室”,稳、快、狠

Nginx(发音同“engine X”)是一个高性能的HTTP服务器和反向代理服务器。它和Apache最大的区别在于架构:Apache为每个连接创建一个进程或线程(阻塞式),而Nginx用一个主线程+少量工作线程(event-driven),通过异步非阻塞I/O模型,轻松支撑数万并发连接。在uWSGI+Nginx架构中,Nginx扮演三个关键角色:

  1. 反向代理(Reverse Proxy):这是它最核心的身份。外部用户访问https://myapp.com,DNS解析到Nginx服务器IP,Nginx收到请求后,不自己处理,而是根据配置(如location / { proxy_pass http://127.0.0.1:8001; }),把请求转发给本机的uWSGI进程(通常监听在127.0.0.1:8001/tmp/uwsgi.sock)。用户只和Nginx打交道,完全不知道后端是Python、Java还是Node.js。
  2. 静态文件服务器(Static File Server):HTML、CSS、JS、图片等文件,根本不需要Python应用处理。Nginx直接从磁盘读取并返回,速度比Python应用快10倍以上。配置一行location /static { alias /var/www/myapp/static/; }即可。
  3. 安全网关(Security Gateway):它能做速率限制(limit_req)、IP黑名单(deny 192.168.1.100;)、请求体大小限制(client_max_body_size 10M;,解决413错误)、SSL/TLS终止(卸载HTTPS加密,把解密后的HTTP请求发给uWSGI)、甚至WAF(Web应用防火墙)基础规则。

三者关系用一张表厘清:

组件类型主要职责是否直接面向用户典型监听地址
WSGI规范(Interface)定义Python应用与Web服务器间的通信契约否(抽象层)
uWSGI应用服务器(App Server)运行Python代码、管理进程、处理业务逻辑否(仅接受Nginx转发)127.0.0.1:8001/tmp/uwsgi.sock
NginxWeb服务器/反向代理(Web Server)接收用户请求、分发给uWSGI、托管静态文件、保障安全是(用户唯一入口)0.0.0.0:80/0.0.0.0:443

这个分工,让每个组件都做到了极致:Nginx处理海量连接和网络IO,uWSGI专注Python执行效率,WSGI规范确保生态兼容性。任何试图让Python应用自己监听80端口、处理SSL、抗DDoS的做法,都是在重复造轮子,而且造得比Nginx差得多。

3. 实操部署全流程:从零开始,在CentOS 7上搭建Django+uWSGI+Nginx生产环境

3.1 环境准备:干净的CentOS 7系统与基础依赖

我们以最典型的生产环境——CentOS 7(内核3.10,Python 2.7默认)为蓝本。虽然现在推荐用Ubuntu 22.04或CentOS Stream,但大量企业服务器仍在用CentOS 7,它的稳定性是经过时间检验的。切记:不要在你的开发机或Mac上跟着操作,一定要在一台全新的、最小化安装的CentOS 7虚拟机或云服务器上进行。我用的是阿里云ECS,配置2核4G,系统盘40GB。

第一步,更新系统并安装基础工具:

# 切换到root用户(后续所有命令均以此身份执行) sudo su - # 更新系统包(耗时约5-10分钟) yum update -y # 安装常用工具:wget(下载)、vim(编辑器)、gcc(编译uWSGI必需)、openssl-devel(SSL支持) yum install -y wget vim-enhanced gcc openssl-devel # 安装Python 3.8(CentOS 7默认只有Python 2.7,必须升级) # 先安装编译依赖 yum groupinstall -y "Development Tools" yum install -y zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel expat-devel # 下载Python 3.8.18源码(选择这个版本是因为它对CentOS 7兼容性最好) cd /usr/src wget https://www.python.org/ftp/python/3.8.18/Python-3.8.18.tgz tar xzf Python-3.8.18.tgz cd Python-3.8.18 # 编译安装(--enable-optimizations开启PGO优化,提升性能约10%) ./configure --enable-optimizations make -j$(nproc) # -j参数让make使用所有CPU核心,并行编译,大幅缩短时间 make altinstall # 使用altinstall避免覆盖系统默认的python2.7 # 验证安装 python3.8 --version # 应输出 Python 3.8.18

实操心得:make -j$(nproc)是提速关键。我在2核机器上实测,不加-j编译要22分钟,加了只要7分钟。make altinstall是安全红线——它安装的可执行文件叫python3.8,而不是python3,这样就不会破坏系统依赖python3的其他软件(如yum)。很多新手在这里翻车,把系统搞瘫痪。

3.2 创建项目与Python虚拟环境:隔离是生产环境的生命线

永远不要在系统Python环境中直接pip install!这是所有线上事故的起点。我们必须为每个项目创建独立的、纯净的Python环境。

# 创建项目目录结构(遵循Linux最佳实践) mkdir -p /var/www/mydjangoapp/{src,logs,static,media} chown -R $USER:$USER /var/www/mydjangoapp chmod -R 755 /var/www/mydjangoapp # 进入项目源码目录 cd /var/www/mydjangoapp/src # 创建并激活Python 3.8虚拟环境 python3.8 -m venv venv source venv/bin/activate # 升级pip到最新版(旧版pip在安装uWSGI时会报错) pip install --upgrade pip # 安装Django(这里用3.2.25 LTS版本,长期支持,企业首选) pip install Django==3.2.25 # 创建Django项目(注意:--name参数指定项目名,-p指定路径) django-admin startproject mysite . # 修改Django配置,适配生产环境 vim mysite/settings.py

settings.py中,找到并修改以下关键配置(这是生产环境的铁律):

# SECURITY WARNING: keep the secret key used in production secret! # 生产环境绝不能用默认的SECRET_KEY!用下面命令生成一个强随机密钥 # python3.8 -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())" SECRET_KEY = 'your_very_strong_random_secret_key_here' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = False # 必须为False!否则会暴露敏感信息 # 允许访问的域名(白名单),防止HTTP Host头攻击 ALLOWED_HOSTS = ['myapp.com', 'www.myapp.com', '123.45.67.89'] # 替换为你的域名或IP # 静态文件配置(Django收集到的CSS/JS等) STATIC_URL = '/static/' STATIC_ROOT = '/var/www/mydjangoapp/static/' # 注意:这是Nginx将要服务的绝对路径 # 媒体文件配置(用户上传的图片等) MEDIA_URL = '/media/' MEDIA_ROOT = '/var/www/mydjangoapp/media/' # 数据库配置(这里用SQLite做演示,生产环境务必换成PostgreSQL或MySQL) DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': '/var/www/mydjangoapp/db.sqlite3', } } # 日志配置(非常重要!没有日志,线上问题等于瞎子) LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'verbose': { 'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}', 'style': '{', }, }, 'handlers': { 'file': { 'level': 'INFO', 'class': 'logging.handlers.RotatingFileHandler', 'filename': '/var/www/mydjangoapp/logs/django.log', 'maxBytes': 1024*1024*5, # 5MB 'backupCount': 5, 'formatter': 'verbose', }, }, 'root': { 'handlers': ['file'], 'level': 'INFO', }, }

注意:ALLOWED_HOSTS是安全开关。如果你填['*'],Django会拒绝所有请求并返回400 Bad Request。必须精确列出你的域名或IP。DEBUG=False后,Django的错误页面会消失,所有错误都会写入日志,所以LOGGING配置必须到位。

3.3 安装与配置uWSGI:让Django应用“活”起来

uWSGI是整个链条的发动机,它的配置文件决定了应用的生死。

# 在已激活的虚拟环境中安装uWSGI pip install uwsgi # 创建uWSGI配置文件(纯文本,无后缀) vim /var/www/mydjangoapp/uwsgi.ini

uwsgi.ini内容如下(每一行都有深意):

# uWSGI配置文件:/var/www/mydjangoapp/uwsgi.ini # 项目基础信息 [uwsgi] project = mydjangoapp uid = root gid = root base = /var/www # Python相关 home = %(base)/%(project)/src/venv # 虚拟环境路径 pythonpath = %(base)/%(project)/src # Python模块搜索路径 module = mysite.wsgi:application # WSGI入口,格式:模块名:可调用对象名 # 进程与性能 master = true # 开启master进程(管理worker) processes = 4 # 启动4个worker进程(根据CPU核心数调整,2核建议2-4个) threads = 2 # 每个worker开2个线程(适合I/O密集型Django) max-requests = 1000 # 每个worker处理1000个请求后自动重启,防内存泄漏 vacuum = true # worker退出时自动清理内存和文件描述符 # 通信方式(关键!选择Unix Socket比TCP更高效、更安全) socket = /var/www/%(project)/%(project).sock chmod-socket = 664 # 设置socket文件权限,让Nginx能读写 chown-socket = root:nginx # socket文件属主为root,属组为nginx uid = root gid = nginx # 安全与日志 die-on-term = true # 收到TERM信号时优雅退出 logto = /var/www/%(project)/logs/uwsgi.log # uWSGI自己的日志 log-maxsize = 10000000 # 10MB log-backupname = /var/www/%(project)/logs/uwsgi.log.old

解析:socket = /var/www/%(project)/%(project).sock这行是精髓。它让uWSGI创建一个Unix域套接字文件(.sock),而不是监听TCP端口。Unix Socket是同一台机器上进程间通信的最快方式,没有网络协议栈开销,且天然隔离——只有本机进程能访问这个文件。chown-socket = root:nginx确保Nginx的worker进程(通常以nginx用户运行)能通过这个文件与uWSGI通信。chmod-socket = 664赋予属主和属组读写权限,其他用户只有读权限,安全又实用。

启动uWSGI进行测试:

# 启动uWSGI(后台运行,-d参数指定日志文件) uwsgi --ini /var/www/mydjangoapp/uwsgi.ini -d /var/www/mydjangoapp/logs/uwsgi.log # 查看uWSGI是否启动成功 ps aux | grep uwsgi # 应该看到master进程和4个worker进程 # 查看uWSGI日志 tail -f /var/www/mydjangoapp/logs/uwsgi.log # 如果看到"WSGI app 0 (mountpoint='') ready in X seconds",说明Django应用已加载成功

此时,uWSGI已经在后台运行,但它监听的是.sock文件,你无法用浏览器直接访问。下一步,让Nginx来“代言”。

3.4 安装与配置Nginx:架起用户与uWSGI之间的桥梁

# 添加Nginx官方仓库(确保安装最新稳定版) rpm -Uvh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm # 安装Nginx yum install -y nginx # 启动Nginx并设置开机自启 systemctl start nginx systemctl enable nginx # 检查Nginx状态 systemctl status nginx # 应显示active (running)

Nginx的核心配置在/etc/nginx/nginx.conf,但我们不直接修改它,而是为每个站点创建独立的配置文件,便于管理:

# 删除默认的default.conf,创建我们的站点配置 rm -f /etc/nginx/conf.d/default.conf vim /etc/nginx/conf.d/mydjangoapp.conf

mydjangoapp.conf内容如下(这是生产环境的黄金配置):

# Nginx配置文件:/etc/nginx/conf.d/mydjangoapp.conf upstream django_app { # 指向uWSGI的Unix Socket server unix:/var/www/mydjangoapp/mydjangoapp.sock; } server { listen 80; server_name myapp.com www.myapp.com; # 替换为你的域名 charset utf-8; # 最大请求体大小,解决413错误(前端上传大文件) client_max_body_size 10M; # 访问日志(按天轮转) access_log /var/www/mydjangoapp/logs/nginx_access.log main; error_log /var/www/mydjangoapp/logs/nginx_error.log warn; # 处理静态文件(CSS, JS, 图片) location /static { alias /var/www/mydjangoapp/static/; expires 1h; # 浏览器缓存1小时 add_header Cache-Control "public, immutable"; } # 处理媒体文件(用户上传) location /media { alias /var/www/mydjangoapp/media/; expires 1d; } # 将所有其他请求转发给Django应用 location / { # 关键:将请求转发给upstream定义的django_app proxy_pass http://django_app; # 传递关键HTTP头,让Django知道真实情况 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; # 超时设置,防止uWSGI慢请求拖垮Nginx proxy_connect_timeout 30s; proxy_send_timeout 30s; proxy_read_timeout 30s; # 重要:启用uWSGI协议(不是HTTP!) include /etc/nginx/uwsgi_params; } }

解析:upstream django_app定义了一个后端服务器组,里面只有一个成员:unix:/var/www/mydjangoapp/mydjangoapp.socklocation /块中的proxy_pass http://django_app;看似是HTTP,但因为include /etc/nginx/uwsgi_params;的存在,Nginx会自动将这个proxy_pass转换为uwsgi协议通信。uwsgi_params是Nginx自带的文件,里面包含了所有uWSGI需要的环境变量映射,比如UWSGI_SCRIPTUWSGI_CHDIR等。proxy_set_header系列指令至关重要,它们把原始请求的真实信息(如用户IP、协议类型)告诉Django,否则Django拿到的全是127.0.0.1,无法做IP限流或HTTPS判断。

最后一步,收集Django静态文件并重启服务:

# 激活虚拟环境 source /var/www/mydjangoapp/src/venv/bin/activate # 进入项目目录 cd /var/www/mydjangoapp/src # 收集所有静态文件到STATIC_ROOT指定的目录 python3.8 manage.py collectstatic --noinput # 重启Nginx(重新加载配置) systemctl restart nginx # 重启uWSGI(先杀掉再启动,确保配置生效) pkill -f "uwsgi --ini /var/www/mydjangoapp/uwsgi.ini" uwsgi --ini /var/www/mydjangoapp/uwsgi.ini -d /var/www/mydjangoapp/logs/uwsgi.log

现在,打开你的浏览器,访问http://你的服务器IPhttp://你的域名。如果一切顺利,你应该看到Django的欢迎页面!这意味着整个uWSGI+Nginx流水线已经打通。

4. 常见问题与排查技巧实录:那些让你抓狂的413、502、504错误

4.1 HTTP 413 Request Entity Too Large:上传文件失败的元凶

现象:前端用<input type="file">上传一个15MB的视频,点击提交后,浏览器Network面板显示状态码413,Nginx日志里有client intended to send too large body

原因:Nginx默认client_max_body_size是1MB,超过即拒收。这是一个安全保护,但太小了。

排查步骤

  1. 检查Nginx配置:grep "client_max_body_size" /etc/nginx/conf.d/mydjangoapp.conf,确认值是否足够(如10M)。
  2. 检查Nginx是否重载:systemctl reload nginx,不是restartreload只重载配置,不中断服务。
  3. 检查uWSGI是否有额外限制:uWSGI默认不限制请求体大小,但可以在uwsgi.ini中加buffer-size = 32768(32KB)来增加缓冲区,不过通常Nginx层限制就够了。

终极解决方案:在mydjangoapp.confserver块中,明确设置:

client_max_body_size 100M; # 根据业务需求调整,100MB足够大

然后systemctl reload nginx注意:这个值不能设得无限大,否则可能被恶意用户用超大请求耗尽服务器内存。

4.2 HTTP 502 Bad Gateway:Nginx找不到uWSGI

现象:浏览器显示502,Nginx错误日志/var/www/mydjangoapp/logs/nginx_error.log里有connect() to unix:/var/www/mydjangoapp/mydjangoapp.sock failed (111: Connection refused)

原因:Nginx尝试连接uWSGI的socket文件,但文件不存在、权限不对、或uWSGI根本没在运行。

排查四步法

  1. 检查uWSGI进程ps aux | grep uwsgi。如果没有输出,说明uWSGI没启动。执行uwsgi --ini /var/www/mydjangoapp/uwsgi.ini,看终端报错。
  2. 检查socket文件ls -l /var/www/mydjangoapp/mydjangoapp.sock。如果文件不存在,说明uWSGI启动失败;如果存在,看权限是否为srw-rw-r--,属主root,属组nginx。如果不是,手动修复:chown root:nginx /var/www/mydjangoapp/mydjangoapp.sock && chmod 664 /var/www/mydjangoapp/mydjangoapp.sock
  3. 检查uWSGI日志tail -f /var/www/mydjangoapp/logs/uwsgi.log。最常见的错误是Python路径错误(ImportError: No module named 'mysite'),这通常是因为pythonpathmodule配置错了。仔细核对uwsgi.ini里的pythonpath是否指向/var/www/mydjangoapp/srcmodule是否是mysite.wsgi:application
  4. 检查SELinux(CentOS特有):CentOS 7默认开启SELinux,它可能阻止Nginx访问socket文件。临时关闭测试:setenforce 0。如果关闭后502消失,说明是SELinux问题。永久解决方案是设置正确策略:setsebool -P httpd_can_network_connect 1

实操心得:我踩过最深的坑是module配置。Django项目结构是/src/mysite/mysite/wsgi.py,有人会误写成src.mysite.wsgi:application。正确的modulemysite.wsgi:application,因为pythonpath已经把/src加进了Python路径,mysite就是顶层包名。

4.3 HTTP 504 Gateway Timeout:uWSGI处理太慢,Nginx等不及了

现象:浏览器显示504,Nginx错误日志有upstream timed out (110: Connection timed out) while reading response header from upstream

原因:Nginx向uWSGI发起请求后,在proxy_read_timeout设定的时间内(默认60秒),没有收到uWSGI的完整响应头,于是Nginx主动断开连接,返回504。

排查与优化

  • 首先确认是真慢还是假慢:在uWSGI日志里搜索OOPSSegmentation fault,看是否有worker崩溃。如果有,加大processesthreads,或检查代码是否有死循环。
  • 检查数据库查询:一个未加索引的SELECT * FROM huge_table WHERE name LIKE '%xxx%'可能执行30秒。用Django Debug Toolbar或数据库慢查询日志定位。
  • 调整超时时间:在mydjangoapp.conf中,适当延长超时:
    proxy_connect_timeout 60s; proxy_send_timeout 60s; proxy_read_timeout 120s; # 关键!从默认60秒延长到120秒
  • 终极方案:异步任务:对于必须耗时的操作(如发送邮件、生成报表),不要在HTTP请求里同步执行。用Celery+Redis/RabbitMQ把它扔到后台队列,HTTP请求立即返回“任务已提交”,前端用AJAX轮询结果。

4.4 uWSGI启动失败:ImportError、ModuleNotFoundError的迷宫

现象:执行uwsgi --ini ...后,终端直接报错退出,日志里是ImportError: No module named 'django'ModuleNotFoundError: No module named 'mysite'

原因:uWSGI找不到Python模块。根源几乎总是home(虚拟环境路径)或pythonpath(代码路径)配置错误。

快速诊断表

错误信息最可能原因检查命令修复方案
No module named 'django'home路径错误,uWSGI没激活虚拟环境ls /var/www/mydjangoapp/src/venv/bin/activate确认home指向/var/www/mydjangoapp/src/venv,且该路径下有bin/activate文件
No module named 'mysite'pythonpath错误,Python找不到mysitels /var/www/mydjangoapp/src/mysite/wsgi.pypythonpath必须是/var/www/mydjangoapp/src,且该目录下有mysite文件夹(含__init__.py
No module named 'mysite.wsgi'module配置错误,或wsgi.py文件损坏cat /var/www/mydjangoapp/src/mysite/wsgi.py | head -10module应为mysite.wsgi:application,且wsgi.py第一行是import os,最后一行是application = get_wsgi_application()

万能调试命令:在uWSGI配置里加py-autoreload=1,它会让uWSGI在检测到Python文件变化时自动重启,方便开发调试。但生产环境严禁使用,因为它会频繁fork进程,消耗CPU。

5. 进阶与生产加固:从能跑通到能扛住百万并发

5.1 使用Supervisor守护uWSGI进程:让它永不宕机

uwsgi --ini ...命令启动的进程,一旦SSH断开就会退出。生产环境需要一个“看门狗”进程,时刻监控uWSGI,一旦崩溃就自动拉起。

# 安装Supervisor yum install -y epel-release yum install -y supervisor # 创建Supervisor配置文件 vim /etc/supervisord.d/mydjangoapp.ini
[program:mydjangoapp] command=uwsgi --ini /var/www/mydjangoapp/uwsgi.ini directory=/var/www/mydjangoapp/src user=root autostart=true autorestart=true redirect_stderr=true stdout_logfile=/var/www/mydjangoapp/logs/supervisor.log
# 启动Supervisor并重载配置 systemctl start supervisord systemctl enable supervisord supervisorctl reread supervisorctl update supervisorctl start mydjangoapp # 查看状态 supervisorctl status

现在,即使你kill -9掉uWSGI进程,Supervisor也会在1秒内把它拉起来。supervisorctl命令还能让你一键重启、查看日志,比手动pkill优雅得多。

5.2 Nginx SSL/TLS配置:让网站拥有绿色小锁

没有HTTPS的网站,在现代浏览器里会被标记为“不安全”。用Let's Encrypt免费获取SSL证书。

# 安装Certbot yum install -y certbot python3-certbot-nginx # 获取证书(假设你的域名myapp.com已解析到服务器IP) certbot --nginx -d myapp.com -d www.myapp.com # Certbot会自动修改/etc/nginx/conf.d/mydjangoapp.conf,添加443端口配置和重定向 # 你只需重启Nginx systemctl restart nginx

Certbot生成的证书90天过期,它会自动添加一个cron任务来续期。你无需操心。

5.3 性能压测与调优:用ab命令量化你的服务器能力

别猜,要测。Apache Bench (ab) 是最简单的HTTP压测工具。

# 安装 yum install -y httpd-tools # 对首页进行100并发、1000次请求的压测 ab -n 1000 -c 100 http://123.45.67.89/ # 关键指标解读: # Requests per second: 324.56 [#/sec] (mean) # 每秒处理324个请求 # Time per request: 308.119 [ms] (mean) # 每个请求平均耗时308ms # Percentage of the requests served within