1. 部署前必须想清楚的几件事1.1 为什么部署准备阶段最容易被低估很多人做Django项目本地跑得飞起runserver一开浏览器一刷感觉整个世界都是自己的。但一到部署环节各种问题就像约好了一样集体爆发静态文件404、数据库连不上、环境变量没配、依赖版本冲突、进程莫名其妙挂掉。我见过太多人在这步卡了整整一周最后心态崩了直接放弃。问题的根源在于开发环境和生产环境是两套完全不同的逻辑。开发时你用的是DEBUGTrueDjango帮你把静态文件、错误页面、数据库查询日志全包了生产环境DEBUGFalse这些便利全部消失你得自己把每一环都接上。所以部署准备阶段的核心任务不是“把代码传上去”这么简单而是把本地那套依赖Django开发服务器兜底的东西全部替换成生产级的方案。这一章我聚焦在“准备服务器”这个环节。所谓准备包括服务器选型、系统初始化、基础环境搭建、安全基线配置这几大块。这些工作做扎实了后面的代码部署、Nginx配置、进程管理才有稳固的地基。我个人的经验是准备阶段多花一小时部署阶段少踩十个坑。1.2 服务器选型的几个关键决策点选服务器这件事没有绝对的最优解只有适不适合你当前阶段。我一般从四个维度来评估第一项目规模与预期流量。个人项目、内部工具、小型对外服务一台1核2G的入门级云服务器完全够用。如果是面向公众的内容站点起步建议2核4G因为Django本身加上数据库、Nginx、进程管理器内存消耗不小。我实测过一个中等复杂度的Django项目gunicorn开3个worker加上PostgreSQL和Nginx空闲状态下内存占用在1.2G左右跑起来峰值能到1.8G。所以2G内存的机器跑生产环境会非常紧张系统时不时触发OOM Killer把进程杀掉你还以为是代码问题。第二操作系统选择。我个人强烈建议用Ubuntu LTS版本比如22.04或24.04。原因很实际社区资料最丰富遇到问题搜索出来的答案大概率能直接套用软件包版本比较新Python版本不会太旧apt包管理用起来顺手。CentOS虽然稳定但Stream版本之后生态有些变化新手容易迷糊。Debian也很稳但软件包偏保守有时候装个新版本的Python还得自己编译徒增麻烦。第三服务器地域与网络。这个要看你的目标用户在哪里。如果用户主要在国内选国内节点延迟低如果面向海外选对应区域的节点。另外要注意的是不同服务商之间的网络质量差异很大同样的配置有的服务商晚高峰丢包严重有的就很稳。建议先用最低配的按量付费实例测试一下网络质量满意了再买长期套餐。第四成本控制。新手容易犯的错是一上来就买高配结果项目根本没几个人访问钱白花。我的建议是从低配起步架构上留好横向扩展的余地。比如你先用一台2核4G的机器把数据库和应用放一起等流量上来了再把数据库拆到独立实例应用层加机器做负载均衡。这种渐进式的思路比一步到位更务实。1.3 准备工作的整体流程图在动手之前先把整个准备阶段的脉络理清楚。我习惯把准备工作分成四个阶段阶段核心任务产出物系统初始化更新系统、创建用户、配置SSH安全的基础系统环境搭建安装Python、数据库、Nginx可运行的服务环境项目适配依赖冻结、配置分离、静态文件可部署的代码包安全加固防火墙、密钥登录、权限控制生产级安全基线这四个阶段有严格的先后顺序。系统没初始化好就装环境后面权限会乱环境没搭好就传代码依赖装不上安全没加固就上线等于裸奔。下面我逐个拆解。2. 服务器系统初始化实操2.1 第一次登录与基础更新拿到服务器后服务商会给你一个公网IP和root密码或者密钥。第一次登录用SSHssh root你的服务器IP登录进去第一件事不是急着装东西而是更新系统。这一步很多人跳过结果后面装软件时各种依赖冲突。命令很简单apt update apt upgrade -yapt update是刷新软件源索引apt upgrade是升级已安装的包。加-y是自动确认省得你一直按回车。这个过程可能要几分钟取决于更新量。更新完之后建议重启一次reboot重启后重新登录。为什么要重启因为有些内核更新需要重启才能生效不重启的话可能留下隐患。注意如果你用的是按量付费的实例重启不会额外扣费放心操作。但如果是某些特殊计费模式的实例重启前确认一下计费规则。2.2 创建专用用户别用root裸奔用root操作服务器是新手最常见的坏习惯。root权限太大一个手误rm -rf就能把系统删干净。正确做法是创建一个普通用户需要提权时用sudo。adduser deploy系统会提示你设置密码和用户信息。密码设复杂一点用户信息可以随便填。然后把这个用户加入sudo组usermod -aG sudo deploy-aG的意思是“追加到组”不是覆盖。这一步做完deploy用户就能用sudo执行管理员命令了。接下来配置SSH密钥登录。在你的本地机器上不是服务器生成一对密钥ssh-keygen -t ed25519 -C deploymyservered25519是目前推荐的加密算法比RSA更安全也更短。一路回车默认会在~/.ssh/下生成id_ed25519私钥和id_ed25519.pub公钥。私钥绝对不能泄露公钥可以随便给。把公钥传到服务器上ssh-copy-id -i ~/.ssh/id_ed25519.pub deploy你的服务器IP这条命令会自动把公钥追加到服务器的~/.ssh/authorized_keys文件里。如果ssh-copy-id不可用也可以手动复制cat ~/.ssh/id_ed25519.pub然后登录服务器切换到deploy用户创建.ssh目录并写入mkdir -p ~/.ssh chmod 700 ~/.ssh echo 你的公钥内容 ~/.ssh/authorized_keys chmod 600 ~/.ssh/authorized_keys权限设置很关键。.ssh目录必须是700authorized_keys必须是600否则SSH会拒绝使用密钥登录。这个坑我踩过当时折腾了半天才发现是权限问题。2.3 关闭密码登录与root远程登录密钥配好之后先别急着关密码登录新开一个终端窗口测试密钥能不能正常登录。确认没问题了再修改SSH配置sudo nano /etc/ssh/sshd_config找到这几行改成PermitRootLogin no PasswordAuthentication no PubkeyAuthentication yesPermitRootLogin no禁止root直接SSH登录PasswordAuthentication no关闭密码认证只允许密钥。改完保存重启SSH服务sudo systemctl restart sshd重要提醒重启SSH之前务必保持当前连接不要断开另开一个窗口测试新配置是否生效。如果配置写错了导致连不上你还有当前这个连接可以救回来。这个习惯能帮你避免“把自己锁在门外”的尴尬。2.4 配置防火墙只开必要的端口服务器暴露在公网上不开防火墙等于把家门敞开。Ubuntu自带ufw用起来很简单sudo ufw allow OpenSSH sudo ufw allow 80/tcp sudo ufw allow 443/tcp sudo ufw enableOpenSSH是预定义的规则允许22端口。80和443分别是HTTP和HTTPS。其他端口一律不放行。启用后查看状态sudo ufw status verbose输出应该显示22、80、443是ALLOW其余是DENY。这里有个细节如果你改了SSH端口记得放行新端口再启用防火墙否则启用瞬间你就断连了。数据库端口比如PostgreSQL的5432绝对不要对公网开放。应用和数据库在同一台机器时走本地回环地址就行如果数据库在另一台机器用内网IP通信或者通过SSH隧道不要直接暴露公网端口。3. 生产环境基础软件搭建3.1 Python环境版本选择与虚拟环境Django项目对Python版本有要求。Django 4.2 LTS支持Python 3.8到3.12Django 5.x要求Python 3.10以上。Ubuntu 22.04自带Python 3.1024.04自带3.12基本够用。先确认版本python3 --version如果系统自带的版本满足要求直接用就行。如果需要其他版本可以用deadsnakesPPA或者pyenv但我不建议在生产环境折腾这些能用系统自带的就用自带的省心。安装必要的系统依赖sudo apt install -y python3-pip python3-venv python3-dev build-essential libpq-dev这几个包的作用python3-pip是包管理器python3-venv创建虚拟环境python3-dev提供编译Python扩展需要的头文件build-essential是编译工具链libpq-dev是PostgreSQL的客户端开发库如果你用PostgreSQL的话。虚拟环境是必须的。系统级的Python环境不要直接装项目依赖否则不同项目之间会打架。在项目目录下创建python3 -m venv venv source venv/bin/activate激活后命令行前面会出现(venv)标识。之后所有pip install都装在这个隔离环境里。3.2 数据库选型与安装Django支持多种数据库生产环境我推荐PostgreSQL。原因有三功能强大支持JSON字段、全文搜索、事务性DDL社区活跃Django对它的支持最完善性能稳定高并发下表现比MySQL更可预测。安装PostgreSQLsudo apt install -y postgresql postgresql-contrib安装完成后PostgreSQL服务会自动启动。切换到postgres用户创建数据库和用户sudo -u postgres psql进入psql命令行后CREATE DATABASE myproject; CREATE USER myuser WITH ENCRYPTED PASSWORD 你的强密码; ALTER ROLE myuser SET client_encoding TO utf8; ALTER ROLE myuser SET default_transaction_isolation TO read committed; ALTER ROLE myuser SET timezone TO Asia/Shanghai; GRANT ALL PRIVILEGES ON DATABASE myproject TO myuser; \q这几条命令的含义创建数据库、创建用户并设密码、设置编码为UTF-8、设置事务隔离级别、设置时区、授权。\q退出。实操心得密码不要用特殊字符太多的因为后面在环境变量文件里写连接字符串时特殊字符需要转义容易出错。用大小写字母加数字的组合长度16位以上就很安全了。3.3 Nginx安装与基础配置Nginx负责两件事处理静态文件以及作为反向代理把动态请求转发给Django应用。安装sudo apt install -y nginx安装后Nginx自动启动。默认配置文件在/etc/nginx/sites-available/下。先删掉默认站点sudo rm /etc/nginx/sites-enabled/default然后为你的项目创建配置文件sudo nano /etc/nginx/sites-available/myproject内容大致如下server { listen 80; server_name your_domain_or_ip; location /static/ { alias /home/deploy/myproject/staticfiles/; } location /media/ { alias /home/deploy/myproject/media/; } 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; } }这个配置的意思是/static/和/media/开头的请求Nginx直接从磁盘读文件返回不经过Django其他请求转发给本机8000端口那里跑着Django应用。proxy_set_header那几行是把客户端的真实信息传给Django否则Django看到的全是Nginx的IP。创建软链接启用站点sudo ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled/测试配置语法sudo nginx -t如果显示syntax is ok和test is successful就可以重载Nginxsudo systemctl reload nginx3.4 进程管理工具的选择Django自带的runserver绝对不能用于生产环境它是单线程的性能差且不安全。生产环境需要用WSGI服务器常见的有gunicorn和uWSGI。我推荐gunicorn配置简单和Django集成好。安装pip install gunicorn但光有gunicorn还不够还需要一个进程管理器来保证它挂了能自动重启、开机自启。systemd是Ubuntu自带的用它就行不需要额外装supervisor。创建systemd服务文件sudo nano /etc/systemd/system/myproject.service内容[Unit] DescriptionGunicorn daemon for myproject Afternetwork.target [Service] Userdeploy Groupwww-data WorkingDirectory/home/deploy/myproject ExecStart/home/deploy/myproject/venv/bin/gunicorn \ --access-logfile - \ --workers 3 \ --bind unix:/home/deploy/myproject/myproject.sock \ myproject.wsgi:application [Install] WantedBymulti-user.target这里有几个关键点User和Group指定运行身份WorkingDirectory是项目根目录ExecStart是启动命令。--workers 3表示开3个worker进程一般公式是2 * CPU核心数 1。--bind用Unix socket比TCP端口更高效因为省去了网络协议栈的开销。注意如果用Unix socketNginx配置里的proxy_pass要改成http://unix:/home/deploy/myproject/myproject.sock并且确保Nginx进程有权限访问这个socket文件。启动服务sudo systemctl start myproject sudo systemctl enable myprojectenable是设置开机自启。查看状态sudo systemctl status myproject如果显示active (running)就说明成功了。4. 项目代码的生产环境适配4.1 依赖冻结与版本锁定本地开发时pip install装的包版本可能和生产环境不一致导致“本地能跑线上报错”。解决办法是冻结依赖版本pip freeze requirements.txt这个文件会列出所有包及其精确版本。部署时用pip install -r requirements.txt但pip freeze有个问题它会把所有间接依赖也列出来文件可能很长。更优雅的方式是用pip-tools维护一个requirements.in只写直接依赖然后编译出锁定的requirements.txt。不过对于大多数项目直接用pip freeze就够了。实操心得每次部署前先在本地用pip freeze更新requirements.txt提交到代码仓库再在服务器上拉取。不要直接在服务器上pip install新包那样版本会失控。4.2 配置分离敏感信息不进代码库SECRET_KEY、数据库密码、邮箱密码这些敏感信息绝对不能硬编码在settings.py里更不能提交到代码仓库。正确做法是用环境变量。安装python-dotenvpip install python-dotenv在项目根目录创建.env文件SECRET_KEY你的密钥 DEBUGFalse DATABASE_URLpostgres://myuser:密码localhost:5432/myproject ALLOWED_HOSTSyour_domain.com,你的服务器IP然后在settings.py里读取import os from dotenv import load_dotenv load_dotenv() SECRET_KEY os.getenv(SECRET_KEY) DEBUG os.getenv(DEBUG, False) True ALLOWED_HOSTS os.getenv(ALLOWED_HOSTS, ).split(,).env文件要加入.gitignore确保不会被提交。服务器上手动创建这个文件权限设为600只有deploy用户能读。4.3 静态文件收集与媒体文件处理DEBUGFalse时Django不再自动服务静态文件。需要先收集python manage.py collectstatic这个命令会把所有app的静态文件复制到STATIC_ROOT指定的目录。在settings.py里配置STATIC_URL /static/ STATIC_ROOT BASE_DIR / staticfiles MEDIA_URL /media/ MEDIA_ROOT BASE_DIR / mediacollectstatic之后Nginx配置里的alias指向staticfiles目录就能正常访问了。媒体文件用户上传的文件不需要收集直接放在MEDIA_ROOTNginx同样配置alias指向它。但要注意权限Nginx进程通常是www-data用户需要对这些目录有读权限。我一般把目录权限设为755文件权限644。4.4 数据库迁移与超级用户创建代码部署上去之后执行迁移python manage.py migrate这个命令会根据迁移文件创建或修改数据库表结构。第一次部署时数据库是空的migrate会建好所有表。然后创建超级用户python manage.py createsuperuser按提示输入用户名、邮箱、密码。这个账号用来登录Django Admin后台。注意migrate之前确保数据库连接配置正确否则会报错。可以先跑python manage.py check --database default测试连接。5. 常见问题与排查技巧实录5.1 部署准备阶段高频问题速查表问题现象可能原因排查方法解决方案SSH连不上防火墙拦截、SSH服务未启动sudo ufw status、systemctl status sshd放行22端口、启动sshd密钥登录失败权限不对、公钥未正确写入查看/var/log/auth.log设置.ssh为700、authorized_keys为600pip安装慢默认源在国外无换国内镜像源数据库连接拒绝服务未启动、密码错误、pg_hba配置systemctl status postgresql启动服务、检查密码和认证配置静态文件404未collectstatic、Nginx路径错检查STATIC_ROOT和Nginx alias执行collectstatic、修正路径502 Bad GatewayGunicorn未启动、socket权限systemctl status myproject启动服务、检查socket文件权限内存不足进程被杀配置太低、worker太多dmesggrep -i kill5.2 几个我踩过的坑和独家技巧坑一时区问题导致日志时间对不上。Django的TIME_ZONE默认是UTC数据库时区也可能不是本地时区。我建议统一设为Asia/Shanghai并且在PostgreSQL里也设置时区。settings.py里TIME_ZONE Asia/Shanghai USE_TZ TrueUSE_TZTrue让Django内部用UTC存储显示时转成本地时区。这样既保证了数据一致性又让用户看到正确的时间。坑二ALLOWED_HOSTS没配导致400错误。DEBUGFalse时Django会严格检查请求的Host头。如果ALLOWED_HOSTS是空的所有请求都会被拒绝。一定要把域名和IP都加进去。坑三Gunicorn worker超时。默认超时是30秒如果某个请求处理时间超过30秒worker会被杀掉重启。对于耗时操作比如发邮件、生成报表要么优化代码要么调大--timeout参数。但调太大也不好会掩盖性能问题。技巧一用journalctl看服务日志。systemd管理的服务日志用journalctl -u myproject -f实时查看比翻日志文件方便。技巧二部署前先在本地用DEBUGFalse跑一遍。很多人本地一直用DEBUGTrue部署时才改成False结果一堆问题。我习惯在本地就用DEBUGFalse测试提前暴露问题。技巧三备份数据库再迁移。migrate有时候会出意外尤其是涉及数据变更的迁移。执行前先pg_dump备份pg_dump -U myuser myproject backup_$(date %Y%m%d).sql出问题了可以快速回滚。5.3 安全加固的几条底线准备服务器时安全配置是底线不能妥协。我总结了几条必须做到的禁用root远程登录用普通用户sudo。禁用密码认证只用密钥登录。防火墙只开必要端口数据库端口绝不暴露公网。定期更新系统apt upgrade至少每月一次。.env文件权限600只有运行用户能读。数据库用户最小权限不要用超级用户跑应用。Nginx隐藏版本号在nginx.conf里加server_tokens off;。这些措施不能保证100%安全但能挡住绝大多数自动化扫描和低级攻击。安全是一个持续的过程不是配一次就完事。6. 准备完成后的验收清单服务器准备完之后别急着部署代码先做一轮验收。我习惯用下面这个清单逐项确认[ ] 系统已更新到最新已重启[ ] 专用用户已创建sudo权限正常[ ] SSH密钥登录正常密码登录已关闭[ ] root远程登录已禁用[ ] 防火墙已启用只放行22、80、443[ ] Python版本符合要求虚拟环境可用[ ] PostgreSQL已安装数据库和用户已创建[ ] Nginx已安装配置文件语法正确[ ] Gunicorn已安装systemd服务已配置[ ] 项目代码已上传依赖已安装[ ].env文件已创建权限600[ ]collectstatic已执行静态文件目录权限正确[ ] 数据库迁移已执行超级用户已创建[ ] 所有服务开机自启已启用这份清单看起来琐碎但每一条都对应一个实际部署中会遇到的坑。全部打勾之后你的服务器就处于一个“随时可以上线”的状态。我个人在实际操作中的体会是部署准备阶段最忌讳“差不多就行”的心态。每一个“差不多”最后都会变成“差很多”。权限差不多就行结果SSH连不上防火墙差不多就行结果数据库被扫版本差不多就行结果依赖冲突。把每一步做扎实后面才能睡得安稳。