Skip to content

Docker 环境中使用 Nginx 和 Let's Encrypt 配置 HTTPS

概述

在现代 Web 开发中,HTTPS 已经成为网站的标准配置。本教程将指导您在 Docker 环境中使用 Nginx 作为 Web 服务器,结合 Certbot 和 Let's Encrypt 来实现 HTTPS 安全连接的完整配置流程。

  • 使用 Docker Compose 启动两个容器,一个是 Nginx,另一个是 Certbot。
  • 使用 Certbot 自动获取和更新 SSL 证书。
  • 通过 Docker 卷将 Certbot 获取的证书文件夹挂载到 Nginx 容器中。
  • 重启 Certbot 容器自动续期证书。

步骤一:配置 Nginx 服务器

基础 Docker Compose 配置

首先创建一个基础的 Nginx 服务:

yaml
version: "3"

services:
  ssl-nginx:
    image: nginx:latest
    ports:
      - 80:80 # HTTP 端口
      - 443:443 # HTTPS 端口
    restart: always
    volumes:
      - ./nginx/conf/:/etc/nginx/conf.d/:ro
      - ./certbot/www:/certbot/www:ro # Certbot 验证文件夹
      - ./certbot/conf/:/etc/nginx/ssl/:ro # Certbot 证书文件夹关联到 Nginx 容器内的 ssl 目录

Nginx 配置文件

./nginx/conf/ 目录下创建配置文件:

nginx
# HTTP 配置 - 用于重定向和证书验证
server {
    listen 80;

    server_name example.com www.example.com;
    server_tokens off;

    # 这个配置允许 Certbot 通过 HTTP 协议验证域名所有权,是获取 SSL 证书的必要步骤。
    location /.well-known/acme-challenge/ {
        root /certbot/www;  # 返回 Certbot 验证文件
    }

    # 其他所有请求重定向到 HTTPS
    location / {
        return 301 https://example.com$request_uri;
    }
}

# HTTPS 配置 - 安全连接处理
server {
    listen 443 ssl http2;  
    server_name example.com;

    # SSL 证书配置
    ssl_certificate /etc/nginx/ssl/live/example.com/fullchain.pem;       
    ssl_certificate_key /etc/nginx/ssl/live/example.com/privkey.pem;     

    # SSL 安全配置
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';
    ssl_session_cache shared:SSL:1m;
    ssl_session_timeout 5m;

  ssl_prefer_server_ciphers on;

    location / {
        # 这里配置应用
        root /var/www/html;
        index index.html index.htm;
        try_files $uri $uri/ =404;
    }
}

步骤二:配置 Certbot 获取证书

添加 Certbot 服务

更新 Docker Compose 文件,添加 Certbot 服务:

yaml
version: "3"

services:
  ssl-nginx:
    image: nginx:latest
    ports:
      - 80:80
      - 443:443
    restart: always
    volumes:
      - ./nginx/conf/:/etc/nginx/conf.d/:ro
      - ./certbot/www:/certbot/www:ro
      - ./certbot/conf/:/etc/nginx/ssl/:ro

  certbot: 
    image: certbot/certbot:latest
    volumes: 
      - ./certbot/www/:/var/www/certbot/:rw # 将验证文件通共享目录的方式关联到 Nginx 容器
      - ./certbot/conf/:/etc/letsencrypt/:rw # 将证书文件通过挂载共享目录的方式关联到 Nginx 容器

权限说明:

  • :ro (read-only):容器只能读取,不能修改
  • :rw (read-write):容器可以读取和写入

测试证书获取

启动服务并测试证书获取(干运行模式):

bash
# 启动 Nginx 服务, 确保可以访问 HTTP 端口
docker compose up -d ssl-nginx

# 测试证书获取(干运行模式:不会实际创建证书,测试配置有效性)
docker compose run --rm certbot certonly \
  --webroot \
  --webroot-path /var/www/certbot/ \
  --dry-run \
  -d example.com

如果看到 "The dry run was successful" 消息,说明配置正确。

​ 为什么使用 --dry-run?​​
  • --dry-run 选项用于模拟证书获取过程,确保配置正确而不实际创建证书。这有助于在正式申请前发现潜在问题。
  • Let's Encrypt 对证书申请有严格的速率限制:每个域名每周最多申请 5 次失败, 使用--dry-run可以避免因配置错误而浪费申请次数。
