使用 Let's Encrypt 证书时,如何自动化 certbot 重启 CouchDB?

解读

面试官真正想考察的是:

  1. 你是否理解 CouchDB 的 TLS 证书热加载机制(默认不热加载,需进程重启)。
  2. 你是否能在 国内网络环境 下(Let's Encrypt 可能走国内 CDN 或反向代理)稳定完成证书申请与续期。
  3. 你是否能把 certbot 的续期钩子与 systemd/Crontab 结合,做到“续期成功→无损重启→失败回滚”的闭环。
  4. 你是否知道 CouchDB 集群模式 下重启顺序与 quorum 的权衡,避免触发不必要的分片重平衡。

一句话:不是“跑完 certbot 就行”,而是“让 CouchDB 零告警、零丢连接、零人工地一直用新证书”。

知识点

  1. CouchDB TLS 配置段[ssl]cert_filekey_filecacert_file 路径固定,不支持通配符路径,必须显式指定 PEM。
  2. 证书文件权限:CouchDB 运行用户(通常是 couchdb:couchdb)需 至少 0440 读取权限,否则启动失败。
  3. certbot 钩子类型
    • --pre-hook:申请前执行(国内场景可用来 临时放行 80 端口 或关闭 nginx)。
    • --deploy-hook:申请/续期成功执行,最适合做重启
    • --post-hook:无论成败都会执行,可用来 还原防火墙
  4. systemd reload-or-restartsystemctl reload-or-restart couchdb.service 在 CouchDB 侧等价于 先尝试 SIGUSR1(配置重载),失败再 SIGTERM,但 TLS 证书不在 SIGUSR1 范围内,因此必须 hard restart
  5. 集群重启顺序:先重启 非 quorum 节点 → 观察 /_up → 再重启剩余节点,避免同时重启 ≥ (N/2)+1 节点。
  6. 国内 Let's Encrypt 加速
    • 使用 --server https://acme-v02.api.letsencrypt.org/directory 默认即可,无需代理;若走国内 CDN,需保证 443 出站 不被限速。
    • 建议 提前预热 DNS TXT 记录(通配符场景)防止 GFW 间歇丢包。
  7. 续期频率:Let's Encrypt 证书 90 天有效期,官方推荐 60 天续期;Crontab 写 0 3 */15 * * 每 15 天运行一次即可,避免扎堆 00:00

答案

生产级自动化方案(单节点示例,集群见拓展):

  1. 准备目录与权限

    mkdir -p /etc/letsencrypt/couchdb
    chown -R couchdb:couchdb /etc/letsencrypt/couchdb
    chmod 750 /etc/letsencrypt/couchdb
    
  2. certbot 申请并强制钩子

    certbot certonly --webroot \
      -w /var/www/certbot \
      -d db.example.com \
      --deploy-hook /usr/local/bin/couchdb-restart.sh \
      --quiet
    
  3. 钩子脚本 /usr/local/bin/couchdb-restart.sh

    #!/bin/bash
    set -e
    # 复制完整链+私钥到 CouchDB 可读目录
    cp /etc/letsencrypt/live/db.example.com/fullchain.pem /etc/letsencrypt/couchdb/cert.pem
    cp /etc/letsencrypt/live/db.example.com/privkey.pem   /etc/letsencrypt/couchdb/key.pem
    chown couchdb:couchdb /etc/letsencrypt/couchdb/*.pem
    chmod 440 /etc/letsencrypt/couchdb/*.pem
    
    # 校验证书有效期
    openssl x509 -checkend 86400 -noout -in /etc/letsencrypt/couchdb/cert.pem || exit 1
    
    # 零宕机重启:systemd 会等待旧连接完成
    systemctl restart couchdb.service
    
    # 健康检查
    sleep 3
    curl -sf https://db.example.com:6984/_up || systemctl restart couchdb.service
    
  4. 自动续期
    创建 /etc/cron.d/couchdb-cert

    0 3 */15 * * root certbot renew --quiet --deploy-hook /usr/local/bin/couchdb-restart.sh
    
  5. CouchDB 本地.ini 片段

    [ssl]
    cert_file = /etc/letsencrypt/couchdb/cert.pem
    key_file  = /etc/letsencrypt/couchdb/key.pem
    ; 如需全链校验客户端
    cacert_file = /etc/ssl/certs/ca-certificates.crt
    

至此,证书续期→复制→权限→重启→健康检查 全自动化,无需人工干预

拓展思考

  1. 集群模式
    把钩子脚本拆成两步:

    • 第一步通过 SSH 免密 把新证书同步到所有节点 /etc/letsencrypt/couchdb/
    • 第二步用 滚动重启脚本 依次对每台节点 systemctl restart,并通过 /_membership 确保 < quorum 节点同时离线。
      可结合 Ansible playbook 实现“一批两台、间隔 30 秒”的灰度策略。
  2. 双证书热备
    不能容忍重启 的核心系统,可搭建 HAProxy+SNI 前置层,让 HAProxy 加载证书并 热加载(hitless reload),CouchDB 走本地 5984 无需重启;此时 certbot 钩子只需 reload HAProxy,实现 CouchDB 零重启

  3. 失败回滚
    在钩子脚本里先把旧证书 cp.bak,若 systemctl restart/_up 返回非 200,立即 还原旧证书并再次重启,同时 告警到钉钉/飞书,保证 不会把坏证书推上线