饮墨

子安饮墨馀三斗,留与卿儿作赋来

Kubernetes cert-manager 自动化证书管理实战:3 步告别 TLS 证书过期故障

8 views

痛点

凌晨 3 点被告警叫醒,某个业务域名证书过期导致 HTTPS 不可用——这个场景每个运维都经历过。手动管理证书的痛点很明确:

  1. 证书散落各处:Nginx、ALB、Ingress 各自持有证书,没有统一视图
  2. 续期靠人记:Let's Encrypt 90 天过期,内部 CA 一年过期,总有漏网之鱼
  3. 多环境同步难:dev/staging/prod 证书版本不一致,排查时浪费大量时间

在 Kubernetes 环境下,cert-manager 是解决证书全生命周期管理的事实标准。它将证书声明为 K8s 资源,自动签发、续期、分发,彻底消除人工介入。

方案概览

cert-manager 核心架构:

Certificate CR → cert-manager controller → ACME/CA Issuer → 自动签发 → 存入 Secret → Ingress/Gateway 引用
                                                ↓
                                    自动续期(到期前 30 天触发)

关键组件: - Issuer/ClusterIssuer:定义证书签发源(Let's Encrypt、内部 CA、Vault) - Certificate CR:声明需要哪个域名的证书,存到哪个 Secret - ACME Solver:自动完成域名验证(HTTP-01 或 DNS-01)

实操步骤

第 1 步:安装 cert-manager

# 使用 Helm 安装(推荐生产方式)
helm repo add jetstack https://charts.jetstack.io
helm repo update

helm install cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --create-namespace \
  --set crds.enabled=true \
  --set prometheus.enabled=true \
  --version v1.16.1

# 验证安装
kubectl get pods -n cert-manager
# 应看到 3 个 Pod Running:cert-manager、cainjector、webhook

第 2 步:配置 ClusterIssuer(Let's Encrypt)

生产环境用 letsencrypt-prod,测试先用 letsencrypt-staging 避免触发速率限制:

# cluster-issuer.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: ops-team@example.com
    privateKeySecretRef:
      name: letsencrypt-prod-key
    solvers:
    # HTTP-01:适合公网可达的 Ingress
    - http01:
        ingress:
          ingressClassName: nginx
    # DNS-01:适合内网服务或通配符证书
    - dns01:
        cloudflare:
          email: ops-team@example.com
          apiTokenSecretRef:
            name: cloudflare-api-token
            key: api-token
      selector:
        dnsZones:
        - "example.com"
kubectl apply -f cluster-issuer.yaml
kubectl get clusterissuer
# 状态应为 Ready=True

第 3 步:为 Ingress 自动签发证书

方式一:注解自动化(推荐)

只需在 Ingress 上加一行注解,cert-manager 自动创建 Certificate 并注入:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - app.example.com
    secretName: app-example-com-tls  # cert-manager 自动填充此 Secret
  rules:
  - host: app.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: app-svc
            port:
              number: 80

方式二:显式 Certificate CR(精细控制)

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: wildcard-example-com
  namespace: default
spec:
  secretName: wildcard-example-com-tls
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
  dnsNames:
  - "*.example.com"
  - "example.com"
  # 提前 45 天续期(默认 30 天)
  renewBefore: 1080h

验证与监控

# 查看证书状态
kubectl get certificate -A
kubectl describe certificate app-example-com-tls

# 查看签发进度
kubectl get certificaterequest -A
kubectl get order -A
kubectl get challenge -A

# Prometheus 监控指标(关键)
# certmanager_certificate_expiration_timestamp_seconds  — 过期时间戳
# certmanager_certificate_ready_status                  — 证书就绪状态

Grafana 告警规则示例:

- alert: CertExpiringSoon
  expr: (certmanager_certificate_expiration_timestamp_seconds - time()) < 7*24*3600
  for: 1h
  labels:
    severity: warning
  annotations:
    summary: "证书 {{ $labels.name }} 将在 7 天内过期"

避坑指南

坑 1:Let's Encrypt 速率限制导致签发失败

Let's Encrypt 对同一域名每周限 50 张证书。频繁删除重建 Certificate 资源会触发限制。

解决:测试阶段务必用 letsencrypt-staging;生产环境不要删除 Secret,cert-manager 会自动续期已有证书。

坑 2:DNS-01 Challenge 超时

使用 DNS-01 验证时,DNS 记录传播可能需要数分钟。cert-manager 默认等待 60 秒,传播慢的 DNS 提供商会超时。

解决:在 Issuer 中增加传播等待时间:

solvers:
- dns01:
    cloudflare:
      apiTokenSecretRef:
        name: cloudflare-api-token
        key: api-token
    # 延长 DNS 传播检测间隔
    cnameStrategy: Follow

同时检查 DNS 提供商 API Token 权限是否包含 Zone:Edit。

坑 3:Webhook Pod 未就绪导致 Certificate 创建失败

cert-manager webhook 负责校验 CR。如果 webhook Pod 异常或网络策略拦截了 apiserver → webhook 的流量,所有证书操作都会卡住。

解决:确保 cert-manager-webhook Pod 健康,NetworkPolicy 放行 apiserver 到 webhook 的 443 端口:

# 快速诊断
kubectl get pods -n cert-manager
kubectl logs -n cert-manager deploy/cert-manager-webhook
# 如果是 NetworkPolicy 问题,临时放行测试
kubectl get networkpolicy -n cert-manager

总结

维度 手动管理 cert-manager
签发方式 手动申请/上传 声明式自动签发
续期 人工 + 日历提醒 到期前自动续期
多域名管理 逐个处理 批量 CR 统一管理
审计追踪 K8s Events + Prometheus

核心建议: 1. 生产环境优先用 DNS-01 验证——支持通配符、不依赖公网入口 2. 必须配置 Prometheus 告警监控证书过期时间 3. 证书 Secret 纳入 Velero 备份范围,集群恢复时避免重新签发触发速率限制

cert-manager 是 Kubernetes 环境下证书管理的终极方案,一次配置,终身无忧。把精力从证书续期中释放出来,去做真正有价值的事。