1. 部署前必须想清楚的几件事
1.1 为什么本地跑得好好的,一上服务器就出问题
我见过太多这样的情况:本地开发环境里一切正常,python manage.py runserver一敲,页面秒开,数据库读写顺畅,静态文件加载完美。结果往服务器上一扔,要么 502,要么静态文件全丢,要么数据库连不上。问题出在哪?出在开发环境和生产环境根本是两套逻辑。
Django 自带的runserver是一个纯开发用的轻量级服务器,单线程、无并发处理、不做静态文件服务优化、没有进程守护。它设计出来就是给你本地调试用的,官方文档里白纸黑字写着“不要在生产环境使用”。生产环境需要的是一个能扛并发、能守护进程、能配合反向代理的完整方案。
所以部署这件事,本质上不是“把代码传上去”这么简单,而是要重新搭建一套运行环境。这套环境包括:操作系统层面的用户权限、Python 运行环境、数据库服务、Web 服务器、反向代理、进程管理工具、静态文件服务、日志系统。每一项都有坑,每一项都需要你提前想清楚。
这一篇我们先聚焦在最基础的一步:准备服务器。服务器没准备好,后面所有部署步骤都是空中楼阁。
1.2 部署架构的整体思路
在动手之前,先把整体架构在脑子里过一遍。一个典型的 Django 生产部署架构是这样的:
- Nginx作为反向代理,负责接收所有来自外部的 HTTP 请求,处理静态文件,把动态请求转发给后端应用服务器
- Gunicorn(或 uWSGI)作为应用服务器,负责运行 Django 应用,处理业务逻辑
- PostgreSQL(或 MySQL)作为数据库服务,独立运行
- Supervisor(或 systemd)作为进程管理工具,负责守护 Gunicorn 进程,挂了自动重启
- Redis(可选)作为缓存和消息队列
这个架构里,Nginx 在最外层,用户永远接触不到 Django 本身。Django 只跟 Gunicorn 打交道,Gunicorn 只跟 Nginx 打交道。每一层各司其职,互不干扰。
为什么要这么设计?因为 Django 本身不擅长处理静态文件和高并发连接,Nginx 擅长;Gunicorn 擅长运行 Python 应用但不擅长处理 SSL 和静态资源,Nginx 擅长。让专业的工具做专业的事,这是部署架构设计的核心原则。
1.3 服务器选型的几个关键考量
选服务器不是越贵越好,也不是配置越高越好。你需要根据项目的实际需求来定。以下几个维度是必须考虑的:
CPU 和内存。Django 应用本身对 CPU 的要求不算高,但对内存有一定要求。每个 Gunicorn worker 进程大约占用 50-100MB 内存,如果你开 4 个 worker,加上系统本身和其他服务,2GB 内存是起步线。如果预算允许,4GB 会更从容。
硬盘。系统盘不需要太大,20-40GB 足够。但如果你的项目涉及大量用户上传文件、日志文件,就需要额外挂载数据盘。SSD 比机械硬盘在数据库读写上的差距非常明显,能上 SSD 就上 SSD。
带宽。这个取决于你的用户量和内容类型。如果只是文字为主的网站,1-2Mbps 带宽就能支撑不错的访问量。如果有大量图片、视频,带宽需求会成倍增长。
操作系统。Linux 是绝对主流,Ubuntu 和 CentOS 是两个最常见的选择。Ubuntu 的软件包更新更快,社区文档更丰富,对新手更友好。CentOS 更稳定,但软件包版本偏旧。我个人更推荐 Ubuntu 22.04 LTS 或 24.04 LTS,长期支持版本能省去很多升级的麻烦。
注意:不要选 Windows Server 来部署 Django。虽然技术上可行,但你会遇到大量路径、权限、进程管理方面的坑,社区资料也少得多。Linux 是 Django 部署的绝对主场。
2. 服务器初始化:从裸机到可用环境
2.1 首次登录与安全加固
拿到一台全新的服务器,第一件事不是急着装软件,而是做安全加固。一台暴露在公网上的服务器,如果没有基本的安全措施,可能在几小时内就会被扫描和尝试登录。
首先用 root 账号登录服务器。你会收到一条包含 IP 地址和 root 密码的信息。在终端里执行:
ssh root@你的服务器IP输入密码后就能进入系统。接下来立刻做三件事:
第一,修改 SSH 端口。默认的 22 端口是自动化扫描的重灾区。改成一个不常用的高位端口,比如 22022 或者 51234,能过滤掉绝大部分无差别扫描。
# 编辑 SSH 配置文件 vim /etc/ssh/sshd_config # 找到 #Port 22 这一行,改成: Port 22022 # 保存后重启 SSH 服务 systemctl restart sshd改完端口后,不要关闭当前 SSH 连接,另开一个终端用新端口测试能否登录。确认能登录后再关闭旧连接,否则一旦配置有误,你就把自己锁在外面了。
第二,创建普通用户。永远不要用 root 账号做日常操作。创建一个普通用户,需要时再用sudo提权。
# 创建新用户 adduser deploy # 把用户加入 sudo 组 usermod -aG sudo deploy第三,配置 SSH 密钥登录。密码登录存在被暴力破解的风险,密钥登录则几乎不可能被攻破。在本地机器上生成密钥对:
# 在本地终端执行,不是服务器上 ssh-keygen -t ed25519 -C "your_email@example.com"一路回车即可。然后把公钥上传到服务器:
# 在本地终端执行 ssh-copy-id -p 22022 deploy@你的服务器IP完成后,回到服务器上编辑 SSH 配置,禁用密码登录:
# 编辑 /etc/ssh/sshd_config PasswordAuthentication no PubkeyAuthentication yes PermitRootLogin no重启 SSH 服务。从此以后,只有持有私钥的人才能登录,安全性大幅提升。
2.2 系统更新与基础工具安装
安全加固完成后,先更新系统软件包,确保所有已安装的软件都是最新版本:
sudo apt update && sudo apt upgrade -y这个命令在 Ubuntu 上会更新所有软件包。CentOS 上对应的是yum update -y。更新完成后,安装一些后续会用到的工具:
sudo apt install -y build-essential curl wget git vim htop net-tools逐个说明这些工具的用途:
- build-essential:包含 gcc、make 等编译工具,安装某些 Python 包时需要编译 C 扩展
- curl / wget:下载文件用
- git:拉取代码用
- vim:编辑配置文件用,如果你习惯 nano 也可以换成 nano
- htop:比 top 更直观的系统监控工具,排查性能问题很有用
- net-tools:包含 netstat 等网络排查工具
2.3 配置时区和主机名
服务器默认时区通常是 UTC,这对日志分析和定时任务都不友好。改成你所在的时区:
sudo timedatectl set-timezone Asia/Shanghai验证一下:
timedatectl输出里应该能看到Time zone: Asia/Shanghai (CST, +0800)。
顺便设置一个有意义的主机名,方便在多台服务器之间区分:
sudo hostnamectl set-hostname django-prod-012.4 配置防火墙
防火墙是服务器的第一道防线。Ubuntu 上默认使用 ufw,配置起来很简单:
# 先允许 SSH 端口,否则启用防火墙后会断连 sudo ufw allow 22022/tcp # 允许 HTTP 和 HTTPS sudo ufw allow 80/tcp sudo ufw allow 443/tcp # 启用防火墙 sudo ufw enable # 查看状态 sudo ufw status注意:启用防火墙之前一定要先放行 SSH 端口。我见过有人直接
ufw enable,结果 SSH 连接立刻断开,只能通过控制台救援。
如果你用的是云服务商,除了系统防火墙,还要检查云平台的安全组规则。两层防火墙都要放行对应端口,缺一不可。
3. Python 环境与数据库准备
3.1 安装 Python 和 pip
Ubuntu 22.04 自带 Python 3.10,Ubuntu 24.04 自带 Python 3.12。先确认一下版本:
python3 --version如果版本满足你的 Django 版本要求,就可以直接用。如果不满足,需要添加第三方源安装更新的版本。不过对于大多数项目来说,系统自带的版本已经够用了。
安装 pip 和虚拟环境工具:
sudo apt install -y python3-pip python3-venv python3-devpython3-dev包含了 Python 开发头文件,编译某些 C 扩展包(比如 psycopg2)时必须要有。
3.2 创建项目目录和虚拟环境
给项目创建一个专门的目录。我习惯放在/srv下面,这是 Linux 系统中存放服务数据的标准位置:
sudo mkdir -p /srv/myproject sudo chown deploy:deploy /srv/myproject把目录所有权给 deploy 用户,这样后续操作不需要频繁 sudo。
进入目录,创建虚拟环境:
cd /srv/myproject python3 -m venv venv source venv/bin/activate激活后命令行提示符前面会出现(venv)标记。以后每次操作项目都需要先激活虚拟环境。
升级 pip 并安装基础依赖:
pip install --upgrade pip pip install wheel3.3 安装和配置 PostgreSQL
数据库选型上,PostgreSQL 是 Django 项目最推荐的搭配。它在数据完整性、并发处理、JSON 字段支持等方面都优于 MySQL,而且 Django 对它的支持也最完善。
安装 PostgreSQL:
sudo apt install -y postgresql postgresql-contrib libpq-devlibpq-dev是 psycopg2 编译时需要的库。安装完成后,PostgreSQL 服务会自动启动。确认一下状态:
sudo systemctl status postgresql接下来创建数据库和用户。切换到 postgres 系统用户:
sudo -u postgres psql进入 PostgreSQL 命令行后,依次执行:
-- 创建数据库 CREATE DATABASE myproject_db; -- 创建用户并设置密码 CREATE USER myproject_user WITH PASSWORD '一个足够复杂的密码'; -- 配置数据库连接权限 ALTER ROLE myproject_user SET client_encoding TO 'utf8'; ALTER ROLE myproject_user SET default_transaction_isolation TO 'read committed'; ALTER ROLE myproject_user SET timezone TO 'Asia/Shanghai'; -- 授予所有权限 GRANT ALL PRIVILEGES ON DATABASE myproject_db TO myproject_user; -- 退出 \q这里有几个细节值得说明。default_transaction_isolation设置为read committed是 Django 官方推荐的值,能避免一些并发场景下的脏读问题。timezone设置成和服务器一致,避免时间字段出现偏差。
从 PostgreSQL 15 开始,普通用户默认没有在 public schema 上创建表的权限。如果你用的是 15 或更高版本,还需要额外执行:
\c myproject_db GRANT ALL ON SCHEMA public TO myproject_user;3.4 测试数据库连接
配置完成后,测试一下能否正常连接:
psql -h 127.0.0.1 -U myproject_user -d myproject_db输入密码后如果成功进入数据库命令行,说明配置没问题。退出后,把连接信息记下来,后面配置 Django 的settings.py时会用到。
实操心得:数据库密码不要用简单的单词或数字组合。我一般用
openssl rand -base64 24生成一个随机密码,然后保存在密码管理器里。生产环境的数据库密码泄露,后果比代码泄露严重得多。
4. 反向代理与进程管理工具准备
4.1 安装 Nginx
Nginx 负责处理静态文件、SSL 终止、反向代理。安装很简单:
sudo apt install -y nginx安装完成后 Nginx 会自动启动。在浏览器里访问服务器 IP,应该能看到 Nginx 的默认欢迎页面。如果看不到,检查一下防火墙是否放行了 80 端口。
先不急着配置 Nginx,等 Django 项目部署上去之后再配置反向代理。现在只需要确认 Nginx 能正常运行即可。
4.2 安装 Gunicorn
Gunicorn 是 Python 的 WSGI 服务器,负责运行 Django 应用。它可以直接用 pip 安装到虚拟环境里:
source /srv/myproject/venv/bin/activate pip install gunicornGunicorn 的 worker 数量有一个经验公式:(2 × CPU核心数) + 1。比如一台 2 核的服务器,开 5 个 worker 比较合适。但这个公式不是绝对的,还要考虑内存限制。每个 worker 大约占用 50-100MB 内存,如果内存不够,worker 开多了反而会触发 OOM。
查看 CPU 核心数:
nproc4.3 安装 Supervisor
Supervisor 是一个进程管理工具,负责守护 Gunicorn 进程。Gunicorn 挂了它会自动重启,服务器重启后它也会自动拉起 Gunicorn。
sudo apt install -y supervisor安装完成后,Supervisor 的服务会自动启动。它的配置文件放在/etc/supervisor/conf.d/目录下,每个进程一个配置文件。
4.4 目录结构规划
在继续之前,先把服务器的目录结构规划清楚。一个清晰的目录结构能让后续维护省很多事:
/srv/myproject/ ├── venv/ # Python 虚拟环境 ├── app/ # Django 项目代码 │ ├── manage.py │ ├── myproject/ │ └── apps/ ├── static/ # 收集的静态文件 ├── media/ # 用户上传的文件 ├── logs/ # 日志文件 └── backups/ # 数据库备份创建这些目录:
cd /srv/myproject mkdir -p app static media logs backups静态文件和媒体文件放在项目代码目录之外,这样代码更新时不会影响这些文件。日志单独放一个目录,方便排查问题时快速定位。
5. 常见问题与排查技巧实录
5.1 服务器准备阶段的典型问题速查
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| SSH 连接超时 | 防火墙未放行端口 | telnet IP 端口测试 | 检查 ufw 和云安全组 |
| SSH 连接被拒绝 | SSH 服务未启动或端口错误 | systemctl status sshd | 检查 sshd_config 配置 |
| 数据库连接失败 | 用户权限或密码错误 | 查看 PostgreSQL 日志 | 重新授权或重置密码 |
| pip 安装包报错 | 缺少编译依赖 | 查看错误信息中的缺失库 | 安装对应的 dev 包 |
| 磁盘空间不足 | 日志或缓存占满 | df -h查看 | 清理日志或扩容 |
| 内存不足 | worker 数量过多 | free -h查看 | 减少 worker 或升级配置 |
5.2 几个容易踩的坑
坑一:改了 SSH 端口后忘记更新防火墙规则。这个坑我踩过不止一次。改完端口重启 SSH 服务,当前连接还在,感觉一切正常。结果下次登录时发现连不上,因为防火墙还只放行了 22 端口。所以改端口和改防火墙规则要同步进行。
坑二:PostgreSQL 的 peer 认证。默认情况下,PostgreSQL 本地连接使用 peer 认证,也就是用系统用户名匹配数据库用户名。如果你用psql -U myproject_user连接,但系统里没有这个用户,就会报错。解决办法是在pg_hba.conf里把本地连接改成 md5 认证,或者用-h 127.0.0.1强制走 TCP 连接。
坑三:虚拟环境路径写错。在 Supervisor 配置里指定 Gunicorn 路径时,一定要用虚拟环境里的绝对路径,比如/srv/myproject/venv/bin/gunicorn,而不是系统全局的 gunicorn。否则会用到错误的 Python 环境,导致依赖找不到。
坑四:时区不一致。服务器时区、数据库时区、Django 的TIME_ZONE设置,这三者必须一致。否则日志时间对不上,定时任务执行时间也会出问题。我一般统一设置为Asia/Shanghai。
5.3 服务器准备完成后的检查清单
在进入下一步部署之前,对照这个清单逐项确认:
- [ ] SSH 端口已修改,密钥登录已配置,密码登录已禁用
- [ ] 普通用户已创建并加入 sudo 组
- [ ] 系统软件包已更新到最新
- [ ] 时区已设置为 Asia/Shanghai
- [ ] 防火墙已启用,SSH、HTTP、HTTPS 端口已放行
- [ ] Python 版本满足 Django 要求
- [ ] 虚拟环境已创建并激活
- [ ] PostgreSQL 已安装,数据库和用户已创建
- [ ] 数据库连接测试通过
- [ ] Nginx 已安装并能正常访问
- [ ] Gunicorn 已安装到虚拟环境
- [ ] Supervisor 已安装
- [ ] 项目目录结构已创建
这份清单看起来繁琐,但每一项都对应着后面部署步骤的依赖。跳过任何一项,后面都可能出问题。
6. 服务器性能调优的几点经验
6.1 系统层面的基础调优
服务器刚装好时,一些默认参数是针对通用场景的,不一定适合 Web 应用。以下几个调整能明显提升性能:
文件描述符限制。Linux 默认每个进程最多打开 1024 个文件,对于高并发场景不够用。编辑/etc/security/limits.conf,在末尾添加:
* soft nofile 65535 * hard nofile 65535TCP 连接优化。编辑/etc/sysctl.conf,添加以下参数:
net.core.somaxconn = 65535 net.ipv4.tcp_max_syn_backlog = 65535 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_fin_timeout = 30执行sudo sysctl -p让配置生效。这些参数能提升服务器处理大量并发连接的能力。
Swap 分区。如果服务器内存较小(比如 1GB),建议配置 Swap 分区作为内存的补充。虽然 Swap 的速度比物理内存慢很多,但能避免内存耗尽时进程被直接杀掉。
# 创建 2GB 的 swap 文件 sudo fallocate -l 2G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile # 开机自动挂载 echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab6.2 监控工具的安装
服务器跑起来之后,你需要知道它的运行状态。安装几个轻量的监控工具:
sudo apt install -y sysstat iotopsysstat包含sar命令,能查看历史 CPU、内存、IO 数据。iotop能实时查看磁盘 IO 情况。这两个工具在排查性能瓶颈时非常有用。
如果项目规模较大,可以考虑部署更专业的监控方案,比如 Prometheus + Grafana。但对于中小型项目来说,系统自带的工具加上 htop 已经够用了。
6.3 日志管理
服务器上的日志会随着时间不断增长,如果不管理,迟早会把磁盘占满。配置 logrotate 自动切割和清理日志:
sudo vim /etc/logrotate.d/myproject写入以下内容:
/srv/myproject/logs/*.log { daily missingok rotate 14 compress delaycompress notifempty create 0640 deploy deploy sharedscripts postrotate supervisorctl restart myproject endscript }这个配置表示:每天切割一次日志,保留 14 天,压缩旧日志,切割后重启应用让新日志写入新文件。
7. 关于服务器准备的个人体会
服务器准备这一步,看起来都是些琐碎的配置工作,但它决定了整个部署的稳定性。我见过太多项目因为服务器没配好,上线后频繁出问题:时区不对导致定时任务错乱、文件描述符不够导致高并发时连接被拒、日志没切割导致磁盘写满服务崩溃。
这些问题在开发环境里永远不会暴露,只有在生产环境才会显现。而一旦显现,排查起来往往很费时间,因为你会先怀疑代码问题,最后才发现是系统配置问题。
我的建议是:服务器准备阶段宁可多花半天时间,把每一项配置都做到位,也不要急着把代码传上去跑起来。基础打牢了,后面的部署和维护会顺畅很多。
另外,把服务器准备的每一步都记录下来,形成一份文档。下次部署新服务器时直接照着文档走,既快又不容易遗漏。这份文档也是团队协作时的重要资产,别人接手你的项目时能快速理解服务器的配置情况。
下一篇文章会讲 Django 项目代码的部署和配置,包括settings.py的生产环境调整、静态文件收集、数据库迁移、Gunicorn 和 Supervisor 的配置。服务器准备好了,后面的步骤就是水到渠成的事。