容器与平台
Kubernetes 用 cert-manager 自动签发 Let's Encrypt 证书
在 Kubernetes 里用 cert-manager 签发 Let's Encrypt 证书分三步:用 Helm 安装 cert-manager,创建 ClusterIssuer,再给 Ingress(或 Gateway)加注解。证书保存在 Secret 里,cert-manager 会自动续期。
适用于 cert-manager 1.18 及以上(截至 2026 年 11 月为 1.21.x)、任意受支持的 Kubernetes 版本和 Helm 3。
前提条件
HTTP-01 验证需要公网能访问到你的 Ingress 控制器或网关。
- Kubernetes 集群和有 cluster-admin 权限的
kubectl,本地装好 Helm 3。 - 有公网 IP 的 Ingress 控制器或 Gateway API 实现,例如
203.0.113.10。还在用社区版 ingress-nginx 的,需要规划迁移,它已于 2026 年 3 月退役。 example.com的 A/AAAA 记录指向这个公网 IP;集群在中国大陆的,域名需已备案。- 公网能通过 80 端口访问控制器(HTTP-01 需要)。80 端口不通时用第四步的 DNS-01。
第一步:用 Helm 安装 cert-manager
cert-manager 用官方 Helm chart 安装,crds.enabled=true 会同时安装 CRD:
helm install cert-manager oci://quay.io/jetstack/charts/cert-manager \
--version v1.21.3 \
--namespace cert-manager --create-namespace \
--set crds.enabled=true
kubectl get pods -n cert-manager
v1.21.3 换成 cert-manager 安装文档里的当前版本。controller、webhook、cainjector 三个 Pod 都应变为 Ready。国内集群拉取 quay.io 镜像慢时,可以先同步到自己的镜像仓库,再通过 Helm 参数覆盖镜像地址。
第二步:创建测试和生产 ClusterIssuer
ClusterIssuer 告诉 cert-manager 向哪个 ACME 服务器申请证书。先建测试和生产两个,用测试环境调通后再切到生产:
# issuers.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-staging
spec:
acme:
server: https://acme-staging-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt-staging-account
solvers:
- http01:
ingress:
ingressClassName: traefik
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt-prod-account
solvers:
- http01:
ingress:
ingressClassName: traefik
kubectl apply -f issuers.yaml
kubectl get clusterissuer
ingressClassName 改成你的控制器的类名。想申请 45 天证书,在 acme: 下加 profile: tlsserver,见 Let's Encrypt 证书配置文件。
第三步:通过 Ingress 申请证书
给 Ingress 加注解并声明 TLS 域名和 Secret 名称,cert-manager 会自动创建 Certificate:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web
annotations:
cert-manager.io/cluster-issuer: letsencrypt-staging
spec:
ingressClassName: traefik
tls:
- hosts:
- example.com
secretName: example-com-tls
rules:
- host: example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 80
kubectl apply -f ingress.yaml
kubectl get certificate example-com-tls -w
测试环境下 READY 变为 True 后,把注解改成 letsencrypt-prod 并删除 Secret,重新签发受信任的证书:
kubectl annotate ingress web cert-manager.io/cluster-issuer=letsencrypt-prod --overwrite
kubectl delete secret example-com-tls
用 Gateway API 的,先开启 cert-manager 的 Gateway 支持,再在 Gateway 上加同样的 cert-manager.io/cluster-issuer 注解,带 tls.certificateRefs 的监听器会自动获得证书(详见 cert-manager 的 Gateway 文档)。
第四步(可选):用 DNS-01 申请通配符证书
通配符证书只能用 DNS-01 验证,在 ClusterIssuer 的 solvers 里加一个 dns01 求解器,以 Cloudflare 为例:
solvers:
- dns01:
cloudflare:
apiTokenSecretRef:
name: cloudflare-api-token
key: api-token
kubectl create secret generic cloudflare-api-token -n cert-manager \
--from-literal=api-token=your-cloudflare-api-token
ClusterIssuer 引用的 Secret 必须放在 cert-manager 命名空间。阿里云 DNS、DNSPod 需要社区 webhook,见常见问题。
验证
签发成功后,Certificate 的 READY 为 True,公网访问返回 Let's Encrypt 证书。用 SSL 证书检查 检查公网访问,或在终端运行:
curl ssl.sh/example.com
在集群里:
kubectl describe certificate example-com-tls
kubectl get secret example-com-tls -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -issuer -dates
自动续期
cert-manager 在证书有效期过去三分之二时自动续期(可用 renewBefore 或 renewBeforePercentage 调整),更新 Secret 后 Ingress 控制器和网关会自动加载。续期时间按证书实际有效期计算,所以 2027 年 2 月 10 日 Let's Encrypt 改为 64 天、2028 年 2 月 16 日改为 45 天时都不用改配置。保持 cert-manager 升级,留意 Ready=False 的 Certificate,并把公网域名加到 ssl.sh 监控(开发中):
kubectl get certificates -A
常见问题排查
按 Certificate → CertificateRequest → Order → Challenge 的顺序找卡在哪一步:
kubectl get certificaterequest,order,challenge -A
kubectl describe challenge -A
- Waiting for HTTP-01 challenge propagation: wrong status code 404:求解器的
ingressClassName和控制器不一致,或 80 端口到不了控制器(安全组、未备案拦截),原因和 certbot 连接超时 相同。 - 客户端报 x509: certificate signed by unknown authority:还在用测试环境证书,见 x509: certificate signed by unknown authority。
- too many certificates already issued:在生产环境反复删除重签,见 Let's Encrypt 频率限制。
- CAA record prevents issuance:见 CAA 记录阻止签发。
- Secret 已存在,控制器却返回默认证书:Ingress 的
tls.secretName或命名空间和 Secret 对不上。
常见问题
为什么先用测试环境的 Issuer?
测试环境的频率限制宽松得多,DNS、Ingress 类名或验证方式配错时不会把生产环境的额度用光。测试证书不受公网信任,客户端会报未知颁发者,确认流程跑通后再把注解改成生产 Issuer。
域名在阿里云 DNS 或 DNSPod,怎么用 DNS-01?
cert-manager 内置的 DNS-01 支持 Cloudflare、Route 53 等少数服务商,阿里云 DNS、DNSPod 需要社区维护的 webhook 求解器,安装和配置以对应项目文档为准。不想引入 webhook 的,也可以在集群外用 acme.sh 签发后写入 Secret,但那样续期要自己负责。
cert-manager 什么时候续期?
默认在证书有效期过去三分之二时续期,90 天证书约在到期前 30 天续期。可以在 Certificate 上用 spec.renewBefore 或 spec.renewBeforePercentage 调整。Secret 更新后 Ingress 控制器或网关会自动加载。