1. 先从一个最常见的崩溃现场说起先说说我为什么想写这个题目。几乎每个Kubernetes集群跑了一年半载之后都会遇到一次莫名其妙的集体故障节点的kubelet还在apiserver还活着但是kubectl get nodes就是一片NotReady查看kubelet日志全是x509: certificate has expired or is not yet valid然后整个团队开始慌乱地百度Kubernetes证书续期急得不行。这种事故不是会不会发生的问题而是什么时候发生的问题。因为Kubernetes集群里几乎所有组件之间的通信都建立在TLS证书之上而证书会过期这件事在设计之初就决定了如果你不去管它它一定会咬你一口。问题只是咬在某个凌晨三点。我见过不少团队建集群的时候用kubeadm一键初始化证书默认一年有效期大家跑得很开心完全没意识到一年后的某一天整个集群会突然脑死亡。等到了那一步再去补证书、重启组件、挨个节点操作手忙脚乱而且一旦操作顺序不对还会引发apiserver反复重启雪上加霜。这篇文章我就把Kubernetes证书这件事从头到尾讲透证书体系是什么结构哪些证书会在什么时候过期哪些会自动续期哪些不会以及当你面对一台证书过期的机器时正确的排查和恢复路径到底是什么。我自己在维护集群时踩过不少坑也总结出一套相对成熟的检查和管理方法写下来分享给正在被证书问题折磨的朋友。2. Kubernetes集群的证书体系全景图2.1 谁在跟谁验证身份Kubernetes整个集群本质上是一个分布式系统组件之间需要通过HTTPS或gRPC通信而通信双方必须解决两个问题一是确认对方不是冒牌货二是防止数据在传输过程中被截获篡改。这就必须依赖TLS证书。把所有通信关系梳理清楚你会发现其实不多kube-apiserver是最核心的枢纽它既要对外提供HTTPS接口又要作为服务端接收来自kubelet、kube-scheduler、kube-controller-manager、kube-proxy以及外部用户kubectl的连接。etcd是数据存储层apiserver需要以客户端身份连接etcd同时etcd集群内部节点之间也有peer通信这两条通道都用TLS加密。kubelet是一个很有意思的角色它既是服务端apiserver要访问它的10250端口获取容器状态、执行exec/log又是客户端它要主动连接apiserver上报状态。这意味着证书体系里至少要覆盖apiserver对外服务的服务端证书、apiserver连接etcd的客户端证书、etcd内部的peer证书、各组件用于连接apiserver的客户端证书、以及kubelet的双向证书。2.2 kubeadm默认生成的证书清单现在绝大多数生产集群都是通过kubeadm初始化的我们以kubeadm生成的证书清单为基准来看。在/etc/kubernetes/pki/目录下你会看到证书文件作用对端用途ca.crt / ca.key集群根CA签发集群内部证书apiserver.crt / apiserver.keyapiserver对外接收kubectl、kubelet等HTTPS请求apiserver-kubelet-client.crt / .keyapiserver访问kubelet客户端身份认证front-proxy-ca.crt / .key聚合层CA签发front-proxy-client证书front-proxy-client.crt / .key聚合层代理连接扩展API Serversa.pub / sa.keyServiceAccount签名ServiceAccount的JWT令牌etcd/ca.crt / ca.keyetcd根CAetcd内部证书体系etcd/server.crt / server.keyetcd服务端接收apiserver连接etcd/peer.crt / peer.keyetcd节点间peer通信etcd/healthcheck-client.crt / .keyetcd健康检查用于etcdctl健康检查另外在/etc/kubernetes/下还有两个容易被忽略但极其重要的文件admin.conf和kubelet.conf。它们虽然是kubeconfig格式但本质上里面就是客户端证书admin证书、kubelet客户端证书——这是集群初始时签发的、权限极大的凭证很多人会忘记它们也会过期。2.3 不同类型的证书有效期差异kubeadm默认签发的证书有效期并不是统一的这是最容易踩坑的地方之一。以默认参数来说根CAca.crt有效期默认10年绝大多数服务端和客户端证书apiserver.crt、kubelet.conf里的证书、admin.conf里的证书等默认1年etcd的证书也是1年front-proxy-client默认1年sa.pub/sa.key这对密钥虽然也是根级密钥但它不用于TLS而是用于ServiceAccount令牌签名过期概念不如TLS证书那么直接这里要特别注意1年不是写死在kubeadm里的它完全可以在初始化或后续更新证书时指定更长或更短的有效期。所以你不能只记住一年这个数字而要去查实际证书上的Not After字段。我的经验是每个集群的管理清单里第一项就是所有证书的有效期记录。别嫌麻烦你只要在初始化后的第一天生成一份后续续期后更新一次就能彻底避免集群突然失联这种戏剧性事故。3. 证书文件里那些容易被忽略的细节3.1 用一条命令看清所有证书的有效期检查证书有效期其实并不复杂推荐的命令是for cert in $(find /etc/kubernetes/pki -name *.crt); do echo $cert openssl x509 -in $cert -noout -dates -subject -issuer done对于kubeconfig里的客户端证书先提取再查看grep client-certificate-data /etc/kubernetes/admin.conf | awk {print $2} | base64 -d | openssl x509 -noout -dates -subject一个实际输出示例 /etc/kubernetes/pki/apiserver.crt notBeforeSep 10 10:00:00 2024 GMT notAfterSep 10 10:00:00 2025 GMT subjectCN kube-apiserver通过这种方式你可以快速判定哪些证书快要到期哪些已经过期。比较理想的监控方式是写一个定时任务每个月检查一次所有证书的剩余天数凡是少于90天的就告警。3.2 SAN字段乱填会坑死你证书有效期只是问题的一个维度另一个隐蔽得多的问题是证书的SANSubject Alternative Name字段是否覆盖了所有访问入口。apiserver.crt里必须包含集群的各种访问地址包括集群内部的Service地址通常是kubernetes.default.svc、kubernetes.default.svc.cluster.local控制节点的IP地址外部接入的负载均衡器IP或域名如果配置了高可用还要包含多个VIP或者域名如果你在初始化集群时用的是内网IP后来因为种种原因要改用域名访问apiserver但证书里没有这个域名那么所有使用该域名访问的客户端都会报证书校验错误。这种问题的排查比证书过期更绕因为openssl s_client -connect能连上但证书就是验证不过。判断逻辑很简单kubectl报错如果是x509: certificate is valid for xxx, not yyy那基本就是SAN缺失和过期无关。3.3 证书之前后依赖关系证书不是孤立存在的它们之间存在签发链条。检查一个证书的信任链是否正确可以这样看openssl verify -CAfile /etc/kubernetes/pki/ca.crt /etc/kubernetes/pki/apiserver.crt正常情况下输出apiserver.crt: OK。如果根CA自身已经过期那么即使某个叶子证书还没到期整个信任链也已经崩了——这种情况极其危险因为根CA过期意味着你连续签叶子证书这条路都走不通了得先处理根CA本身。注意根CA过期和叶子证书过期处理方式完全不同。叶子证书过期可以用现有CA直接重签根CA过期则通常需要重建整个集群几乎没有平滑升级的路径除非集群创建之初就规划了根CA轮换机制。我在某个实际集群里见过一次诡异故障所有节点正常业务也在跑但kubeadm certs check-expiration里显示apiserver证书剩余时间还有大半年etcd的证书也没问题可kubelet就是不停地报错。最后查下来是kubelet.conf里的客户端证书过期了——它会悄悄导致apiserver不认这个kubelet节点被标记为NotReady但集群表面看起来又没什么大问题。所以再次强调检查证书有效期必须把所有kubeconfig文件里的内嵌证书和pki目录下的证书全部查一遍缺一不可。4. 证书对时间的敏感性你以为的时间同步可能并不靠谱4.1 x509校验底层的时钟依赖TLS证书校验依赖两个时间判断当前时间是否在notBefore之后、notAfter之前。这意味着每台机器上的时钟必须准确。如果某台节点的时间比集群内其他机器快了或慢了超过几分钟证书校验就会立即失败。最常见的场景是云上虚拟机的时间漂移。虽然大多数云平台自带NTP但有些自定义镜像或离线环境里NTP是关掉的机器跑一个月就可能漂移几分钟然后整个集群就出现各种奇怪报错有的节点连不上apiserver有的Pod调度失败有的etcd选主异常。4.2 expiresOn字段和kubectl版本依赖Kubernetes的节点管理里有一个容易被忽略的字段kubectl get node node-name -o yaml | grep -i expires在node对象的status里nodeInfo.expiresOn表示kubelet证书的到期时间。但这里有个隐蔽问题如果你的kubectl版本太老可能看不到这个字段或者显示出来的时间不准确。原因是node的status是kubelet上报的kubelet如果版本不一致上报信息格式也可能不一样。之前排查过一次某台节点显示expiresOn明明还有一个多月但实际连上去一看证书还有三天就过期了就是因为集群里混用了老版本kubelet。所以在做证书巡检时不能只看node状态里的expiresOn要直接到节点上看证书文件本身。4.3 NTP配置的检查与修理正确的NTP检查姿势是确认systemd-timesyncd或chronyd服务正常运行systemctl status chronyd chronyc tracking如果系统时间不准先同步再重启组件chronyc makestep systemctl restart kubelet注意时间跳跃修复后etcd集群可能因为Raft协议的时间敏感性出现短暂的不稳定建议在业务低峰期操作并且一次只处理一台节点。5. 哪些证书会自动续期kubelet的低保机制5.1 kubelet证书轮换背后的设计思路Kubernetes从1.8左右开始引入了kubelet证书轮换机制它的设计思路是kubelet在启动时会生成一对全新的私钥和证书签名请求CSR然后通过本地bootstrap凭证通常是bootstrap-kubelet.conf里的token向apiserver提交CSR由集群里的CSR控制器自动签发。签发成功后kubelet会用新证书替换旧的并在新证书快到期时再次发起轮换。这套机制大大减低了kubelet的运维负担但注意它只解决kubelet客户端证书的问题不解决apiserver证书、etcd证书、admin.conf、kubelet.conf如果你用的是静态配置而不是动态轮换等证书的续期问题。千万不要以为有自动续期就万事大吉。5.2 查看kubelet是否开启了证书轮换默认情况下kubelet的证书轮换是开启的但受控于两个配置开关rotateCertificates: true # 是否允许轮换 serverTLSBootstrap: true # 是否允许给kubelet服务端证书也做轮换serverTLSBootstrap这个开关值得单独说。如果你的集群没有开启它那么kubelet的服务端证书就是apiserver访问kubelet时用的那一个不会自动续期只能靠kubelet启动时自动生成的自签证书或者靠Controller Manager里的csr-approve流程来实现。默认情况下kubeadm会在kubelet配置里设置serverTLSBootstrap: true但有些场景——比如你手动拼装二进制部署集群——很容易漏掉。查看节点上的实际配置cat /var/lib/kubelet/config.yaml | grep -A5 rotateCertificates\|serverTLSBootstrap5.3 kubelet轮换的CSR被卡住怎么办如果rotateCertificates开着但看CSR列表一直处于Pending常见原因有用户没配置kube-controller-manager的--cluster-signing-cert-file和--cluster-signing-key-file导致签名控制器没有权限给kubelet签发证书。ClusterRole和ClusterRoleBinding缺失CSR无法被自动审批。bootstrap token过期kubelet无法重新认证。排查CSR状态kubectl get csr如果看到Pending要么手动批准kubectl certificate approve csr-name要么修改Controller Manager的配置加上自动批准规则。自动批准通常用--cluster-signing-duration控制签发的有效时长默认是8760h也就是一年。5.4 日志特征与告警标记kubelet如果证书轮换出了问题日志里会有非常典型的特征Unable to connect to the server: x509: certificate has expired or is not yet valid另一条常见的是certificate signed by unknown authority这两种日志虽然都指向证书问题但前者明确是过期后者则是不信任此签发者——很可能是控制器使用的CA和集群实际CA不一致导致的多了最常见的场景就是复制了其他集群的CSR签名配置。6. 什么时候不用管什么时候必须管6.1 哪些证书到期后会自动重建这里要澄清一个常见误解不是所有证书过期都会导致灾难性后果。某些证书即使过期也会因为组件重启而自动重新生成。典型的有kubelet的服务端自签证书如果kubelet的配置里没指定证书它会自己生成一张短期自签证书重启即刷新。kube-proxy等组件连接apiserver用的证书这类组件通常通过ServiceAccount挂载的token来认证token的有效期由apiserver的--service-account-signing-key-file和--service-account-issuer控制与外层TLS证书是两码事。aggregated API Server的客户端证书如果你没有安装任何聚合API扩展它几乎不会影响核心功能。所以做证书巡检时要分清重要与不重要的边界根CA、apiserver服务端证书、etcd证书、admin.conf、kubelet.conf如果不用轮换才是核心资产。6.2 必须主动管理的证书清单证书路径是否可自动重建过期后果/etc/kubernetes/pki/ca.crt否所有TLS信任链崩溃集群基本不可恢复/etc/kubernetes/pki/apiserver.crt否kubectl无法访问apiserver但集群内部节点通信可能暂时正常/etc/kubernetes/pki/apiserver-kubelet-client.crt否apiserver无法访问kubeletexec/log/attach全部失效/etc/kubernetes/pki/etcd/*.crt否etcd节点间通信中断数据面崩溃/etc/kubernetes/admin.conf否管理员无法用kubectl操作集群/etc/kubernetes/kubelet.conf看配置kubelet认证失败节点NotReady/etc/kubernetes/controller-manager.conf否controller-manager连不上apiserver集群控制器全部停摆/etc/kubernetes/scheduler.conf否scheduler连不上apiserverPod无法调度注意controller-manager.conf和scheduler.conf也是kubeadm生成的kubeconfig里面内嵌了客户端证书一样会过期。很多人只盯着pki目录把这两个文件漏了导致集群里一堆功能异常但主证书看着又都正常排查起来极其迷惑。6.3 用定时任务建立巡检基线我强烈建议每个集群上线后第一周内建立一套证书巡检基线至少包括所有证书的路径清单全部列出来每个证书的有效期截止时间登记成表格每月一次的定时检查脚本扫描证书剩余天数小于90天时告警并进入续期流程其实一个Shell脚本就能搞定大半#!/bin/bash BASE_DIR/etc/kubernetes ALERT_DAYS90 for cert in $(find $BASE_DIR -name *.crt); do EXPIRY$(openssl x509 -in $cert -noout -enddate | cut -d -f2) EXPIRY_TS$(date -d $EXPIRY %s) NOW_TS$(date %s) REMAIN_DAYS$(( (EXPIRY_TS - NOW_TS) / 86400 )) if [ $REMAIN_DAYS -lt $ALERT_DAYS ]; then echo WARNING: $cert expires in $REMAIN_DAYS days fi done这个脚本不复杂但能帮你把会不会过期变成什么时候必须处理而不是每次等到故障才被动响应。7. 证书过期后的处理实操kubeadm renew与手动替换7.1 kubeadm certs renew的正确用法如果你的集群是用kubeadm初始化的证书续期有非常标准化的路径kubeadm certs renew all这条命令会重新生成所有证书但不自动重启组件。和很多人想象的不同续期完必须手动重启改了证书的服务否则新的证书不会生效。先看一下当前状态kubeadm certs check-expiration输出类似[check-expiration] Reading configuration from the cluster... [check-expiration] FYI: You can look at this config file with kubectl -n kube-system get cm kubeadm-config -o yaml CERTIFICATE EXPIRES RESIDUAL TIME CERTIFICATE AUTHORITY EXTERNALLY MANAGED admin.conf Sep 10, 2026 10:00 UTC 365d ca no apiserver Sep 10, 2026 10:00 UTC 365d ca no ...续期后官方推荐的操作是kubeadm certs renew all systemctl restart kubelet但仅仅这样不够还需要更新admin.conf和kubelet的kubeconfig因为你本地的~/.kube/config可能还拿着老证书。实际操作中我会把控制节点的/etc/kubernetes/admin.conf复制到本地作为kubeconfig确保客户端侧也拿到最新凭证。7.2 手动续期apiserver证书的完整链路如果某些场景下不用kubeadm renew也可以手动完成。这里我记录一个高可用集群里续期apiserver证书的标准过程备份旧证书cp -r /etc/kubernetes/pki /etc/kubernetes/pki.bak.$(date %Y%m%d)重新生成apiserver证书需要确保SAN配置和原来一致最好直接读原配置文件的--apiserver-cert-extra-sans将新证书同步到所有控制节点重启apiserver如果是静态Pod直接移动或删除/etc/kubernetes/manifests/kube-apiserver.yaml让kubelet重建或者用kubectl delete pod强制重建重启controller-manager和scheduler因为它们连接apiserver用的客户端证书也需要更新验证kubectl get nodes、kubectl get csr、检查apiserver日志里有没有证书相关报错注意手动续期禁止并行在多个控制节点上操作。apiserver集群如果同时断掉多个成员会影响租约和Leader选举最典型的表现是controller-manager不断抢锁失败日志刷屏。7.3 手动续期etcd证书的完整链路etcd证书的续期比apiserver更敏感因为它涉及Raft集群的安全通信。步骤备份/etc/kubernetes/pki/etcd/整个目录用etcd的CA重新签发server、peer、healthcheck-client三类证书一台一台地替换替换完一台就重启etcd静态Pod也是删除manifest让kubelet拉起来等该节点回到Healthy状态后再操作下一台全部完成后将新的etcd/server.crt和etcd/ca.crt同步到所有控制节点因为apiserver作为client连接etcd时需要用到这里必须强调一点etcd peer证书和服务端证书不能混用它们的使用范围不一样。很多人图省事复制server证书当peer证书用初始化时没事一旦节点需要重新加入或选主就会翻车报invalid peer certificate。7.4 续期后验证与回滚方案每次续期后我至少跑三遍验证# 检查新证书生效时间 kubeadm certs check-expiration # 检查集群节点状态 kubectl get nodes # 检查核心组件是否正常上报 kubectl get pods -n kube-system | grep -E etcd|apiserver|controller|scheduler如果操作过程中发现异常可以直接恢复备份目录并重启相关组件。一个比较稳妥的备份习惯是每次续期前都留一个带日期的、完整的pki目录副本并且至少保留最近两三次的旧版本方便逐级回滚。经验总结在在线迁移或高可用集群里证书续期最忌讳一把梭同时改所有节点宁可慢一点一台一台来。对于业务无状态的三节点控制面通常半小时内能完成全部续期和验证。8. 根CA过期与无法平滑续期的例外场景8.1 根CA过期为什么等于重建集群前面反复提到根CA的特殊性这里把原因讲透。根CA是集群所有信任的锚点。apiserver验证其他组件时用的是根CAkubelet验证apiserver时用的也是根CA。如果根CA过期所有基于它签发的叶子证书在验证时都会失败因为它们共同的信任源头已经失效。根CA过期的另一个麻烦在于你自己已经无法用它来签发任何新证书因为签发新证书时CA也要校验自己的有效期。相当于一个机构的公章过期了下面所有文件盖章都失去了法律效力。一般处理方式只能是生成新根CA有效期拉长比如20年用新根CA重新签发集群里每个组件的证书所有节点上的ca.crt信任关系全部替换每个kubeconfig里的certificate-authority-data也要同步更新所有节点的kubelet重启所有静态Pod重建这样做一轮基本等于做了一次小型的集群重建。所以最好的策略是从集群第一天起就监控根CA有效期根CA一定要用长时间比如10年或更长。8.2 非kubeadm集群的证书处理差异还有相当多的集群不是用kubeadm建的而是通过托管安装或二进制手动部署。这类集群的证书管理方式差异极大有的自建集群把所有证书放在统一目录但是有效期各不相同只能逐个手动更换。有的集群完全依赖云托管的证书管理节点本地根本看不到证书文件遇到问题只能联系底层运维。对于这类环境我建议的排查思路是永远先看组件日志再决定要不要动证书。因为很多云上环境会自动轮换证书你看到的本地证书可能只是临时下载的一份过期了会自动重新拉取。贸然手动替换反而会破坏系统自动管理的流程。8.3 应对证书过期但业务还在跑的迷惑场景现实中最迷惑的情况是某台节点证书显示过期但业务Pod照样在跑集群看起来一切正常。这里面通常有两个原因该节点上的kubelet和apiserver之间的长连接还没断开TLS session在有效期内可以继续使用直到重新握手才会发现证书问题。证书过期影响的只是新建连接已经建立的连接不受影响。所以容器一直在跑但新Pod无法调度到该节点或者exec/log功能开始报错。处理这种半失效状态不能因为业务还活着就忽略而是要尽快安排续期窗口因为一旦kubelet重连节点就会立刻变成NotReady影响范围瞬间扩大。9. 日常巡检与告警的落地建议9.1 检查频率怎么定才合理证书检查其实不需要太高频率因为正常情况下证书有效期都大于一个月。但考虑到集群变更、节点漂移、人为误删等因素我建议每个控制节点每天检查一次check-expiration输出到日志文件每周汇总一次所有证书剩余天数低于90天告警每次版本升级或配置变更后手动跑一遍完整检查9.2 一个可落地的巡检脚本示例下面这个脚本综合了集群内所有证书路径可以直接放到定时任务里用。#!/bin/bash CRITICAL30 WARNING90 cert_list$(find /etc/kubernetes/pki -name *.crt) for kubeconfig in /etc/kubernetes/*.conf; do cert_data$(grep client-certificate-data $kubeconfig | awk {print $2} | base64 -d 2/dev/null) if [ -n $cert_data ]; then echo $cert_data | openssl x509 -noout -enddate 2/dev/null | sed s/^/$kubeconfig: / fi done for cert in $cert_list; do END$(openssl x509 -in $cert -noout -enddate | cut -d -f2) END_TS$(date -d $END %s) REMAIN$(( (END_TS - $(date %s)) / 86400 )) if [ $REMAIN -lt $CRITICAL ]; then echo CRITICAL: $cert expires in ${REMAIN}d elif [ $REMAIN -lt $WARNING ]; then echo WARNING: $cert expires in ${REMAIN}d else echo OK: $cert expires in ${REMAIN}d fi done9.3 如何把告警接入现有监控体系如果你用Prometheus之类的监控体系可以写一个简单的exporter或者文本收集器按月采集证书剩余天数并上报。更轻量的方式是直接在巡检脚本里判断剩余天数通过webhook或邮件发送告警。核心目标只有一个在证书过期的前90天让你知道而不是在过期后的凌晨三点让你被电话吵醒。监控告警配好之后还有一个隐藏收益当你对证书生命周期有了清晰的掌控Kubernetes集群在你眼里会从一个随时可能暴雷的黑盒变成一套有明确运维节奏的系统。这种掌控感对长期维护的稳定性帮助极大。10. 实战复盘一次etcd证书过期的抢救记录10.1 故障现象与第一轮排查有次一个测试集群在升级完网络插件后突然出现etcd节点间歇性掉线查看日志能看到etcdserver: request timed out transport: x509: certificate has expired当时第一反应是网络插件的问题因为升级前后只动了网络配置。但多翻几层日志看到etcd的peer通信在互相拒绝再用etcdctl查看成员状态才发现peer证书过期了。这里有个教训Kubernetes集群里的故障往往互相嵌套一个表面现象可能掩盖着底层证书问题。排查时不要只盯着一层日志要顺着链路上溯。10.2 恢复过程从备份到签发到重启确定是etcd peer证书过期后我们走了如下步骤从备份目录里确认了原CA还在有效期运气好只有叶子证书过期用现有CA重新签发所有etcd相关证书按节点顺序替换每替换完一个节点检查etcdctl endpoint health确认三个etcd成员全部healthy后再同步apiserver端的etcd客户端证书依次重启kube-apiserver观察集群恢复整个过程大约花了40分钟没有推倒重建。核心原因是根CA仍然有效所以可操作空间很大。这也侧面体现了根CA一定要监控的重要性。10.3 事后补齐的三项措施抢救完成后我们立即做了三件事把所有证书有效期纳入告警剩余60天就通知写了一个自动续期的脚本在低峰期执行kubeadm certs renew all并重启组件在所有节点的/var/lib/kubelet/config.yaml里确认了serverTLSBootstrap是开启的避免kubelet服务端证书再次成为漏网之鱼后来这套机制维持了很长时间没有再出过证书相关的故障。想想很真实证书过期不是突然发生的它只是你长期忽视的结果。只要平时花一点点时间做检查就能避免那些让整个团队提心吊胆的半夜事故。