☰
etcd证书体系与节点重建、续签实操:K8s运维避坑指南
2026/10/15 5:01:53 网站建设 项目流程

接手生产环境K8s集群的维护之后,有两个话题几乎每个同事都会绕进去:一个是etcd集群本身到底怎么运作,另一个就是etcd那套证书体系。尤其是当你碰到“etcd节点坏了,要重装一台”或者“etcd证书还有一个月就到期,怎么换”这样的需求时,如果只背命令不搞懂原理,很容易在最后一步把集群搞挂。这篇文章就围绕这两个实例展开,把etcd证书的来龙去脉、重建节点的完整链路、更换证书的实操过程全部讲透,适合所有在自建K8s集群上做运维、或者正在备考CKA/CKAD的同学反复读几遍。

先说一个我自己的体会:etcd相关的证书之所以容易把人绕晕,是因为它不像apiserver证书那样只有“客户端/服务端”两个角色。etcd在K8s体系里既是服务端,又是客户端,还是集群成员之间互相通信的对等端,一套证书身份就要兼顾三种场景。如果这个底层逻辑没理顺,后面无论是删除节点还是续签证书,都会在某个小细节上翻车。

1. 先搞清etcd证书的“身份链”:谁签发的、谁在验证谁

1.1 一套证书,三种身份

etcd集群里的TLS证书,按用途可以分成下面几类。可以简单把它理解成一个“门禁系统”:每扇门都有单独的钥匙,但所有钥匙都由同一把“主钥匙”签发,这把主钥匙就是etcd CA。

证书用途典型文件路径(kubeadm部署)对应的“门”
etcd CA/etc/kubernetes/pki/etcd/ca.crt 与 ca.key整套门禁的主钥匙
etcd server 证书/etc/kubernetes/pki/etcd/server.crt对客户端提供2379端口服务
etcd peer 证书/etc/kubernetes/pki/etcd/peer.crt节点之间2380端口通信
healthcheck-client 证书/etc/kubernetes/pki/etcd/healthcheck-client.crtetcd自身健康检查
apiserver-etcd-client 证书/etc/kubernetes/pki/apiserver-etcd-client.crtkube-apiserver访问etcd时证明自己的客户端身份

在kubeadm部署的集群里,这五类文件全部位于/etc/kubernetes/pki/etcd/目录下,但apiserver-etcd-client比较特殊,它放在/etc/kubernetes/pki/下面,不放在etcd子目录,因为签发它的对象可以从etcd CA签,也可以用apiserver自己的CA。kubeadm默认用etcd CA来签发它,这点在更换证书时尤其要注意。

1.2 证书流转路径

搞清楚“谁在验证谁”,是排查一切etcd证书问题的核心。K8s控制平面里,证书流向是这样的:

  1. kube-apiserver启动时读取apiserver-etcd-client.crt和apiserver-etcd-client.key,用它去连接本机etcd的2379端口。
  2. etcd服务端收到连接后,启用--client-cert-auth=true,用/etc/kubernetes/pki/etcd/ca.crt来校验客户端证书是否由这个CA签发。
  3. etcd节点之间通信时,双方都是“对等端”,各自出示peer.crt,同时验证对方的peer证书是否由etcd CA签发。
  4. kubelet启动etcd静态Pod时,通过etcd.yaml里的参数把证书路径传给etcd进程,例如--cert-file、--key-file、--peer-cert-file、--peer-key-file等。

你可以登录到控制平面节点上,直接查看/etc/kubernetes/manifests/etcd.yaml。这个静态Pod清单文件里会把上述证书的挂载关系写得明明白白。最常踩的坑是:证书文件本身没坏,但某个组件用的证书和被验证方要求的CA不是同一个。比如你换了etcd的CA,但没有同步替换apiserver信任的CA文件,apiserver就会立刻报证书校验失败。

1.3 证书里的SAN比有效期更重要

证书有效期只是一个维度,另一个更容易被忽略的是SAN(Subject Alternative Name)。etcd server证书和peer证书里,必须包含相应的IP或域名。

  • server证书里至少要有127.0.0.1、localhost、etcd节点IP、service ClusterIP这类地址,因为apiserver是通过https://127.0.0.1:2379访问etcd的。
  • peer证书里必须有节点的实际IP或主机名,以及localhost。

