从 IP 到 身份:部署 Django、SSL、Nginx 与 Docker 的完整指南
Source: Dev.to
1. The Architectural Blueprint
Before we type a single command, understand the Flow of Traffic:
| Component | Role |
|---|---|
| User | 输入 yourdomain.com。 |
| GoDaddy DNS | 将域名指向您的 AWS Elastic IP。 |
| AWS Security Group | 充当防火墙,允许 80 (HTTP) 和 443 (HTTPS) 端口的流量。 |
| Nginx (inside Docker) | 接收请求,处理 SSL 握手,并将流量代理到 Daphne(Django ASGI 服务器)。 |
2. 基础设施设置 (GoDaddy & AWS)
步骤 1 – 在 AWS 锁定 IP
默认情况下,EC2 实例的 IP 在重启后会更改。
- 前往 EC2 Dashboard → Elastic IPs。
- 点击 Allocate Elastic IP,然后 Associate 它到你的实例。
- 你的实例现在拥有永久的“家庭地址”。
步骤 2 – 更新 GoDaddy DNS
创建两条记录,使 yourdomain.com 和 www.yourdomain.com 都能正常工作。
| 记录类型 | 主机 | 值 |
|---|---|---|
| A | @ | Your_Elastic_IP |
| CNAME | www | @ (将 www 指向主域名) |
Source: …
3. 使用 Certbot(Let’s Encrypt)获取 SSL
Certbot 与 Let’s Encrypt 通信,以证明域名所有权并颁发证书。
我们使用 Standalone 模式,因为 Nginx 运行在 Docker 中,不想干扰宿主机层面的 Nginx。
安装与链接
# 1. 更新系统
sudo apt update
# 2. 安装 Snap(Certbot 推荐的方式)
sudo snap install core; sudo snap refresh core
# 3. 安装 Certbot
sudo snap install --classic certbot
# 4. 创建符号链接,以便全局运行 'certbot'
sudo ln -s /snap/bin/certbot /usr/bin/certbot
颁发证书
重要提示: 在运行以下命令前,请停止占用 80 端口的所有服务(例如正在运行的 Nginx 容器)。
# 获取证书
sudo certbot certonly --standalone -d yourdomain.com -d www.yourdomain.com
成功输出
你应该会看到类似以下的信息:
Congratulations! Your certificate and chain have been saved at:
/etc/letsencrypt/live/yourdomain.com/fullchain.pem
刚才发生了什么?
Certbot 在你的 EC2 实例上创建了 /etc/letsencrypt/ 目录。该文件夹包含:
| 文件 | 描述 |
|---|---|
fullchain.pem | 公共证书(包括链) |
privkey.pem | 私钥 |
我们将 挂载 此文件夹到 Docker 中,以便 Nginx 能读取这些证书。
4. 生产配置文件
nginx.conf – 流量警官
在项目根目录创建此文件。它会将 HTTP(端口 80)重定向到 HTTPS(端口 443),并将请求代理到 Daphne。
upstream app_server {
server web:8000;
}
/* 1. Redirect ALL HTTP traffic to HTTPS */
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
return 301 https://$host$request_uri;
}
/* 2. The Secure Server */
server {
listen 443 ssl;
server_name yourdomain.com www.yourdomain.com;
# Paths are INSIDE the container (mapped via docker‑compose)
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
location /static/ {
alias /app/staticfiles/;
}
location / {
proxy_pass http://app_server;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto https;
# WebSocket support (essential for real‑time apps)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
docker-compose.yml – 编排器
关键在于 volumes 部分,它把主机上的 SSL 文件夹挂载到 Nginx 容器中。
services:
web:
build: .
command: ["daphne", "-b", "0.0.0.0", "-p", "8000", "myproject.asgi:application"]
env_file: .env
restart: always
nginx:
image: nginx:latest
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
- static_volume:/app/staticfiles:ro
# Mount the host certificates here:
- /etc/letsencrypt:/etc/letsencrypt:ro
depends_on:
- web
restart: always
volumes:
static_volume:
5. Django 安全设置(.env 与 settings.py)
当 Nginx 终止 SSL 时,Django 从代理收到的是普通的 HTTP。我们必须告知 Django 原始请求是 HTTPS。
.env
ALLOWED_HOSTS=yourdomain.com,www.yourdomain.com,your_ip
CSRF_TRUSTED_ORIGINS=https://yourdomain.com,https://www.yourdomain.com
settings.py
import os
ALLOWED_HOSTS = os.getenv("ALLOWED_HOSTS", "").split(",")
# Trust the X-Forwarded-Proto header from Nginx
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
# Django 4.0+ – required for HTTPS form submissions
CSRF_TRUSTED_ORIGINS = os.getenv("CSRF_TRUSTED_ORIGINS", "").split(",")
6. 操作命令
| 操作 | 命令 |
|---|---|
| 部署 / 更新 | docker compose up -d --force-recreate |
| 停止所有 | docker compose down |
| 检查日志 | docker compose logs -f nginx |
| 验证证书 | ls -la /etc/letsencrypt/live/yourdomain.com/ |
| 检查 SSL 健康 | curl -Iv https://yourdomain.com |
7. 常见错误排查
| 症状 | 可能原因 | 解决办法 |
|---|---|---|
| 502 错误网关 | Nginx 无法访问 web 容器。 | 确保 upstream 名称(web)与 docker‑compose.yml 中的服务名称匹配。确认容器正在运行(docker compose ps)。 |
| SSL 握手失败 | 证书路径错误或挂载缺失。 | 确认 /etc/letsencrypt 已正确挂载到 Nginx 容器,并且 ssl_certificate / ssl_certificate_key 指向正确的文件。 |
| 重定向循环 | HTTP 和 HTTPS 块监听同一端口。 | 确保 HTTP 服务器块仅监听 80,HTTPS 块仅监听 443。 |
| CSRF 错误 | 未设置 CSRF_TRUSTED_ORIGINS 或缺少 SECURE_PROXY_SSL_HEADER。 | 验证这两个环境变量已加载,并且 SECURE_PROXY_SSL_HEADER 已在 settings.py 中声明。 |
| 域名不可达 | DNS 未传播或弹性 IP 未关联。 | 再次检查 GoDaddy 的 A/CNAME 记录,并确认弹性 IP 已绑定到运行中的 EC2 实例。 |
其他问题
500 内部服务器错误
通常是 Django 崩溃。使用以下命令查看日志:
docker compose logs web
最常见的原因是 DisallowedHost 错误(请检查 .env 设置)。
Nginx 启动失败
检查 Nginx 日志:
docker compose logs nginx
如果日志中出现 “file not found”,则 /etc/letsencrypt 的卷映射可能不正确。
连接被拒绝
AWS 安全组可能阻止了 443 端口。请调整安全组规则,允许该端口的入站流量。
部署愉快!🚀
