在 Kubernetes 里用 cert-manager + Venafi 自动签发和轮换证书
Posted on 三 15 4月 2026 in Journal
| Abstract | 在 Kubernetes 里用 cert-manager + Venafi 自动签发和轮换证书 |
|---|---|
| Authors | Walter Fan |
| Category | Journal |
| Status | v1.0 |
| Updated | 2026-04-15 |
| License | CC-BY-NC-ND 4.0 |
在 Kubernetes 里用 cert-manager + Venafi 自动签发和轮换证书
短大纲
- 手工换证书在 Kubernetes 里为什么迟早翻车
- cert-manager 和 Venafi 各管什么,别把锅甩错对象
- 一套最小可用配置:
ClusterIssuer、Certificate、Secret、Ingress - 自动续期和私钥轮转真正的边界:证书会变,应用未必会跟着变
- 最后给一张思维导图和一份能照抄的上线检查清单
正文
证书这活儿,最怕“差不多就行”
很多团队对 TLS 证书的态度,像对消防演练。平时嫌麻烦,真响警报时才想起来楼道里还堆着纸箱。服务一旦跑进 Kubernetes,这事又多了几层:Secret 怎么更新,Ingress 怎么引用,应用进程到底会不会 reload。要是还靠手工申请、手工导入、手工替换 tls.crt,迟早会在某个周五晚上把值班同学从床上薅起来。
这也是 cert-manager 和 Venafi 这对搭档存在的意义。前者负责在集群里把证书生命周期跑起来,后者负责企业 PKI 的策略、审批、审计和签发。一个像施工队,一个像门卫兼档案管理员。只靠其中一个,都容易把事情做成半拉子工程。
顺手说一句,Venafi 后来并入了 CyberArk,所以有些新文档已经写成 CyberArk Certificate Manager。不过在 cert-manager 这边,字段名还是 spec.venafi。这不是我手滑,也不是你 YAML 写错了,只是产品名变了,接口名还没跟着折腾。
先把角色分工说清楚
很多人第一次配这套东西,脑子里就一个问号:到底是谁向 CA 要证书?谁把证书塞进 Secret?谁来让服务切到新证书?把分工拆开,事情就清爽了。
| 组件 | 它负责什么 | 你最该盯什么 |
|---|---|---|
cert-manager controller |
监听 Certificate,生成 CSR,请求签发,更新 Secret,到期前续期 |
能不能成功签发,续期时间算得对不对 |
Venafi zone / policy |
决定谁能签、签多久、字段怎么约束、归属到哪个应用 | 策略、审批、审计、模板 |
Issuer / ClusterIssuer |
把 cert-manager 和某个 Venafi zone 连起来 | 凭据、namespace、zone 有没有配错 |
Certificate |
声明我要什么域名、多长有效期、提前多久续、是否轮换私钥 | dnsNames、renewBefore、privateKey.rotationPolicy |
Secret |
真正落地的 tls.crt / tls.key |
有没有被正确引用 |
Ingress / 应用进程 |
消费证书并对外提供 TLS | Secret 变化后会不会 reload |
一句话:cert-manager 负责“把流程跑起来”,Venafi 负责“按什么规矩签”,你的服务负责“吃到新证书后别装死”。
@startuml
!theme plain
skinparam backgroundColor #FEFEFE
skinparam defaultFontSize 12
skinparam componentStyle rectangle
actor "Developer / GitOps" as Dev
cloud "Venafi / CyberArk\nZone + Policy" as Ven
rectangle "Kubernetes Cluster" {
component "Certificate CR" as Cert
component "cert-manager\ncontroller" as CM
database "Secret\n(tls.crt / tls.key)" as Sec
component "Ingress / Service" as Svc
}
Dev --> Cert : apply YAML
Cert --> CM : watch
CM --> Ven : request CSR / renew
Ven --> CM : signed cert chain
CM --> Sec : write tls.crt + tls.key
Svc --> Sec : mount or reference
CM --> CM : renew before expiry
CM --> Sec : rotate key if needed
Svc --> Svc : reload or restart
@enduml

