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

> cert-manager 的 Certificate 一直 READY False，问题出在 Certificate、CertificateRequest、Order、Challenge 中的某一环。用 kubectl describe 顺着链条找到报错再修。

Source: https://ssl.sh/zh/errors/cert-manager-certificate-not-ready/
Last updated: 2026-11-10

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

## 快速修复

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

```bash
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](https://ssl.sh/zh/errors/certbot-dns-problem-nxdomain/)。

### 3. Issuer 不是 Ready 或引用错了

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

### 4. CA 拒绝了订单

- **确认：** Order 或 CertificateRequest 的信息里有 `rateLimited`、`caa` 或 `rejectedIdentifier`。
- **修复：** 见 [Let's Encrypt 速率限制](https://ssl.sh/zh/errors/lets-encrypt-rate-limit/) 和 [CAA record prevents issuance](https://ssl.sh/zh/errors/caa-record-prevents-issuance/)。不是合法公网域名的名字，公网 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 检测工具](https://ssl.sh/zh/tools/ssl-checker/)从外部确认 Ingress 实际对外出示的是这张证书，而不是控制器自带的默认假证书。

## 不要这样做

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

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