Docker 容器部署 SSL 证书完整教程:从自签名到 Let’s Encrypt 自动续签

Docker 部署 HTTPS 最常见的坑:证书路径写错、配置不生效、续签失败。这篇教程覆盖两种场景——开发环境用自签名证书快速验证,生产环境用 Let’s Encrypt 免费证书 + 自动续签,让你少走弯路。

准备工作

确保服务器已安装:

  • Docker(推荐 20.10 及以上版本)
  • Docker Compose(推荐 V2)
  • 如使用 Let’s Encrypt,需要一个已解析到服务器 IP 的域名,且 80 端口可被公网访问

方案一:自签名证书(开发 / 内网环境)

自签名证书不受浏览器信任,适合开发调试或内网服务,3 分钟搞定。

第一步:生成自签名证书

mkdir -p /opt/nginx/ssl
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout /opt/nginx/ssl/nginx.key \
  -out /opt/nginx/ssl/nginx.crt \
  -subj "/C=CN/ST=Beijing/L=Beijing/O=Dev/CN=localhost"

生成两个文件:nginx.key(私钥)和 nginx.crt(证书),有效期 365 天。

第二步:写 Nginx 配置

创建 /opt/nginx/conf.d/ssl.conf

server {
    listen 443 ssl;
    server_name localhost;

    ssl_certificate     /etc/nginx/ssl/nginx.crt;
    ssl_certificate_key /etc/nginx/ssl/nginx.key;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;

    location / {
        root  /usr/share/nginx/html;
        index index.html;
    }
}

注意:ssl_certificate 填的是容器内路径,不是宿主机路径!这是最常见的配置错误。

第三步:启动容器

docker run -d --name my-nginx \
  -p 80:80 -p 443:443 \
  -v /opt/nginx/conf.d:/etc/nginx/conf.d \
  -v /opt/nginx/ssl:/etc/nginx/ssl \
  nginx

访问 https://localhost 验证,浏览器会提示”不安全”(自签名正常现象),点击继续即可。

热重载配置(无需重启容器)

docker exec my-nginx nginx -s reload

方案二:Let’s Encrypt 免费证书 + 自动续签(生产环境)

Docker + Let's Encrypt SSL

Let’s Encrypt 证书当前有效期 90 天(2027 年起将逐步缩短至 45 天),Certbot 按约 2/3 有效期自动续签。整套方案用两个容器搞定:Nginx 负责反向代理,Certbot 负责证书申请和续签。

目录结构

/opt/myapp/
├── docker-compose.yml
├── nginx/
│   ├── conf.d/
│   │   └── app.conf
│   └── html/
├── certbot/
│   ├── conf/         # 证书存储目录
│   └── www/          # HTTP 验证临时目录

第一步:创建 docker-compose.yml

version: '3'

services:
  nginx:
    image: nginx:latest
    container_name: my-nginx
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d
      - ./nginx/html:/usr/share/nginx/html
      - ./certbot/conf:/etc/letsencrypt
      - ./certbot/www:/var/www/certbot
    command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"
    networks:
      - web

  certbot:
    image: certbot/certbot
    container_name: certbot
    volumes:
      - ./certbot/conf:/etc/letsencrypt
      - ./certbot/www:/var/www/certbot
    entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
    networks:
      - web

networks:
  web:
    driver: bridge

两个关键设计

  • Nginx 每 6 小时自动 reload,确保续签后的新证书立即生效
  • Certbot 每 12 小时检查一次,按证书剩余有效期的约 1/3 时间点触发续签

2026 年重要变更:Let’s Encrypt 正在推进证书有效期从 90 天缩短至 45 天——2026 年 5 月 13 日起可选加入,2027 年 2 月 10 日起全面切换为 64 天过渡期,2028 年 2 月最终到 45 天。使用本文 docker-compose 方案(Certbot 自动循环检查)无需修改,但建议确保 Certbot 镜像保持最新版本以获得 ARI 支持。

第二步:首次申请证书前,先配置 HTTP 验证

创建 nginx/conf.d/app.conf暂时只配 HTTP,不配 HTTPS):

server {
    listen 80;
    server_name your-domain.com;

    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

your-domain.com 替换成你的域名。

第三步:启动服务

cd /opt/myapp
docker-compose up -d

第四步:申请证书

docker-compose run --rm certbot certonly --webroot \
  -w /var/www/certbot \
  -d your-domain.com \
  --email your@email.com \
  --agree-tos \
  --no-eff-email

看到 “Congratulations!” 提示表示申请成功,证书存放在 ./certbot/conf/live/your-domain.com/

第五步:更新 Nginx 配置,启用 HTTPS

修改 nginx/conf.d/app.conf

server {
    listen 80;
    server_name your-domain.com;

    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl http2;
    server_name your-domain.com;

    ssl_certificate     /etc/letsencrypt/live/your-domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;

    # SSL 性能优化
    ssl_session_cache   shared:SSL:10m;
    ssl_session_timeout 10m;
    ssl_protocols       TLSv1.2 TLSv1.3;
    ssl_ciphers         'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';
    ssl_prefer_server_ciphers on;

    # 强制 HTTPS(HSTS)
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    location / {
        root  /usr/share/nginx/html;
        index index.html;
    }
}

第六步:重载 Nginx

docker-compose exec nginx nginx -s reload

访问 https://your-domain.com 验证,地址栏出现绿锁即配置成功。

验证自动续签是否正常

# 模拟续签(不实际执行,只验证流程)
docker-compose run --rm certbot renew --dry-run

# 查看续签日志
docker-compose logs certbot

SSL 安全加固(可选)

Docker 容器部署 SSL

配置 OCSP Stapling 和安全响应头,可以把 SSL Labs 评分提到 A+:

# 在 443 server 块内追加以下内容

# OCSP Stapling(加快证书验证速度)
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/your-domain.com/chain.pem;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;

# 安全响应头
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";

配置完成后,访问 SSL Labs 测试工具 测一下分数。

反向代理配置(附加场景)

如果 Nginx 需要把流量转发给其他 Docker 容器(比如 Node.js、Go 应用),在 server 块内把 location / 替换成:

upstream backend {
    server backend-api:3000;  # backend-api 是目标容器名
    keepalive 32;
}

location / {
    proxy_pass http://backend;

    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";

    proxy_connect_timeout 60s;
    proxy_send_timeout    60s;
    proxy_read_timeout    60s;
}

容器间通信用容器名(如 backend-api:3000),不是 localhost,这是 Docker 网络的基本规则。

常见问题排查

问题原因解决方法
证书路径 404写的是宿主机路径而非容器内路径配置文件里用容器内路径(/etc/letsencrypt/...
申请证书失败域名未解析到当前服务器,或 80 端口被占用检查 DNS 解析 + docker ps 确认 80 端口
续签后证书未生效Nginx 未重载Nginx 容器命令已内置每 6 小时 reload,或手动执行 nginx -s reload
502 Bad Gateway后端容器未启动,或未加入同一网络docker ps 检查状态,确认两个容器在同一 network
浏览器仍显示不安全使用了自签名证书,或证书域名与访问域名不匹配生产环境使用 Let’s Encrypt,或检查 server_name

常用命令速查

# 检查 Nginx 配置语法
docker exec my-nginx nginx -t

# 优雅重载(不中断现有连接)
docker exec my-nginx nginx -s reload

# 查看 Certbot 日志
docker-compose logs certbot

# 测试证书续签流程
docker-compose run --rm certbot renew --dry-run

参考资料

耕读君
耕读君

一个筋斗十万八千里,说明上云好啊

文章: 231

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注