Kubernetes 集群高可用部署实践:etcd 多节点 + kube-apiserver 负载均衡 + 自动故障转移
Kubernetes 高可用部署:不是“配齐三节点就万事大吉”了
大家好,我是老张,在金融和 SaaS 领域搞 K8s 运维和平台建设快 6 年了。今天想和大家掏心窝子聊聊 Kubernetes 集群高可用(HA) ——尤其是那个被很多人“配完就忘”的环节:etcd 多节点 + kube-apiserver 负载均衡 + 自动故障转移。说实话,我第一次上线 HA 集群时,以为照着官方文档搭完三个 master 就稳了,结果某天 etcd 节点磁盘满导致 leader 切换失败,整个集群 API 不响应…折腾了半天才定位到 --quota-backend-bytes 没调、wal 目录没做独立挂载。血泪教训啊!
为什么“三 Master”不等于“真高可用”?
很多同学误以为只要 kubeadm init --control-plane-endpoint="lb.example.com:6443" 就搞定了。但真相是:
- ✅ etcd 是 K8s 的“大脑”,所有状态都存这儿;单点 or 奇数节点不足(如仅2节点)会导致脑裂或不可用
- ✅ kube-apiserver 是唯一入口,必须通过外部负载均衡器(LB) 对外暴露,且 LB 必须支持健康检查(如 TCP 端口探测 or HTTP
/healthz) - ❌ 如果 LB 把流量打到已宕机的 apiserver 上,或者 etcd 成员失联未自动剔除,集群照样“假活”
⚠️ 重要提示:kubeadm 生成的
admin.conf默认只写第一个 master 的 IP。生产环境务必用--control-plane-endpoint绑定 DNS 名(如k8s-api.internal),并确保该域名始终解析到健康 LB VIP。
实战四步走:从零搭建可落地的 HA 架构
# 步骤 1:etcd 集群初始化(静态成员模式)
我们用 3 台 control-plane 节点(cp1/cp2/cp3),全部作为 etcd 成员。关键:每个节点必须显式指定 --initial-cluster 和唯一 --name
# 在 cp1 上执行(注意替换 IP 和 hostname)
ETCD_NAME="cp1"
ETCD_IP="10.10.1.11"
ETCD_INITIAL_CLUSTER="cp1=https://10.10.1.11:2380,cp2=https://10.10.1.12:2380,cp3=https://10.10.1.13:2380"
etcd \
--name ${ETCD_NAME} \
--data-dir /var/lib/etcd \
--listen-peer-urls https://${ETCD_IP}:2380 \
--listen-client-urls https://${ETCD_IP}:2379,https://127.0.0.1:2379 \
--advertise-client-urls https://${ETCD_IP}:2379 \
--initial-advertise-peer-urls https://${ETCD_IP}:2380 \
--initial-cluster ${ETCD_INITIAL_CLUSTER} \
--initial-cluster-token k8s-etcd-cluster \
--initial-cluster-state new \
--cert-file=/etc/kubernetes/pki/etcd/server.crt \
--key-file=/etc/kubernetes/pki/etcd/server.key \
--peer-cert-file=/etc/kubernetes/pki/etcd/peer.crt \
--peer-key-file=/etc/kubernetes/pki/etcd/peer.key \
--trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt \
--peer-trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt \
--quota-backend-bytes=8589934592 \ # �� 关键!设为 8GB,防 wal 堆积
--auto-compaction-retention=12h
⚠️ 踩坑总结:etcd 启动后务必立刻验证
etcdctl --endpoints=https://127.0.0.1:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/client.crt --key=/etc/kubernetes/pki/etcd/client.key member list—— 确保所有成员state为started,且clientURLs可连通。曾因防火墙漏开2380端口,卡在unstarted状态半天。
# 步骤 2:配置高可用 LB(以 HAProxy 为例)
我们不用云厂商 LB(避免绑定),手撸轻量 HAProxy。它需监听 6443,并轮询后端 apiserver 的 localhost:6443(因为 apiserver 默认只绑 127.0.0.1)。
# /etc/haproxy/haproxy.cfg
frontend k8s-api
bind *:6443
mode tcp
option tcp-check
default_backend k8s-apiservers
backend k8s-apiservers
mode tcp
option tcp-check
tcp-check connect
tcp-check send "GET /healthz HTTP/1.0\r\n\r\n"
tcp-check expect status 200
balance roundrobin
server cp1 127.0.0.1:6443 check
server cp2 127.0.0.1:6443 check
server cp3 127.0.0.1:6443 check
# 步骤 3:kubeadm 初始化与 join(关键参数!)
kubeadm init 时,必须关闭默认 etcd 托管(因为我们自建了),并指定外部 etcd:
# kubeadm-config.yaml
kind: ClusterConfiguration
apiVersion: kubeadm.k8s.io/v1beta3
controlPlaneEndpoint: "k8s-api.internal:6443" # ← LB DNS 名
etcd:
external:
endpoints:
- https://10.10.1.11:2379
- https://10.10.1.12:2379
- https://10.10.1.13:2379
caFile: /etc/kubernetes/pki/etcd/ca.crt
certFile: /etc/kubernetes/pki/apiserver-etcd-client.crt
keyFile: /etc/kubernetes/pki/apiserver-etcd-client.key
然后 kubeadm init --config kubeadm-config.yaml,再用 kubeadm join ... --control-plane 加入其他节点。
故障转移实测:模拟 cp1 宕机
我故意 systemctl stop kubelet 在 cp1 上,观察:
- etcd 自动选出新 leader(
etcdctl endpoint status -w table显示 leader 变更) - HAProxy 在 5 秒内将 cp1 标为
DOWN(日志可见Health check for server k8s-apiservers/cp1 failed) kubectl get nodes依然秒回 —— 流量已切至 cp2/cp3 的 apiserver
总结 & 一起讨论
真高可用 ≠ 数量堆砌,而是 etcd 健康拓扑 + LB 智能路由 + apiserver 无状态设计 三者咬合。我建议你:
- etcd 必做监控:
etcd_disk_wal_fsync_duration_seconds、etcd_network_peer_round_trip_time_seconds - LB 必启主动健康检查:别信“TCP 端口通就代表 API 可用”
- 定期演练故障:每月 kill 一个 master,看自动恢复是否在 30 秒内完成
最后抛个问题:你们线上用的是 Keepalived + nginx 还是 HAProxy?有没有遇到过 LB 健康检查误判(比如 apiserver 响应慢但没挂)的情况?欢迎评论区分享你的骚操作或翻车现场 ��


