平台和 DevOps

READY False:cert-manager 证书一直不 Ready 怎么排查

作者: · 最后核实

报错信息READY: False Message: Issuing certificate as Secret does not exist

Certificate 卡在 READY False 的意思是:cert-manager 还没拿到签好的证书,原因藏在它创建的下层资源里。 对 Let's Encrypt 这类 ACME 签发者,链条是 Certificate → CertificateRequest → Order → Challenge,沿着链条往下找,第一个报错的资源就是要修的地方。

快速修复

沿 Certificate → CertificateRequest → Order → Challenge 逐级 describe,第一个报错的资源就是故障点。在 Certificate 所在的命名空间里执行:

kubectl describe certificate example-com -n web
kubectl get certificaterequest,order,challenge -n web
kubectl describe challenge -n web
kubectl describe clusterissuer letsencrypt-prod

Challenge 的 Status 和 Events 里通常有 CA 返回的原文(比如 404、超时、DNS problem)。把它修好,然后等 cert-manager 重试,或者删掉失败的 CertificateRequest 重新开始。

原因

Certificate 不 Ready 有 5 类原因,ACME 签发时最常见的是 HTTP-01 验证请求进不了集群。

1. HTTP-01 验证请求到不了

CA 要通过你的 Ingress 访问 http://<域名>/.well-known/acme-challenge/<令牌>,cert-manager 会为此临时创建求解器 Pod、Service 和 Ingress。

  • 确认: Challenge 显示 Waiting for HTTP-01 challenge propagation,或者 wrong status code '404' 之类的错误;从外部 curl http://example.com/.well-known/acme-challenge/test 到不了集群。
  • 修复: 求解器的 ingressClassName(或 class 注解)要和你的 Ingress 控制器一致;控制器的 LoadBalancer 要对外开放 80 端口(阿里云 SLB、腾讯云 CLB 的监听和安全组都要放行);强制 HTTPS 跳转、鉴权中间件要对验证路径豁免;域名解析要指向 Ingress 的负载均衡地址。

2. DNS-01 凭据、区域或生效时间

  • 确认: Challenge 报 API 错误(403、invalid credentials)、NXDOMAIN,或者一直在等待生效。
  • 修复: 检查求解器引用的 Secret 和 DNS 服务商的权限(阿里云 RAM 子账号要有云解析权限);确认求解器操作的是实际生效的区域。集群用了内外网分离解析时,给控制器设置 --dns01-recursive-nameservers 指向公网 DNS。参见 NXDOMAIN。

3. Issuer 不是 Ready 或引用错了

  • 确认: kubectl get issuer,clusterissuer -A 显示 READY False,或者 CertificateRequest 提示找不到 issuer。
  • 修复: Issuer 是命名空间级的,只服务同一命名空间的 Certificate;全集群共用要在 issuerRef 里写 kind: ClusterIssuer。同时检查 ACME server 地址、邮箱和账户私钥 Secret。

4. CA 拒绝了订单

5. Secret 冲突或 webhook 异常

  • 确认: 事件里提到和已有 Secret 冲突,或者 cert-manager webhook 无法访问(failed calling webhook)。
  • 修复: 一个 secretName 只能由一个 Certificate 管理;确认 cert-manager、cainjector、webhook 三个 Pod 都在运行,网络策略允许 API Server 访问 webhook。

各环境修法

各环境的共同要求是让 CA 能访问到验证路径或查到 DNS 记录;公网访问不到 80 端口的集群改用 DNS-01。

在 Ingress 上加注解(cert-manager.io/cluster-issuer): cert-manager 会自动创建 Certificate,名字就是 tls.secretName,按这个名字 describe。

Gateway API: 通过 Gateway API 做 HTTP-01,需要在求解器里配置 gatewayHTTPRoute,并且 Gateway 有 80 端口的监听。

私有集群或 NAT 后面的集群: HTTP-01 需要公网能访问 80 端口,做不到就用 DNS-01。

云负载均衡: 负载均衡上的健康检查、WAF 规则可能拦截或重定向验证请求,对验证路径豁免。

先用测试环境: 用 staging 的 ClusterIssuer 排错,不消耗正式环境的额度。

证书签下来之后: 用 kubectl get secret <名称> -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -dates -issuer 确认 Secret 里的证书确实是新签发的、签发者正确;再用 SSL 检测工具从外部确认 Ingress 实际对外出示的是这张证书,而不是控制器自带的默认假证书。

不要这样做

这 3 种做法会耗光签发额度、扩大明文 HTTP 的暴露面,或者让证书到期后无人续期。

  • 反复删了重建 Certificate。 每个新 Order 都计入 CA 的限制,先修好失败的环节。
  • 为了让验证通过全局关闭 HTTPS 跳转。 只豁免 /.well-known/acme-challenge/。
  • 手动把证书塞进 Secret,Certificate 放着不管。 它不会续期,到期时悄无声息地失效。

常见问题

正常情况下多久能 Ready?

HTTP-01 或 DNS-01 配置正确时,一般几分钟以内;DNS-01 还要加上记录生效的等待时间。十分钟还没 Ready,就别再等了,直接看 Challenge 资源。

怎么让 cert-manager 立刻重试?

修好原因后,用 cmctl renew <证书名> 触发签发,或者删掉失败的 CertificateRequest 让它重新创建。不手动触发的话,cert-manager 也会按退避策略自动重试。

国内的 Kubernetes 集群(ACK、TKE)有什么特别要注意的?

两点:一是域名要已备案,否则 80/443 访问可能被云厂商拦截,HTTP-01 验证拿不到正确内容;二是 cert-manager 镜像默认从 quay.io 拉取,国内拉取慢时需要在镜像仓库里做同步,但不要使用来源不明的镜像。DNS-01 可以用阿里云解析、DNSPod 的社区 webhook。

要先用 Let's Encrypt 测试环境吗?

建议这样做。单独建一个指向 https://acme-staging-v02.api.letsencrypt.org/directory 的 ClusterIssuer,Certificate 先用它,Ready 之后再把 issuerRef 切到正式环境。正式环境的失败会计入速率限制。

资料来源

  1. cert-manager: Troubleshooting
  2. cert-manager: Troubleshooting Problems with ACME / Let's Encrypt Certificates
  3. cert-manager: Certificate resource
  4. cert-manager: ACME issuer configuration
  5. Let's Encrypt: Rate Limits