Skip to content

PM2 部署 Django 项目

一、核心要点速览

💡 核心考点

  • 部署方案: Gunicorn/uWSGI + PM2 进程管理
  • 配置文件: ecosystem.config.js 指定 Python 解释器
  • 虚拟环境: 正确配置 PYTHONPATH 和 PATH
  • Nginx 反向代理: 处理静态文件和 HTTPS
  • 自动化部署: deploy.sh 脚本一键部署

二、Gunicorn + PM2 方案

1. 完整部署流程

bash
# ========== 步骤 1: 安装依赖 ==========
cd /var/www/my-django-app

# 创建虚拟环境
python3 -m venv venv
source venv/bin/activate

# 安装 Gunicorn 和依赖
pip install gunicorn django


# ========== 步骤 2: 创建配置文件 ==========
cat > ecosystem.config.js << 'EOF'
module.exports = {
  apps: [{
    name: 'my-django-app',
    script: './venv/bin/gunicorn',
    args: 'myproject.wsgi:application --bind 127.0.0.1:8000 --workers 4',
    interpreter: 'python3',
    
    env: {
      DJANGO_SETTINGS_MODULE: 'myproject.settings.production',
      PYTHONPATH: '/var/www/my-django-app',
      PATH: '/var/www/my-django-app/venv/bin:/usr/local/bin:/usr/bin:/bin'
    },
    
    log_date_format: 'YYYY-MM-DD HH:mm:ss',
    error_file: './logs/gunicorn-error.log',
    out_file: './logs/gunicorn-out.log',
    
    autorestart: true,
    max_restarts: 10,
    min_uptime: '10s'
  }]
}
EOF


# ========== 步骤 3: 启动应用 ==========
pm2 start ecosystem.config.js

# 验证
pm2 status
pm2 logs my-django-app


# ========== 步骤 4: 配置 Nginx ==========
cat > /etc/nginx/sites-available/django-app << 'EOF'
server {
    listen 80;
    server_name example.com;
    
    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 /var/www/my-django-app/static/;
    }
    
    location /media/ {
        alias /var/www/my-django-app/media/;
    }
}
EOF

ln -s /etc/nginx/sites-available/django-app /etc/nginx/sites-enabled/
nginx -t
systemctl restart nginx


# ========== 步骤 5: 收集静态文件 ==========
source venv/bin/activate
python manage.py collectstatic --noinput

2. 配置详解

┌──────────────────────────────────────────────────────────┐
│           Gunicorn + PM2 配置要点                         │
└──────────────────────────────────────────────────────────┘

关键配置项:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
script: './venv/bin/gunicorn'
  ✓ 指向虚拟环境中的 gunicorn 可执行文件
  ✓ 确保使用正确的 Python 版本

args: 'myproject.wsgi:application --bind 127.0.0.1:8000 --workers 4'
  ✓ WSGI 应用入口
  ✓ 绑定地址和端口
  ✓ worker 数量(通常是 CPU 核心数 × 2 + 1)

interpreter: 'python3'
  ✓ 指定 Python 解释器
  ✓ 必须是 python3 或完整路径

env.PYTHONPATH: '/var/www/my-django-app'
  ✓ 添加项目根目录到 Python 路径
  ✓ 确保 Django 能找到项目模块

env.PATH: '/var/www/my-django-app/venv/bin:...'
  ✓ 优先使用虚拟环境的包
  ✓ 包含系统路径避免冲突
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

三、uWSGI + PM2 方案

1. 配置步骤

bash
# ========== 安装 uWSGI ==========
pip install uwsgi


# ========== 创建 uWSGI 配置 ==========
cat > uwsgi.ini << 'EOF'
[uwsgi]
chdir = /var/www/my-django-app
module = myproject.wsgi:application
home = /var/www/my-django-app/venv

master = true
processes = 4
threads = 2

socket = 127.0.0.1:8000
chmod-socket = 664
vacuum = true

die-on-term = true

logto = /var/www/my-django-app/logs/uwsgi.log
EOF


# ========== PM2 配置 ==========
cat > ecosystem.config.js << 'EOF'
module.exports = {
  apps: [{
    name: 'django-uwsgi',
    script: '/var/www/my-django-app/venv/bin/uwsgi',
    args: './uwsgi.ini',
    interpreter: 'python3',
    
    env: {
      DJANGO_SETTINGS_MODULE: 'myproject.settings.production',
      PYTHONPATH: '/var/www/my-django-app'
    },
    
    autorestart: true,
    max_restarts: 10
  }]
}
EOF

# 启动
pm2 start ecosystem.config.js

2. Gunicorn vs uWSGI 对比

