证书管理与自动轮换:cert-manager 与 kubelet 证书轮换

系统梳理 Kubernetes 的证书体系与自动轮换:控制面证书清单与 kubeadm certs 命令、kubelet TLS Bootstrap 与证书自动轮换、ServiceAccount Token 从 Secret 到 TokenRequest 投影卷的演进、cert-manager 的 Issuer/Certificate/Ingress 集成,以及证书过期告警实践。

证书过期是 Kubernetes 集群最经典的「定时炸弹」:一个运行了 366 天的集群,可能在某天早上突然 kubelet 全部失联、kubectl 报 x509: certificate has expired,而根因只是一张一年期证书到期了。Kubernetes 的证书体系横跨控制面(apiserver、etcd、controller-manager)、节点(kubelet 客户端/服务端证书)与工作负载(ServiceAccount Token、Ingress TLS、mTLS)三层,每一层的轮换机制都不同。本文把这些证书理清,给出可执行的检查、轮换与自动化方案。

1. Kubernetes 里的证书版图

1.1 三层证书

层证书签发者默认有效期
控制面apiserver 服务端证书集群 CA1 年
控制面apiserver → kubelet 客户端证书集群 CA1 年
控制面apiserver → etcd 客户端证书etcd CA1 年
控制面etcd 服务端/peer 证书etcd CA1 年
控制面front-proxy 客户端证书front-proxy CA1 年
节点kubelet 客户端证书(bootstrap)集群 CA1 年(可轮换)
节点kubelet 服务端证书自签或集群 CA1 年
工作负载ServiceAccount Token集群 SA key可配置(默认 1h 起)
工作负载Ingress TLS / mTLScert-manager / 外部 CA90 天(ACME)
# 一览 kubeadm 管理的所有证书与到期时间
kubeadm certs check-expiration
CERTIFICATE                EXPIRES                  RESIDUAL TIME   ...
admin.conf                 Oct 07, 2027 08:00 UTC   364d            ...
apiserver                  Oct 07, 2027 08:00 UTC   364d            ...
apiserver-kubelet-client   Oct 07, 2027 08:00 UTC   364d            ...
controller-manager.conf    Oct 07, 2027 08:00 UTC   364d            ...
etcd-healthcheck-client    Oct 07, 2027 08:00 UTC   364d            ...
etcd-peer                  Oct 07, 2027 08:00 UTC   364d            ...
front-proxy-client         Oct 07, 2027 08:00 UTC   364d            ...

关键认知:kubeadm certs check-expiration 只覆盖 kubeadm 管理的证书,kubelet 客户端证书与 ServiceAccount Token 不在其中——它们由各自机制轮换。

1.2 证书链与信任关系

集群 CA(ca.crt)
  ├─ apiserver 服务端证书(SAN 含所有 API 地址与 LB VIP)
  ├─ apiserver-kubelet-client(apiserver 冒充客户端访问 kubelet)
  ├─ front-proxy-client(聚合层身份透传)
  └─ kubelet 客户端证书(system:node:<name>)
etcd CA(独立)
  ├─ etcd server / peer 证书
  └─ etcd-healthcheck-client
ServiceAccount key pair(RSA/ECDSA)
  └─ 签发/验证 ServiceAccount Token(JWT),不是 X.509

front-proxy 是聚合层的关键:apiserver 用它作为客户端证书访问聚合 API(如 metrics-server),并依赖 --requestheader-client-ca-file 验证回来时的 X-Remote-User 头。

2. kubeadm 证书体系与轮换

2.1 检查与续期

# 检查所有证书到期时间
kubeadm certs check-expiration

# 续期全部证书(就地覆盖 /etc/kubernetes/pki 下的文件)
kubeadm certs renew all

# 续期单个证书
kubeadm certs renew apiserver
kubeadm certs renew apiserver-kubelet-client

# 续期 kubeconfig(admin.conf / kubelet.conf 里的客户端证书)
kubeadm certs renew admin.conf
kubeadm certs renew all --config=kubeadm-config.yaml   # 从配置读 SAN

续期后必须重启使用该证书的组件,否则新证书不会被加载:

# 静态 Pod 部署的控制面:重启容器即可
systemctl restart kubelet          # 让静态 Pod 重建

# 或直接删容器让 kubelet 重建
crictl ps | grep -E "kube-apiserver|kube-controller|kube-scheduler"
crictl rm -f <container-id>

2.2 kubeadm 的自动续期

kubeadm 在 kubeadm upgrade 时会自动续期所有证书(每次升级顺带续一年)。除此之外没有内置的自动轮换——一年后如果没做任何操作,证书就会过期。

