痛点
凌晨 3 点被告警叫醒,某个业务域名证书过期导致 HTTPS 不可用——这个场景每个运维都经历过。手动管理证书的痛点很明确:
- 证书散落各处:Nginx、ALB、Ingress 各自持有证书,没有统一视图
- 续期靠人记:Let's Encrypt 90 天过期,内部 CA 一年过期,总有漏网之鱼
- 多环境同步难: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 环境下证书管理的终极方案,一次配置,终身无忧。把精力从证书续期中释放出来,去做真正有价值的事。