对比维度GunicornuWSGI说明
配置复杂度⭐⭐⭐⭐⭐ (简单)⭐⭐⭐ (复杂)Gunicorn 开箱即用,uWSGI 配置繁琐
实现语言PythonCuWSGI 性能更优,但调试困难
功能丰富度⭐⭐⭐⭐⭐⭐⭐⭐uWSGI 支持更多协议和功能
资源占用Gunicorn 更轻量
社区支持活跃成熟两者都有良好生态
学习曲线平缓陡峭Gunicorn 上手更快
适用规模中小型项目大型项目按项目规模选择

Gunicorn 特点:

优势 ✓劣势 ✗
配置简单,开箱即用功能相对单一
纯 Python 实现,易维护不支持 WebSocket 等高级协议
性能优秀,资源占用少监控和管理功能较弱
社区活跃,文档完善-

uWSGI 特点:

优势 ✓劣势 ✗
功能丰富,配置灵活配置复杂,学习曲线陡
支持多种协议(HTTP、WebSocket)调试和排查问题困难
强大的监控和管理功能文档繁多,难以全面掌握
C 语言实现,性能极致资源占用相对较高

推荐选择:

项目类型推荐方案理由
中小型项目Gunicorn简单够用,快速部署
大型项目uWSGI功能强大,可扩展性强
团队熟悉 PythonGunicorn易于维护和调试
有专业运维团队uWSGI发挥全部功能潜力

四、自动化部署脚本

1. deploy.sh

bash
#!/bin/bash
# deploy.sh - Django 项目自动化部署脚本

set -e

PROJECT_NAME="my-django-app"
PROJECT_DIR="/var/www/$PROJECT_NAME"
PYTHON_VERSION="3.10"

echo "🚀 开始部署 Django 项目..."

# 进入项目目录
cd $PROJECT_DIR

# 拉取最新代码
git pull origin main

# 更新虚拟环境
python$PYTHON_VERSION -m venv venv
source venv/bin/activate

# 安装依赖
pip install --upgrade pip
pip install -r requirements.txt

# 数据库迁移
python manage.py migrate

# 收集静态文件
python manage.py collectstatic --noinput

# 重启应用
pm2 restart $PROJECT_NAME

# 清理旧版本
pip cache purge

echo "✅ 部署完成!"
pm2 status $PROJECT_NAME

2. 使用方法

bash
# 赋予执行权限
chmod +x deploy.sh

# 执行部署
./deploy.sh

# 输出示例:
# 🚀 开始部署 Django 项目...
# Already up to date.
# Requirement already satisfied: django in ./venv
# Applying blog.0001_initial... OK
# Collecting static files... OK
# ✅ 部署完成!
# ┌────┬─────────────┬──────────┬──────┬───────────┬──────────┐
# │ id │ name        │ mode     │ ↺    │ status    │ cpu      │
# ├────┼─────────────┼──────────┼──────┼───────────┼──────────┤
# │ 0  │ my-django-… │ fork     │ 10   │ online    │ 0%       │
# └────┴─────────────┴──────────┴──────┴───────────┴──────────┘

五、Django 项目结构

/var/www/my-django-app/
├── manage.py
├── requirements.txt
├── ecosystem.config.js      # PM2 配置
├── uwsgi.ini               # uWSGI 配置(可选)
├── .env                    # 环境变量

├── myproject/              # Django 项目
│   ├── __init__.py
│   ├── settings/
│   │   ├── base.py
│   │   ├── development.py
│   │   └── production.py
│   ├── urls.py
│   └── wsgi.py

├── apps/                   # 应用目录
│   ├── blog/
│   └── api/

├── static/                 # 静态文件源
│   ├── css/
│   ├── js/
│   └── images/

├── staticfiles/            # collectstatic 目标
│   └── ...

├── media/                  # 用户上传文件
│   └── uploads/

├── logs/                   # 日志目录
│   ├── gunicorn-error.log
│   ├── gunicorn-out.log
│   └── django.log

└── venv/                   # Python 虚拟环境
    ├── bin/
    │   ├── python
    │   ├── pip
    │   └── gunicorn
    └── lib/

六、Nginx 配置详解

1. 基础配置

nginx
server {
    listen 80;
    server_name example.com;
    
    # 最大上传文件大小
    client_max_body_size 10M;
    
    # 超时设置
    proxy_connect_timeout 60s;
    proxy_send_timeout 60s;
    proxy_read_timeout 60s;
    
    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;
        
        # WebSocket 支持(可选)
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
    
    # 静态文件
    location /static/ {
        alias /var/www/my-django-app/static/;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }
    
    # 媒体文件
    location /media/ {
        alias /var/www/my-django-app/media/;
        expires 7d;
    }
    
    # 安全头部
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
}

2. HTTPS 配置