项目测试命令正式命令
--dry-run 参数✅ 包含❌ 不包含
域名数量通常只测试一个域名可以包含多个域名
证书文件🚫 不会创建实际证书✅ 创建真实证书文件
Let's Encrypt 配额🚫 不计入申请次数限制✅ 计入申请次数限制

这样的设计确保了在正式申请证书之前,所有配置都是正确的,避免了因配置错误而触发 Let's Encrypt 的速率限制。

正式获取证书

确认测试通过后,获取真实证书:

bash
# 获取正式证书
docker compose run --rm certbot certonly \
  --webroot \
  --webroot-path /var/www/certbot/ \
  -d example.com \ 
  -d www.example.com

证书获取成功后,证书文件将保存在 `./certbot/conf/live/example.com/` 目录中。

​ 为什么用 docker compose run 而不是 docker run?​​
  • docker compose run 会自动继承 docker-compose.yml 中为 certbot 服务定义的完整环境 ​(包括卷挂载、网络、环境变量、依赖关系等)。若用 docker run,需手动复制所有参数(例如冗长的挂载参数),极易出错。

  • 证书申请过程会调用/.well-known/acme-challenge/进行域名验证。因此必须先通过 docker compose up -d ssl-nginx 确保服务已就绪。

  • docker compose run certbot 会临时启动一个新容器执行命令,与 Compose 文件中 certbot 的容器是否启动无关。

步骤三:重载 Nginx 配置

重启服务

bash
# 重启所有服务
docker compose restart

# 或者仅重载 Nginx 配置(无服务中断)
docker compose exec ssl-nginx nginx -s reload

验证 HTTPS 配置

访问您的网站验证 HTTPS 是否正常工作:

bash
# 检查证书状态
curl -I https://example.com

# 检查 SSL 评级(可选)
curl -s "https://api.ssllabs.com/api/v3/analyze?host=example.com" | jq

步骤四:证书自动续期

Let's Encrypt 证书特点

WARNING

证书有效期 Let's Encrypt 证书有效期仅为 90 天,需要定期续期以避免证书过期。

手动续期

bash
# 续期所有即将过期的证书
docker compose run --rm certbot renew

# 续期后重载 Nginx 配置
docker compose exec webserver nginx -s reload

自动化续期脚本

创建自动续期脚本:

bash
#!/bin/bash

# 自动续期 SSL 证书脚本
cd /path/to/your/project

echo "开始检查证书续期..."

# 尝试续期证书
docker compose run --rm certbot renew

# 重载 Nginx 配置
if [ $? -eq 0 ]; then
    echo "证书续期成功,重载 Nginx 配置..."
    docker compose exec webserver nginx -s reload
    echo "Nginx 配置重载完成"
else
    echo "证书续期失败"
    exit 1
fi

echo "证书续期检查完成"
bash
#!/bin/bash

# 设置定时任务
# 每月 1 号凌晨 2 点执行证书续期
echo "0 2 1 * * /path/to/your/scripts/renew-certs.sh >> /var/log/ssl-renew.log 2>&1" | crontab -

echo "定时任务设置完成"

设置 Cron 定时任务

bash
# 编辑 crontab
crontab -e

# 添加以下行(每月 1 号凌晨 2 点执行)
0 2 1 * * /path/to/your/scripts/renew-certs.sh >> /var/log/ssl-renew.log 2>&1

:::

故障排除

常见问题及解决方案

点击查看常见问题

问题 1:证书获取失败

bash
# 检查域名 DNS 解析
nslookup example.com

# 检查防火墙端口
sudo ufw status
sudo ufw allow 80
sudo ufw allow 443

# 检查 Nginx 配置
docker compose exec webserver nginx -t

问题 2:证书验证路径不可访问

bash
# 检查文件权限
ls -la ./certbot/www/

# 测试验证路径
curl http://example.com/.well-known/acme-challenge/test

问题 3:SSL 握手失败

bash
# 检查 SSL 配置
openssl s_client -connect example.com:443 -servername example.com

# 检查证书有效性
openssl x509 -in ./certbot/conf/live/example.com/fullchain.pem -text -noout

日志检查

bash
# 查看 Nginx 日志
docker compose logs ssl-nginx

# 查看 Certbot 日志
docker compose logs certbot

# 查看实时日志
docker compose logs -f ssl-nginx