☰
二进制部署Etcd集群:从证书签发到故障恢复全指南
2026/10/21 18:58:38 网站建设 项目流程

如果你走的是二进制部署 Kubernetes 这条路,Etcd 集群大概是你躲不开的第一道正餐。很多朋友搭 K8s 的时候习惯先把 kubeadm 拉起来,Etcd 交给它一并托管,看起来省事,但等你真正需要做离线交付、定制启动参数、审计证书体系,或者控制面出问题需要单独排查 Etcd 时,就知道黑盒式的托管会带来多少麻烦。这篇文章我把完整跑过的 3 节点 Etcd 集群二进制部署流程整理了一遍,从证书签发、系统服务、健康检查到故障恢复都覆盖到了,主要面向已经能用 kubeadm 搭出集群、想往深走一步的运维和交付工程师,也适合需要在隔离内网从零准备 K8s 的同学。文中的经验都是实际操作中总结出来的,照着做能少走不少弯路。

1. 为什么放着 kubeadm 不用,非得二进制部署 Etcd

1.1 kubeadm 托管 Etcd 的代价

kubeadm 初始化集群的时候确实会自动把 Etcd 部署成静态 Pod,很多人觉得这一步很省心,没必要自己搞。但只要你接下来有任何一个环节对 Etcd 有"额外要求",托管方式的短板就暴露了。

首先是配参不透明。Etcd 的启动参数被写进 kube-system 命名空间下的 ConfigMap 里,改一个参数要经历导出、修改、回写、滚动重启一堆步骤。现场排障时,你对着 kubelet 日志和容器状态绕半天,才知道问题可能出在 Etcd 参数上,定位成本很高。

其次是证书体系太自动。kubeadm 会在集群初始化时自动生成一套 Etcd 证书,看起来方便,但证书到期时间、签发机构、SAN 范围都由它决定。等你某天要统一轮换证书或者对接公司内部的 PKI 体系,就会发现在静态 Pod 里改证书比手动部署还要繁琐。而二进制部署时,证书是自己签的,CA 和私钥自己保管,从签发到吊销整条链路都在掌控里。

第三是故障边界模糊。Etcd 在 kubeadm 下是静态 Pod,和 kubelet 绑定在同一生命周期里。Kubelet 出问题,Etcd 容器也起不来;Etcd 状态异常,又会影响 kubelet 对节点状态的感知。经常一个故障你判断不清是容器运行时的问题还是 Etcd 自身的问题。

1.2 自己部署换来了什么

二进制部署 Etcd,本质上是把这个组件从 K8s 里独立出来。Etcd 进程直接跑在宿主机上,由 systemd 托管,这是我最喜欢的一点:Etcd 的生命周期和 K8s 解耦。

你可以先启动一个完整的 Etcd 集群,验证健康之后,再去部署 kube-apiserver。控制面万一出问题,排查边界非常清晰——Etcd 是 Etcd,API Server 是 API Server,两边连接通过显式的地址和证书,不会互相掩盖日志。

另一个实际好处是离线交付。内网环境通常没有外网包源,你只需要把三个东西搬进去:Etcd 二进制、证书目录、配置文件。我可以把整套东西打成一个小压缩包,在任何一台新机器上解压就能起服务,不需要依赖任何安装器或者镜像仓库。这和静态 Pod 的交付方式比,少了很多运行时依赖。

参数层面也是完全放开。Etcd 性能相关参数、心跳间隔、选举超时、快照策略,全部可以按生产环境需求调。而且配置可以放进统一的环境文件里,通过配置管理工具下发到每台机器,后续做变更审计也有据可查。

1.3 这套方案适合谁,不适合谁

我的判断标准很简单:如果只是本地起个实验环境,为了快速体验 K8s 功能,那 kubeadm 一步到位更舒服;但如果你要做生产集群、私有化交付,或者你正在学 K8s 控制面原理,Etcd 建议亲手部署一次。

生产环境还有一个很现实的需求:数据落盘位置完全可控。托管部署下 Etcd 的数据在宿主机某个固定目录,你很难按自己的存储规划调整。二进制部署我可以在安装前就把独立数据盘挂好,把数据目录、WAL 目录、日志目录全部规划清楚,后期扩容和迁移都有据可循。

