Cert-Manager Kubernetes 证书签发与管理详解
在 Kubernetes 集群上通过 Helm 从零安装 cert-manager,签发并与自动续期自签名/Let's Encrypt 证书,覆盖 issuer/certificate CR 结构与 Duration、RenewBefore 配置及 kubectl 验证。
Cert-Manager Kubernetes 证书签发与管理详解
目标:在已有 Kubernetes 集群上通过 Helm 从零安装 cert-manager,并签发由集群内部 CA、以及 Let's Encrypt 自动续期的 TLS 证书,覆盖 Issuer / Certificate 自定义资源的含义、Duration 与 RenewBefore 控制续期时机的配置,以及
kubectl全流程验证与 CLI 速查。
cert-manager 是 Kubernetes 生态中最常用的证书签发与自动化续期组件。它通过声明式自定义资源(CRD)把「证书申请」抽象为一种资源:你只需描述「我要一张给哪个域名、由谁签发的证书」,cert-manager 便会在后台申请、验收并在到期前自动续期,证书内容以 Secret 形式供 Ingress 等引用。
⚠️ 前置说明:cert-manager 是一款只运行在 Kubernetes 之上的控制器组件,正文整套命令均在「已可用的 Kubernetes 集群」上执行。部署前需先准备好可用的 Kubernetes 集群(含
kubectl管理端),并已安装 cert-manager 所需的 CustomResourceDefinitions(CRD);若集群通过 Helm 安装则相关 CRD 会一并装入。
环境信息
- Kubernetes 集群:Kubernetes v1.28+(已通过
kubectl可访问) - Helm 版本:v3.12 及以上(安装 cert-manager 主程序)
- cert-manager 版本:v1.15 系列
- 目标命名空间:
cert-manager(控制面组件自身) - 示例应用命名空间:
myapp(业务证书) - 签发对象:集群自签名 CA(
ClusterIssuer)、Let's Encrypt 说明(章节四) - 相关自定义资源:
Issuer、ClusterIssuer、Certificate、CertificateRequest
一、版本选型与获取
cert-manager 以 Helm Chart 方式发布,Chart 仓库位于 Jetstack:
helm repo add jetstack https://charts.jetstack.io
helm repo update
查询可用的 cert-manager 版本:
helm search repo jetstack/cert-manager --versions
| 版本 | 发布说明 | 推荐度 |
|---|---|---|
| v1.15.x | 当前稳定主线,支持 k8s 1.28+,API 资源版本稳定 | 推荐 |
| v1.14.x | 前序稳定版本 | 兼容期 |
| 更旧版本 | 依赖较老 k8s | 不建议新部署 |
💡 说明:cert-manager 主程序为 Go 二进制,默认堆内存不大;日常有限资源集群可通过 Helm
--set controller.resources.limits.memory=256Mi --set controller.resources.limits.cpu=200m限制使用(见 2.3)。它不依赖 JVM,无需配置-Xmx之类参数。
二、在集群上安装 cert-manager
2.1 前置检查
确认 kubectl 可连接集群:
kubectl cluster-info
kubectl get nodes
确认 Helm 已就绪:
helm version
2.2 安装 cert-manager CRD 与控制器
⚠️ 前置:cert-manager 依赖其 CRD(Issuer、ClusterIssuer、Certificate、CertificateRequest、Challenge、Order)。安装过程会同时建立 CRD,若希望与 Helm package 生命周期解耦,可先单独应用 CRD 清单(使用
kubectl apply到crds.cert-manager.io复用现有 CRD,避免重复安装)。以下为标准安装:
创建命名空间并安装:
kubectl create namespace cert-manager
helm install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--version v1.15.3 \
--set installCRDs=true
⚠️ 注意:
--set installCRDs=true会在 Helm 安装时一并创建 cert-manager 的全部 CRD。生产环境建议显式以kubectl apply单独管理 CRD 版本再安装控制器,并把该参数保持true以保证首次可用。
实测预期回显(helm install 成功):
NAME: cert-manager
LAST DEPLOYED: Wed Oct 8 12:58:00 2026
NAMESPACE: cert-manager
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
cert-manager has been deployed successfully!
(省略 Chart NOTES 正文中其余说明)
2.3 查看控制器 Pod 与状态
kubectl get pods -n cert-manager
预期回显:
NAME READY STATUS RESTARTS AGE
cert-manager-7d8f9d6f6b-abc12 1/1 Running 0 2m
cert-manager-cainjector-6d7df5cd8f-xyz34 1/1 Running 0 2m
cert-manager-webhook-5c7c6f6f8b-qr56 1/1 Running 0 2m
💡 说明:
cert-manager(控制器)、cert-manager-cainjector(注入 CA BUNDLE)、cert-manager-webhook(校验 CR)三个 Deployment 各就一位即表示正常。
校验 CRD 已安装:
kubectl get crd | grep cert-manager.io
预期回显(kubectl get crd 会列出该命名空间下的全部 CRD,此处仅过滤 cert-manager.io 相关,并省略列宽):
certificates.cert-manager.io
certificaterequests.cert-manager.io
challenges.acme.cert-manager.io
clusterissuers.cert-manager.io
issuers.cert-manager.io
orders.acme.cert-manager.io
2.4 systemd 说明
⚠️ 注意:cert-manager 作为控制面组件本身运行在 Kubernetes 工作负载内,由 Kubernetes 调度并保持,不通过 Linux 宿主机的 systemd 托管。承载 Kubernetes 节点的 Linux 主机上,Pod 运行时与 kubelet 由各自 systemd 单元管理,本文不再赘述。
三、签发集群自签名 CA 证书
3.1 创建自签名 ClusterIssuer
创建一个自签名的 ClusterIssuer(颁发主体自身),用于为集群内部签发测试证书:
# cluster-issuer-selfsigned.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: selfsigned-issuer
spec:
selfSigned: {}
kubectl apply -f cluster-issuer-selfsigned.yaml
kubectl get clusterissuer selfsigned-issuer -o wide
预期回显:
NAME TYPE STATUS AGE
selfsigned-issuer SelfSigned True 5s
3.2 创建由该 Issuer 签发的 Certificate 资源
通过 Certificate 资源申请一张给 myapp 命名空间下服务的证书:
# myapp-certificate.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: myapp-tls
namespace: myapp
spec:
secretName: myapp-tls
issuerRef:
name: selfsigned-issuer
kind: ClusterIssuer
commonName: myapp.example.com
dnsNames:
- myapp.example.com
duration: 2160h # 90 天
renewBefore: 360h # 到期前 15 天续期
⚠️ 前置:
myapp命名空间须已存在(kubectl create namespace myapp)。
kubectl create namespace myapp
kubectl apply -f myapp-certificate.yaml
kubectl get certificate -n myapp myapp-tls -o wide
预期回显:
NAME READY SECRET ISSUER STATUS AGE
myapp-tls True myapp-tls selfsigned-issuer Ready 10s
3.3 Duration 与 RenewBefore 续期配置说明
| 参数 | 含义 | 默认值 | 示例 |
|---|---|---|---|
spec.duration | 证书有效期 | 90 天(2160h) | 2160h、720h |
spec.renewBefore | 到期前多久触发续期 | 30 天(720h) | 360h(提前 15 天) |
spec.renewBeforePercent | 按生命周期比例触发 | 无 | 60(剩余 60% 时续期) |
⚠️ 注意:
renewBefore不得大于或等于duration,否则 cert-manager 会持续触发续期并报错。为资源手动改名不会重新签发,需要续期时机调整应直接改这两个字段,cert-manager 会按到达时间自动重签。
3.4 验证签发的 Secret 与证书内容
查看 Secret 是否被自动填充:
kubectl get secret -n myapp myapp-tls -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -subject -dates -ext subjectAltName
预期回显(subject 到期时间来自 3.2 的 duration):
subject=CN = myapp.example.com
notBefore=Oct 8 12:59:00 2026 GMT
notAfter=Jan 6 12:59:00 2027 GMT
X509v3 Subject Alternative Name:
DNS:myapp.example.com
💡 说明:证书以
tls.crt、tls.key两把键存放在myapp-tlsSecret 内,Ingress 可通过secretName: myapp-tls直接引用。
四、签发 Let's Encrypt 证书(外网签发说明)
Let's Encrypt 提供受公共信任的证书,适合对外 Ingress。流程与自签名一致,区别在 Issuer 改为 ACME 类型,并需要域名解析可用验证(HTTP-01 默认校验)。
⚠️ 前置:使用 Let's Encrypt 需要
- 待签发域名(如
app.example.com)能正确解析到集群 Ingress 的对外地址;- 集群能出网访问
https://acme-v02.api.letsencrypt.org/directory(暂不生产可用时,可用预演环境LE_STAGINGURL 测试);- 已部署并配置 Ingress Controller 以暴露
.well-known/acme-challenge。
4.1 创建 ClusterIssuer(Let's Encrypt 生产)
# letsencrypt-prod.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: admin@example.com
privateKeySecretRef:
name: letsencrypt-prod-account-key
solvers:
- http01:
ingress:
class: nginx
kubectl apply -f letsencrypt-prod.yaml
kubectl get clusterissuer letsencrypt-prod -o wide
预期回显:
NAME TYPE STATUS AGE
letsencrypt-prod ACME True 5s
4.2 通过 Let's Encrypt 签发证书
# app-certificate.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: app-tls
namespace: myapp
spec:
secretName: app-tls
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- app.example.com
duration: 2160h
renewBefore: 360h
kubectl apply -f app-certificate.yaml
kubectl get certificate -n myapp app-tls -o wide
预期回显(首次出站验证需要时间):
NAME READY SECRET ISSUER STATUS AGE
app-tls True app-tls letsencrypt-prod Ready 45s
💡 说明:签发过程中会先生成
CertificateRequest与 ACMEChallenge/Order,验证通过后Ready=True。可用kubectl describe certificate -n myapp app-tls查看事件详情。
五、查看签发请求与资源
5.1 查看 CertificateRequest
每个 Certificate 生成时都会创建对应的 CertificateRequest 资源:
kubectl get certificaterequest -n myapp
预期回显:
NAME APPROVED DENIED READY ISSUER REQUESTOR AGE
myapp-tls-pk True True selfsigned-issuer system:serviceaccount:cert-manager:cert-manager 5m
5.2 查看全部资源类型
kubectl api-resources | grep cert-manager.io
预期回显:
certificates cert.cert-manager.io/v1
certificaterequests cert.cert-manager.io/v1
challenges acme.cert-manager.io/v1
clusterissuers cert-manager.io/v1
issuers cert-manager.io/v1
orders acme.cert-manager.io/v1
5.3 更新 Certificate 资源触发重签
修改 duration 或 dnsNames 后 kubectl apply 即可,cert-manager 会按新的配置重新签发:
kubectl apply -f myapp-certificate.yaml
kubectl get certificate -n myapp myapp-tls -o wide
六、常用 CLI 命令速查
因为 cert-manager 是 Kubernetes 里的控制器,它的「CLI」实际就是通过 kubectl 操作其自定义资源:
| 操作 | 命令 |
|---|---|
| 查看 ClusterIssuer | kubectl get clusterissuer -o wide |
| 查看 Issuer | kubectl get issuer -A |
| 查看证书 | kubectl get certificate -A -o wide |
| 查看签发请求 | kubectl get certificaterequest -A |
| 查看 ACME 订单/挑战 | kubectl get orders,challenges -A |
| 查看证书事件 | kubectl describe certificate -n <ns> <name> |
| 查看控制器日志 | kubectl logs -n cert-manager deploy/cert-manager |
| 查看所有已发证书 Secret | kubectl get secret -n <ns> --field-selector type=kubernetes.io/tls |
6.1 查看证书到期时间(续期生效检验)
kubectl get secret -n myapp myapp-tls -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -enddate
预期回显:
notAfter=Jan 6 12:59:00 2027 GMT
💡 说明:证书到期时间由
spec.duration(有效期)决定;当系统时间距notAfter的差值小于spec.renewBefore时,cert-manager 会自动用对应 Issuer 续期并覆盖 Secret 中的tls.crt/tls.key。
七、常见问题(FAQ)
Q1:CRD 版本与控制器版本不匹配导致 no matches for kind?
多数是因 CRD 未正确安装。解决办法:确认
kubectl get crd | grep cert-manager.io列表完整(见 2.3),并以对应版本重新安装 CRD 后重启控制器 Deployment。
Q2:Certificate 一直 READY=False,如何排查?
先用
kubectl describe certificate -n <ns> <name>查看事件;定位到CertificateRequest失败时,接着查看kubectl get certificaterequest、kubectl describe certificaterequest与kubectl get orders,challenges。ACME 常见失败为域名解析/校验不通过。
Q3:Let's Encrypt 验证总是失败?
三处核对:域名 A/AAAA 记录是否解析到 Ingress 对外 IP;Ingress Controller 是否覆盖
/.well-known/acme-challenge与 HTTP 80 通道;集群是否可出网访问 ACME 目录。 可用预演环境(staging)先在Issuer.spec.acme.server指向https://acme-staging-v02.api.letsencrypt.org/directory排查,确认通过后再切生产地址。
Q4:证书续期了但 notAfter 没变化?
确认
spec.duration是否被修改;同时检查renewBefore < duration。若二者不符,cert-manager 会持续触发新 Order 且证书可能被频繁替换。
Q5:卸载 cert-manager?
先删除所有
Certificate、Issuer/ClusterIssuer资源,再卸载 Helm release:
kubectl delete certificate,issuer,clusterissuer,certificaterequest -A --all
helm uninstall cert-manager -n cert-manager
kubectl delete namespace cert-manager
若 CRD 是单独以
kubectl管理而需一并移除,可执行:
kubectl delete crd certificates.cert-manager.io certificaterequests.cert-manager.io challenges.acme.cert-manager.io clusterissuers.cert-manager.io issuers.cert-manager.io orders.acme.cert-manager.io
生产谨慎,删除 CRD 会移除其全部资源定义。
八、参考链接
- cert-manager 官方文档:https://cert-manager.io/docs/
- cert-manager 安装指南:https://cert-manager.io/docs/installation/helm/
- Jetstack Helm 仓库:https://charts.jetstack.io
- Let's Encrypt ACME 文档:https://letsencrypt.org/docs/