🌐 阿里云 Linux 服务器 + Docker 中运行 Nginx 的 Let's Encrypt 多网站 SSL 证书部署指南

113 阅读4分钟

🌐 阿里云 Linux 服务器 + Docker 中运行 Nginx 的 Let's Encrypt 多网站 SSL 证书部署指南

适用系统:Alibaba Cloud Linux 3(兼容 CentOS/RHEL)
Web 服务器:Nginx(运行在 Docker 容器中)
场景:托管 多个独立网站(如 site1.comsite2.net 等)
更新时间:2025 年 11 月
作者:DevOps Guide


✅ 一、核心原则

  • 每个独立网站使用独立的 Let's Encrypt 证书
  • Certbot 在宿主机运行(不放入容器)
  • 证书通过 Volume 挂载到 Nginx 容器
  • 使用 webroot 验证方式(无需停机)
  • 自动续期 + 自动重载 Nginx

🚀 二、前提条件

要求说明
1. 阿里云 ECS 实例操作系统为 Alibaba Cloud Linux 3
2. 多个域名已解析各域名 A 记录均指向 ECS 公网 IP
3. 安全组开放 80/443入站规则允许 0.0.0.0/0 访问 80 和 443
4. 域名已备案(如适用)中国内地地域必须完成 ICP 备案
5. Docker 已安装且能正常运行容器

📦 三、目录结构规划(宿主机)

/home/user/my-websites/
├── docker-compose.yml
├── nginx.conf
├── site1/                 # 网站1 静态文件
├── site2/                 # 网站2 静态文件
└── /var/www/letsencrypt   # ← 全局 ACME 验证目录(由 Certbot 写入)

💡 /etc/letsencrypt 由 Certbot 自动管理,无需手动创建


🔧 四、完整操作流程

步骤 1:创建 ACME 验证目录(宿主机)

sudo mkdir -p /var/www/letsencrypt
sudo chmod 755 /var/www/letsencrypt

此目录将用于所有网站的 HTTP-01 挑战验证


步骤 2:编写 docker-compose.yml

# docker-compose.yml
version: '3'
services:
  nginx:
    image: nginx:alpine
    container_name: nginx
    ports:
      - "80:80"
      - "443:443"
    volumes:
      # ACME 验证目录(只读)
      - /var/www/letsencrypt:/var/www/letsencrypt:ro

      # Let's Encrypt 证书目录(只读)
      - /etc/letsencrypt:/etc/letsencrypt:ro

      # 各网站根目录
      - ./site1:/var/www/site1:ro
      - ./site2:/var/www/site2:ro

      # Nginx 主配置
      - ./nginx.conf:/etc/nginx/nginx.conf:ro

    restart: unless-stopped

步骤 3:编写 Nginx 配置 nginx.conf

# nginx.conf
events {
    worker_connections 1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    # === 网站 1:site1.com ===
    server {
        listen 80;
        server_name site1.com www.site1.com;

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

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

    server {
        listen 443 ssl;
        server_name site1.com www.site1.com;

        ssl_certificate     /etc/letsencrypt/live/site1.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/site1.com/privkey.pem;

        root /var/www/site1;
        index index.html;
        try_files $uri $uri/ =404;
    }

    # === 网站 2:site2.net ===
    server {
        listen 80;
        server_name site2.net app.site2.net;

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

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

    server {
        listen 443 ssl;
        server_name site2.net app.site2.net;

        ssl_certificate     /etc/letsencrypt/live/site2.net/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/site2.net/privkey.pem;

        root /var/www/site2;
        index index.html;
        try_files $uri $uri/ =404;
    }
}

✅ 每个网站独立 server 块,独立证书路径


步骤 4:启动 Nginx 容器

# 启动前确保配置正确
sudo nginx -t -c ./nginx.conf

# 启动容器
docker-compose up -d

✅ 此时应能通过 http://site1.comhttp://site2.net 访问(会重定向到 HTTPS,但证书还未部署)


步骤 5:安装 Certbot(宿主机)

sudo dnf install -y epel-release
sudo dnf install -y certbot

步骤 6:为每个网站申请证书(宿主机)

# 网站1
sudo certbot certonly --webroot -w /var/www/letsencrypt -d site1.com -d www.site1.com

# 网站2
sudo certbot certonly --webroot -w /var/www/letsencrypt -d site2.net -d app.site2.net

✅ 证书将分别存放在:

  • /etc/letsencrypt/live/site1.com/
  • /etc/letsencrypt/live/site2.net/

步骤 7:验证证书

sudo certbot certificates

输出应类似:

Certificate Name: site1.com
  Domains: site1.com www.site1.com
  Expiry Date: 2026-02-20 ...

Certificate Name: site2.net
  Domains: site2.net app.site2.net
  Expiry Date: 2026-02-22 ...

步骤 8:测试 HTTPS 访问

浏览器访问:

  • https://site1.com → 应显示安全锁
  • https://site2.net → 应显示安全锁

步骤 9:配置自动续期(关键!)

9.1 测试续期(不会真续)
sudo certbot renew --dry-run

✅ 应看到:All simulated renewals succeeded.

9.2 添加定时任务
sudo crontab -e

粘贴:

0 2 * * * /usr/bin/certbot renew --quiet --post-hook "docker exec nginx nginx -s reload"
9.3 验证
sudo crontab -l

--post-hook 会在任一证书续期成功后自动重载 Nginx,加载新证书


🔍 五、常见问题排查

❌ 问题 1:404 访问 /.well-known/acme-challenge/xxx

原因:Nginx 未正确路由 ACME 路径
解决

  • 确保每个 server { listen 80; } 块包含:
    location /.well-known/acme-challenge/ {
        root /var/www/letsencrypt;
    }
    
  • 确认容器挂载了 /var/www/letsencrypt

❌ 问题 2:Permission denied 读取证书

原因:容器内 Nginx 无权读取 /etc/letsencrypt
解决

  • 挂载时使用 :ro(只读)通常足够
  • 若仍报错,可复制证书到项目目录并调整权限:
    sudo cp -r /etc/letsencrypt ./certs
    sudo chown -R $USER:$USER ./certs
    
    并修改 docker-compose.yml 挂载 ./certs:/certs

❌ 问题 3:续期后 HTTPS 仍用旧证书

原因:未触发 nginx -s reload
解决

  • 检查 cron 中的 --post-hook 是否正确
  • 手动测试:
    docker exec nginx nginx -s reload
    

📎 六、扩展场景

🌐 通配符证书(*.example.com

需使用 DNS 验证(阿里云 DNS):

sudo dnf install -y certbot-dns-alidns

# 创建凭证文件 ~/.secrets/alidns.ini
dns_alidns_access_key = YOUR_KEY
dns_alidns_secret_key = YOUR_SECRET

chmod 600 ~/.secrets/alidns.ini

# 申请通配符证书
sudo certbot certonly \
  --dns-alidns \
  --dns-alidns-credentials ~/.secrets/alidns.ini \
  -d '*.example.com' -d example.com

⚠️ 通配符证书不能覆盖多级子域(如 a.b.example.com


✅ 七、总结:多网站最佳实践

步骤操作说明
1创建统一 ACME 目录/var/www/letsencrypt
2编写多 server Nginx 配置每个网站独立块
3为每个主域名申请证书使用 certbot certonly --webroot
4挂载 /etc/letsencrypt 到容器只读即可
5设置自动续期 cron--post-hook "nginx -s reload"

🎉 完成!多个网站均拥有自动更新的免费 HTTPS


🔗 官方参考

💡 提示:每季度运行 sudo certbot certificates 检查到期时间,确保万无一失。