如果你用的是kubeadm,它会在生成证书时自动把当前节点的IP、主机名、127.0.0.1等写进SAN,基本不用操心。但如果你是企业里手工用openssl或cfssl签发etcd证书,SAN写漏一个IP,等到节点重建或者新增节点时就会吃大亏。后面实例一里我会重点演示这个坑。

2. 动任何证书之前,先把这三件事做完

再说一遍,etcd的证书操作属于“成功案例没人夸、失败案例上事故复盘”的操作。我在每次动手之前,无论多么小规模的变更,都一定会先做三件事。

2.1 对etcd数据做快照,并且验证快照真的能读

etcd快照的完整命令如下:

ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \ --cacert=/etc/kubernetes/pki/etcd/ca.crt \ --cert=/etc/kubernetes/pki/apiserver-etcd-client.crt \ --key=/etc/kubernetes/pki/apiserver-etcd-client.key \ snapshot save /backup/etcd-snapshot-$(date +%F).db

很多人以为“命令执行成功就完事了”,实际上snapshot save成功只代表数据写到了本地文件,不代表快照文件就一定可恢复。我会追加一条验证命令:

ETCDCTL_API=3 etcdctl snapshot status /backup/etcd-snapshot-$(date +%F).db

这一步能打印出快照的版本、条目数和哈希值。如果哈希校验失败,这个快照在真正恢复时大概率是废的,必须重新生成。

2.2 备份整个pki目录和所有kubeconfig

证书文件是K8s集群的“根身份”,一旦丢失,哪怕快照还在,恢复后apiserver也无法让etcd信任自己。所以每次动手前,我都会打包:

