做K8s运维这几年,我见过各种花式翻车现场,但最让人后背发凉的一类,绝对是认证文件丢失。apiserver起不来、kubectl报unauthorized、node全部变成NotReady,整个集群像被锁在门外——数据还在,但就是摸不着。这篇不是新手部署入门,是给已经在维护集群的兄弟们的一份急救手册,覆盖从文件备份、应急恢复到常见坑排查的完整闭环。看完不说让你封神,至少下次真遇到这类事故,你能稳住不慌,知道第一步该动哪里、哪条命令能救场。
1. 认证文件丢失不是小概率事件:先搞清楚影响面
先说一个现实:认证文件丢失这事儿,在很多团队里不是“会不会发生”的问题,而是“什么时候发生”的问题。经常出现的场景包括:清理磁盘时误删了/etc/kubernetes/pki,重装服务器前没做备份,或者某个同事为了腾空间直接把目录删了。也有更隐蔽的情况——文件还在,但权限被改了,服务启动时读不了,和丢失基本没区别。
1.1 哪些文件算K8s认证文件
K8s的认证体系是一整套 PKI,不是单一一个文件。下面这张表基本覆盖了 kubeadm 部署方式下的核心认证文件:
| 文件路径 | 用途 | 丢失后的影响 |
|---|---|---|
/etc/kubernetes/pki/ca.crt和ca.key | 集群根CA,所有组件证书的签发源头 | 集群身份体系崩盘,几乎无法在不重建CA的前提下恢复 |
/etc/kubernetes/pki/apiserver.crt和apiserver.key | apiserver对外提供HTTPS服务的证书 | apiserver启动失败,kubectl无法访问 |
/etc/kubernetes/pki/apiserver-kubelet-client.crt和.key | apiserver访问kubelet时用的客户端证书 | kubelet端拒绝连接,日志抓不到,exec进不了容器 |
/etc/kubernetes/pki/etcd/下的ca、server、peer证书 | etcd集群的通信认证 | etcd起不来,集群数据层直接瘫痪 |
/etc/kubernetes/pki/front-proxy-ca.crt和.key | 聚合API扩展组件认证 | metrics-server、ingress controller等异常 |
/etc/kubernetes/admin.conf等kubeconfig文件 | kubectl、kubelet、controller-manager等组件的连接凭证 | 对应组件连接apiserver失败 |
注意,/etc/kubernetes/pki里还有 sa.pub 和 sa.key,这是 ServiceAccount 的签发密钥。如果 sa.key 丢了,旧的 ServiceAccount token 全部失效,新token也签不出来,这是另一个深坑。
1.2 丢失后会发生什么
影响面取决于丢的是哪一层。如果只是丢了admin.conf,那影响相对可控——重新生成一个admin kubeconfig就行,集群本身还能跑。但如果是整个pki目录被删,那就是灾难级别:
- kube-apiserver 组件本身是静态Pod或者systemd服务,启动时会去加载证书,文件不存在直接起不来。
- 就算 apiserver 侥幸起来了,etcd 那边认证过不去,数据层还是废的。
- kubelet 已经注册到集群里的可能还残留在节点列表里,但状态会迅速变成 NotReady;新的kubelet想加入,没有CA签发身份也进不来。
- 整个集群对外表现就是:kubectl 连不上、业务Pod状态异常、节点集体失联。
这个场景和证书过期不一样。证书过期还能续期,根CA私钥丢了,等于整个集群的身份体系从根上断了。
1.3 应急定级:先判断还能不能救
遇到认证文件丢失,第一件事不是手忙脚乱找资料,而是冷静做一次定级:
- 第一级:只丢了某一个 kubeconfig,比如 admin.conf 或 kubelet.conf。集群大部分组件还在正常工作,恢复难度低。
- 第二级:丢了单个组件的证书和私钥,比如 apiserver.crt 和 apiserver.key。服务无法启动,但根CA完好,可以通过重新签发或者kubeadm重建阶段解决。
- 第三级:根CA或者etcd的CA私钥丢了。这是最麻烦的情况。除非有完整备份,否则想保留原集群身份几乎不可能,通常要考虑重建集群,或者至少重建整个PKI体系再逐个节点回归。
定的级决定了后面要走哪条路。我的建议是:先看备份有没有,再决定是“恢复文件”还是“重建认证体系”,不要一上来就重装集群,那是最差方案。
2. 防患于未然:我最推荐的备份策略
运维这行,再好的应急都不如事前备份。认证文件不像业务数据那么占空间,一个集群所有证书加一起也就几MB,但丢一次就让你通宵。所以我一直主张,证书备份是K8s集群维护的底线操作。
2.1 备份清单:只备份证书不够
很多新手以为备份就是cp -r /etc/kubernetes/pki,其实不够。除了PKI目录,还要把这个文件一起纳入备份范围:
/etc/kubernetes/admin.conf /etc/kubernetes/kubelet.conf /etc/kubernetes/controller-manager.conf /etc/kubernetes/scheduler.conf /etc/kubernetes/kubeadm-config.yaml /var/lib/kubelet/kubeadm-flags.env特别是kubeadm-config.yaml,它记录了集群初始化时的配置。后面如果用kubeadm init phase certs all --config重建证书,必须依赖这个文件,没有它,新签出来的apiserver证书可能SAN不完整,导致访问IP变化就报证书校验错误。
etcd的证书和数据也要考虑。etcd的 pki 目录通常独立成子目录,位于/etc/kubernetes/pki/etcd,如果etcd 是独立部署(不在 /etc/kubernetes 下),还要根据实际路径备份。另外建议定期用etcdctl snapshot save做数据快照,因为证书重建的最终目的还是保住数据,数据没救回来等于白忙。
2.2 一个可落地的备份脚本
备份脚本没什么玄学,关键是便于执行和轮转。我自己的备份脚本差不多长这样:
#!/bin/bash BACKUP_BASE=/backup/k8s-pki DATE=$(date +%F) BACKUP_DIR=$BACKUP_BASE/$DATE mkdir -p $BACKUP_DIR # 备份证书目录 tar czf $BACKUP_DIR/pki.tar.gz -C /etc/kubernetes pki # 备份所有kubeconfig和集群配置 tar czf $BACKUP_DIR/config.tar.gz -C /etc/kubernetes \ admin.conf \ kubelet.conf \ controller-manager.conf \ scheduler.conf \ kubeadm-config.yaml # 备份etcd证书(如果路径存在) if [ -d /etc/kubernetes/pki/etcd ]; then tar czf $BACKUP_DIR/etcd-pki.tar.gz -C /etc/kubernetes/pki etcd fi # 清理7天前的备份 find $BACKUP_BASE -maxdepth 1 -type d -mtime +7 -exec rm -rf {} \;脚本本身工作量不大,真正关键的是跑起来。在控制节点上加个 systemd timer,每天凌晨执行一次,几行配置的事:
# /etc/systemd/system/k8s-pki-backup.service [Unit] Description=Backup K8s PKI files [Service] Type=oneshot ExecStart=/usr/local/bin/k8s-pki-backup.sh # /etc/systemd/system/k8s-pki-backup.timer [Unit] Description=Run k8s pki backup daily [Timer] OnCalendar=*-*-* 02:17:00 [Install] WantedBy=timers.target然后systemctl enable --now k8s-pki-backup.timer就行。这里有个细节:随机选一个凌晨时间而不是整点,避免和大量定时任务撞车,尤其是数据库备份和高峰备份时段错开,省得半夜磁盘I/O打架。
2.3 备份安全策略:加密、离线、权限
备份做完不等于万事大吉。纯文本的证书压缩包如果被人拿到,等于拿到了集群的控制权。建议:
- 备份目录权限设为600或700,不要给默认644。
- 用
tar配合对称加密,比如openssl enc -aes-256-cbc -salt -in pki.tar.gz -out pki.tar.gz.enc,密码放在专门的密钥管理工具或密码管理器里。 - 至少保留一份备份在集群之外,比如对象存储或者另一台主机。原因很直接:集群整体宕机时要能拿到备份,如果备份就在出事的机器上,可能跟着一起丢了。
- 恢复时先验证备份的完整性,解压前用
gpg --verify或者直接tar -tzf 看一下文件列表,不要等到恢复了一半才发现文件损坏。
我在实际处理中还遇到过一个问题:备份脚本本身没有加环境变量,cron 里 PATH 不完整,运行时tar和find命令找不到,脚本静默失败。所以脚本里尽量写绝对路径,或者至少明确定义PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin。这也是为什么备份脚本要定期手动跑一次,而不是写完就再也不管。
3. 应急处理:从故障到恢复的实操流程
备份做得再好,总有可能在第一次备份前就出事,或者备份本身也丢了。所以应急流程还得按“有备份”和“没有备份”两种情况分别准备。
3.1 有备份:十分钟恢复路径
假设你敢在故障后拍着胸脯说“备份肯定在”,那恢复的路径非常明确:
- 先在出事的节点上,看看
/etc/kubernetes到底还剩什么:ls -la /etc/kubernetes/ find /etc/kubernetes/pki -type f 2>/dev/null - 把备份里的证书解压回去:
cd /etc/kubernetes tar xzf /backup/k8s-pki/2025-06-01/pki.tar.gz tar xzf /backup/k8s-pki/2025-06-01/config.tar.gz - 检查文件权限。证书目录通常是 644,key 文件必须 600,所有者 root。如果权限不对,服务照样起不来:
chown -R root:root /etc/kubernetes/pki chmod 600 /etc/kubernetes/pki/*.key chmod 600 /etc/kubernetes/pki/etcd/*.key - 如果 kubeadm 部署的,可以用 kubeadm 显式刷新一遍所有配置:
kubeadm init phase kubeconfig admin --config=/etc/kubernetes/kubeadm-config.yaml - 重启 kubelet,同时如果 apiserver 是静态Pod,kubelet 会自动把容器拉起来;如果是二进制部署的服务,直接
systemctl restart kube-apiserver。 - 用
kubectl get nodes验证控制面恢复。
这里提醒一句:备份中如果只备份了证书,没备份 kubeadm-config.yaml,恢复后可能面临SAN问题,所以第一步先确认这个配置文件在不在,不在的话,后续 apiserver 证书大概率要重建。
3.2 没有备份:用kubeadm重建认证体系
没有备份就麻烦很多,但也不是完全没救。对 kubeadm 部署的集群,kubeadm 提供了按阶段生成证书的能力。
先看一下现在的CA情况:
ls -la /etc/kubernetes/pki/如果ca.crt和ca.key都还在,只是其他组件证书丢了,可以直接用 kubeadm 重新生成缺失的证书:
kubeadm init phase certs all --config=/etc/kubernetes/kubeadm-config.yaml这个命令会补齐 apiserver、apiserver-kubelet-client、front-proxy-client、etcd等组件的证书。它会读取配置里指定的SAN列表,所以只要 kubeadm-config.yaml 还在,问题就不大。
生成完之后,再重新生成各组件连接集群用的 kubeconfig:
kubeadm init phase kubeconfig all --config=/etc/kubernetes/kubeadm-config.yaml最后重启 kubelet:
systemctl restart kubelet如果ca.key本身也丢了,但ca.crt还在,理论上你还能用现有CA证书继续签,但实际上ca.key丢失就无法用同一CA签发新证书。这种情况下,kubeadm 也不会帮你凭空变出私钥,通常只能通过kubeadm init phase certs all配合--config重新生成一套新的CA,这意味着整个集群的信任关系变了,所有存量组件都要重新认证。
我实际处理过一个案例:集群跑了一年多,磁盘被人清理,pki目录整个没了,备份也没有。最后没办法,只能新建一套CA和证书,然后用kubeadm reset把所有节点重置,再通过kubeadm join重新加入,相当于整个集群重建了一遍。幸运的是,业务数据都在etcd里,而etcd数据目录没被删,所以最后数据保住了。这种“身份全丢但数据还在”的情况,处理起来特别考验思路,没有备份真的会让运维人一夜白头。
3.3 kubeconfig单独丢失:最轻量级的恢复
只丢admin.conf的情况,恢复起来很轻量,不需要动上面那些证书目录。
kubeadm init phase kubeconfig admin --config=/etc/kubernetes/kubeadm-config.yaml这条命令会根据根CA和apiserver的地址,重新生成 admin.conf。生成后把它复制到本地~/.kube/config即可:
mkdir -p $HOME/.kube cp -i /etc/kubernetes/admin.conf $HOME/.kube/config如果 kubelet.conf 丢了,同理执行:
kubeadm init phase kubeconfig kubelet --config=/etc/kubernetes/kubeadm-config.yaml或者干脆用kubeadm init phase kubeconfig all一次性全部生成。
有人可能问,如果管理员本地的~/.kube/config也一起丢了呢?这种情况可以通过 apiserver 的日志或者配置找回集群地址和CA串。就算没有现成的 admin 凭证,只要能SSH到控制节点,并且控制节点上 kubelet 还在正常跑,都可以在节点上用kubectl --kubeconfig=/etc/kubernetes/admin.conf get nodes来验证。
3.4 worker节点认证文件丢失:重新加入集群
worker节点的认证文件主要是/etc/kubernetes/kubelet.conf和/var/lib/kubelet/pki/kubelet-client-current.pem。如果只是kubelet客户端证书过期或者丢失,最快的办法是重新执行kubeadm join。
控制面上先生成新的token:
kubeadm token create --print-join-command输出会直接包含kubeadm join <apiserver>:<port> --token <token> --discovery-token-ca-cert-hash sha256:<hash>。
但在执行 join 之前,先把节点上的旧身份清理掉:
kubeadm reset -f rm -rf /etc/kubernetes rm -rf /var/lib/kubelet然后执行控制面输出的 join 命令。注意,kubeadm reset会删除节点上的kubelet配置和所有相关文件,执行前确认这是一个可重建的节点,不是承载无状态服务必须保留本机文件的特殊情况。
如果发现节点上控制面无法访问,检查 apiserver 地址、防火墙规则和token有效期。token 默认有效期24小时,如果是很久之前生成的token,建议重新用kubeadm token create生成。
4. 手动签发证书的底层逻辑:理解之后不会再慌
kubeadm 封装了很多证书生成逻辑,但真实生产环境有很多二进制部署的集群,或者因为网络隔离、安全要求,不能直接用 kubeadm。这种时候,手动签发证书的能力就是保命技能。
4.1 K8s的PKI体系是怎么串起来的
K8s的认证体系很像一个公司内部的门禁系统。根CA是集团HR,负责给每个员工发工牌;apiserver是大门闸机,需要验证来访者身份;kubelet、controller-manager、scheduler是不同区域的员工,手里拿着各自工牌。所有工牌都由同一个HR签发,大家都认可这个HR的公章,这就是为什么根CA是整个体系的核心。
根CA有个自签名的证书对:ca.crt是公钥,ca.key是私钥。所有组件证书都以ca.crt为签发者。只要根CA不丢,任何组件证书丢了都可以基于同一个根CA重新签一份,老组件的高速缓存也能继续信任,因为签发者没变。
好,基于这个逻辑,手动签发apiserver证书就变得很清晰:用已有的ca.crt和ca.key,生成或重新生成某个组件的私钥,然后用CA私钥给组件的证书签名。
4.2 手动签发apiserver证书实操
假设根CA还在,apiserver.crt 和 apiserver.key 丢了,需要手动签发。步骤如下:
先生成apiserver的私钥:
openssl genrsa -out apiserver.key 2048准备一个OpenSSL配置文件,这里的关键是
subjectAltName必须包含apiserver的几种访问地址:
[ req ] default_bits = 2048 prompt = no default_md = sha256 distinguished_name = dn req_extensions = req_ext [ dn ] CN = kube-apiserver [ req_ext ] subjectAltName = @alt_names [ v3_ext ] authorityKeyIdentifier=keyid,issuer basicConstraints=CA:FALSE keyUsage = digitalSignature, keyEncipherment extendedKeyUsage = serverAuth subjectAltName = @alt_names [ alt_names ] DNS.1 = kubernetes DNS.2 = kubernetes.default DNS.3 = kubernetes.default.svc DNS.4 = kubernetes.default.svc.cluster.local DNS.5 = localhost IP.1 = 127.0.0.1 IP.2 = 192.168.1.10这里的192.168.1.10要替换成你的 apiserver 对外访问地址,如果是云环境,还应该把负载均衡器地址加进去。漏了任何一个访问地址,都会导致那个地址访问时报证书校验失败。
生成证书签名请求:
openssl req -new -key apiserver.key -out apiserver.csr -config openssl.cnf用根CA签发证书:
openssl x509 -req -in apiserver.csr \ -CA ca.crt -CAkey ca.key -CAcreateserial \ -out apiserver.crt -days 3650 \ -extensions v3_ext -extfile openssl.cnf把签好的证书放到对应目录,设置好权限:
install -m 644 apiserver.crt /etc/kubernetes/pki/apiserver.crt install -m 600 apiserver.key /etc/kubernetes/pki/apiserver.key
签发其他组件的证书也是同一个套路,区别在于extendedKeyUsage:apiserver的证书需要serverAuth,apiserver访问kubelet的客户端证书需要clientAuth,而kubelet自己的服务端证书一般两者都带。
4.3 证书SAN缺失是改完之后最常见的坑
手动签发证书时,最容易踩的坑就是SAN没写全。K8s的apiserver对外访问方式很多样:本地回环、集群IP、Service名称、集群外部的负载均衡IP、域名。只要有一个访问地址没放进SAN,客户端从那个地址访问时就会提示证书信任失败。
报错信息通常长这样:
x509: certificate is valid for 10.96.0.1, 192.168.1.10, not 172.20.0.5解决办法也很直接:重新生成apiserver证书,把报错里提到的IP或者域名加入alt_names,再替换证书并重启apiserver。这种问题不需要重建集群,但如果不理解原因,很容易折腾半天找不到方向。
5. 恢复过程中的常见坑和排查技巧
实际操作中,认证文件恢复并不总是顺顺利利,很多坑是文档里不会写的。我总结了一些高频问题和排查思路。
5.1 常见报错速查表
| 报错或现象 | 可能原因 | 排查与处理 |
|---|---|---|
x509: certificate has expired or is not yet valid | 证书过期,或服务器时间不对 | 先确认时间同步;再看证书有效期;kubeadm可以直接续期 |
x509: certificate is valid for ..., not ... | apiserver证书SAN缺地址 | 重新签发apiserver证书,补充SAN |
certificate signed by unknown authority | 客户端不信任签发证书的CA,或者CA换了 | 检查是不是根CA被替换;把新CA加入受信任列表 |
kubelet报403 Forbidden | apiserver的 client certificate 没权限 | 检查 apiserver-kubelet-client 证书和RBAC绑定 |
| etcd报证书错误 | etcd peer证书/server证书丢失或过期 | 如果是etcd独立部署,用etcdctl检查和重新签发 |
Unable to connect to the server: x509 | kubeconfig里的CA数据和新CA不一致 | 重新生成admin.conf |
kubeadm init phase certs all生成后apiserver还是起不来 | 证书权限或路径不对 | 用journalctl -u kubelet或查看静态Pod日志 |
5.2 用openssl快速判断证书状态
判断证书能不能用,openssl是最趁手的工具。三个命令搞定大部分问题:
# 查看证书有效期和主题信息 openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -dates -subject -issuer # 验证证书与私钥是否匹配 openssl x509 -noout -modulus -in /etc/kubernetes/pki/apiserver.crt | openssl sha256 openssl rsa -noout -modulus -in /etc/kubernetes/pki/apiserver.key | openssl sha256 # 验证证书是否由当前CA签发 openssl verify -CAfile /etc/kubernetes/pki/ca.crt /etc/kubernetes/pki/apiserver.crt前两条命令输出的哈希一致,说明证书和私钥是配套的。第三条命令输出OK,说明证书链信任没问题。这套检查基本能把认证问题锁定在“证书自身问题”还是“组件配置问题”上。
我用这套组合排查过好几次集群故障,包括一次apiserver反复崩溃的现场,最后发现是apiserver.crt和apiserver.key不匹配,估计是之前某次操作混用了新老文件。
5.3 最后的兜底:干脆重建集群
如果根CA私钥彻底丢失,且没有任何备份,同时还涉及etcd证书丢失,就不建议继续在原集群废墟上折腾了,直接重建可能更快。具体路径:
- 用
etcdctl snapshot save或从磁盘数据目录尽量导出已有的etcd数据快照。 - 记录当前业务命名空间里的Deployment、Service等对象清单,有条件的直接导出为YAML:
kubectl get deploy,svc,cm,secret -n <namespace> -o yaml > backup.yaml。 - 在新机器上初始化一个新集群,重新生成整套PKI。
- 把旧etcd快照恢复到新集群的etcd里(这一步操作要非常谨慎,版本不匹配会连环报错)。
- 用导出的YAML重新创建业务资源。
这个方案损失的时间最多,但胜在不会在旧集群上越修越乱。尤其是证书体系和etcd全部丢失的情况,硬修往往比重建更花时间。
6. 最后说几句实在话
做运维这么多年,我越来越觉得,认证文件这类东西有点像家门钥匙:你在家的时候不会觉得它重要,哪天出门倒垃圾被风把门带上,才会意识到问题有多严重。K8s集群不像单机服务,牵一发动全身,证书丢失的影响是链式的,apiserver挂掉导致kubectl各种报错,kubelet接二连三掉线,etcd也开始报警,整个群乱成一锅粥。
我自己的体会是,遇到这类事故,最重要的不是背几条命令,而是先建立一套恢复顺序:先判断根CA是否还在,再判断有没有备份,最后才决定走哪条恢复路径。无脑重启那是最低效的做法。
另外我强烈建议:找个周末,拿一台测试集群,把pki目录打包挪走,然后按本文的流程走一遍恢复。真演练过之后,你会发现生产环境再次遇到这个问题时,手是完全不抖的。很多事情看着简单,真正在限时故障、业务方连环催的情况下操作,完全是另一回事。这就是为什么我一直强调演练,哪怕只是在自己的笔记本虚拟机上跑一遍也行。