# 升级时顺带续期(kubeadm 默认行为)
kubeadm upgrade apply v1.30.0

# 若不希望续期(罕见),可显式关闭
kubeadm upgrade apply v1.30.0 --certificate-renewal=false

运维结论:要么保证至少每年升级一次集群,要么设置定时任务主动 kubeadm certs renew all。更稳妥的做法是写一个定时脚本,在证书剩余有效期 < 90 天时自动续期并重启控制面组件:

#!/bin/bash
set -euo pipefail
DAYS_LEFT=$(kubeadm certs check-expiration | awk '/admin.conf/ {print $4}' | tr -d 'd')
[ "${DAYS_LEFT:-0}" -lt 90 ] && kubeadm certs renew all && systemctl restart kubelet

2.3 集群升级与证书的联动

证书续期与集群升级是同一件事的两面:kubeadm upgrade 会续期,而续期后的证书 SAN 必须匹配新的 API 地址。升级前请确认 ClusterConfiguration 里的 apiServer.certSANs 包含所有实际访问地址(LB VIP、域名),否则续期后 apiserver 会因为「证书不匹配」而无法被访问。升级流程详见 Kubernetes 集群升级 。

一句话:kubeadm 的证书「随升级续期」但不「自动轮换」——不升级的集群必须自己设定时续期任务,否则一年后必然集体过期。

3. kubelet 证书轮换

3.1 TLS Bootstrap 流程

kubelet 首次启动时没有客户端证书,它通过 TLS Bootstrap 向 apiserver 申请:

kubelet 启动
  1. 用 bootstrap token(bootstrap-kubelet.conf 里的 token)认证
  2. 提交 CSR(CertificateSigningRequest),CN=system:node:<node-name>
  3. apiserver 的 CSR 控制器(或人工)批准(Approve)
  4. 签发证书,kubelet 写入 kubelet.conf
  5. 之后用该证书与 apiserver 通信
# 查看待批准的 CSR
kubectl get csr
kubectl certificate approve <csr-name>

# 节点加入集群时若卡在 NotReady,先查这里
kubectl get csr | grep Pending

注意:system:node 用户的 CSR 默认需要人工批准(system:certificates.k8s.io:certificatesigningrequests:nodeclient)。若要自动批准,需要给相应的 ClusterRoleBinding 授权——这是安全与便利的权衡点。

3.2 自动轮换机制

kubelet 支持客户端证书自动轮换,通过两个参数控制:

# KubeletConfiguration
rotateCertificates: true         # 客户端证书轮换(默认 true)
serverTLSBootstrap: true         # 服务端证书也走 CSR(默认 false)
# 检查 kubelet 配置是否开启
cat /var/lib/kubelet/config.yaml | grep -E "rotateCertificates|serverTLSBootstrap"

轮换行为:

kubelet 证书剩余有效期 < 20%(约 73 天)时:
  → 自动生成新私钥 + 提交 CSR(同名 CN)
  → 等待批准 → 拿到新证书 → 原子替换 kubelet.conf
  整个过程不需要重启 kubelet
参数默认含义
rotateCertificatestrue客户端证书自动轮换
serverTLSBootstrapfalse服务端证书走 CSR(默认自签,1 年后过期不轮换)

serverTLSBootstrap: false 是一个隐藏的过期风险:kubelet 的服务端证书默认自签且不轮换。apiserver 访问 kubelet(kubectl logs、kubectl exec)时会校验该证书,证书过期后这些操作会失败。生产建议开启 serverTLSBootstrap: true,并配套自动批准 selfnodeclient/selfnodeserver 的 CSR。

3.3 kubelet 证书排障

# kubelet 客户端/服务端证书信息
openssl x509 -in /var/lib/kubelet/pki/kubelet-client-current.pem -noout -dates -subject
openssl x509 -in /var/lib/kubelet/pki/kubelet.crt -noout -dates -subject

# 看轮换相关日志
journalctl -u kubelet | grep -iE "certificate|csr|rotat"

# 手动触发一次轮换:删除当前证书后重启 kubelet
rm /var/lib/kubelet/pki/kubelet-client-current.pem && systemctl restart kubelet

典型故障:x509: certificate has expired or is not yet valid 出现在 kubelet 日志里,说明客户端证书过期且未轮换(通常因为 rotateCertificates: false 或 CSR 长期未被批准)。恢复方式是用 bootstrap token 重新引导,或手动签发新证书。

一句话:kubelet 的客户端证书会自动轮换,但服务端证书默认不轮换——serverTLSBootstrap: true + CSR 自动批准,才是完整的节点证书方案。

4. ServiceAccount Token

4.1 从 Secret 到 TokenRequest

