Kubernetes单控制面扩容双master实战:从证书同步到VIP切换的踩坑记录

📅 发布时间:2026/10/16 7:05:29
Kubernetes单控制面扩容双master实战:从证书同步到VIP切换的踩坑记录
前阵子给一套运行中的Kubernetes集群做扩容目标很明确把单控制面的集群改成双master的高可用结构。整个过程比我想象中曲折不少网上的教程大多是“第一条命令在旧节点跑第二条命令在新节点跑完事”实际做起来从证书同步到etcd成员变更再到VIP切换每一步都有讲究。这篇文章把完整过程和我踩过的坑整理出来给准备做同样操作的同行一个参考。1. 为什么我的集群直到最近才下决心加第二个master1.1 单控制面节点的风险到底有多大这套集群跑了一些内部服务和自动化任务规模不大但每天都有定时调度的东西在跑。之前控制面只有一台节点一直没出过大事但有一次机房维护导致这台虚拟机关机整个集群立刻就瘫痪了Pod还在跑可是kubectl完全连不上调度器、控制器全部失效想临时扩个副本、改个配置都做不到。当时的第一反应是让运维把虚拟机重新拉起但等恢复的几分钟里业务方不断来问“为什么服务还在却感觉不对劲”。真正的原因在于Kubernetes的控制面是一整套协作组件kube-apiserver是所有人访问的唯一入口kube-scheduler负责把Pod调度到合适的节点kube-controller-manager负责维持Deployment、Service等资源的期望状态而它们的状态全部存放在etcd里。控制面节点挂掉等于这个协作体系整体失联。所以给控制面加入第二个master不只是一个“多台机器跑同一个程序”的问题而是要让apiserver、scheduler、controller-manager和etcd从“单点”变成“可相互接管”的分布式结构。对于还在用单控制面跑生产的团队我建议有条件就尽早做这件事别等故障来了再临时抱佛脚。1.2 高可用的真正含义它不是负载均衡很多人以为高可用就是把同一套控制面软件装到两台机器上请求轮流分发就完事。这里有个关键点容易被忽略控制面组件本身通过leader election机制选主同一时刻只有一个scheduler和controller-manager在真正工作apiserver倒是可以多副本同时对外服务但它必须访问同一份etcd数据。这意味着两个master真正要解决两个层面的问题一是etcd怎么从单实例变成多成员集群二是API入口怎么对外提供不中断的服务。我用的是kubeadm初始化的默认模式etcd以静态Pod方式跑在master节点上。加入第二个master之后etcd的成员数从1变成2从技术上讲两成员的etcd在故障容忍度上依然是0——因为quorum要求多数节点存活两节点坏一个就失去法定人数。所以严格来说只加一个master形成的是“控制面组件高可用etcd弱高可用”更好的目标其实是三个控制面节点。但实际场景里资源有限先把第二个master加上至少解决“控制面组件失联”这个最痛的点后续再补第三个节点也顺理成章。这个思路我在评估阶段就和团队说明白了避免有人以为双master就能做到任意一台宕机都完全无感。2. 动手前的环境准备最容易忽略的几件小事2.1 硬件、网络与端口要求加入第二个master前先把新节点的底子打好。我用的新节点规格和现有master保持一致2 vCPU、4GB内存、50GB系统盘另外单独挂了一块20GB的盘给/var/lib/etcd。很多人觉得master节点不需要太大但etcd的磁盘性能直接影响整个集群的稳定性IO延迟高会导致leader心跳超时、频繁选举这个坑后面细说。网络方面所有master节点之间需要互通以下端口端口/协议用途说明6443/TCPKubernetes API Server对外服务端口所有kubectl和组件都访问这里2379/TCPetcd客户端通信apiserver访问etcd用的端口2380/TCPetcd集群内部通信多etcd成员之间同步数据用10250/TCPkubelet APIkubelet与apiserver通信10257/TCPkube-controller-manager健康检查和指标10259/TCPkube-scheduler健康检查和指标这些端口我在防火墙和安全组里提前放开了。这里特别提醒一句有些环境默认只放6443和22端口等kubeadm join执行到一半发现etcd通信失败排查起来很被动。另外所有节点的时间必须同步。etcd对时钟偏移极其敏感我用的是chrony配置好NTP源后在每台机器上执行chronyc tracking确认时间偏差在可接受范围内。hostname和/etc/hosts也提前各写一份确保三个节点的hostname能互相解析。2.2 版本一致性与镜像预拉取Kubernetes集群的版本一致性在扩容时特别容易翻车。新节点的kubeadm、kubelet、kubectl版本必须和现有集群一致否则join时会出现版本校验失败或者加入后kubelet反复崩溃。我这里现有集群是1.28.x新节点直接用相同小版本的二进制包。检查时用kubeadm version对比两端的输出别只看kubectl get nodes显示的大版本。kubelet版本差一个小版本短期可能看着没毛病但后续升级和证书轮换时会出现各种怪问题。镜像方面kubeadm join的时候会拉取一堆控制面组件镜像比如kube-apiserver、kube-controller-manager、kube-scheduler、etcd和pause。国内网络环境拉镜像老出问题我的做法是先在一台能正常拉取的节点上用kubeadm config images list列出所有需要的镜像然后提前拉到新节点kubeadm config images list --kubernetes-version v1.28.2拿到的镜像列表自己核对一遍有镜像加速配置的提前配置好/etc/containerd/config.toml里的registry地址没有加速的就把镜像打包传到新节点再ctr -n k8s.io images import导入。这一步做扎实了后面join就能少踩一个“卡在拉镜像”的坑。2.3 证书与引导令牌的准备这是我理解上走了弯路最多的地方。网上很多教程教人直接scp把旧master的/etc/kubernetes/pki整个目录拷到新节点利用已有CA签发新证书。kubeadm 1.28之后这种做法容易出问题官方推荐的方式是先在旧master上执行kubeadm init phase upload-certs --upload-certs把控制面证书加密上传到集群中然后生成一个带certificate-key的join命令。这个key是一次性的、带时间限制的新节点join时用这个key解密证书包比自己手动拷目录安全得多也能避免“拷贝了一堆证书却少了某个关键文件”的尴尬。另外要注意kubeadm token create生成的引导令牌默认有效期是24小时证书上传key的有效期默认是2小时。从生成命令到实际执行join中间别拖太久否则会提示token过期。我一般准备好环境后在旧master上一次性生成好命令马上到新节点执行中间不干别的事。3. kubeadm join --control-plane 的完整执行过程3.1 在主节点生成更新证书与新的引导令牌在旧master上按顺序执行两条命令# 第一步上传控制面证书得到certificate-key kubeadm init phase upload-certs --upload-certs # 第二步生成新的引导令牌并打印完整的join命令 kubeadm token create --print-join-command第二条命令的输出大致是这样kubeadm join 192.168.1.10:6443 --token abcdef.0123456789abcdef \ --discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxx注意这里输出的命令默认只能加入worker节点。要加入master需要手动在命令里补上--control-plane和--certificate-key参数--certificate-key的值就是第一步命令输出的那串64位十六进制字符串。拼好的完整命令类似kubeadm join 192.168.1.10:6443 --token abcdef.0123456789abcdef \ --discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxx \ --control-plane --certificate-key yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy每次生成时注意看输出信息尤其--certificate-key是跟着upload-certs生成的如果不执行这步就手动加--control-planejoin会卡在校验证书上。执行完upload-certs后旧的certificate-key不能复用必须重新生成这是容易踩的坑。3.2 新节点的kubeadm join操作准备好之后到新master节点上把上一节拼好的完整命令粘贴执行。join过程会持续一两分钟中间会看到如下阶段提示验证CA证书信息将控制面证书解密并写入/etc/kubernetes/pki生成apiserver、controller-manager、scheduler的static Pod清单将新节点加入etcd集群配置kubelet并启动如果最后出现类似这样一段话说明控制面加入成功You can now join any number of control-plane nodes by running the following command...别忘了在新master上配置kubectlmkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config这时执行kubectl get nodes应该能看到新的master节点是Ready状态。3.3 对外的API入口怎么处理VIP还是负载均衡两个master都运行起来之后kubectl访问哪个地址是个问题。实际部署中我推荐在master节点前加一层VIP或者负载均衡让所有客户端访问同一个稳定地址。这套环境最终选型是keepalived做VIP三台master节点都部署VIP绑定在其中一台这台出问题时VIP自动漂移到另一台。keepalived配置里除了vrrp_instance基础部分还有两个容易踩的细节后面章节细讲。如果不想用keepalived也可以用负载均衡器做TCP转发到所有master的6443端口健康检查指向/healthz。注意负载均衡和后端节点之间如果跨网段要确保源地址不被转换掉否则apiserver看到的客户端IP全是LB地址审计日志会失真。没有特殊要求的话小规模集群用keepalived最省事。另外工作节点的kubelet连接apiserver的地址默认是初始化时那个master的地址。如果以后VIP才对外提供访问需要在每个worker节点的/etc/kubernetes/kubelet.conf里把server字段改成VIP否则VIP漂移后worker节点还是连旧地址。我这次操作时先做的是将VIP配好并把两个master的kubelet.conf都指向VIP这一步很关键。4. 扩容后我实际踩过的五个坑4.1 坑一证书目录不完整导致join失败第一次尝试时我没有用upload-certs图省事直接拷贝旧master的/etc/kubernetes/pki整个目录到新节点。结果join刚开始就报错日志提示证书信息不匹配或certificate key无效。排查之后发现手动拷贝会把很多本应通过安全通道分发的文件带过去而部分关键证书因为源文件权限或目录结构问题丢了。后来改用kubeadm init phase upload-certs --upload-certs配合--certificate-key才顺利通过。这里我的建议是不要复制整个pki目录只复制你确实知道用途的文件或者干脆让kubeadm自己完成证书分发。如果非要手动拷贝至少保证ca.crt、ca.key、sa.key、sa.pub、front-proxy-ca.crt、front-proxy-ca.key、etcd/ca.crt、etcd/ca.key这八个文件都在缺一个后续都可能出问题。4.2 坑二etcd数据目录残留导致成员加入失败第二次尝试时证书通过了但etcd成员加入失败。日志里反复出现类似etcd cluster has no member或者failed to reconcile with etcd的报错。原因大概率是新节点上残留了旧的etcd数据。kubeadm检测到本地有数据就会尝试以已有成员身份恢复结果数据和集群不一致加入失败。解决办法是把新节点上/var/lib/etcd目录清掉再重试systemctl stop kubelet systemctl stop containerd mv /var/lib/etcd /var/lib/etcd.bak systemctl start containerd systemctl start kubelet清掉之后重新执行join命令这次etcd成员正常加入了。注意清理不会影响集群现有数据因为新节点本来就还没有数据。4.3 坑三双控制面的apiserver地址配置不一致这是扩容完成后最隐蔽的一个问题。我用VIP作为统一的访问入口但两个master的/etc/kubernetes/kubelet.conf里server地址一开始没有同步改。结果就是VIP还绑定在旧master时一切正常手动把VIP切到新master之后所有worker节点的kubelet日志开始刷Unable to connect to the serverPod状态变成NotReady但控制面本身看起来又是正常的。排查思路是先看worker节点的kubelet日志确认它连的地址再看kubelet.conf里server字段最后把server地址改成VIP重启kubelet。这类问题在双master场景下特别容易发生因为表面看都是“地址配置正确”实际每个节点各连各的没有统一入口。建议从一开始就把所有master和worker节点的apiserver地址统一指向VIP或负载均衡地址省得后期挨个改。4.4 坑四keepalived抢占异常导致VIP漂移混乱keepalived配置好后我测了一下手动切换停掉旧master上的keepalivedVIP正常漂到新master。但重新把旧master的keepalived拉起来时VIP在两个节点之间反复抢占客户端偶尔出现连接重置。后来发现是默认的MASTER/BACKUP抢占模式下两个节点的优先级配置有重叠且没有开启nopreempt。具体做法把所有master节点的keepalived都配置成BACKUP状态配上不同的priority然后开启nopreempt这样只有当前持有VIP的主节点失效时才会触发切换解决了震荡问题。另外记得在配置里加unicast_peer指定对端IP避免组播模式下VIP切来切去引发ARP混乱。配置文件大致如下vrrp_instance VI_1 { state BACKUP interface ens3 virtual_router_id 51 priority 120 nopreempt advert_int 1 unicast_src_ip 192.168.1.11 unicast_peer { 192.168.1.12 192.168.1.13 } virtual_ipaddress { 192.168.1.100/24 dev ens3 } }4.5 坑五网络插件与VIP共存时节点失联双master加好之后运行一段时间发现一个新问题Pod之间通信偶尔超时而且旧master上的Pod访问新master的Service时有时通有时不通。抓包发现VIP地址和某些Pod网段存在路由层面的干扰。我用的网络插件在节点上创建了默认路由策略对VIP所在网段的流量处理方式比较特殊导致包被错误转发。解决路径有两条一是把VIP所在的网段从网络插件的管理范围中排除二是给VIP增加显式路由让所有节点访问VIP时走物理网卡而不是Pod虚拟网卡。我最终在每台节点上加了一条静态路由解决了ip route add 192.168.1.100/32 dev ens3想永久生效就写进/etc/network/interfaces或者用network-scripts的route文件。这个问题不是每个环境都必现但一旦出现定位要多查路由表和网络插件的日志。5. 怎么证明你的集群是真的高可用了5.1 基础验证节点、etcd成员与控制面组件扩容完成后别急着宣布成功先做一轮系统性验证。最基本的看得见的有三点kubectl get nodes显示两个master节点都是Ready状态kubectl get pods -n kube-system里control-plane相关Pod在双master上都有副本两个master上kube-apiserver、kube-controller-manager、kube-scheduler都活着etcd的成员列表也要单独确认。kubeadm部署的etcd不需要手动改member但可以临时进到etcd容器里看kubectl exec -n kube-system etcd-旧master主机名 -- etcdctl \ --cacert/etc/kubernetes/pki/etcd/ca.crt \ --cert/etc/kubernetes/pki/etcd/server.crt \ --key/etc/kubernetes/pki/etcd/server.key \ endpoint health --cluster正常会看到两个endpoint都返回healthy。如果只有一个endpoint健康说明新节点虽然join成功但etcd没有真正加入集群后面要及时处理。另外kube-system里kube-scheduler和kube-controller-manager的日志会有leader election的记录。如果想确认选主机制在工作可以分别在两个master上看日志会发现只有一个是leader。另一个是standby随时准备接管。5.2 故障演练实测停掉一台master之后验证高可用最直接的办法就是制造故障。我当时挑了一个业务低峰期把旧master的kube-apiserver容器整个停掉同时模拟网络断开的场景。实测结果符合预期VIP很快漂移到新masterkubectl get nodes大概在几秒内恢复可用。scheduler和controller-manager的leader切换时间会略长一点但整体业务感知较小。这个测试的意义不只是“看看能不能切”更是为了验证证书、etcd数据、kubeconfig这些配套环节在切换后是否真的还能正常运作。建议每个季度做一次类似的切换演练。很多集群第一次故障时才发现某些节点配置不一致、证书过期、etcd备份缺失这些隐患在日常运行中根本不会暴露。演练时把步骤记录下来作为后续运维手册的一部分。5.3 高可用集群的日常维护提醒双master搭好之后日常维护有几个点要注意证书定期续期。kubeadm部署的证书默认一年有效期可以用kubeadm certs check-expiration查看快到期时执行kubeadm certs renew all并在所有节点同步。etcd备份不能停。双master即使都正常也不代表数据不会丢。我的做法是每天凌晨通过cron对etcd做快照备份文件保存到独立存储。新节点加入后别忘了检查kubelet、kubeadm、kubectl的版本是否和现有集群一致。以后升级集群时也要记得master节点逐个升级而不是同时重启否则控制面可能会短暂不可用。三个master上的keepalived配置最好用自动化工具统一管理防止手动修改某一台导致配置漂移。我用配置管理工具维护这些文件后续改优先级、加回拨账号时不会漏节点。根据我这次扩容的经验给还在观望的同行一个建议如果你的集群已经在跑业务不管规模多小控制面单点问题早晚会找上门。加入第二个master不是特别复杂的操作但一定要按照kubeadm推荐的流程走证书分发、etcd成员变更、入口VIP配置这三件事想清楚再动手。遇到问题别慌顺着日志一步步查大概率能定位到具体环节。