2. 集群拓扑与证书设计:动手前把账算明白

2.1 节点规模:三节点是性价比最高的最小集

Etcd 集群推荐奇数节点,这是 Raft 协议的投票机制决定的。Raft 要求集群里超过半数的节点存活才能选出 Leader、才能写入数据。对 3 节点来说,最多能容忍 1 台宕机;对 5 节点来说,最多能容忍 2 台。

那为什么不直接上 5 节点?因为每增加一个节点,写入请求都要复制到多数节点上才算成功,节点越多,同步开销越大,写性能反而下降。跨机房容灾场景下 5 节点确实有用,但绝大多数单机房或同城双活场景,3 节点足够。

还有一点容易被忽略:偶数节点没有意义。4 节点和 3 节点容错能力一样,都是最多挂 1 台,但多出来的那个节点纯粹增加同步负担。所以如果你在规划阶段看到 4 台,建议直接砍成 3 台。

2.2 通信端口、数据目录与主机名规划

Etcd 有两个核心端口,必须先搞明白它们的区别:

  • 2379:客户端通信端口,供 kube-apiserver、etcdctl 等客户端访问。
  • 2380:节点间通信端口,Raft 日志复制、Leader 选举全部走这个端口。

还有一个概念容易混淆:listen 地址和 advertise 地址。listen 是"门开在哪里",比如只在本机某个 IP 上监听;advertise 是"我对外宣布我在哪里",其他节点要通过这个地址来访问你。所以 advertise 地址必须能被集群内所有节点路由到,不能写 127.0.0.1,否则别的节点根本找不到你。

我习惯在规划阶段就用表格把拓扑定死,避免后面东改西改:

主机名IP角色数据盘
etcd-110.0.0.11Etcd 节点/data
etcd-210.0.0.12Etcd 节点/data
etcd-310.0.0.13Etcd 节点/data

主机名不是随便起的,Etcd 启动后会把自己注册成集群成员,成员名称就是配置里的 ETCD_NAME。三台机器的主机名最好和规划一致,这样等会儿看 member list 输出时一目了然,不会出现"这个 id 到底是哪台机器"的尴尬。

数据目录方面,我强烈建议给每台机器挂一块独立的数据盘,挂载到 /data。Etcd 对磁盘 IO 非常敏感,而且数据目录一旦损坏,整个 K8s 的控制面状态都可能丢失,后果很严重。

2.3 Etcd 的 TLS 证书该怎么设计

二进制部署 Etcd 时,TLS 证书是必须的。生产环境绝对不能裸奔,不然任何能访问 2379 端口的人都能把集群里的数据读走,甚至直接写入恶意数据。

Etcd 需要两类证书:CA 证书和服务器证书。CA 就是整个信任链的根,所有 Etcd 相关的证书都由它签发。服务器证书则同时承担两个职责:

  • 作为 Etcd 服务端证书,加密客户端访问,让 kube-apiserver 通过 HTTPS 访问 Etcd。
  • 作为 Peer 证书,加密节点之间的 Raft 通信。

这两个职责其实可以共用一张证书,因为证书的用途字段里同时声明了 server auth 和 client auth。这样做的好处是简化证书管理,生产环境证书数量少、轮换方便。如果你有更严格的安全要求,也可以把 peer 证书和 client 证书分开签,但这会让运维成本上升不少,项目初期不必这么做。

证书里的 SAN 字段是关键中的关键。Etcd 服务端证书的 SAN 必须包含所有节点的 IP 和访问 Etcd 会用到的主机名。比如我用 https://10.0.0.11:2379 访问 Etcd,那么 10.0.0.11 必须出现在 SAN 里。如果某个节点 IP 漏了,从那个 IP 发起的连接会直接报证书校验失败,而且是那种特别隐晦的握手失败,不好排查。

3. 用 cfssl 签发 Etcd 证书:最啰嗦也最不能省的一步

3.1 准备工作与 CA 配置

证书这块我推荐用 cfssl,因为它的配置文件可以完全代码化,生成过程可复现。准备工作很简单,在一台管理机上创建 /etc/etcd/pki 目录,专门用来放证书相关文件。

先写 cfssl 的签名策略文件 ca-config.json:

