十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Python Web项目部署实战:Nginx+Gunicorn+Supervisor全流程指南

Python Web项目部署实战:Nginx+Gunicorn+Supervisor全流程指南 1. 项目概述从开发到上线的最后一公里写代码和让代码真正跑起来完全是两码事。我见过太多开发者在本地环境里把功能调试得完美无缺一到部署环节就手忙脚乱各种环境依赖、端口冲突、权限问题接踵而至最后项目只能躺在自己的电脑里。今天我们就来彻底打通这“最后一公里”把一个典型的Python Web项目比如用Flask或Django写的从你的本地开发机稳稳当当地部署到一台云服务器上让它7x24小时对外提供服务。这个过程远不止是git push和python app.py那么简单。它涉及到服务器环境准备、代码托管与拉取、依赖管理、进程守护、反向代理配置以及安全加固等一系列环环相扣的步骤。我们将采用目前最主流、也最稳妥的“Nginx Gunicorn Supervisor”组合方案来部署一个WSGI应用。这套方案的优势在于分工明确Gunicorn作为高性能的WSGI服务器负责处理Python请求Supervisor作为进程管理工具确保服务异常退出后能自动重启Nginx作为反向代理和静态文件服务器处理外部请求、负载均衡和SSL加密。无论你是刚写完第一个Django博客的新手还是需要为团队项目搭建标准化部署流程的开发者这篇教程都能给你一份可直接“抄作业”的详细清单。2. 部署前的核心准备环境与代码在动手敲部署命令之前充分的准备工作能避免后续80%的坑。这个阶段的核心是让服务器环境尽可能与你的开发环境一致并确保代码能够被安全、便捷地获取。2.1 服务器环境初始化假设你已经拥有一台干净的Linux服务器以Ubuntu 20.04/22.04 LTS为例。第一件事不是急着装Python而是进行系统更新和安装基础编译工具。# 更新软件包列表并升级现有包 sudo apt update sudo apt upgrade -y # 安装后续可能需要的编译工具和基础库 sudo apt install -y build-essential zlib1g-dev libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libreadline-dev libffi-dev libsqlite3-dev wget curl接下来是Python环境。虽然系统可能自带Python3但版本往往较低且直接操作系统的Python可能引发依赖冲突。强烈建议使用pyenv来安装和管理独立的Python版本这是生产环境的最佳实践。# 安装pyenv的依赖 sudo apt install -y make git libssl-dev libbz2-dev libreadline-dev libsqlite3-dev # 使用官方脚本安装pyenv curl https://pyenv.run | bash # 将pyenv初始化命令添加到shell配置文件中如 ~/.bashrc echo export PATH$HOME/.pyenv/bin:$PATH ~/.bashrc echo eval $(pyenv init -) ~/.bashrc echo eval $(pyenv virtualenv-init -) ~/.bashrc # 使配置生效 source ~/.bashrc # 使用pyenv安装指定版本的Python例如Python 3.10.12 pyenv install 3.10.12 pyenv global 3.10.12 # 验证安装 python --version使用pyenv的好处是隔离性。你可以为不同项目创建独立的虚拟环境互不干扰。接下来为你的Web项目创建一个专属的虚拟环境。# 假设项目名为mywebapp pyenv virtualenv 3.10.12 mywebapp-env pyenv activate mywebapp-env # 激活后命令行提示符前通常会显示虚拟环境名 (mywebapp-env)2.2 代码托管与拉取策略如何将本地代码放到服务器上直接scp传输只适用于临时测试对于需要持续更新的项目必须使用版本控制系统。Git是唯一选择。首先在服务器上配置Git全局信息用于提交记录虽然服务器通常只拉取。git config --global user.name Server Deploy git config --global user.email deployyourdomain.com然后你需要决定代码拉取策略。这里有两个主流方案SSH密钥拉取推荐在服务器上生成SSH密钥对将公钥添加到你的GitHub、GitLab或Gitee账户的SSH Keys中。这样服务器就可以无需密码直接拉取私有仓库。ssh-keygen -t rsa -b 4096 -C deployyourdomain.com # 一路回车使用默认路径 cat ~/.ssh/id_rsa.pub # 复制输出的公钥内容添加到你的代码托管平台部署密钥Deploy Key大部分平台支持为仓库单独设置只读的部署密钥比使用个人SSH密钥更安全权限隔离更好。配置好密钥后就可以克隆代码了。切忌将代码放在/root或/home目录下随意摆放。建议建立一个统一的部署目录例如/var/www/。sudo mkdir -p /var/www sudo chown -R $USER:$USER /var/www # 将目录所有权改为当前用户避免权限问题 cd /var/www git clone gitgithub.com:yourusername/your-repo.git mywebapp cd mywebapp此时你的项目代码已经位于/var/www/mywebapp。接下来需要在虚拟环境中安装项目依赖。项目根目录下必须有一个requirements.txt文件。# 确保虚拟环境已激活 (pyenv activate mywebapp-env) pip install --upgrade pip pip install -r requirements.txt注意requirements.txt应该通过pip freeze requirements.txt在开发环境生成但务必检查是否包含了仅开发所需的包如测试框架、调试工具。生产环境可以考虑使用pip install -r requirements/production.txt来区分。3. WSGI服务器与进程守护让应用持续运行在开发时我们使用python manage.py runserver或flask run启动的服务器是单线程、性能低下且不稳定的仅用于调试。生产环境需要一个专业的WSGI服务器。我们选择Gunicorn它性能好、配置简单、与Django/Flask等框架集成度高。3.1 Gunicorn的安装与基础配置首先在虚拟环境中安装Gunicorn如果requirements.txt里没有的话。pip install gunicornGunicorn可以通过命令行直接启动但更规范的做法是使用配置文件。在项目根目录/var/www/mywebapp下创建一个gunicorn_config.py文件。# gunicorn_config.py import multiprocessing # 绑定的IP与端口。0.0.0.0表示监听所有外部请求端口常用8000或8080 bind 0.0.0.0:8000 # 工作进程数。推荐值为 (CPU核心数 * 2) 1 workers multiprocessing.cpu_count() * 2 1 # 工作模式。默认为sync对于I/O密集型应用使用gevent或eventlet可能提升性能但需额外安装 worker_class sync # 每个工作进程处理请求的最大数达到后重启进程防止内存泄漏 max_requests 1000 max_requests_jitter 50 # 超时时间秒超过此时间工作进程会被重启 timeout 30 # 守护进程模式。如果为TrueGunicorn会进入后台运行。但我们用Supervisor管理此处设为False daemon False # 错误日志和访问日志路径 errorlog /var/log/gunicorn/mywebapp_error.log accesslog /var/log/gunicorn/mywebapp_access.log # 日志级别 loglevel info # 设置环境变量例如指定Django的settings模块 raw_env [ DJANGO_SETTINGS_MODULEmyproject.settings.production, # PYTHONPATH/var/www/mywebapp, # 如果需要可以设置Python路径 ]对于Django项目你需要将myproject.settings.production替换为你的生产环境配置文件路径。对于Flask应用通常需要设置FLASK_APP环境变量如raw_env [FLASK_APPapp:create_app()]。创建日志目录并赋予权限sudo mkdir -p /var/log/gunicorn sudo chown -R $USER:$USER /var/log/gunicorn现在你可以测试用Gunicorn启动应用了# Django项目启动方式 gunicorn myproject.wsgi:application -c gunicorn_config.py # Flask项目启动方式 (假设app对象在app.py的create_app函数中) gunicorn app:create_app() -c gunicorn_config.py如果看到输出监听在0.0.0.0:8000说明应用启动成功。但此时如果你断开SSH连接这个进程就会终止。我们需要一个“守护者”来管理它。3.2 使用Supervisor进行进程管理Supervisor是一个用Python写的进程控制工具可以监控进程状态在异常退出时自动重启并方便地管理启动、停止、查看日志。安装Supervisorsudo apt install -y supervisor为我们的应用创建一个Supervisor配置文件/etc/supervisor/conf.d/mywebapp.conf[program:mywebapp] ; 命令。这里关键是指定虚拟环境的Python和Gunicorn路径 command/home/your_username/.pyenv/versions/mywebapp-env/bin/gunicorn myproject.wsgi:application -c /var/www/mywebapp/gunicorn_config.py ; 项目目录 directory/var/www/mywebapp ; 以哪个用户运行 useryour_username ; 自动启动 autostarttrue autorestarttrue ; 启动后等待几秒判断是否成功 startsecs10 ; 标准错误和标准输出日志路径 stderr_logfile/var/log/supervisor/mywebapp_err.log stdout_logfile/var/log/supervisor/mywebapp_out.log ; 环境变量这里非常重要确保在正确的虚拟环境中运行 environmentPYTHONPATH/var/www/mywebapp,PATH/home/your_username/.pyenv/versions/mywebapp-env/bin:%(ENV_PATH)s,VIRTUAL_ENV/home/your_username/.pyenv/versions/mywebapp-env ; 停止信号 stopsignalINT踩坑点command和environment中的路径是最大的坑。command必须使用虚拟环境下的绝对路径指向gunicorn。environment中必须设置PATH将虚拟环境的bin目录放在最前面这样才能确保使用虚拟环境中的Python和依赖。PYTHONPATH有时也需要指定。更新Supervisor配置并启动我们的程序sudo supervisorctl reread sudo supervisorctl update sudo supervisorctl start mywebapp检查状态sudo supervisorctl status mywebapp如果显示RUNNING恭喜你你的Python应用已经在后台稳定运行了你可以通过sudo supervisorctl tail -f mywebapp stdout来查看实时日志。4. Nginx配置反向代理、静态文件与安全现在应用运行在服务器的8000端口但外部无法通过80HTTP或443HTTPS端口访问。同时用Python处理静态文件CSS, JS, 图片效率很低。我们需要Nginx作为反向代理对外接收80/443端口的请求转发给内网的Gunicorn8000端口并直接处理静态文件请求。4.1 安装Nginx与基础站点配置安装Nginxsudo apt install -y nginx删除默认站点配置sudo rm /etc/nginx/sites-enabled/default为我们的应用创建站点配置文件/etc/nginx/sites-available/mywebappserver { listen 80; server_name your_domain.com www.your_domain.com; # 替换为你的域名或服务器IP # 静态文件配置。假设Django的静态文件收集到了/var/www/mywebapp/static/ location /static/ { alias /var/www/mywebapp/static/; expires 30d; add_header Cache-Control public, immutable; } # 媒体文件配置用户上传 location /media/ { alias /var/www/mywebapp/media/; expires 30d; add_header Cache-Control public; } # 将其他所有动态请求转发给Gunicorn location / { # 包含一些代理通用参数 include proxy_params; # 转发到Gunicorn绑定的地址和端口 proxy_pass http://127.0.0.1:8000; # 设置正确的Host头某些框架如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; # 如果应用支持WebSocket需要以下配置 # proxy_http_version 1.1; # proxy_set_header Upgrade $http_upgrade; # proxy_set_header Connection upgrade; } # 禁止访问隐藏文件如.git, .env等 location ~ /\. { deny all; } }创建proxy_params文件通常包含一些通用的代理设置如果不存在可以手动创建/etc/nginx/proxy_paramsproxy_set_header Host $http_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_connect_timeout 60s; proxy_send_timeout 60s; proxy_read_timeout 60s; proxy_buffering off; proxy_request_buffering off;启用站点配置并测试Nginx语法sudo ln -s /etc/nginx/sites-available/mywebapp /etc/nginx/sites-enabled/ sudo nginx -t # 测试配置语法必须显示syntax is ok和test is successful如果测试成功重启Nginxsudo systemctl restart nginx现在你应该能通过服务器的IP地址或你配置的域名如果DNS已解析访问到你的网站了所有静态文件请求将由Nginx直接高效处理动态请求则被代理到Gunicorn。4.2 配置HTTPSSSL/TLS加密在当今互联网不使用HTTPS是不负责任的。我们将使用Let‘s Encrypt提供的免费SSL证书并通过certbot工具自动化获取和续期。安装Certbot和Nginx插件sudo apt install -y certbot python3-certbot-nginx获取并自动配置SSL证书确保server_name中的域名已正确解析到你的服务器IPsudo certbot --nginx -d your_domain.com -d www.your_domain.com按照交互提示操作主要是输入邮箱同意条款。Certbot会自动修改你的Nginx配置添加SSL相关设置并设置自动续期。完成后你的Nginx配置会被更新包含监听443端口的server块以及重定向HTTP到HTTPS的配置。验证自动续期任务是否已添加sudo systemctl status certbot.timer # 或手动测试续期 sudo certbot renew --dry-run至此你的网站已经可以通过https://your_domain.com安全访问了。5. 部署工作流与持续维护部署不是一次性的动作而是一个持续的过程。我们需要建立一套可靠的流程来应对代码更新、数据备份和故障排查。5.1 自动化部署脚本每次更新代码都手动登录服务器执行一系列命令既繁琐又容易出错。一个简单的部署脚本能极大提升效率。在项目根目录创建一个deploy.sh脚本#!/bin/bash # deploy.sh - 简易部署脚本 set -e # 遇到错误立即退出 echo 开始部署... # 1. 进入项目目录 cd /var/www/mywebapp # 2. 拉取最新代码 echo 拉取最新代码... git fetch --all git reset --hard origin/main # 假设主分支是main可根据需要修改 # 3. 激活虚拟环境如果脚本在非虚拟环境下运行 export PATH/home/your_username/.pyenv/versions/mywebapp-env/bin:$PATH # 4. 安装/更新依赖 echo 安装依赖... pip install -r requirements.txt # 5. 执行数据库迁移Django项目 echo 执行数据库迁移... python manage.py migrate --noinput # 6. 收集静态文件Django项目 echo 收集静态文件... python manage.py collectstatic --noinput --clear # 7. 重启应用服务 echo 重启Gunicorn... sudo supervisorctl restart mywebapp # 8. 可选重启Celery Worker等如果有 # sudo supervisorctl restart celery_worker echo 部署完成给脚本添加执行权限并在本地通过SSH远程执行chmod x /var/www/mywebapp/deploy.sh # 本地执行 ssh your_usernameyour_server_ip cd /var/www/mywebapp ./deploy.sh更进阶的做法是结合Git的Webhook如GitHub Actions、GitLab CI/CD在代码推送到特定分支时自动触发服务器端的部署脚本。5.2 日志管理与故障排查系统出问题时日志是你最好的朋友。我们已经在关键位置配置了日志应用日志/var/log/gunicorn/mywebapp_error.log(Gunicorn错误日志)应用访问日志/var/log/gunicorn/mywebapp_access.log进程管理日志/var/log/supervisor/mywebapp_out.log和mywebapp_err.logWeb服务器日志/var/log/nginx/access.log和error.log常用的日志查看命令# 实时查看Gunicorn错误日志 tail -f /var/log/gunicorn/mywebapp_error.log # 查看Supervisor管理的应用输出 sudo supervisorctl tail -f mywebapp stdout # 查看最近Nginx错误 sudo tail -f /var/log/nginx/error.log # 查看特定时间段的Nginx访问日志用于分析流量或问题 sudo grep 25/May/2024:14 /var/log/nginx/access.log当网站无法访问时一个标准的排查链路是检查Nginx状态sudo systemctl status nginx。如果停止尝试sudo nginx -t检查配置然后重启。检查Supervisor状态sudo supervisorctl status mywebapp。如果是FATAL或BACKOFF查看对应的stderr日志。检查端口占用sudo netstat -tlnp | grep :8000和sudo netstat -tlnp | grep :80确保Gunicorn和Nginx在监听。检查防火墙sudo ufw status确保80和443端口是允许的Allow。逐级查看日志从Nginx error.log开始再到Supervisor的err.log最后到Gunicorn的error.log通常能找到具体的错误信息。5.3 安全加固与性能调优部署上线后安全与性能是永恒的主题。安全加固建议防火墙确保只开放必要的端口SSH的22HTTP的80HTTPS的443。关闭其他所有端口。sudo ufw allow 22/tcp sudo ufw allow 80/tcp sudo ufw allow 443/tcp sudo ufw enable禁用SSH密码登录使用SSH密钥对登录并修改SSH默认端口可选但建议。保持系统更新定期运行sudo apt update sudo apt upgrade。保护敏感数据永远不要将SECRET_KEY、数据库密码等硬编码在代码中或提交到Git。使用环境变量.env文件或专门的密钥管理服务并通过python-dotenv等库加载。数据库安全如果使用数据库如PostgreSQL/MySQL确保只允许本地连接127.0.0.1并为应用创建专属的、权限受限的用户。性能调优思路Gunicorn Workers根据服务器CPU核心数和应用类型I/O密集或CPU密集调整workers数量。对于I/O密集型应用如大量数据库查询、网络请求可以尝试使用gevent或eventlet作为worker_class并增加worker_connections。数据库连接池使用django-db-connections或SQLAlchemy的连接池功能避免频繁建立/断开数据库连接的开销。缓存引入Redis或Memcached作为缓存后端缓存数据库查询结果、会话数据等。静态文件CDN将/static/和/media/路径通过Nginx配置指向CDN减轻服务器负担。Nginx缓存对于不常变化的动态页面可以在Nginx层设置代理缓存。部署一个Python Web项目就像组装一台精密仪器每个部件都有其作用任何一个环节的疏漏都可能导致整个系统不稳定。从干净的虚拟环境到稳健的进程守护再到高效安全的反向代理每一步都需要清晰的理解和仔细的配置。我个人的体会是初期严格按照一个经过验证的流程比如本篇教程的步骤来操作能帮你避开绝大多数“坑”。等到这套流程跑顺了再去深入研究每个组件的高级特性和调优策略比如用Docker容器化部署、用Kubernetes做编排那又是另一片天地了。但无论如何把今天这套“Nginx Gunicorn Supervisor”的基础打牢是你日后应对更复杂部署场景的底气。
返回列表