先跑通一张最小可用的证书
文章不是为了把 YAML 贴满屏,而是为了先跑通一张能签发、能续期、能轮换的证书。下面这套配置,够你从 0 走到 1。
1. 装好 cert-manager
如果集群里还没有 cert-manager,先把它装上去:
helm repo add jetstack https://charts.jetstack.io
helm repo update
helm upgrade --install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--set crds.enabled=true
如果你们公司要求从 CyberArk / Venafi 提供的 OCI registry 拉镜像,就按对应文档换镜像源。核心点不在安装姿势,而在于 controller、webhook、cainjector 这些组件得齐,别装了个壳子就以为万事大吉。
2. 准备 Venafi 凭据
这里先用 Venafi SaaS / Control Plane 那条路径来举例,因为它最顺手。先把 API key 做成一个 Secret:
kubectl create secret generic venafi-vaas \
-n cert-manager \
--from-literal=apikey='YOUR_VENAFI_API_KEY'
如果你后面打算用的是 ClusterIssuer,这个 Secret 默认就该放在 cert-manager namespace。很多人第一坑就踩在这里:ClusterIssuer 创建得挺开心,结果 cert-manager 根本找不到凭据。
3. 建一个 ClusterIssuer
接着创建一个连到 Venafi zone 的 ClusterIssuer:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: venafi-platform
spec:
venafi:
zone: 'Platform\Kubernetes'
cloud:
apiTokenSecretRef:
name: venafi-vaas
key: apikey
这里有个很容易被忽略的点:一个 Venafi Issuer 对应一个 zone。 不同业务线、不同审批策略、不同所有权边界,最好老老实实拆成不同的 Issuer 或 ClusterIssuer。别想着一个配置打天下,到头来不是权限过大,就是审计说不清。
4. 声明一张 Certificate
然后让 cert-manager 去申请证书:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: hello-api-tls
namespace: demo
spec:
secretName: hello-api-tls
issuerRef:
name: venafi-platform
kind: ClusterIssuer
dnsNames:
- api.demo.example.com
duration: 2160h # 90d
renewBeforePercentage: 25
privateKey:
algorithm: RSA
size: 2048
rotationPolicy: Always
这里我建议盯住三件事:
dnsNames才是你真正要守住的字段。现在别再迷信commonName了,终端证书的域名匹配主要看这里。renewBeforePercentage: 25表示在证书剩下 25% 生命周期时就开始续期。这个写法比死写renewBefore: 360h更稳妥,因为有些 CA 实际发下来的有效期会比你想象的略短。rotationPolicy: Always我建议写死,不要靠“我记得某个版本默认已经是这样”来混日子。配置文件要写给半年后的自己看,不是写给今天的记忆力看。
有些版本较老的 cert-manager 还不支持 renewBeforePercentage,那就退一步写成:
renewBefore: 360h # 15d
签发成功之后,目标 Secret 里通常会有 tls.crt 和 tls.key。如果 CA 链信息可得,还可能带上 ca.crt。不过 tls.crt 里不会把 root cert 也一股脑塞进去,这不是 cert-manager 偷懒,而是正常设计。
5. 让服务真正吃到这张证书
如果你的 TLS 终止点在 Ingress,那事情相对简单,直接引用这个 Secret:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: hello-api
namespace: demo
spec:
ingressClassName: nginx
tls:
- hosts:
- api.demo.example.com
secretName: hello-api-tls
rules:
- host: api.demo.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: hello-api
port:
number: 8080
到这里,证书就完成了从 Venafi 到 Kubernetes Secret,再到入口流量的闭环。很多 Ingress Controller 对 Secret 变化的处理都比较成熟,证书更新后能比较平滑地切过去。
如果 TLS 是你的应用自己终止,不是在 Ingress 上,那就把 Secret 通过 volume mount 挂进去,并让进程支持文件变化后的 reload。别把证书塞进环境变量里,这条路和“自动轮转”基本是相看两厌。
6. 如果你用的是 TPP,不是 SaaS
如果你的 Venafi 是 TPP,那配置只是换个认证块,骨架并没有变:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: venafi-tpp
spec:
venafi:
zone: '\VED\Policy\devops\k8s'
tpp:
url: https://tpp.example.com/vedsdk
credentialsRef:
name: venafi-tpp-credentials
这里要额外记住两件事:
ClusterIssuer用到的 credentials Secret,默认同样要放在cert-managernamespace。- TPP policy 最好允许 User Provided CSR。不然你在
Certificate里写的 key algorithm、key size 等字段,要么被策略覆盖,要么干脆签发失败。到那时你盯着 YAML 看半天,也只是对空气生气。
如果你的 TPP 用的是自签 CA 或公司内网 CA,还得补上 caBundle 或 caBundleSecretRef,不然 cert-manager 和 TPP 自己都握不成手。
轮转到底轮了什么
很多文章写到“自动轮转”就一句带过,仿佛配完 YAML 之后世界和平。现实没那么诗意,真正要弄明白的是:到底是哪一层在自动,哪一层还得你自己兜底。
cert-manager 会根据证书实际有效期和你的 renewBefore / renewBeforePercentage 来安排续期。如果你什么都不写,它默认会在证书生命周期走到大约三分之二时尝试续签。也就是说,它确实会替你记日子,这一点比人靠谱。
如果你显式配置了 privateKey.rotationPolicy: Always,那么每次重签时都会生成新的私钥。这个做法更稳妥,一来减少旧私钥长期复用的风险,二来也能尽早暴露“应用是否真的支持换 key”这个现实问题。平时不演练,出事时就只能演砸。
还有一个细节很重要:cert-manager 在拿到新签名证书之前,不会贸然覆盖 Secret 里的 tls.key。这一点是为了尽量减少中间态带来的宕机风险。可即便如此,Secret 更新了,不等于业务进程已经切到新证书。
很多 Ingress Controller 会监听 Secret 变化并 reload;你自己写的 Go、Java、Python 服务,就不一定了。如果进程只在启动时读一次证书文件,那新证书只会安安静静躺在磁盘上,业务继续拿旧证书对外服务。碰到这种应用,要么自己实现文件变更监听和优雅 reload,要么在 Secret 变化后做一次 rollout restart,或者上一个专门盯 Secret 变化的重启控制器。
手工演练时,我建议至少看三样东西:
kubectl describe certificate hello-api-tls -n demo
kubectl get certificaterequest -n demo
cmctl renew hello-api-tls -n demo
重点不是把命令背下来,而是看清楚几个状态:证书是不是 Ready=True,有没有反复 Issuing,续期时间是不是符合预期。想手工触发续签,用 cmctl renew。不要靠“删掉 Secret 试试看”这种方式碰运气,那不是自动化,是拉老虎机。
几个最容易踩的坑
坑一:ClusterIssuer 能看到证书,看不到凭据
这是最常见的低级错误。ClusterIssuer 是集群级资源,但它找凭据 Secret 时,默认会去 cert-manager 的 cluster resource namespace,也就是 cert-manager。你把 Secret 放到业务 namespace,表面看资源都在,实际 cert-manager 两手一摊:我也想签,可我没钥匙。
坑二:一个 zone 想管所有业务
Venafi 的 zone 不只是路径,它其实承载了策略、审批和归属关系。把所有场景都压到同一个 zone 里,前期看着省事,后面审计、权限、模板一变,大家一起遭殃。平台类服务、外网服务、内网服务,最好拆开。
坑三:证书续了,进程没换
这类问题特别像“表面一切正常”。Certificate 是绿的,Secret 也更新了,可线上扫出来还是旧证书。问题不在 Venafi,也不在 cert-manager,而在应用进程没 reload。机器没有读心术,文件变了不代表它会自己刷新内存。
坑四:舍不得换私钥
不少团队对私钥有一种莫名其妙的感情,好像“沿用老 key 更稳”。其实很多企业级 issuer 本来就不鼓励,甚至不允许重复使用旧私钥。把 rotationPolicy: Always 写明白,省得以后靠猜版本默认值。
坑五:把续期窗口写得过于激进
renewBefore 不是写得越早越保险。你要是把它贴得离 duration 太近,就可能把自己卷进 renewal loop。新版能用 renewBeforePercentage 的话,我更建议直接用百分比,让 cert-manager 按实际签下来的证书时长去算。
坑六:把问题都怪到 cert-manager 头上
有时签发失败真不是 cert-manager 的锅,而是 Venafi policy 卡住了。比如 zone 配错了,审批策略没过,TPP policy 不接受你提交的 CSR,或者自签 CA 的 trust bundle 没带上。工具链里一共有好几环,别一出错就逮着最前台那个控制器骂。
总结
cert-manager适合当 Kubernetes 里的执行层:监听Certificate、生成 CSR、续期、更新 Secret。Venafi适合当企业 PKI 的策略层:管 zone、模板、审批、审计和归属。- 真正的自动轮转,不只是“证书能签下来”,还包括“私钥能换”“应用能 reload”“出故障时你知道去哪一层排查”。
- 配置上我最想强调三件小事:
ClusterIssuer的凭据放对 namespace,Certificate显式写rotationPolicy: Always,应用侧提前演练 reload。
思维导图(源码见下节 PlantUML,以下为渲染图):