tar czf /backup/k8s-pki-$(date +%F).tar.gz \ /etc/kubernetes/pki \ /etc/kubernetes/*.conf

私钥文件通常权限比较敏感,建议打包后再用高强度的加密方式归档到独立存储里。这里没有统一标准,但至少要保证“证书崩溃后能恢复到10分钟前的状态”。

2.3 一张表看清所有剩余有效期

kubeadm部署的集群,直接执行:

kubeadm certs check-expiration

它会列出apiserver、apiserver-kubelet-client、front-proxy-client、etcd-server、etcd-peer、etcd-healthcheck-client、apiserver-etcd-client等全部证书的到期时间。输出大概是这样的:

CERTIFICATE EXPIRES RESIDUAL TIME admin.conf Feb 10, 2025 14:41 UTC 341d apiserver Feb 10, 2025 14:41 UTC 341d apiserver-etcd-client Feb 10, 2025 14:41 UTC 341d etcd-healthcheck-client Feb 10, 2025 14:41 UTC 341d etcd-peer Feb 10, 2025 14:41 UTC 341d etcd-server Feb 10, 2025 14:41 UTC 341d

如果是手工部署的etcd,没有kubeadm命令,那就用openssl逐文件查看:

for f in /etc/kubernetes/pki/etcd/*.crt; do echo "$f: $(openssl x509 -in "$f" -noout -enddate)" done

这一步的真实价值不是让你记住哪个证书到期,而是让你在更换证书前,先建立起“当前集群里有哪几类证书身份”的完整图景。替换的时候才知道需要同时更新哪些配套文件。

3. 实例一:etcd节点数据盘损坏后的完整重建过程

3.1 先定性:是节点坏了,还是证书把节点“踢出群聊”了

生产环境里,etcd节点“起不来”通常分两种原因:节点级故障和证书级故障。它们的排查思路完全不同。

节点级故障的典型表现是:磁盘I/O错误、数据目录损坏、容器反复重启,日志里出现wal: file does not exist、apply request took too long、space is running low等。证书级故障的表现则是:证书过期、找不到SAN、签名者不匹配、certificate is valid for 10.0.0.5, not 10.0.0.6。

我遇到过最坑的一次是:某节点的确磁盘报错了,但与此同时它的一对peer证书也刚好过期。当时只盯着磁盘处理,废了好大劲重建后,新节点始终无法加入集群,最后才发现是证书SAN和有效期双重复合问题。所以重建前一定要先做两件事:

  1. 登录etcd容器或直接查看日志,确认到底是证书问题还是数据问题;
  2. 在仍然健康的其他节点上执行etcdctl member list,确认当前幸存成员和ID。

3.2 从多数派中剔除坏节点

假设原集群三个etcd节点:etcd-1、etcd-2、etcd-3,其中etcd-1的数据盘损坏。当前etcd-2、etcd-3仍构成多数派,可以正常选主。那么第一步是从集群中删除坏成员:

ETCDCTL_API=3 etcdctl member list \ --endpoints=https://10.0.0.2:2379 \ --cacert=/etc/kubernetes/pki/etcd/ca.crt \ --cert=/etc/kubernetes/pki/apiserver-etcd-client.crt \ --key=/etc/kubernetes/pki/apiserver-etcd-client.key

假设输出中etcd-1的ID是1a2b3c4d5e6f7080,然后执行:

ETCDCTL_API=3 etcdctl member remove 1a2b3c4d5e6f7080 \ --endpoints=https://10.0.0.2:2379 \ --cacert=/etc/kubernetes/pki/etcd/ca.crt \ --cert=/etc/kubernetes/pki/apiserver-etcd-client.crt \ --key=/etc/kubernetes/pki/apiserver-etcd-client.key

注意,member remove只是让集群“忘记”这个成员,机器上的旧进程并不会马上退出。如果坏节点还能联网,一定要立刻把它的kubelet停掉,或者断开它的网络,最好直接拔盘关停。否则它可能会以旧身份重新尝试加入,在集群里反复刷错误日志,干扰新节点的正常加入。

3.3 新节点如何带着证书重新入伙

清理完旧节点后,开始准备新节点。这里要区分两种情况:kubeadm管理的控制平面节点和手工管理的etcd节点。

情况A:kubeadm stacked etcd

这种方式下,etcd跑在控制平面节点上,推荐直接用“新控制平面节点加入集群”的方式完成重建:

  1. 准备好新节点,安装好kubelet、kubeadm、kubelet等基础组件,但先不要执行join。
  2. 从任一控制平面节点复制/etc/kubernetes/pki目录到新节点。注意:必须包含/etc/kubernetes/pki/etcd/ca.crt和/etc/kubernetes/pki/etcd/ca.key。如果只有ca.crt而没有ca.key,新节点无法为自己签发新的peer证书和server证书,join过程会在生成etcd证书这一步失败。
  3. 执行加入命令:
kubeadm join 10.0.0.2:6443 \ --control-plane \ --certificate-key <key> \ --token <token> \ --discovery-token-ca-cert-hash sha256:<hash>

kubeadm在执行过程中会自动完成以下动作:用etcd CA为新节点签发peer/server证书;通过静态Pod启动本机etcd;向现有etcd集群执行member add;以initial-cluster-state=existing的方式加入。

这里的体验确实很好,因为kubeadm把“证书生成”和“member add”都自动化了。但有一个前提:新节点从旧节点拷贝过来的CA必须和当前etcd集群的CA完全一致,否则即使机器起来了,apiserver把请求发到etcd时仍然会因CA不匹配而被拒绝。

情况B:手工管理的etcd集群

如果是独立部署的etcd集群,不在kubeadm体系里,那么整个链路要自己来。核心步骤是:

  1. 在健康节点上执行etcdctl member remove <旧ID>(同上)。
  2. 执行etcdctl member add <新节点名> --peer-urls=https://10.0.0.4:2380。命令会返回一段ETCD_INITIAL_CLUSTER=...和ETCD_INITIAL_CLUSTER_STATE=existing。
  3. 为新的etcd-4节点签发peer证书。这一步必须把新节点的IP放进SAN,否则即便member add成功,新节点与老节点之间的2380端口TLS握手也会失败。
  4. 把证书、ca、启动参数配置好,启动etcd,命令里带上--initial-cluster-state=existing。
  5. 等新节点追平数据后,用etcdctl member list和etcdctl endpoint health验证。

手工管理方式的坑,几乎全在SAN这一步。我见过有人用老节点的peer证书直接复制给新节点用,结果证书里的IP还是老节点的,新节点死活起不来,journalctl里一堆peer certificate is valid for 10.0.0.1, not 10.0.0.4。

3.4 重建过程中证书的“一票否决”点

我在重建dn节点时总结过一个顺序:先解决成员列表,再解决证书,再启动进程。这跟“先启动进程再注册成员”的直觉相反,所以特别容易做错。

如果你先启动了一个新etcd进程,再想用member add把它拉进集群,很可能会遇到“节点启动时发现没有集群状态”的尴尬:它要么尝试自建一个单节点集群,要么试图从空数据目录里找回旧状态。正确姿势是让kubeadm或手工member add 先确认好“集群同意你入群”,然后新进程以existing状态加入。

另外提醒一句,旧节点如果后来被修复了、想重新入群,千万不要直接拿着旧数据目录来启动。既然它已经被member remove了,旧的数据目录里的wal和snap对它来说是“过去式”,直接启动很可能触发版本和数据的冲突。正确做法是清空/var/lib/etcd之后,把它当作一个全新的成员重新加入。

4. 实例二:etcd集群证书到期更换的完整过程

4.1 换证书之前,先给“生效链路”打个草稿

这是我从一个前辈那里学到的,思维模型比操作命令重要得多。etcd证书更新,本质上不是“哪个文件过期换哪个”,而是“让整条信任链的每一环都切换到新证书”。这条链包括:

  1. etcd节点之间信任关系(peer证书);
  2. etcd对apiserver的信任(server证书 + CA);
  3. apiserver对etcd出示身份(apiserver-etcd-client证书);
  4. 运维工具连接etcd时用的客户端证书(比如etcdctl用的apiserver-etcd-client证书,或独立的client证书)。

只要这条链上有一环没切换,就会出现“apiserver能连etcd但etcd集群内部互相踢”或“etcd集群正常但apiserver连不上”的诡异现象。所以动手前,我会先写一张表,把当前集群里每个角色当前用的证书文件和目标证书文件列出来。

4.2 同一CA体系下,直接用kubeadm续签

如果你的集群是kubeadm部署的,并且这次只做“证书续签”,不换CA,那过程其实非常干净:

在控制平面节点上执行:

kubeadm certs renew all --config=/etc/kubernetes/kubeadm-config.yaml

如果要单独处理etcd相关证书,也可以执行:

kubeadm certs renew etcd-server etcd-peer etcd-healthcheck-client apiserver-etcd-client

执行完成后用kubeadm certs check-expiration确认新的到期时间。这一步会更新/etc/kubernetes/pki/etcd/*.crt和/etc/kubernetes/pki/apiserver-etcd-client.crt等文件,也会更新/etc/kubernetes/admin.conf等kubeconfig文件。

但有个关键认知必须给出:kubeadm renew只负责签发新证书,不负责让运行中的进程加载新证书。etcd和apiserver都是常驻进程,只有重启它们才会重新读取证书文件。我的经验是:不要天真地以为等kubelet“自动感知”就能生效,静态Pod的yaml文件名没变,kubelet根本不会去检测证书文件内容变化。

4.3 滚动重启etcd和apiserver的关键顺序

重启顺序只有一条铁律:先etcd,后apiserver。原因很简单:apiserver的新apiserver-etcd-client证书必须要被etcd信任才能正常连接,而信任它的前提是etcd已经被更新为接受新证书。先重启apiserver的话,apiserver会拿着新证书去连旧etcd,虽然CA没变、通常也能连上,但如果连CA一起换了,就必然断连。

具体操作,我在每个控制平面节点上一次处理:

cd /etc/kubernetes/manifests mv etcd.yaml /tmp/ sleep 10 mv /tmp/etcd.yaml .

等etcd容器起来后,马上在另一个健康节点上验证:

ETCDCTL_API=3 etcdctl endpoint health \ --endpoints=https://127.0.0.1:2379 \ --cacert=/etc/kubernetes/pki/etcd/ca.crt \ --cert=/etc/kubernetes/pki/apiserver-etcd-client.crt \ --key=/etc/kubernetes/pki/apiserver-etcd-client.key

确保这一台健康后,再操作下一台apiserver。同理,apiserver也要通过移走再放回静态Pod清单的方式来触发重启:

cd /etc/kubernetes/manifests mv kube-apiserver.yaml /tmp/ sleep 5 mv /tmp/kube-apiserver.yaml .

apiserver重启后,kubectl get nodes输出正常就说明和etcd的链路已经打通。

4.4 如果是手工管理的etcd集群,怎么换证书

手工管理的etcd集群,没有kubeadm帮忙,整套流程要自己拆解。我建议按这个顺序:

  1. 准备好新证书。如果只是续签,则继续用旧CA签发新server/peer证书;如果泄露需要换CA,则先重新生成整套CA和所有证书,并把新CA同时分发给etcd所有节点、apiserver节点、运维终端。
  2. 在所有etcd节点上,把新CA文件放到相同路径。不要急着重启,先确保每个节点的证书文件都被替换成新的。
  3. 逐节点滚动重启,不要两台同时。先在一台上停掉etcd,再用新证书启动。这一步肯定会短暂影响集群的选主能力,但只要多数派存活,服务不会中断。
  4. 如果换了新CA,还要把信任方也一起换掉。etcd的server端已经在验证新CA,apiserver侧也需要在启动参数里指向新的etcd-ca.crt。手工集群里apiserver通常通过--etcd-cafile指定CA文件,把这个文件替换成新CA,再依次重启apiserver。

我碰过一次很头疼的情况:etcd内部所有节点都换成了新CA,集群健康;但apiserver启动参数里的--etcd-cafile还指向旧CA,结果apiserver和etcd之间握手失败。这个问题的排查过程很简单,看apiserver日志里的x509: certificate signed by unknown authority就能定位。

5. 换证之后容易翻车的几个细节,以及我的长期建议

5.1 健康检查证书和新节点加入时的ca.key

etcd集群里有一个经常被忽略的healthcheck-client.crt。如果你用kubeadm certs renew,它会一起续签,问题不大。但如果是手工管理的集群,很多人只换了server和peer,漏了healthcheck-client,结果etcd的启动参数--cert-file都改好了,但健康检查探针因为它而不通过,kubelet误判etcd容器不健康,反复重启。

另外,前面提到的新节点加入必须要/etc/kubernetes/pki/etcd/ca.key这件事,在换证书场景下同样成立。很多人想给集群的证书“换新”,顺手把旧的ca.key删了,想着“反正所有证书我都换新的了”。结果两个月后要扩容控制平面节点,发现没有ca.key就无法签新证书,只能再次整体换CA,非常痛苦。我的建议很简单:ca.key是根身份,即使不在生产节点上明文存放,也必须在加密备份里留一份,而且要标注好它的指纹和当前CA的对应关系。

5.2 更新后的kubeadm secret也要同步

kubeadm从某些版本开始,会把控制平面证书以secret的形式存放在kube-system命名空间里,供新节点join时使用。如果你在控制平面节点上执行了kubeadm certs renew,把本地证书文件都换了,但kubeadm-certs这个secret里的旧证书不会自动更新。

这会导致一个隐蔽问题:后续某个新节点执行kubeadm join --control-plane --certificate-key xxx时,从secret里解密出来的证书还是旧的,生成的控制平面组件和当前集群证书体系不一致。最直接的解决方法是重新上传:

kubeadm init phase upload-certs --upload-certs --config=/etc/kubernetes/kubeadm-config.yaml

执行完记得把新的--certificate-key记录到自己的密钥管理工具里,避免旧key继续被使用。

5.3 把证书检查常态化,而不是等告警

经历过几次证书问题后,我现在把证书巡检做成了固定动作,而不是等告警来找我。这里分享一个简单的脚本思路:

#!/bin/bash BACKUP_DIR="/backup/etcd-tls" mkdir -p "$BACKUP_DIR" # 检查所有pem文件有效期 for f in /etc/kubernetes/pki/etcd/*.crt /etc/kubernetes/pki/apiserver-etcd-client.crt; do days=$(openssl x509 -in "$f" -noout -enddate | awk -F'=' '{print $2}') # 通过转换时间差来判断剩余天数 done # 剩余不足90天时输出警告

这类脚本可以放进crontab,每周跑一次。99%的时间里它不会输出任何内容,但只要某一天它发来告警,就等于给了你至少一个月的时间去从容安排换证窗口,而不是凌晨三点被电话叫起来处理。

5.4 我个人的换证习惯

最后聊一个工作习惯。每次换完证书,我都会做一次“双人复核”:一个人执行,另一个人只负责看结果。因为证书体系里涉及的文件太多,同一个人很容易被“自己以为已经换了”的惯性欺骗。复核时重点看三件事:新证书的到期时间是否符合预期;etcd集群所有成员是否都在线且健康;apiserver日志里有没有出现x509相关的错误。

这个习惯其实不需要额外工具,关键在于执行之前,先在一张纸上列出“这条信任链上有多少环、每环对应哪个文件、哪个进程”。把所有环节都列明白了,etcd证书这个K8s运维里的“硬骨头”,也就没那么难啃了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询