从 IP 到 身份:部署 Django、SSL、Nginx 与 Docker 的完整指南

发布: (2025年12月27日 GMT+8 11:10)
6 分钟阅读
原文: Dev.to

Source: Dev.to

Ajit Kumar

1. The Architectural Blueprint

Before we type a single command, understand the Flow of Traffic:

ComponentRole
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 在重启后会更改。

  1. 前往 EC2 Dashboard → Elastic IPs
  2. 点击 Allocate Elastic IP,然后 Associate 它到你的实例。
  3. 你的实例现在拥有永久的“家庭地址”。

步骤 2 – 更新 GoDaddy DNS

创建两条记录,使 yourdomain.comwww.yourdomain.com 都能正常工作。

记录类型主机
A@Your_Elastic_IP
CNAMEwww@ (将 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 安全设置(.envsettings.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 端口。请调整安全组规则,允许该端口的入站流量。

部署愉快!🚀

Back to Blog

相关文章

阅读更多 »