从零到一:基于Docker容器化与Nginx+Gunicorn在阿里云ECS部署Django博客(实战篇)
1. 为什么选择Docker+Nginx+Gunicorn部署Django?
很多开发者第一次部署Django项目时,往往会直接使用python manage.py runserver命令启动开发服务器。这种方式虽然简单,但存在几个致命问题:
- 性能瓶颈:开发服务器是单线程的,并发能力极差,实测在阿里云1核1G的ECS上,QPS很难超过50
- 安全隐患:开发模式会暴露调试信息,可能泄露敏感数据
- 环境依赖:服务器需要手动安装所有依赖,容易产生"在我机器上是好的"这类问题
我在早期部署项目时,就遇到过因为服务器缺少某个系统库导致项目无法运行的尴尬情况。后来改用Docker容器化方案后,这类问题再没出现过。
Docker+Nginx+Gunicorn的组合完美解决了这些问题:
- Docker:将应用及其所有依赖打包成标准化单元,实现"一次构建,到处运行"
- Gunicorn:Python WSGI HTTP服务器,支持多worker进程,轻松处理并发请求
- Nginx:高性能Web服务器,处理静态文件请求,反向代理动态请求到Gunicorn
2. 环境准备与项目配置
2.1 阿里云ECS基础配置
首先登录阿里云控制台,购买ECS实例时建议选择:
- 操作系统:Ubuntu 22.04 LTS
- 实例规格:至少1核2G(个人博客2核4G更佳)
- 安全组:开放80(HTTP)、443(HTTPS)、22(SSH)端口
实例创建完成后,通过SSH连接服务器:
ssh root@your-server-ip更新系统软件包并安装基础工具:
apt update && apt upgrade -y apt install -y git vim net-tools2.2 本地Django项目改造
假设我们已有本地开发好的博客项目,需要进行生产环境适配:
- 修改
settings.py关键配置:
DEBUG = False # 必须关闭调试模式 ALLOWED_HOSTS = ['your-domain.com', '服务器IP'] STATIC_ROOT = BASE_DIR / 'staticfiles' # 静态文件收集目录- 生成依赖清单:
pip freeze > requirements.txt- 测试Gunicorn能否正常运行:
gunicorn your_project.wsgi:application --bind 0.0.0.0:80003. Docker化Django应用
3.1 编写Dockerfile
在项目根目录创建Dockerfile:
# 使用官方Python镜像作为基础 FROM python:3.10-slim-bullseye # 设置工作目录 WORKDIR /app # 安装系统依赖 RUN apt-get update && apt-get install -y \ gcc \ python3-dev \ && rm -rf /var/lib/apt/lists/* # 复制依赖文件并安装 COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # 复制项目代码 COPY . . # 收集静态文件 RUN python manage.py collectstatic --noinput # 暴露端口 EXPOSE 8000 # 启动命令 CMD ["gunicorn", "your_project.wsgi:application", "--bind", "0.0.0.0:8000"]3.2 使用Docker Compose编排服务
创建docker-compose.yml文件:
version: '3.8' services: web: build: . restart: always environment: - DJANGO_SECRET_KEY=your-secret-key - DJANGO_DEBUG=False volumes: - static_volume:/app/staticfiles expose: - "8000" nginx: image: nginx:alpine ports: - "80:80" volumes: - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf - static_volume:/app/staticfiles:ro depends_on: - web volumes: static_volume:3.3 配置Nginx反向代理
创建nginx/nginx.conf配置文件:
upstream django { server web:8000; } server { listen 80; server_name your-domain.com; location /static/ { alias /app/staticfiles/; expires 30d; } location / { proxy_pass 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; } }4. 部署到阿里云ECS
4.1 上传项目代码
推荐使用Git管理代码:
git init git add . git commit -m "Initial commit"在服务器上克隆项目:
git clone https://github.com/yourname/your-project.git cd your-project4.2 构建并启动服务
docker compose up -d --build检查服务状态:
docker compose ps docker compose logs -f4.3 常见问题排查
静态文件404错误:
- 确认
STATIC_ROOT设置正确 - 检查Nginx配置中的
alias路径是否与Docker卷挂载路径一致 - 确保执行了
collectstatic命令
- 确认
数据库连接问题:
- 如果使用MySQL/PostgreSQL,确保数据库服务已启动
- 检查
settings.py中的数据库配置 - 测试数据库连接:
python manage.py dbshell
Gunicorn worker崩溃:
- 适当增加worker数量:
--workers 4 - 使用
--timeout 120增加超时时间 - 查看日志定位具体错误:
docker compose logs web
- 适当增加worker数量:
5. 生产环境优化建议
5.1 性能调优
Gunicorn配置优化:
command: > gunicorn your_project.wsgi:application --bind 0.0.0.0:8000 --workers 4 --threads 2 --worker-class gthread --max-requests 1000 --timeout 120Nginx优化配置:
# 在http块中添加 gzip on; gzip_types text/plain text/css application/json application/javascript; # 在server块中添加 client_max_body_size 20M; keepalive_timeout 65;5.2 安全加固
使用HTTPS加密:
# 在nginx.conf中添加 listen 443 ssl; ssl_certificate /path/to/cert.pem; ssl_certificate_key /path/to/key.pem;限制管理后台访问:
# settings.py ADMIN_URL = os.getenv('ADMIN_URL', 'custom-admin-path/')定期更新依赖:
docker compose build --no-cache
5.3 监控与日志
配置日志轮转:
# 在docker-compose.yml中添加 logging: driver: "json-file" options: max-size: "10m" max-file: "3"使用Supervisor管理进程(可选):
[program:gunicorn] command=/usr/local/bin/gunicorn your_project.wsgi:application --bind 0.0.0.0:8000 directory=/app user=root autostart=true autorestart=true stderr_logfile=/var/log/gunicorn.err.log stdout_logfile=/var/log/gunicorn.out.log6. 持续集成与自动化部署
6.1 GitHub Actions自动化
创建.github/workflows/deploy.yml:
name: Deploy to Aliyun ECS on: push: branches: [ main ] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Install SSH key uses: webfactory/ssh-agent@v0.7.0 with: ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} - name: Deploy to server run: | ssh root@your-server-ip "cd /path/to/project && git pull && docker compose up -d --build"6.2 备份策略
数据库定期备份:
# 每天凌晨备份 0 0 * * * docker exec -t your-db-container pg_dump -U username dbname > /backups/db-$(date +\%Y\%m\%d).sql使用阿里云OSS存储备份:
# backup.py import oss2 from datetime import datetime auth = oss2.Auth('your-access-key', 'your-secret-key') bucket = oss2.Bucket(auth, 'your-endpoint', 'your-bucket-name') backup_file = f'db-{datetime.now().strftime("%Y%m%d")}.sql' bucket.put_object_from_file(backup_file, f'/backups/{backup_file}')7. 实际部署中的经验分享
在多次项目部署中,我总结出几个关键点:
容器资源限制:在
docker-compose.yml中为服务设置内存和CPU限制,防止单个容器占用过多资源web: deploy: resources: limits: cpus: '1' memory: 1G环境变量管理:敏感信息如数据库密码、API密钥等必须通过环境变量传递,不要硬编码在配置文件中
零停机部署:使用Nginx的
upstream模块实现蓝绿部署upstream django { server web:8000 max_fails=3 fail_timeout=30s; server web-backup:8000 backup; }健康检查:为容器添加健康检查,确保服务真正可用
healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000/health/"] interval: 30s timeout: 10s retries: 3多阶段构建:优化Docker镜像大小
# 构建阶段 FROM python:3.10 as builder RUN pip install --user -r requirements.txt # 运行阶段 FROM python:3.10-slim COPY --from=builder /root/.local /root/.local
遇到502 Bad Gateway错误时,我通常会按照以下步骤排查:
- 检查Gunicorn是否正常运行:
docker compose exec web ps aux - 查看Nginx错误日志:
docker compose logs nginx - 测试端口连通性:
docker compose exec nginx curl -v http://web:8000 - 检查防火墙设置:
ufw status