4. 证书轮换作业(CertificateRotationJob)设计原理:基于 cert-manager 或 kubelet 证书轮换机制

证书轮换作业是保障集群节点身份安全的核心组件。在 Kubernetes 中,节点证书(kubelet 客户端证书)具有有限生命周期,到期前必须完成轮换,否则节点将被 API Server 拒绝准入。本章深入剖析基于 cert-manager 和 kubelet 内置轮换机制 两种主流方案的作业设计原理,并阐述如何通过污点容忍策略控制轮换作业的节点准入行为。

4.1 证书轮换的核心挑战

节点证书轮换面临以下关键问题:

  • 证书生命周期管理:默认 kubelet 证书有效期 1 年,需在到期前完成轮换。
  • 轮换窗口控制:过早轮换浪费资源,过晚轮换导致节点不可用。
  • 节点准入约束:轮换作业应仅在特定节点(如未标记为不可调度)上执行。
  • 证书签名请求(CSR)审批:自动或手动审批 CSR 的流程可靠性。
关键指标:证书轮换作业的 SLA 通常要求:轮换成功率 ≥ 99.9%,轮换延迟 ≤ 证书剩余有效期的 10%,且不得因轮换导致节点连续不可用超过 30 秒。

4.2 基于 cert-manager 的证书轮换机制

cert-manager 作为 Kubernetes 原生的证书管理控制器,通过 Certificate 资源对象驱动证书的签发、续期和轮换。其核心流程如下:

  1. 定义证书资源:创建 Certificate CRD 对象,指定证书名称、密钥名称、签发者(Issuer)、DNS 名称、有效期等参数。
  2. 证书签发:cert-manager 控制器监听 Certificate 资源,调用指定的 Issuer(如自签名、ACME、Vault)签发 X.509 证书。
  3. 证书存储:签发的证书和私钥以 Kubernetes Secret 形式存储,供 kubelet 或其他组件挂载使用。
  4. 自动续期:cert-manager 监控证书到期时间,在证书剩余有效期达到配置的 renewBefore 阈值时,自动触发重新签发流程。
  5. 轮换触发:新证书签发后,cert-manager 更新 Secret 中的证书数据,kubelet 通过文件监视或 API 监听感知变化并加载新证书。
关键配置示例:以下是一个 cert-manager Certificate 资源定义,用于 kubelet 节点证书轮换。
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: kubelet-node-cert
  namespace: cert-manager
spec:
  secretName: kubelet-node-tls
  duration: 8760h  # 1 年
  renewBefore: 720h  # 提前 30 天续期
  subject:
    organizations:
      - system:nodes
  commonName: system:node:{{ node_name }}
  usages:
    - client auth
  dnsNames:
    - "{{ node_name }}"
    - "{{ node_name }}.cluster.local"
  issuerRef:
    name: ca-issuer
    kind: ClusterIssuer

4.3 基于 kubelet 内置证书轮换机制

Kubernetes 自 v1.8 起内置了 kubelet 证书轮换功能,无需外部控制器即可自动轮换节点证书。其工作原理如下:

  • 启动参数:kubelet 启动时需设置 --rotate-certificates=true 和 --kubelet-certificate-authority 等参数。
  • CSR 自动生成:kubelet 在证书到期前自动生成私钥和证书签名请求(CSR),并通过 API Server 提交 CSR 资源。
  • CSR 审批:集群管理员或自动审批控制器(如 kube-controller-manager 的 CSR 审批循环)批准 CSR。
  • 证书分发:审批通过后,kubelet 从 API Server 获取签发的证书,并写入本地文件系统。
  • 证书热加载:kubelet 检测到证书文件变化后,重新加载 TLS 配置,无需重启进程。
注意:kubelet 内置轮换机制依赖集群的 CSR 审批策略。若未配置自动审批,管理员需手动批准每个节点的 CSR,在大规模集群中不可行。建议结合 approve-node-csr 控制器或 cert-manager 的 CSR 审批能力。

4.4 两种机制对比分析

对比维度 cert-manager 方案 kubelet 内置方案
依赖组件 cert-manager 控制器、Issuer(CA/ACME/Vault) kube-controller-manager(CSR 审批)
证书存储 Kubernetes Secret 节点本地文件系统
轮换触发 基于 renewBefore 时间阈值 基于证书到期时间(默认 80% 有效期)
CSR 审批 支持自动审批(通过 cert-manager 的 approver) 需外部自动审批或手动审批
证书格式 标准 X.509,支持自定义扩展 标准 X.509,受 kubelet 约束
节点准入控制 可通过污点容忍策略控制轮换作业调度 节点级轮换,不受调度器控制
适用场景 需要统一证书管理、多签发者、复杂策略的集群 简单集群、无需外部 CA 的场景

4.5 证书轮换作业的节点准入设计

无论采用哪种机制,证书轮换作业都需要在特定节点上执行。通过污点(Taint)和容忍(Toleration)策略,可以精确控制轮换作业的节点准入行为:

  • 节点标记:为需要轮换证书的节点添加特定污点,如 node-role.kubernetes.io/cert-rotation=true:NoSchedule。
  • 作业容忍:证书轮换作业(Job 或 CronJob)的 Pod 模板中配置对应的容忍策略,允许其调度到标记节点。
  • 轮换窗口:通过 activeDeadlineSeconds 和 backoffLimit 控制轮换作业的执行时间和重试次数。
  • 节点状态检查:轮换作业启动前检查节点状态(如 Ready 状态、磁盘压力等),避免在异常节点上执行轮换。