nginx
server {
    listen 443 ssl http2;
    server_name example.com;
    
    # SSL 证书
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    
    # SSL 优化
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;
    
    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 https;
    }
    
    location /static/ {
        alias /var/www/my-django-app/static/;
        expires 30d;
    }
}

# HTTP 重定向到 HTTPS
server {
    listen 80;
    server_name example.com;
    return 301 https://$server_name$request_uri;
}

七、性能优化

1. Gunicorn 优化

bash
# 计算最佳 worker 数量
# 公式:worker 数 = (CPU 核心数 × 2) + 1

# 4 核 CPU 示例
workers = (4 × 2) + 1 = 9

# ecosystem.config.js
args: 'myproject.wsgi:application \
  --bind 127.0.0.1:8000 \
  --workers 9 \
  --worker-class sync \
  --timeout 120 \
  --keep-alive 5'

2. 数据库连接池

python
# settings.py
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'mydb',
        'USER': 'user',
        'PASSWORD': 'password',
        'HOST': 'localhost',
        'PORT': '5432',
        'CONN_MAX_AGE': 600,  # 连接持久化
        'OPTIONS': {
            'MAX_CONNS': 20,  # 最大连接数
            'MIN_CONNS': 5,   # 最小连接数
        }
    }
}

3. 缓存配置

python
# settings.py
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.redis.RedisCache',
        'LOCATION': 'redis://127.0.0.1:6379/1',
        'OPTIONS': {
            'CLIENT_CLASS': 'django_redis.client.DefaultClient',
            'CONNECTION_POOL_KWARGS': {
                'max_connections': 50,
            }
        }
    }
}

# 每页缓存
@cache_page(60 * 15)
def my_view(request):
    ...

八、常见问题解决

1. 启动失败

bash
# 问题 1: 找不到模块
# 检查 PYTHONPATH
export PYTHONPATH=/var/www/my-django-app:$PYTHONPATH

# 问题 2: 权限错误
sudo chown -R $USER:$USER /var/www/my-django-app
chmod -R 755 /var/www/my-django-app

# 问题 3: 端口被占用
lsof -i :8000
kill -9 <PID>

# 修改绑定的端口
args: '--bind 127.0.0.1:8001 ...'

2. 静态文件 404

bash
# 确保已收集静态文件
python manage.py collectstatic --noinput

# 检查 Nginx 配置
ls -la /var/www/my-django-app/static/

# 重新加载 Nginx
nginx -t && systemctl reload nginx

3. 数据库迁移失败

bash
# 查看迁移状态
python manage.py showmigrations

# 回滚迁移
python manage.py migrate app_name 0001

# 伪造迁移(表已存在)
python manage.py migrate --fake-initial

# 清除迁移缓存
find . -path "*/migrations/*.pyc" -delete

九、面试标准回答

使用 PM2 部署 Django 项目通常结合 Gunicorn 或 uWSGI

基本步骤是:

  1. 创建 Python 虚拟环境并安装 Gunicorn/uWSGI
  2. ecosystem.config.js 中配置 script 指向 gunicorn,args 指定 WSGI 应用
  3. 正确设置 PYTHONPATH 和 PATH 环境变量
  4. 配置 Nginx 反向代理和静态文件服务
  5. 执行 pm2 start 启动应用

Gunicorn 和 uWSGI 的选择

  • Gunicorn 配置简单,适合中小型项目
  • uWSGI 功能强大,适合大型复杂项目
  • 两者都能很好地与 PM2 集成

性能优化方面,我会:

  • 根据 CPU 核心数设置 worker 数量(核心数×2+1)
  • 配置数据库连接池减少开销
  • 使用 Redis 缓存热点数据
  • 配置 Nginx 缓存静态文件

实际项目中,我编写了自动化部署脚本,实现了 git pull、依赖安装、数据库迁移、静态文件收集和 PM2 重启的一键部署流程,大大提高了部署效率。


十、记忆口诀

部署 Django 歌诀:

Django 部署有方案,
Gunicorn 配 PM2。
虚拟环境要激活,
PATH 变量不能忘!

Nginx 反代配好,
静态文件单独搞。
HTTPS 证书装上,
生产稳定没烦恼!

自动化脚本写,
一键部署效率高。
数据库先迁移,
collectstatic 不能少!

Worker 数量算好,
CPU 核心乘以 2 加 1。
缓存连接池配,
性能优化要做到!

十一、推荐资源


十二、总结一句话

  • 部署方案: Gunicorn/uWSGI + PM2 = Python 应用标配 🐍
  • 配置文件: ecosystem.config.js = 环境变量是关键 ⚙️
  • Nginx 代理: 反向代理 + 静态文件 = 性能提升保障
  • 自动化: deploy.sh 脚本 = 高效部署利器 🚀
最近更新