ServiceAccount Token 的机制经历过一次重要演进:

时期机制特点
旧(≤ v1.20)静态 Secret 里的 token 字段永不过期,长期有效,泄露即长期风险
新(v1.21+)TokenRequest API + 投影卷(projected volume)有过期时间,自动轮换,绑定 Pod 与 audience
# 现代写法:Pod 自动挂载投影 Token(无需手动创建 Secret)
spec:
  serviceAccountName: myapp
  containers:
    - name: app
      volumeMounts:
        - name: kube-api-access
          mountPath: /var/run/secrets/kubernetes.io/serviceaccount
          readOnly: true
  volumes:
    - name: kube-api-access
      projected:
        sources:
          - serviceAccountToken:
              path: token
              expirationSeconds: 3600      # 1 小时
              audience: https://kubernetes.default.svc
          - configMap: { name: kube-root-ca.crt }

4.2 自动轮换

投影 Token 的轮换是由 kubelet 自动完成的:

kubelet 监控投影 Token 的过期时间
  → 剩余 80% 生命周期(或 < 24h)时,调用 TokenRequest 申请新 Token
  → 原子替换挂载路径下的 token 文件
  → 应用需要重新读取文件才能用上新 Token

应用侧的关键要求:如果应用把 Token 读一次就缓存,轮换后会用到过期 Token。正确做法是每次使用前重新读文件,或使用支持自动重读的客户端库(如 client-go 的 NewForConfig + in-cluster 配置会周期性重读)。

安全基线:v1.24+ 集群不应再自动创建长期 Token Secret(kubectl get secrets -A --field-selector type=kubernetes.io/service-account-token 应无输出);若历史遗留了此类 Secret,应清理并改用投影卷。这与 Kubernetes 集群安全加固 的基线要求一致。

5. cert-manager

5.1 定位与安装

cert-manager 是集群内的证书控制器,把「申请、签发、续期、注入」证书变成声明式资源。它支持 ACME(Let’s Encrypt)、Vault、自建 CA、私有 PKI 等签发者。

kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.15.0/cert-manager.yaml
kubectl get pods -n cert-manager

5.2 Issuer 与 ClusterIssuer

# ClusterIssuer:ACME(Let's Encrypt),DNS-01 校验
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: ops@example.com
    privateKeySecretRef: { name: letsencrypt-prod-account-key }
    solvers:
      - dns01:
          route53:
            region: us-east-1
            accessKeyIDSecretRef: { name: route53-credentials, key: access-key-id }
# Issuer:自建 CA(用于内部服务 mTLS)
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: internal-ca
  namespace: prod
spec:
  ca:
    secretName: internal-ca-keypair      # 内含 tls.crt / tls.key
类型用途校验方式
ACME(Let’s Encrypt)公网域名证书HTTP-01 / DNS-01
CA内部 PKI用给定 CA 直接签
Vault企业 PKIVault PKI 引擎
SelfSigned自签(引导用)无

5.3 Certificate 资源

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: web-tls
  namespace: prod
spec:
  secretName: web-tls-secret        # 签好的证书写进这个 Secret
  duration: 2160h                   # 90 天
  renewBefore: 720h                 # 到期前 30 天开始续期
  subject:
    organizations: ["Example Inc"]
  dnsNames:
    - app.example.com
    - www.app.example.com
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer

续期逻辑:cert-manager 在 renewBefore(本例 30 天)到达时自动重新签发并更新 Secret。Ingress Controller 会自动感知 Secret 变化并热加载新证书,无需人工介入。

5.4 Ingress 集成

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod     # 自动创建 Certificate
spec:
  tls:
    - hosts: [app.example.com]
      secretName: web-tls-secret
  rules:
    - host: app.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web
                port: { number: 80 }

加上那一条注解,cert-manager 就会自动生成 Certificate 并完成 ACME 校验。Ingress 与 Gateway API 的证书注入方式不同,后者用 Gateway 的 tls.certificateRefs,可参考 Ingress 与 Gateway API 。

5.5 cert-manager 排障

# 资源状态
kubectl get certificate -A
kubectl get certificaterequest -A
kubectl get order,challenge -A            # ACME 专用

# 详情(Ready=False 时会说明原因)
kubectl describe certificate web-tls -n prod
kubectl describe challenge -n prod

# cert-manager 控制器日志
kubectl logs -n cert-manager deploy/cert-manager --tail=100
症状常见原因
Ready: False,Waiting for DNS-01 challengeDNS 传播慢 / DNS 服务商凭证错误
HTTP-01 challenge failedIngress 未就绪 / 域名未解析到 Ingress
Order failed: rate limitedLet’s Encrypt 频率限制(同一域名每周 5 次)
Secret 未生成RBAC 不足 / issuerRef 指向不存在的 Issuer