作业调度示例:以下是一个证书轮换 Job 的 Pod 模板,配置了节点污点容忍和轮换逻辑。
apiVersion: batch/v1
kind: Job
metadata:
  name: cert-rotation-job
  namespace: kube-system
spec:
  template:
    spec:
      tolerations:
        - key: "node-role.kubernetes.io/cert-rotation"
          operator: "Exists"
          effect: "NoSchedule"
      nodeSelector:
        node-role.kubernetes.io/worker: ""
      containers:
        - name: cert-rotator
          image: registry.example.com/cert-rotator:v1.0
          command:
            - /bin/sh
            - -c
            - |
              # 检查节点证书到期时间
              expiry=$(openssl x509 -in /var/lib/kubelet/pki/kubelet-client-current.pem -noout -enddate | cut -d= -f2)
              expiry_epoch=$(date -d "$expiry" +%s)
              now_epoch=$(date +%s)
              remaining=$(( (expiry_epoch - now_epoch) / 86400 ))
              if [ $remaining -lt 30 ]; then
                echo "证书将在 $remaining 天后到期,触发轮换"
                # 触发 cert-manager 续期或 kubelet CSR
                kubectl certificate approve $(kubectl get csr | grep node-$(hostname) | awk '{print $1}')
              else
                echo "证书有效期充足(剩余 $remaining 天),跳过轮换"
              fi
          volumeMounts:
            - name: kubelet-pki
              mountPath: /var/lib/kubelet/pki
      restartPolicy: Never
      volumes:
        - name: kubelet-pki
          hostPath:
            path: /var/lib/kubelet/pki
            type: Directory

4.6 证书轮换作业的完整流程

下图展示了基于 cert-manager 的证书轮换作业从触发到完成的完整流程,以及污点容忍策略如何控制作业的节点准入。

证书轮换作业完整流程(基于 cert-manager) 证书到期检测 污点容忍检查 作业调度到节点 CSR 生成与提交 CSR 自动审批 证书签发与存储 kubelet 证书加载 API Server 连接验证 轮换完成 & 状态更新 说明: 1. 证书到期检测:轮换作业启动前检查节点证书剩余有效期,若低于阈值(如 30 天)则触发轮换。 2. 污点容忍检查:作业 Pod 的容忍策略必须匹配节点污点,否则作业无法调度到目标节点。 3. CSR 自动审批:cert-manager 的 approver 组件自动批准符合条件的 CSR,无需人工干预。 4. 证书加载:kubelet 通过文件监视或 API 监听感知证书变化,热加载新证书。 5. 连接验证:轮换完成后,kubelet 使用新证书与 API Server 建立连接,验证证书有效性。

4.7 轮换作业的容错与重试策略

证书轮换作业必须设计健壮的容错机制,以应对网络故障、证书签发延迟、节点不可用等异常情况:

异常场景 处理策略 配置参数
证书签发超时 设置作业超时时间,超时后自动重试 activeDeadlineSeconds: 600
CSR 审批失败 记录失败原因,等待人工干预或自动重试 backoffLimit: 3
节点不可达 跳过当前节点,标记为轮换失败,后续重试 自定义重试队列
证书格式错误 回滚到旧证书,记录错误日志 备份旧证书文件
并发轮换冲突 使用分布式锁或节点级互斥 基于 ConfigMap 的锁机制
重要提示:证书轮换作业应避免在集群升级、节点维护等操作期间执行。建议通过污点策略将轮换作业与维护窗口隔离,例如在节点添加 node.kubernetes.io/maintenance=true:NoSchedule 污点时,轮换作业的容忍策略不应匹配该污点。

4.8 污点容忍策略与轮换作业的集成设计

将污点容忍策略与证书轮换作业深度集成,可以实现精细化的节点准入控制:

  • 轮换窗口污点:在计划轮换的节点上动态添加 cert-rotation.kubernetes.io/window=true:NoSchedule 污点,仅允许轮换作业调度。
  • 节点角色污点:为不同角色节点(如 control-plane、worker、edge)设置不同污点,轮换作业按角色分批执行。
  • 轮换状态污点:轮换完成后自动移除污点,恢复节点正常调度。
  • 紧急轮换污点:当证书即将过期(如剩余不足 7 天)时,添加 cert-rotation.kubernetes.io/emergency=true:NoExecute 污点,强制立即执行轮换。
最佳实践:建议将证书轮换作业设计为 CronJob,定期扫描集群中所有节点的证书状态,并通过污点容忍策略控制作业的调度范围。以下是一个 CronJob 示例,每 6 小时执行一次证书轮换检查。
apiVersion: batch/v1
kind: CronJob
metadata:
  name: cert-rotation-check
  namespace: kube-system
spec:
  schedule: "0 */6 * * *"
  jobTemplate:
    spec:
      template:
        spec:
          serviceAccountName: cert-rotation-sa
          tolerations:
            - key: "cert-rotation.kubernetes.io/window"
              operator: "Exists"
              effect: "NoSchedule"
          containers:
            - name: checker
              image: registry.example.com/cert-checker:v1.0
              command:
                - /bin/sh
                - -c
                - |
                  # 获取所有节点证书到期时间
                  for node in $(kubectl get nodes -o name | cut -d/ -f2); do
                    expiry=$(kubectl get node $node -o jsonpath='{.status.nodeInfo.kubeletVersion}')
                    # 实际逻辑:检查节点证书文件
                    echo "检查节点 $node 证书状态"
                  done
          restartPolicy: OnFailure

通过上述设计,证书轮换作业能够安全、可靠地在集群中执行,同时通过污点容忍策略确保作业仅调度到符合条件的节点,避免对集群正常负载造成影响。