Kubernetes 证书自动签发:cert-manager HTTP-01 全链路实战与排错

发布时间:2026/10/16 15:06:11
Kubernetes 证书自动签发:cert-manager HTTP-01 全链路实战与排错 Kubernetes 里给 HTTPS 服务发证书我现在的默认答案只有四个字交给 cert-manager。它通过 ACME 协议跟证书颁发机构打交道其中 HTTP-01 是最常见也最容易搞懂的一种校验方式——CA 会访问你域名的 80 端口确认你真的控制了这个域名然后把证书签出来。整个过程从创建 Ingress 注解到拿到可用证书正常情况下只要几十秒。这篇文章就围绕这个“自动签发”链路把原理、配置、排错一次讲透适合那些刚把服务迁到集群、正在被证书问题卡住的同学也适合想搞懂 cert-manager 到底在背后做了什么的人。我在自己的集群里用这套方案已经跑了很长时间期间踩过不少坑也把 HTTP-01 的完整工作细节摸得比较清楚。下面我先从校验原理讲起再一步步带你完成安装、配置、签发和排错最后分享几个常规文档里不会写的经验点。1. 动手前先拆链路HTTP-01 挑战是怎么把证书送进集群的很多人配置 cert-manager 的时候只是照着模板抄了一个 ClusterIssuer然后发现证书没签发成功就开始各种求助。这时候最缺的不是更多配置片段而是搞清楚一条完整链路CA 到底在什么时候、用什么方式、访问了哪些资源来验证你对域名的控制权。1.1 一个容易理解的生活类比把 ACME 协议的 HTTP-01 挑战理解成一次“暗号交接”会更直观。假设 CA 是一个开锁师傅它要确认你就是域名的主人于是给了你一张纸条上面写着一个暗号 token。它要求你把纸条贴在自己家大门上也就是http://你的域名/.well-known/acme-challenge/token这个固定位置。贴好之后开锁师傅会专门跑过来看一眼如果纸条上的 token 和他给你的完全一致就证明了“这个房子确实是你说了算”于是开锁师傅才会把他保管的正式钥匙也就是证书交给你。在这个类比里cert-manager 的角色是你家的管家。它负责从 CA 那里领 token、生成对应的纸条、把纸条贴到正确的位置然后在 CA 来检查完、签出证书之后再把纸条收走不留一点痕迹。这个流程听起来简单但在 Kubernetes 集群里有几个关键环节特别容易出问题纸条贴的位置必须让公网访问进来时能准确命中挑战 Pod 必须能被 CA 访问到并且整个过程中的时间窗口很短通常以秒计任何一步慢了或错了挑战就会失败。1.2 从 token 到 key authorizationHTTP-01 的协议细节ACME 协议里HTTP-01 挑战并不是简单返回一个 token 就行它要求返回的是 key authorization。这个值的格式是token.account_thumbprint其中 token 是 CA 在创建订单时下发的随机字符串account thumbprint 是基于你的 ACME 账户公钥做 SHA-256 摘要后得到的识别码。cert-manager 会自动完成这两部分的拼接你不需要手工计算但了解这一点会在排查问题时有很大帮助。证书签发的完整流程大致是cert-manager 向 CA 的 ACME endpoint 发起新订单请求带上你想申请的域名列表。CA 创建订单生成 token并告诉你需要完成 HTTP-01 挑战。cert-manager 在集群里创建一个临时 Ingress 资源和对应的 Challenge Pod。临时 Ingress 把/.well-known/acme-challenge/token路径转发到 Challenge Pod。CA 从公网发起 HTTP 请求访问该路径并拿到 key authorization校验匹配后标记挑战完成。cert-manager 收到挑战成功通知提交 CSRCA 签发证书。证书连同私钥被写入 Secret整个流程结束。这条链路里第 4 步是最容易出问题的。因为 Challenge Pod 本身是临时存在的如果 CA 来访问时它还没就绪或者 Ingress 路由规则没有及时生效请求就会 404挑战直接失败。1.3 HTTP-01 和 DNS-01 怎么选做证书签发时另外一个绕不开的问题是到底选 HTTP-01 还是 DNS-01。我在多个项目里都做过对比简单给一个结论对比维度HTTP-01DNS-01通配符证书不支持支持验证方式通过公网访问 80 端口通过操作 DNS TXT 记录依赖条件域名解析到集群入口80 端口公网可达需要 DNS 服务商 API 凭证配置复杂度低中高需要配置 Secret 保存凭证常见失败原因端口不通、Ingress 未生效凭证权限不足、TXT 记录生效延迟如果你的业务用的是普通二级域名并且入口流量已经统一走 Ingress不需要通配符证书那我建议直接用 HTTP-01。它少一个云厂商凭证管理的环节安全风险也更小。只有当你确实需要*.example.com这种通配符证书时才值得去折腾 DNS-01。这个选择放在最开始做能避免后面走弯路。2. 环境初始化选版本、装 cert-manager、配好 ClusterIssuer看完了原理接下来进入到动手环节。这一部分我会把从安装到出证书的每一步都过一遍保证你照着做就能跑通。2.1 版本选型别在这个细节上偷懒cert-manager 的版本升级节奏很快不同版本对 Kubernetes 版本的兼容范围也不一样。我见过不少人直接在集群里装了很老的版本结果 CRD 与 API 版本不匹配Ingress 注解完全不生效最后排查了半天才发现是版本问题。安装之前建议先去官方文档查一下当前最新版本对应的 compatibility matrix确认你的 Kubernetes 版本在支持列表里。简单来说尽量选择近半年内发布的版本同时保证集群版本不低于 cert-manager 要求的最低版本。如果集群版本比较旧不要盲目追新选一个同时兼容旧集群和当前集群的版本即可。另外要注意一点cert-manager 会安装一批 CRD 资源比如 Certificate、Issuer、ClusterIssuer、Order、Challenge。这些 CRD 属于集群级别的资源卸载或者升级时要特别小心不要直接删除所有 CRD否则已签发的证书对象可能一并被清理。2.2 部署 cert-manager两种方式选一种即可官方支持两种主流部署方式直接用 Kubernetes manifest或者用 Helm Chart。我个人在实际项目里更推荐 Helm因为后续升级和参数调整都方便而且可以对 CRD 的安装行为做显式控制。使用 Helm 部署时核心命令如下helm repo add cert-manager https://charts.jetstack.io helm repo update helm install cert-manager cert-manager/cert-manager \ --namespace cert-manager \ --create-namespace \ --set crds.enabledtrue如果你不想引入 Helm也可以直接用官方 release 页面提供的 manifest 文件kubectl apply -f cert-manager-manifest-url这里有一个关键提醒无论你用哪种方式都一定要保证 CRD 也被同时安装。cert-manager 运行后需要这些 CRD 才能工作。如果用了 Helm 却把crds.enabled设成 false但事前又没有手动安装 CRD那么后来创建 ClusterIssuer 时会直接报 “no matches for kind”非常容易踩。安装完成后检查一下运行状态kubectl -n cert-manager get pod正常情况下你会看到三个组件cert-manager 主控制器、cert-manager-cainjector、cert-manager-webhook。三个 Pod 都进入 Running 状态说明基础环境已经就绪。2.3 配置 ClusterIssuer把 CA 的连接信息告诉 cert-managercert-manager 装好之后还要告诉它从哪个 CA 申请证书。这个信息通过 Issuer 或 ClusterIssuer 资源来表达。两者的区别很简单Issuer 是命名空间级别的只能给同一个命名空间里的 Certificate 使用ClusterIssuer 是集群级别的所有命名空间都能用。对于大多数中大型集群我推荐直接用 ClusterIssuer因为证书申请往往涉及多个业务命名空间每个命名空间单独建 Issuer 只会增加管理成本。下面是一个适用于 HTTP-01 的 ClusterIssuer 配置apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: my-http01-issuer spec: acme: email: your-emailexample.com server: https://acme-staging.example-ca.com/directory privateKeySecretRef: name: acme-account-key solvers: - http01: ingress: class: nginx有四个字段需要重点解释一下email这是你的 ACME 账户邮箱。证书快过期时CA 会给这个邮箱发提醒也用于账户注册。serverACME endpoint 地址。我强烈建议先使用测试环境做验证确认流程没问题了再切换到生产环境的 endpoint。因为正式签发的证书有数量上限和频率限制频繁测试容易把你的额度打满。privateKeySecretRefcert-manager 会为你的 ACME 账户生成一对密钥存在这里。注意不要多个 ClusterIssuer 共用同一个 Secret 名字容易导致账户状态混乱。solvers这里配 http01并指定 Ingress class。cert-manager 创建临时 Ingress 时会使用这个 class 来匹配你的 Ingress Controller。2.4 验证 Issuer 状态Ready 才是真的好了创建完 ClusterIssuer 后用下面的命令确认状态kubectl get clusterissuer my-http01-issuer -o yaml看status.conditions里的 Ready 条件。如果值是 True说明 cert-manager 已经成功注册 ACME 账户并且能正常访问 CA 服务。如果一直是 False查看 messages 字段常见原因包括邮箱格式不对、CA endpoint 不可达、网络不通等。我通常会在这一步停一会确认 Ready 之后再进行证书签发实操。因为这一步如果没就绪后面所有的挑战都会失败而且错误信息不一定能直接指向这里的配置问题。3. 核心实战一条注解让证书自动签发并续期Issuer 就绪之后就可以正式申请证书了。cert-manager 提供了好几种触发方式但最顺手的还是直接在 Ingress 上写注解剩下的由证书自动申请机制来处理。3.1 三种触发方式先搞懂再上手我见过的大多数业务最后都用了 Ingress 注解方式。但在选它之前值得花一分钟了解另外两种方式因为你迟早会在排查别人代码时遇到。第一种是手动创建 Certificate 资源。它的好处是职责清晰不依赖 Ingress 是否存在。比如你只想给一个内部服务的域名签证书不打算配置 Ingress 转发规则那可以手动写一个 Certificate指向要保存的 Secret 名称即可。第二种是通过 Ingress 注解触发这也是 cert-manager 默认具备的能力。你只需要在 Ingress 上标注哪把证书对应哪个域名cert-manager 会自动创建 Certificate 对象并替你维护整个生命周期。它的优势在发布时尤其明显应用上线加一条注解证书就跟着出来了不需要额外写证书配置。第三种是手动调用 API 创建 CertificateRequest一般用于自定义内部 CA 场景纯 ACME 场景用得少。如果你是标准 Web 服务我建议无脑选第二种。下面重点介绍这种方式。3.2 Ingress 注解写对这几个字段就够了假设你的服务叫 my-app部署在 default 命名空间域名是app.example.com。一个能触发证书自动签发的 Ingress 配置如下apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: my-app-ingress annotations: cert-manager.io/cluster-issuer: my-http01-issuer spec: ingressClassName: nginx tls: - hosts: - app.example.com secretName: my-app-tls rules: - host: app.example.com http: paths: - path: / pathType: Prefix backend: service: name: my-app port: number: 80这里有两个字段是核心注解cert-manager.io/cluster-issuer告诉 cert-manager 使用哪个 ClusterIssuer。如果你用的是命名空间级别的 Issuer则写成cert-manager.io/issuer。spec.tls.secretName证书签发完成后会写入这个 Secret 名称。Ingress 也会自动引用这个 Secret 来启用 HTTPS。注意如果证书申请失败但 Secret 已经存在但内容无效或过期Ingress 依然会引用它导致浏览器访问时出现证书错误。所以排查时不能只看 Ingress 有没有配置还要进一步检查 Secret 里的证书内容。3.3 HTTP-01 挑战如何临时“借用”你的入场流量当我配置上面这个 Ingress 后cert-manager 的控制器会在后台做一系列动作你可以通过下面命令实时观察kubectl get certificate kubectl get order kubectl get challenge kubectl get secret my-app-tls值得特别说一句的是HTTP-01 挑战的流量路由方式和普通业务流量有一点点不同。cert-manager 并不会直接修改你刚才创建的业务 Ingress而是会临时创建一个新的 Ingress 资源这个 Ingress 专门响应/.well-known/acme-challenge/*路径下的请求并把请求转发到 challenge Pod。这也是为什么我反复强调 Ingress class 一定要配对的原因。如果 cert-manager 创建的临时 Ingress 没带对 ingressClass或者 Ingress Controller 和 IngressClass 之间有网络策略隔离那么 CA 的验证请求根本进不了 Challenge Pod挑战就会一直处于 pending。整个签发过程正常时长在 10 到 60 秒之间。签发完成后kubectl get certificate会看到 READYTrue同时 my-app-tls 这个 Secret 会包含tls.crt和tls.key两个字段。3.4 证书续期全自动但你要知道它“什么时候动手”ACME 签发的证书有效期一般是 90 天。cert-manager 不会等到过期才去续期它默认会在证书到期前约 30 天就开始检查并自动续期。这个提前量可以通过 Certificate 资源的spec.renewBefore字段调整单位是小时。我把默认值调整为720h的理解是相当于提前一个月。这个窗口足够宽就算某次续期失败也还有充足时间去排查不会立刻影响线上流量。如果你想手动触发一次续期可以先删除已有的 Secret然后重启对应的 Certificate 自动流程或者使用命令行工具来强制更新一个已有证书kubectl cert-manager renew my-app-tls这个命令背后做的事情就是给证书生成一个新的 CertificateRequest走同样的 ACME 流程。如果新证书签发成功cert-manager 会直接更新原来的 Secret。这种手动续期通常在证书刚改过域名、或者你怀疑当前证书有问题时用得上。4. 问题排查HTTP-01 四种典型失败与修复实录这一部分是我最想写的。配置证书这件事本身不难难的是出了问题以后怎么从一堆日志和状态里找到真正的原因。我把实际工作中最常碰到的四种 HTTP-01 失败场景整理成了一张排查表然后逐个展开讲讲每个场景我都实际遇到过。4.1 典型失败对照表先对号入座现象可能原因首要排查动作Challenge 状态一直是 pending最终超时失败CA 无法访问集群 80 端口公网 curl 域名根路径确认能返回业务响应报错 404 Not Found临时 Ingress 未生效或 class 不匹配查看 challenge Pod 是否运行查 cert-manager 日志报错 connection refused集群入口没有监听 80 端口检查 Ingress Controller Service 端口映射报错 400 / invalid response已有业务逻辑拦截了.well-known请求或者返回的不是 key auth 内容curl 对应挑战路径对比返回内容Order 一直失败Unknown Error网络策略、安全组拦截了 CA 来源 IP检查 NetworkPolicy 和安全组白名单规则下面结合具体经验把其中四个高频场景展开说清楚。4.2 场景一80 端口公网不可达HTTP-01 最大的硬性条件是域名必须能从公网访问并且访问的是 80 端口。很多同学部署 Ingress 时只把 443 端口映射出来了认为反正最终只走 HTTPS80 端口用不上。但对 HTTP-01 来说CA 来验证的时候明确请求的就是http://你的域名/.well-known/...这属于明文 HTTP 访问必须得能通。解决办法很简单把 Ingress Controller 的 Service 配置成同时暴露 80 和 443 端口并且确认你的云安全组、防火墙规则放行了 80 端口。还有一个容易被忽略的点是如果 80 端口的流量到了业务服务后服务端自动做了一次 301 跳转到 HTTPS那么 CA 的请求也会跟着跳到 HTTPS但 ACME 客户端做 HTTP-01 校验时并不跟随重定向就会失败。所以业务层面不要对/.well-known/acme-challenge/路径做强制跳转或者至少要把这个路径放行。我在本地自测时习惯用这个命令先验证公网链路是否正常curl -I http://app.example.com/.well-known/acme-challenge/test-token如果返回的是业务首页内容说明 80 端口确实通了。如果返回超时或 connection refused那就先把网络链路修好再谈后面的事。4.3 场景二临时 Ingress 未生效如果你确定 80 端口没问题但挑战还是一直失败那就要重点看临时 Ingress 了。cert-manager 在开始挑战前会先创建一个名为类似cm-acme-http-solver-xxxx的 Ingress 资源。用下面的命令确认它是否存在kubectl get ingress -n 你的命名空间 -l acme.cert-manager.io/http01-solvertrue如果这个临时 Ingress 不存在说明 cert-manager 在处理 HTTP-01 挑战时连路由都没建出来原因很可能是solver.http01.ingress.class和你 Ingress Controller 实际使用的 IngressClass 对不上。我记得有一次就是集群里有两个 Ingress Controller临时 Ingress 被另一个 Controller 捡走了导致流量走到了错误的入口。如果临时 Ingress 存在但挑战仍然 404可以进一步查看挑战 Pod 是否 Readykubectl get pod -n 命名空间 -l acme.cert-manager.io/http01-solvertrue正常情况下这个 Pod 会表现为一个正在监听:8089的 HTTP 服务专门返回 key authorization。如果 Pod 一直 ContainerCreating 或者 CrashLoopBackOff那就需要去看 Pod 日志和事件常见原因包括镜像拉取失败、CPU/内存资源不足。4.4 场景三网络策略把挑战 Pod 挡住了在启用了 NetworkPolicy 的集群里另一个高频坑是业务逻辑都没问题但挑战 Pod 没有放行来自 Ingress Controller 的流量。因为 CA 的请求路径是公网 - Ingress Controller - 临时 Ingress 路由 - Challenge Pod。只要中间有一段流量被网络策略拦下挑战就会失败。遇到这种情况你要检查集群里是否有默认拒绝流量的 NetworkPolicy。如果有需要额外放行一条规则允许 Ingress Controller 访问命名空间里带有acme.cert-manager.io/http01-solvertrue标签的 Pod。通常我会在业务命名空间里加一个这样的策略同时保证策略只影响挑战 Pod不影响业务 Pod 之间的正常通信。顺带一提我这里说的挑战 Pod 标签和前面查看临时 Ingress 的标签是同一套在实际排查时可以直接复用标签选择器。4.5 场景四证书续期失败而非签发失败还有一种特别隐蔽的情况证书第一次签得很顺利到期前续期却失败。这个时候你去查 Certificate 状态会发现 READY 还是 True但 Secret 里的证书其实已经过期了浏览器直接报安全错误。为什么会这样常见的原因有两个第一个原因是 ACME 账户密钥发生变化。比如证书对应的 ClusterIssuer 被删掉重建过导致之前的账户私钥丢失CA 那边无法识别旧订单对应关系。此时一般重新创建一个 ClusterIssuer然后手动 renew 一次即可解决。第二个原因是续期时 CA 的验证请求再次走到线上但这时候 80 端口转发规则和挑战 Pod 的资源配额已经发生变化。很多人在初装时测试过一次成功后来改了 Ingress Controller 的 Service 配置或者缩了资源导致续期时挑战路径走不通了。续期失败不会马上报警所以建议把证书过期的监控做起来至少每周检查一次所有 Certificate 的到期时间。4.6 三条避坑经验能少走很多弯路第一永远先用测试环境。第一次配置 ACME 时建议先把 ClusterIssuer 的 server 地址指向测试环境。测试环境签出来的证书浏览器会报不信任但这不重要你要验证的是挑战流程能否跑通。流程通了再切换生产环境这样可以避免把宝贵的正式签发额度浪费在不断试错上。切换时只需要改 server 地址以及给生产环境单独建一个发放证书的 ClusterIssuer。第二解析记录不能只做一半。域名解析到集群入口时要确保解析记录在公网确实生效。如果域名刚迁过来DNS 缓存还没刷新CA 请求时可能解析不到你的集群入口挑战自然失败。建议先在自己的本机dig或nslookup一下确认解析正确再发起签发。第三不要动挑战路径上的流量。不管业务层用了什么网关、反代还是 WAF 规则都不要拦截或改写/.well-known/acme-challenge/路径。这个路径一旦被改写CA 拿到的内容和你配置的 key authorization 不一致挑战必失败。如果是大型网关还需要确认 HTTP 到 HTTPS 的强跳转规则对这段路径做了豁免。我在实际操作中最大的体会是HTTP-01 本身不复杂复杂的是它涉及的环节太多——DNS、端口、Ingress 路由、网络策略、CA 状态一个点卡住整条链就断了。但只要把这条链路画在脑子里碰到问题时按顺序逐段排查绝大多数问题都能在十分钟内定位到。希望这篇实战记录能让你在配置 cert-manager 时少踩几个坑把时间花在真正的业务上。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询