
简介Kubernetes集群证书过期会导致服务中断这套面向集群运维人员的一键续期脚本资源聚焦kubeadm环境下证书管理的常见痛点提供可直接执行的update-kubeadm-cert.sh脚本覆盖证书备份、过期检测、重新生成、配置更新与组件重启等核心环节。压缩包仅含1个shell脚本大小约3KB轻量易用适合熟悉kubectl命令并希望提升证书维护效率的中高级运维工程师。脚本内置完整执行逻辑可自动扫描kubelet客户端、apiserver、etcd、kube-proxy及service account令牌等常见证书类型减少手工操作失误并保留阈值调整、证书路径定制等扩展入口便于适配不同集群配置。已有466人学习下载对于正在处理证书告警或规划周期性续期任务的团队是可直接落地的实用工具也能作为理解kubeadm证书机制的参考样例。 k8s集群证书续期算是每个运维都会撞上的经典事故现场。平时集群跑得好好的突然某天kubectl get nodes直接给你甩一个Unauthorized或者kube-apiserver日志里全是certificate has expired or is not yet valid这时候十有八九是证书到期了。更难受的是kubeadm 部署的集群默认证书有效期只有一年意味着你每年都得跟它打一次交道。这篇就来聊聊我用 shell 脚本把“k8s证书一键续期”这件事彻底自动化掉的完整过程包括脚本的设计思路、实际代码、执行验证还有几次被证书坑到怀疑人生的排查记录适合所有用 kubeadm 部署、又不想手动敲一长串kubeadm命令的 k8s 运维同学参考。1. 先把证书体系捋清楚k8s里到底哪些证书在“持证上岗”写脚本之前如果不把 k8s 的证书体系摸透后面大概率要出岔子。因为“证书续期”这四个字听着简单实际上并不是管好一个文件就完事集群里有太多角色各自持有自己的那份证书。1.1 核心组件的证书分布与有效期以 kubeadm 部署的集群为例证书基本都集中在/etc/kubernetes/pki/目录下不管你是单master还是高可用架构这套路径都差不多。我习惯先把这些证书按“功能角色”分成三类方便后续脚本设计角色证书文件主要用途默认有效期apiserverapiserver.crt / apiserver.key对外提供 HTTPS API 服务1年apiserver-kubelet-clientapiserver-kubelet-client.crt / .keyapiserver 访问 kubelet1年front-proxy-clientfront-proxy-client.crt / .keyaggregator 插件通信1年etcd 服务端etcd/server.crt / server.keyetcd 对外服务1年etcd 对端etcd/peer.crt / peer.keyetcd 集群内部通信1年etcd 客户端etcd/healthcheck-client.crt / .keykube-apiserver 访问 etcd1年kubeconfig 文件admin.conf、controller-manager.conf、scheduler.conf各管理组件与 apiserver 通信1年这里有个很多新手容易忽略的地方除了 pki 目录下的证书/etc/kubernetes/下还会有admin.conf、controller-manager.conf、scheduler.conf三个 kubeconfig 文件它们内部也嵌了客户端证书。所以只续 pki 目录是不够的这些 kubeconfig 里的证书同样必须跟着刷新否则kubectl一样连不上集群。1.2 证书过期后的故障表现为什么症状五花八门证书过期之后的故障现象其实和“哪个证书先挂”有直接关系。我遇到过的几种典型情况如果是 etcd 证书先过期一般表现为 apiserver 频繁重启kubectl get nodes直接报etcdserver: request timed out。原因是 apiserver 连不上 etcd整个控制面等于瘫痪了。如果是 kubelet 客户端证书过期症状则更隐蔽——kubectl get nodes能看到节点但描述 Pod 的时候提示Unable to authenticate节点状态一直NotReady。因为 kubelet 向 apiserver 上报心跳时需要验证身份证书一挂就断了联系。如果只有admin.conf过期那就更典型了——集群里其实一切都正常运行但你的本地kubectl就是提示Unauthorized。最坑的是很多人这时候第一反应是 RBAC 权限被改了实际上纯属证书到期。正因为症状多样化所以脚本里我特意加了“证书巡检”这一环在续期动作执行前先把所有证书的有效期扫一遍谁快到期一目了然。2. 为什么不能手搓 openssl 续期手工操作的三个大坑第一版“续期方案”大家都想过用 openssl 重新生成自签证书比如openssl req -new -x509 -days 3650这种。但实际验证下来这条路在 k8s 里很容易翻车原因是 k8s 的证书体系对 SAN 字段要求极严格。2.1 SAN 字段和证书信任链apiserver 的证书里必须包含 ClusterIP、节点 IP、域名、Service 名称等多个 SAN。手动用 openssl 签发时如果你不小心漏掉了某个 IP 或域名apiserver 启动时大概率直接报x509: certificate is valid for xxx, not yyy导致整个 API Server 起不来。etcd 更麻烦它的 peer 证书要求你必须在生成时带上集群所有节点的 IP如果集群后来扩过容、加过节点手工维护这份 SAN 名单会非常痛苦。这也是为什么我一直强调能用 kubeadm 原生的续期命令就坚决不要自己拿 openssl 手工搞。2.2 单文件更新不等于全链路修复还有一个容易被忽略的问题就算你只续了 apiserver 证书但 kubelet 连接 apiserver 用的校验方式以及kubeconfig里的证书不一定自动跟着更新。比如你只重新生成了 pki 目录下的apiserver.crt但 controller-manager.conf 里的客户端证书还是旧的那一份那 controller-manager 照样无法正常认证。这也是“一键续期脚本”存在的核心意义不是说执行一条kubeadm certs renew就完事而是要保证“所有相关证书 所有 kubeconfig 文件”被统一、协调地更新一遍并且不漏掉分发和重启环节。2.3 手动操作最致命的场景记错执行顺序即便是熟练工手动续期时也容易在顺序上翻车。比如先删除了旧证书新证书还没生成完成这时候一批静态 Pod 检测到文件变化就会自动重启然后在半初始化状态下疯狂 CrashLoopBackOff。如果是在生产集群上这种“半吊子状态”的恢复成本比直接续期高得多。所以脚本的第一原则就是所有高危动作之前强制备份而且备份是带时间戳的全量拷贝而不是覆盖式备份。3. 一键续期脚本的设计思路与代码解读明确知道“要管哪些”和“为什么不能手工来”之后接下来是重头戏实际写脚本。我的设计原则很简单——用 kubeadm 自带的续期能力脚本只做编排和兜底绝不自己实现证书签发逻辑。3.1 脚本执行流程的六步设计整个脚本我在生产环境跑过很多遍最后的流程收敛为六个阶段环境检查确认 kubeadm 命令存在、确认证书目录存在、确认当前节点是 master。到期巡检扫描所有证书文件列出距离过期时间不足 30 天的证书也支持强制全部续期。全量备份把/etc/kubernetes/整个目录打带时间戳的 tar 包。执行续期调用kubeadm certs renew all刷新 pki 下的证书再用kubeadm init phase kubeconfig all --config或者不指定 config生产环境请根据实际版本调整刷新三个 kubeconfig 文件。分发更新把新生成的admin.conf拷贝到~/.kube/config如果脚本在 master 上直接执行。重建静态 Pod删除 apiserver、controller-manager、scheduler、etcd 的静态 Pod 目录下的 yaml 文件kubelet 会自动重建或使用 crictl 删除对应容器等待自动拉起。之所以要删静态 Pod yaml 而不是直接systemctl restart kubelet是因为 apiserver 和 etcd 这类组件本来就是 kubelet 托管下的静态 Pod。你重启 kubelet它并不会主动重新加载已经运行中的容器。必须让 kubelet 感知到 Pod 定义文件变化它才会自动重建容器。3.2 核心代码逐段拆解先放上最核心的一个执行函数代码我简化过但核心逻辑和线上跑的版本一致#!/bin/bash set -e CERTS_DIR/etc/kubernetes/pki KUBE_CONFIG_DIR/etc/kubernetes BACKUP_DIR/data/backup/k8s-certs KUBEADM_BIN$(command -v kubeadm || true) # 1. 环境检查 if [ -z $KUBEADM_BIN ]; then echo [ERROR] kubeadm not found in PATH exit 1 fi if [ ! -d $CERTS_DIR ]; then echo [ERROR] $CERTS_DIR does not exist. exit 1 fi # 2. 到期巡检列出所有剩余有效期不足30天的证书 echo [INFO] Start checking certificate expiration... for crt in $(find $CERTS_DIR -name *.crt); do expiry$(openssl x509 -enddate -noout -in $crt 2/dev/null | cut -d -f2) expiry_epoch$(date -d $expiry %s) now_epoch$(date %s) days_left$(( (expiry_epoch - now_epoch) / 86400 )) echo [CHECK] $crt : ${days_left} days left if [ $days_left -le 30 ]; then need_renew1 fi done # 3. 全量备份 mkdir -p $BACKUP_DIR backup_file$BACKUP_DIR/k8s-kubernetes-$(date %Y%m%d%H%M%S).tar.gz tar czf $backup_file -C /etc kubernetes echo [INFO] Backup created: $backup_file # 4. 执行续期 if [ $need_renew 1 ] || [ $FORCE_RENEW 1 ]; then echo [INFO] Renewing certificates... kubeadm certs renew all --config/etc/kubernetes/kubeadm-config.yaml || { # 如果没有 kubeadm-config.yaml则使用默认方式 kubeadm certs renew all } # 刷新kubeconfig if [ -f /etc/kubernetes/kubeadm-config.yaml ]; then kubeadm init phase kubeconfig all --config/etc/kubernetes/kubeadm-config.yaml else kubeadm init phase kubeconfig all fi fi这里要特别解释一下kubeadm certs renew all这个命令。在 kubeadm 比较新的版本里certs renew默认会把/etc/kubernetes/pki下所有由 kubeadm 管理的证书都刷一遍包括 etcd 的三套证书比手动逐个renew apiserver、renew etcd-server要省心很多。但它的默认续期结果依然是一年有效期如果想让证书时间更长需要提前在 kubeadm 配置里设置certificateValidityPeriod对自定义 CA 场景也可以传入--csr-only后再签名。3.3 重启静态 Pod 的细节处理证书续完之后最难的一步其实是“重启生效”。很多脚本在这里直接写kubectl delete pod -n kube-system ...但此时 apiserver 的证书可能刚换过你的kubectl还不一定连得上或者连上了但删除 Pod 后 kubelet 重建时使用的还是证书缓存。我推荐的更稳妥做法是直接操作文件系统用rm -f /etc/kubernetes/manifests/*.yaml触发 kubelet 清空静态 Pod等十几秒后再恢复文件。不过这个操作在生产上有风险如果集群异常可能面临控制面长时间不可用的局面。折中方案是删完 yaml 后立刻 drop caches再用crictl ps | grep kube-apiserver观察容器是否重建rm -f /etc/kubernetes/manifests/kube-apiserver.yaml rm -f /etc/kubernetes/manifests/kube-controller-manager.yaml rm -f /etc/kubernetes/manifests/kube-scheduler.yaml sleep 10 cp /data/backup/manifests/kube-apiserver.yaml /etc/kubernetes/manifests/ cp /data/backup/manifests/kube-controller-manager.yaml /etc/kubernetes/manifests/ cp /data/backup/manifests/kube-scheduler.yaml /etc/kubernetes/manifests/ sleep 30 crictl ps | grep -E kube-apiserver|kube-controller-manager|kube-scheduler有人会问:etcd 的静态 Pod 要不要也删如果kubeadm certs renew all刷新了 etcd 的 server/peer 证书那么 etcd 确实需要重启。但 etcd 是集群的“心脏”操作不当会导致数据面不可用。我的实践经验是优先只重启 apiserver 组件除非确认 etcd 证书确实过期否则不要轻易把 etcd 静态 Pod 也删掉。因为 etcd 一旦短暂失去 leader整个集群的读写都会卡住。4. 实测运行记录与验证方法脚本写完之后我在一台测试集群上专门模拟了一次“证书还剩7天过期”的场景整个过程和验证方式这里详细记录一下方便大家照着踩完坑不踩第二遍。4.1 过期前执行脚本的完整输出我故意把测试集群的系统时间往后拨了一个月让所有证书都进入“即将过期”状态然后执行脚本。关键输出如下$ bash renew-k8s-certs.sh [CHECK] /etc/kubernetes/pki/apiserver.crt : 5 days left [CHECK] /etc/kubernetes/pki/apiserver-kubelet-client.crt : 5 days left [CHECK] /etc/kubernetes/pki/front-proxy-client.crt : 5 days left [CHECK] /etc/kubernetes/pki/etcd/server.crt : 5 days left [CHECK] /etc/kubernetes/pki/etcd/peer.crt : 5 days left [CHECK] /etc/kubernetes/pki/etcd/healthcheck-client.crt : 5 days left [INFO] Backup created: /data/backup/k8s-certs/k8s-kubernetes-20250110120000.tar.gz [INFO] Renewing certificates... [renew] Reading configuration from the cluster... [renew] FYI: You can look at this config file with kubectl -n kube-system get cm kubeadm-config -o yaml [renew] Renewing apiserver certificate [renew] Renewing apiserver-kubelet-client certificate [renew] Renewing front-proxy-client certificate [renew] Renewing etcd-server certificate [renew] Renewing etcd-peer certificate [renew] Renewing etcd-healthcheck-client certificate [renew] Renewing admin.conf certificate [renew] Renewing controller-manager.conf certificate [renew] Renewing scheduler.conf certificate执行完后apiserver 等静态 Pod 会被自动重启新版 kubeadm 在续期 kubeconfig 后会自动触发 apiserver 证书 reload大约 30 秒后整个控制面恢复。4.2 验证是否真的续期成功不要看到脚本输出renewing就认为万事大吉。我见过太多“执行了续期命令但实际没生效”的情况所以验证环节必须独立于脚本执行之外。第一步看证书实际的有效期openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -dates # 输出示例 notBeforeJan 10 10:00:00 2025 GMT notAfterJan 9 10:00:00 2026 GMT第二步验证 apiserver 是否能正常响应请求kubectl get nodes如果这一步还是报Unable to connect to the server: x509: certificate has expired or is not yet valid大概率是本地~/.kube/config里的旧证书没有同步更新。脚本里我已经加了自动拷贝 admin.conf 的逻辑但如果你是在 Master 之外执行脚本务必手动执行cp /etc/kubernetes/admin.conf ~/.kube/config第三步检查 systemd 日志里有没有证书相关报错journalctl -u kubelet -f | grep -i cert这个命令值得挂着观察一两分钟确认没有certificate has expired或者x509: Unknown authority之类的关键字才算真正续期成功。4.3 从一年改到十年提升续期频率的另类思路很多运维同学会觉得“一年续一次”太烦希望直接把证书有效期拉到十年甚至一百年。这个思路本身没错但要分场景。如果你有成熟的脚本和巡检机制一年一次并不算高频率但如果是一个没什么变更的稳定性项目我个人还是推荐把有效期拉长。具体做法是调整 kubeadm 配置在初始化集群时通过ClusterConfiguration里的certificateValidityPeriod字段控制。对于已经存在的集群可以在续期时配合自定义配置文件实现apiVersion: kubeadm.k8s.io/v1beta3 kind: ClusterConfiguration certificateValidityPeriod: 87600h # 10年然后执行kubeadm certs renew all --config/tmp/kubeadm-config.yaml。需要注意的是这个修改只对本次续期产生的证书生效集群里已经存在的老证书有效期不会变化。不过要提醒一句10年证书虽然省心但一旦私钥泄露长有效期意味着更大的暴露面。如果有条件建议配合定期轮换机制而不是一味延长有效期。5. 常见问题与排查技巧实录脚本用了快一年前前后后也踩过不少坑。下面这些问题基本都是真实的线上咨询和我自己的复盘挑几个典型场景分享下排查思路。5.1 证书过期后集群完全连不上脚本都执行不了怎么办这是最尴尬的一种情况你想用脚本续期但 apiserver 已经因为证书过期拒绝一切请求kubectl根本不能用。这种状态下不要慌直接在 master 节点上用本地 kubeconfig 和 docker/containerd 命令操作。我的处理思路是临时提高 apiserver 证书有效期。先在 master 上找到 kube-apiserver 的静态 Pod 目录/etc/kubernetes/manifests/kube-apiserver.yaml加一个--kubelet-preferred-address-typesInternalIP之类的参数本质是让 apiserver 尽快重启或者更直接一点用crictl stop停掉 apiserver 容器再执行脚本。因为脚本内的 kubeadm 命令是不依赖 apiserver 的它直接读写本地证书文件所以即便集群控制面不可用续期动作也能完成。等证书刷新后再把静态 Pod yaml 恢复回来kubelet 会自动拉起 apiserver。5.2 kubelet 客户端证书续了又报错一定要确认证书轮换开关用 kubeadm 部署的集群kubelet 默认会向 apiserver 发起证书签名请求CSR自动轮换自己的客户端证书。但如果你的集群比较老或者手动改过 kubelet 配置很可能没开RotateKubeletClientCertificate。这导致你每次只续了 apiserver 证书kubelet 那边还是旧的客户端证书依旧报 TLS 认证失败。排查方式非常直接kubectl get csr如果发现大量Pending状态的 CSR多半是 kubelet 没有自动 approve。你可以快速手动批准kubectl certificate approve cert-name同时去/var/lib/kubelet/config.yaml里检查rotateCertificates: true顺手把serverTLSBootstrap: true也打开这样 kubelet 服务端证书也能自动轮换。5.3 续期后 Pod 调度不成功提示创建 sandbox 失败这种情况大部分不是证书本身的问题而是 kubelet 在证书刷新后和容器运行时之间的认证握手失败。通常重启一次 kubelet 就能解决systemctl restart kubelet如果还不行检查/var/log/pods/下的日志看看有没有x509: certificate signed by unknown authority。如果有说明 kubelet 的--root-ca-file指向的 CA 和 apiserver 当前使用的 CA 不一致。常见原因是之前手动续期时重新生成了 CA但 kubelet 侧的根 CA 没有同步。解决方法是把ca.crt重新拷贝到/etc/kubernetes/pki/ca.crt再重启 kubelet。5.4 备份文件占磁盘空间越来越大脚本里备份逻辑是全量备份/etc/kubernetes每次大概几十兆如果跑得勤快/data 分区确实会慢慢被撑满。建议加一个清理机制我一般保留最近 5 份即可find $BACKUP_DIR -name *.tar.gz -mtime 30 -delete加在脚本末尾用mtime 30控制只保留最近一个月的备份。正式环境里备份文件建议同步到对象存储或另一个节点防止 master 宕机时连备份一起丢失。5.5 脚本被 cron 定时执行时环境变量和 PATH 对不上这是我踩过的比较隐蔽的坑。crontab 里的环境变量和手动 SSH 登录时完全不一样kubeadm如果装在/usr/local/bin下普通由 cron 启动的脚本很容易在command -v kubeadm这一步就挂了。所以脚本开头一定要显式设置 PATHexport PATH/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin这样配合 cron 做“定期巡检 到期自动续期”的组合就稳了。建议 cron 频率是每周检查一次而不是等到最后一天。写在最后的一点经验这套脚本跑下来我对 k8s 证书续期最大的体会是技术难度不高但流程完整性要求极高。证书续期不是敲一条命令那么简单它牵扯到备份、检查、续期、kubeconfig 刷新、组件重启、验证回滚等多个环节任何一步漏掉都可能在线上引发二次故障。我自己的习惯是即使有了自动脚本也会把巡检命令单独抽出来加进监控系统每周自动播报一次证书剩余有效期。毕竟证书过期这件事最怕的不是处理不了而是完全没有预警。另外如果你在一个多 master 的集群上操作别忘了每个 master 节点都要执行一遍etcd 证书和 apiserver 证书不会因为你在一台机器上续过就自动同步。希望这篇内容能帮你提前避坑少熬几个夜。本文还有配套的精品资源点击获取