多台负载均衡服务器共享 Let's Encrypt 证书,最稳妥的做法是在单一控制节点完成续签,再通过安全通道分发到各节点,配合 DNS 挑战模式避免 HTTP 验证冲突。
先说结论:避免每台机器独立续签,防止速率限制和文件不一致。
- 适合:拥有多台后端或边缘节点的生产环境
- 先准备:确定一台主续签机器和免密同步通道
- 验收:所有节点证书日期一致且服务无中断
核心风险与原理
Let's Encrypt 默认每 90 天过期,续签频率高。如果每台负载均衡器都独立运行续签脚本,容易触发 API 速率限制(Rate Limits),导致续签失败。此外,HTTP 挑战要求请求必须落到运行续签脚本的那台机器,在多节点负载均衡环境下,请求可能被分发到其他节点,导致验证失败。
关键技术风险:
- 软链接失效:Let's Encrypt 的
live目录是软链接,指向archive目录。仅同步live会导致目标机器证书文件缺失,服务无法启动。 - 权限错误:私钥文件(
privkey.pem)权限若过于开放(如 644),Nginx 等服务会拒绝加载。 - 服务中断:同步过程中若直接覆盖正在读取的文件,可能导致服务重载失败或短暂中断。
前置准备:DNS 挑战配置
在主续签节点上,建议使用 DNS-01 挑战模式,无需开放 80 端口。以下以 Cloudflare 为例:
1. 安装 DNS 插件
# Ubuntu/Debian
apt install python3-certbot-dns-cloudflare
# 或使用 pip
pip install certbot-dns-cloudflare
2. 配置 API 凭证
创建凭证文件 ~/.cloudflare.ini,权限设为 600:
cat > ~/.cloudflare.ini <<EOF
dns_cloudflare_email = your_email@example.com
dns_cloudflare_api_key = YOUR_API_KEY
EOF
chmod 600 ~/.cloudflare.ini
3. 执行首次续签
certbot certonly `--dns-cloudflare` \
`--dns-cloudflare-credentials` ~/.cloudflare.ini \
-d your_domain.com -d *.your_domain.com \
`--agree-tos` `--non-interactive`
自动化同步与部署脚本
在主节点创建部署脚本 /usr/local/bin/sync_cert.sh,该脚本包含备份、原子替换、权限修正及服务重载检查。
#!/bin/bash
set -e
DOMAIN="your_domain.com"
NODES=("user@lb-node-01" "user@lb-node-02")
LETSENCRYPT_PATH="/etc/letsencrypt"
echo "开始同步证书到节点..."
for NODE in "${NODES[@]}"; do
echo "处理节点:$NODE"
# 1. 同步整个 letsencrypt 目录,确保 archive 和 live 软链接完整
rsync -avz `--delete` "$LETSENCRYPT_PATH/" "$NODE:$LETSENCRYPT_PATH/"
# 2. 远程修正私钥权限,防止权限过大导致服务拒绝启动
ssh "$NODE" "chmod 600 $LETSENCRYPT_PATH/live/$DOMAIN/privkey.pem"
# 3. 远程检查配置并重载服务 (以 Nginx 为例)
ssh "$NODE" "nginx -t && systemctl reload nginx"
if [ $? -ne 0 ]; then
echo "错误:节点 $NODE 重载失败,请检查日志!"
exit 1
fi
done
echo "所有节点证书同步并重载完成。"
脚本关键点说明:
rsync `--delete`:确保旧证书文件也被清理,避免磁盘占用过高。chmod 600:强制修正私钥权限,消除安全隐患。nginx -t:重载前先测试配置,防止因配置文件错误导致服务宕机。
配置定时任务
在主节点配置 Cron 任务,让 Certbot 自动续签并触发同步脚本。
# 编辑 crontab
crontab -e
# 添加以下内容(每月 1 号凌晨 2 点执行)
0 2 1 * * /usr/bin/certbot renew `--quiet` `--post-hook` "/usr/local/bin/sync_cert.sh"
或者使用 Systemd Timer 获得更详细的日志记录。
验证与监控
1. 检查证书日期
在各负载均衡节点执行以下命令,确保证书到期日一致且已更新:
openssl x509 -in /etc/letsencrypt/live/your_domain/fullchain.pem -noout -enddate
2. 验证服务状态
检查 Web 服务或负载均衡进程是否正常运行,无报错:
systemctl status nginx
journalctl -u nginx -n 20
3. 在线工具检测
使用浏览器访问域名查看锁图标,或使用 SSL 检测工具确认证书链完整,无过期警告。
常见坑与排查
1. 软链接断裂
若同步后服务启动报错 "file not found",检查 live 目录下的软链接是否指向了正确的 archive 版本。务必同步整个 /etc/letsencrypt 目录而非仅 live。
2. 私钥权限错误
同步后私钥权限若变为可读,某些服务(如 Nginx)会拒绝启动。脚本中已包含 chmod 600,若手动同步请务必执行。
3. 重载未生效
证书文件更新了,但进程仍占用旧文件句柄。必须执行 reload 或 restart,且要确认 reload 成功,否则流量仍走旧证书。
4. DNS 传播延迟
使用 DNS 挑战时,若 API 更新记录后立刻验证,可能因 DNS 缓存导致失败。客户端通常会自动等待,但需确保 API 权限正确。
参考来源
- Let's Encrypt - Rate Limits: https://letsencrypt.org/docs/rate-limits/
- Certbot Documentation - DNS Challenge: https://certbot.eff.org/docs/using.html#dns-plugins
- Nginx - Reloading Configuration: https://nginx.org/en/docs/control.html