@startmindmap
* K8s cert-manager + Venafi
** 分工
*** cert-manager: 申请、续期、写 Secret
*** Venafi: 策略、审批、审计、签发
*** Service/Ingress: reload 新证书
** 最小配置
*** Secret: API key / credentials
*** ClusterIssuer: 绑定 zone
*** Certificate: dnsNames + 生命周期
*** Secret: tls.crt / tls.key
** 轮转
*** renewBefore / renewBeforePercentage
*** privateKey.rotationPolicy = Always
*** cmctl renew 手工演练
*** 证书更新 != 进程已 reload
** TPP 注意项
*** policy folder 单独规划
*** credentials Secret 放对 namespace
*** User Provided CSR 要允许
*** 自签 CA 需要 caBundle
** 常见坑
*** Secret 放错 namespace
*** 一个 zone 想管所有业务
*** renewal loop
*** 应用不支持热更新
*** 误删 Secret 碰运气
@endmindmap
可执行 CheckList
- [ ] 确认 cert-manager 版本,能用
renewBeforePercentage就优先用它 - [ ]
ClusterIssuer的 credentials Secret 放在cert-managernamespace - [ ] 不同业务和不同策略拆成不同 Venafi zone,不要混成一锅
- [ ] 每张
Certificate显式写privateKey.rotationPolicy: Always - [ ] 服务通过 Secret volume 消费证书,并验证 reload 逻辑
- [ ] 做一次
cmctl renew演练,确认业务侧能平滑切证书 - [ ] 监控
Certificate的到期时间和签发失败事件
扩展阅读
- cert-manager
Certificate文档:https://cert-manager.io/docs/usage/certificate/ - cert-manager 的 Venafi 配置文档:https://cert-manager.io/v1.16-docs/configuration/venafi/
- CyberArk / Venafi 的 cert-manager 安装说明:https://docs.venafi.cloud/vaas/k8s-components/t-certmanager-install/
本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。