生产注意:先用 staging 环境(acme-staging-v02)验证,避免触发 Let’s Encrypt 生产环境的频率限制;DNS-01 比 HTTP-01 更适合通配符证书与内网场景。

6. 内部 PKI 与 mTLS

6.1 用 cert-manager 做服务间 mTLS

每个服务一份 Certificate,用短有效期降低泄露风险,usages 同时声明 server 与 client auth:

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: payment-svc
spec:
  secretName: payment-svc-tls
  duration: 24h                              # 短有效期
  renewBefore: 8h
  commonName: payment.prod.svc.cluster.local
  usages: [server auth, client auth]
  issuerRef: { name: internal-ca, kind: Issuer }

6.2 与 Service Mesh 的关系

Service Mesh(Istio/Linkerd)自带独立的证书体系(Istiod 作为 CA,为每个工作负载签发短期 SVID),与集群 CA 和 cert-manager 是三套并行的 PKI:

场景建议
Mesh 内部 mTLS交给 Mesh 的 CA,不掺 cert-manager
Mesh 边界(Ingress/Gateway)cert-manager 签公网证书,Gateway 挂载
非 Mesh 的服务间 mTLScert-manager + 内部 CA
证书信任根明确各套 PKI 的信任边界,避免互信混乱

Mesh 的证书与 mTLS 机制见 Kubernetes 服务网格 。

一句话:cert-manager 管「工作负载证书」,kubeadm 管「控制面证书」,Mesh 管「数据面 mTLS」——三套体系各司其职,混用时要先划清信任边界。

7. 监控与告警

7.1 证书过期监控

# 用 blackbox-exporter 或 ssl_exporter 探测端点证书
- alert: CertificateExpiringSoon
  expr: probe_ssl_earliest_cert_expiry - time() < 86400 * 30   # 30 天
  for: 1h
  labels: { severity: warning }
- alert: CertificateExpired
  expr: probe_ssl_earliest_cert_expiry - time() < 0
  labels: { severity: critical }

7.2 cert-manager 指标

certmanager_certificate_expiration_timestamp_seconds   # 证书到期时间戳
certmanager_certificate_ready_status                   # Ready 状态(0/1)

certmanager_certificate_ready_status{condition="False"} == 1 持续 30 分钟即应告警——它意味着某个 Certificate 的签发或续期已经失败。

7.3 关键节点证书的检查清单

# 控制面证书(kubeadm 集群)
kubeadm certs check-expiration
# kubelet 客户端/服务端证书
openssl x509 -in /var/lib/kubelet/pki/kubelet-client-current.pem -noout -enddate
openssl x509 -in /var/lib/kubelet/pki/kubelet.crt -noout -enddate
# etcd 证书
openssl x509 -in /etc/kubernetes/pki/etcd/server.crt -noout -enddate
# Ingress 证书
kubectl get secrets -A --field-selector type=kubernetes.io/tls
检查项频率告警线
控制面证书剩余有效期每日< 90 天
kubelet 证书剩余有效期每日< 30 天
cert-manager Certificate Ready实时False 持续 30 分钟
APIService caBundle每月与对应 Secret 一致
上游 DNS/网关证书实时< 30 天

8. 小结

层证书轮换机制关键动作
控制面apiserver/etcd/kubeconfigkubeadm 随升级续期每年升级或定时 certs renew all
节点kubelet 客户端rotateCertificates: true 自动确保 CSR 能被批准
节点kubelet 服务端默认自签不轮换开 serverTLSBootstrap: true
工作负载ServiceAccount Token投影卷 + kubelet 自动应用每次使用前重读文件
工作负载Ingress / mTLScert-manager renewBefore装 exporter 做到期告警
数据面Mesh SVIDMesh CA 自动与集群 PKI 划清信任边界

一句话记住:Kubernetes 的证书管理是「三套体系 + 一个告警」——kubeadm 管控制面(随升级续期)、kubelet 管节点(客户端自动、服务端要显式开启)、cert-manager 管工作负载(声明式 + 自动续期),而贯穿三者的唯一保险是证书到期监控。把所有证书的剩余有效期变成 Prometheus 指标并设置 90/30/7 天三级告警,就能把「某天早上集群全挂」这类事故彻底消除在发生之前。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「云原生」更多文章

  1. Sidecar 模式与原生边车容器:init 容器、生命周期与重启策略
  2. CoreDNS 与服务发现:插件链、NDOTS 与 Headless Service
  3. etcd 运维与性能调优:备份恢复、碎片整理与配额