{ "signing": { "default": { "expiry": "87600h" }, "profiles": { "etcd": { "usages": ["signing", "key encipherment", "server auth", "client auth"], "expiry": "87600h" } } } }

这里的 87600h 是 10 年,一次性签个长期证书能省掉后面频繁轮换的麻烦。profiles 里叫 etcd 的这个策略,把 server auth 和 client auth 都加上了,这样用同一张证书既能给服务端用,也能给客户端用,正是我们前面说的设计。

再写 CA 自己用的 csr 文件 etcd-ca-csr.json:

{ "CN": "etcd-ca", "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "O": "etcd", "OU": "etcd-cluster" } ] }

生成 CA 只需一条命令:

cd /etc/etcd/pki cfssl gencert -initca etcd-ca-csr.json | cfssljson -bare etcd-ca

执行完当前目录会多出三个文件:etcd-ca.pem、etcd-ca-key.pem、etcd-ca.csr。前两个才是我们需要的,csr 文件用不上。etcd-ca-key.pem 是整个信任链的根私钥,一定要单独保管好,最好再离线备份一份。它丢了或者泄露了,后果都比较严重。

3.2 生成服务器证书

接下来给 Etcd 集群签发服务器证书。csr 文件里最重要的是 hosts 数组,这里决定了证书能被哪些 IP 和域名访问:

{ "CN": "etcd-server", "hosts": [ "10.0.0.11", "10.0.0.12", "10.0.0.13", "127.0.0.1", "localhost", "kubernetes.default.svc.cluster.local" ], "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "O": "etcd", "OU": "etcd-cluster" } ] }

注意 hosts 数组里,三台节点的 IP 是必须的。127.0.0.1 和 localhost 建议也写上,方便在单机上进行维护操作。那个 kubernetes.default.svc.cluster.local 是看情况加的,如果你的环境里 kube-apiserver 通过集群内部 DNS 名称访问 Etcd,就一定要写;如果你只用 IP 直连,不写也问题不大。

签发命令:

cfssl gencert \ -ca=etcd-ca.pem \ -ca-key=etcd-ca-key.pem \ -config=ca-config.json \ -profile=etcd \ etcd-server-csr.json | cfssljson -bare etcd-server

这条命令会生成 etcd-server.pem 和 etcd-server-key.pem,这两张才真正用得上。

3.3 证书分发与校验清单

证书签发完别急着丢到机器上,先检查一遍。我用 openssl 看 SAN 有没有问题:

openssl x509 -in etcd-server.pem -text -noout | grep -A 1 "Subject Alternative Name"

输出里必须能看到三台节点的 IP,少一个都不行。这个步骤还能顺便看一眼证书有效期,确认是按 10 年签的。

然后把这四个文件分发到三台节点的同一个目录下:

  • etcd-ca.pem
  • etcd-ca-key.pem
  • etcd-server.pem
  • etcd-server-key.pem

权限必须要处理:Etcd 运行用户需要能读到私钥,但私钥又不能给太宽的权限。我一般把私钥权限设置成 600,属主改成 etcd 用户。如果后面 Etcd 启动报权限不足,多半就是忘了这一步。

3.4 我见过的证书类故障

证书是 Etcd 部署中最容易翻车的环节。有一种很典型的故障:节点 A 上证书只包含了 10.0.0.11 和 10.0.0.12,等后面加第四台节点 10.0.0.14 时,从新节点连集群总是握手失败,日志里报 "certificate signed by unknown authority" 或者 "hostname and IP are not allowed"。

这种问题的根源就是新节点 IP 不在证书 SAN 里。解决方案也只能重新签发证书并替换所有节点的证书文件。所以我的经验是:第一次签发时就把可能用到的 IP 都写进去,宁可多写几个也不要事后补。尤其是你计划后续扩节点,提前留出余量能省大事。

另外一个小经验:不要复用其他组件的 CA 来签发 Etcd 证书。K8s 里会有 kube-apiserver、kubelet 各自的证书体系,如果 Etcd 的 CA 和它们混在一起,以后吊销、隔离、审计都很麻烦。独立 CA、独立 CN、独立目录,这是最干净的做法。

4. 把 Etcd 跑成系统服务:环境文件与 systemd 单元

4.1 数据盘准备与基础系统参数

证书到位之后,开始准备主机环境。先把独立数据盘格式化并挂载到 /data:

mkfs.xfs /dev/sdb mkdir -p /data mount /dev/sdb /data echo "/dev/sdb /data xfs defaults 0 0" >> /etc/fstab

然后创建 Etcd 运行所需的目录结构和用户:

groupadd --system etcd useradd --system -g etcd -s /sbin/nologin etcd mkdir -p /data/etcd/data mkdir -p /data/etcd/wal mkdir -p /data/etcd/logs chown -R etcd:etcd /data/etcd

系统参数方面,Etcd 没有那么多内核参数要调,但文件描述符限制值得注意。Etcd 在高并发下会打开大量连接,默认的 1024 肯定不够,所以要在 systemd 单元里做限制,这个下面会讲到。

时间同步也是一定要检查的。Etcd 的 Raft 协议对时钟偏移很敏感,节点之间时钟差太大会导致选举失败或心跳异常。每台机器装上并启用 chrony,确认时间一致之后再继续。

4.2 环境配置文件逐项拆解

Etcd 的配置方式我推荐 EnvironmentFile,把所有启动参数放到 /etc/etcd/etcd.conf 里,systemd 单元通过 EnvironmentFile 加载。这样配置集中管理,也方便配置管理工具统一下发。

以 etcd-1 为例,配置文件长这样:

ETCD_NAME="etcd-1" ETCD_DATA_DIR="/data/etcd/data" ETCD_WAL_DIR="/data/etcd/wal" ETCD_LISTEN_PEER_URLS="https://10.0.0.11:2380" ETCD_LISTEN_CLIENT_URLS="https://10.0.0.11:2379,https://127.0.0.1:2379" ETCD_ADVERTISE_CLIENT_URLS="https://10.0.0.11:2379" ETCD_INITIAL_ADVERTISE_PEER_URLS="https://10.0.0.11:2380" ETCD_INITIAL_CLUSTER="etcd-1=https://10.0.0.11:2380,etcd-2=https://10.0.0.12:2380,etcd-3=https://10.0.0.13:2380" ETCD_INITIAL_CLUSTER_STATE="new" ETCD_INITIAL_CLUSTER_TOKEN="etcd-k8s-cluster" ETCD_CERT_FILE="/etc/etcd/pki/etcd-server.pem" ETCD_KEY_FILE="/etc/etcd/pki/etcd-server-key.pem" ETCD_TRUSTED_CA_FILE="/etc/etcd/pki/ca.pem" ETCD_PEER_CERT_FILE="/etc/etcd/pki/etcd-server.pem" ETCD_PEER_KEY_FILE="/etc/etcd/pki/etcd-server-key.pem" ETCD_PEER_TRUSTED_CA_FILE="/etc/etcd/pki/ca.pem"

逐个说下几个重点。ETCD_NAME 是成员名字,集群内必须唯一,我直接用 etcd-1 这种编号,简单明了。ETCD_DATA_DIR 是数据目录,这是 Etcd 唯一需要持久化的地方。ETCD_WAL_DIR 单独指到 WAL 目录,原因下面单独讲。

ETCD_LISTEN_CLIENT_URLS 这里我特意同时监听了节点 IP 和 127.0.0.1,目的是后续在本机排查问题时,可以用 localhost 快速访问,不用绕网络。但 ETCD_ADVERTISE_CLIENT_URLS 只写节点 IP,不写 127.0.0.1。这个细节很重要:advertise 地址是告诉 kube-apiserver"我在这里",如果写成了 127.0.0.1,那远程的 apiserver 就会尝试连自己本机的 2379 端口,自然失败。

ETCD_INITIAL_CLUSTER 是三个节点的 peer 地址,三个节点上这一行必须完全一致,少写一个都不行。ETCD_INITIAL_CLUSTER_STATE 在首次创建集群时写 new,这是"从零建集群"的标识。

4.3 systemd 单元里的几个隐藏细节

环境文件写好后,创建 systemd 单元 /etc/systemd/system/etcd.service:

[Unit] Description=etcd service Documentation=https://github.com/etcd-io/etcd After=network.target Requires=network.target [Service] Type=notify User=etcd Group=etcd EnvironmentFile=-/etc/etcd/etcd.conf ExecStart=/usr/local/bin/etcd Restart=on-failure RestartSec=5 LimitNOFILE=65536 TimeoutStartSec=0 [Install] WantedBy=multi-user.target

这里面最值得说的是 Type=notify。Etcd 启动完成会主动向 systemd 发送就绪通知,systemd 收到后才会认为服务启动成功。如果用默认的 Type=simple,systemd 只要看到进程 fork 出来就算启动了,但实际上 Etcd 可能还在初始化,后面你 systemctl status 会看到"active"但实际上没接上,坑过不少人。

LimitNOFILE 设置成 65536 是经验值,应对高并发下大量连接的情况。Restart=on-failure 表示挂掉自动拉起,但注意如果 Etcd 频繁重启,不要盲目依赖这个机制,得去看日志找根因。TimeoutStartSec=0 是防止启动慢的时候 systemd 误杀进程。

启动之前先 daemon-reload:

systemctl daemon-reload systemctl enable --now etcd

启动顺序有个小技巧:三台机器最好间隔十几秒依次启动,不要同时全部拉起。第一台启动后会等待其他节点加入,日志里出现类似 "waiting for connection" 的提示是正常的,不是故障。

4.4 数据目录与 WAL 目录为什么必须分开

很多人配置 Etcd 时只设置 DATA_DIR,WAL 目录用默认的。我的建议是显式设置 WAL 目录,并尽量让它在不同的磁盘上。

原因是两者的写入模式完全不同:WAL 是追加式顺序写,Data 目录里是类似 KV 存储的文件,会有随机写和合并操作。如果放在同一块磁盘上,WAL 的顺序写请求可能会被 Data 的随机写阻塞,导致事务提交延迟。条件有限时至少用同一块 SSD,两个逻辑目录分开;条件允许就挂两块盘,WAL 一块,Data 一块。

这个配置对故障恢复速度也有影响。WAL 是 Etcd 的可靠性保证,所有写入必须先持久化到 WAL 才算成功。WAL 目录所在磁盘 IO 越好,Etcd 的写入延迟越低,Leader 越稳定。

5. 启动集群与健康检查:用日志和 etcdctl 看懂集群状态

5.1 第一次启动,日志该怎么观察

三台机器按顺序启动后,先别急着看状态,直接看日志:

journalctl -fu etcd

正常情况下,你会在日志里看到这些关键信息:

  • 节点初始化自己的成员信息,然后向集群里其他地址发起连接。
  • 当其他节点陆续启动,日志里会出现成员间互相发现、建立连接的记录。
  • 当集群达成多数派,会有人被选为 Leader,日志里出现 "elected leader" 字样,同时能看出当前 term 号。

一条很有用的排查思路是:如果一直卡在等待其他节点的阶段,说明 peer 地址或者 2380 端口网络有问题;如果连上了但反复断,很大概率是证书不匹配。

我第一次部署时就犯过一个低级错误,三台机器的 ETCD_NAME 都配成了 etcd-1,结果后面的节点一直加入失败。这个错误在日志里不明显,但当过一段时间回头看 member list 就会发现名字全重复了。所以启动前建议先在三台机器上逐一确认配置里的 ETCD_NAME,不要偷懒。

5.2 etcdctl 三板斧:member list / endpoint health / endpoint status

Etcd 自带的 etcdctl 是验证集群状态最重要的工具。先把环境变量准备好,省得每条命令都带一大串证书参数:

export ETCDCTL_API=3 export ETCDCTL_ENDPOINTS="https://10.0.0.11:2379,https://10.0.0.12:2379,https://10.0.0.13:2379" export ETCDCTL_CACERT=/etc/etcd/pki/ca.pem export ETCDCTL_CERT=/etc/etcd/pki/etcd-server.pem export ETCDCTL_KEY=/etc/etcd/pki/etcd-server-key.pem

第一条命令看成员列表:

etcdctl member list

输出类似:

8e9e05c52164694d, started, etcd-1, https://10.0.0.11:2380, https://10.0.0.11:2379, false f5b1a1c9f7d8e6ba, started, etcd-2, https://10.0.0.12:2380, https://10.0.0.12:2379, false c7430a4d25f1c2a0, started, etcd-3, https://10.0.0.13:2380, https://10.0.0.13:2379, false

三个成员都是 started 状态,名字和 IP 一一对应,这就是健康的第一步。第二列如果出现 unstarted,说明这个节点还没完成启动,继续看日志。

接着看端点健康:

etcdctl endpoint health

输出三行 "is healthy",每行代表一个节点。这个命令会实际请求每个节点的健康检查接口,比 member list 更贴近真实可用性。

再看端点状态:

etcdctl endpoint status --write-out=table

这个命令输出的信息很全,包括 Raft term、Leader 所在端点、DB 大小、节点版本。我主要看两件事:三台机器的 term 是否一致,以及 Leader 那行是否正常。如果各节点 term 不一致,说明可能发生过选举或者网络分区。

5.3 写入读取验证与 kube-apiserver 接入

健康检查过了,还要验证数据的复制能力。在任意一台节点上写入一条测试数据:

etcdctl put /test/hello "world" etcdctl get /test/hello --prefix

然后在另外两台节点上执行 get,如果都能读到同样的值,说明 Raft 的复制链路是通的,数据已经同步到多数节点。

这一步看起来简单,但能发现很多潜在问题。比如证书虽然能握手,但 peer 通信的加密通道可能有问题,导致数据复制不完整;再比如某台节点时钟偏移大,可能间接导致选举问题。

Etcd 集群就绪后,接下来要接入 kube-apiserver。apiserver 那侧的启动参数里其实就三件事:etcd 的访问地址、Etcd 的 CA 证书、apiserver 自己的客户端证书。把 CA 证书和客户端证书路径传给 apiserver,它就能通过 HTTPS 和 Etcd 握手。这里有个前提不能漏:apiserver 使用的客户端证书必须由同一个 Etcd 的 CA 签发,否则 Etcd 会拒绝这个客户端。

如果你和我一样整套 K8s 都走二进制部署,建议先手动用 curl 验证 apiserver 到 Etcd 的链路:

curl --cacert /etc/etcd/pki/ca.pem \ --cert /etc/etcd/pki/etcd-server.pem \ --key /etc/etcd/pki/etcd-server-key.pem \ https://10.0.0.11:2379/health

返回 {"health":"true"} 就说明 2379 端口的 HTTPS 服务完全正常。

5.4 节点级故障的快速验证手段

集群部署完不等于万事大吉,我在实际运维中会把这些命令记成一个小脚本,出问题先跑一遍:

etcdctl endpoint health etcdctl endpoint status --write-out=table etcdctl alarm list

alarm list 是用来查 Etcd 自身的告警状态的。比如磁盘空间不足时会触发 alarm,这时候写入可能被拒。这个命令在日常检查中很实用,能提前发现存储空间问题,而不是等写入失败才去排查。

如果 endpoint health 显示某个节点不健康,先分清楚是网络问题、证书问题还是负载问题。我一般会先 ping 对端 IP 检查网络,再通过 journalctl 看那台机器的 Etcd 日志,最后用压测工具排除是不是磁盘 IO 被打满了。不要上来就重启节点,重启只能掩盖问题,等集群再次遇到同样负载时大概率还会炸。

6. 成员管理与故障恢复:这些坑我替你踩过了

6.1 快照备份:做一次不难,难的是定期做

Etcd 的数据是 K8s 的命根子,快照备份必须常态化。最简单的方式是用 etcdctl 直接保存快照:

etcdctl snapshot save /data/backup/etcd-$(date +%F).db

快照文件是二进制的,不能靠简单的文件复制替代,一定要走 snapshot save。而且只在单节点上保存还不够,理论上任何节点的数据都可能有细微差异,但多数派的数据是最准的。我更推荐在 Leader 节点上保存快照,或者三节点都存一份再定期校验。

快照备份之后,还得定期做恢复演练。没有做过恢复演练的备份,在真正灾难来临时很可能发现恢复命令和实际环境不匹配。恢复流程是这样的:

etcdctl snapshot restore /data/backup/etcd-2025-01-01.db \ --name etcd-1 \ --data-dir /data/etcd/data \ --initial-cluster "etcd-1=https://10.0.0.11:2380,etcd-2=https://10.0.0.12:2380,etcd-3=https://10.0.0.13:2380" \ --initial-cluster-token etcd-k8s-restored

注意三台机器都要从同一个快照恢复,只是 --name 换成各自的节点名。恢复完数据目录后,千万别忘了把属主改回 etcd:

chown -R etcd:etcd /data/etcd

我见过有人恢复后直接启动,结果是 etcd 用户没有权限访问数据目录,服务一直起不来。这种问题看起来很蠢,但压力大的时候就是容易漏。

6.2 单节点宕机后的替换流程

一台节点物理损坏且确定无法修复时,替换流程要分步走,不要上来就etcdctl member remove。

先确认集群现在是否健康,如果坏一台之后还有两台存活,那集群是可以正常读写的。先执行:

etcdctl member list

记下故障节点的 member ID,然后移除它:

etcdctl member remove <故障节点member-id>

接着新加一台机器,假设新节点是 10.0.0.14,名字叫 etcd-4:

etcdctl member add etcd-4 --peer-urls=https://10.0.0.14:2380

member add 之后,把证书、配置同步到新节点,然后在新的环境文件里把 ETCD_INITIAL_CLUSTER_STATE 改成 existing,ETCD_INITIAL_CLUSTER 中要包含所有现存成员和新成员。此时启动新节点,它会从现有集群中同步数据,这个过程中日志可能会显示正在追赶快照,耐心等待即可。

这里有个非常容易被忽略的问题:新节点的证书必须额外包含新 IP 10.0.0.14 的 SAN。如果一开始签发证书时没有预留,就需要重新签发并全量替换证书,否则新节点永远无法加入集群。

6.3 数据目录残留与集群角色错位

清理故障节点数据时,我有一条原则:不要删,要改名。比如把 /data/etcd/data 改成 /data/etcd/data.old,保留现场数据。这样如果后面发现误判,还可以从旧数据里抢救配置或者定位问题。

另外添加新节点时,一定要保证新节点的数据目录是空的。如果新节点的数据目录里残留了旧集群的数据,Etcd 启动时会误以为自己还是旧集群的一员,加入新集群时会报成员 ID 冲突或者数据不一致。

如果一台机器之前起过另一个 Etcd 集群,你在同一数据目录上继续使用,问题会更多。最典型的就是启动后集群状态混乱,Leader 频繁切换。遇到这种情况,清空数据目录重来是最快的。数据目录不是普通进程的工作目录,它记录了成员信息、集群 ID、当前 term 等状态,跨集群复用等于把两个人的病历放到一个文件夹里,怎么看怎么乱。

6.4 扩容或重建时容易被忽略的事情

很多人以为 Etcd 集群扩容就是加机器,但 Etcd 每增加一个节点,写入性能会下降一点。Raft 要把数据复制到多数节点才算成功,5 节点的多数是 3,7 节点的多数是 4,复制份数越多越慢。三节点能支撑绝大部分场景,不需要为了好看而扩到五节点。

真正需要扩容的场景通常是跨机房容灾。这时候你要小心网络分区问题:如果两个机房各放一半节点,网络一断,两边都凑不齐多数派,集群就不可写了。正确做法是确保任何一个分区内都有超过半数的节点,通常是在主站点放 3 个节点,异地只放 1 到 2 个。

全集群重建时,所有节点必须用同一个快照恢复,然后以新 token 启动。token 的作用是区分不同的集群实例,你可以把它理解成一个命名空间标识。恢复时换一个 token 完全没问题,数据内容不受影响,但不要让一个集群里 token 混乱,否则节点之间会互相不认账。

最后提一下时间同步。Etcd 的 Raft 选举依赖心跳,默认心跳间隔是 100ms,选举超时是 1 秒。如果网络抖动或者时钟偏移超过这个范围,就可能出现无意义的选举,甚至频繁切 Leader。生产环境一定要保证所有节点的时间同步服务正常运行,这是 Etcd 集群稳定运行的前提中的前提。

根据我的实操经验,二进制部署 Etcd 集群一旦走完一遍,后面再折腾证书、参数调优、故障恢复都会从容很多。如果这篇文章里的某个步骤让你觉得"原来如此",那这套流程就没白写。按部就班地准备、签发、启动、验证,你已经把 K8s 控制面最底层的那块基石牢牢握在手里了。

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

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

立即咨询