单机Kubernetes如何扩容为多Master高可用集群?kubeadm实操全流程

发布时间:2026/10/22 4:00:02
单机Kubernetes如何扩容为多Master高可用集群?kubeadm实操全流程 我最早的一批Kubernetes环境清一色是kubeadm init出来的单机集群。控制面只有一个master节点apiserver、etcd、controller-manager、scheduler全在这一台机器上跑。这种部署方式学习、跑Demo、做个人项目确实香省机器、省事出问题重启就完事。可一旦有人跟你说“这个集群要放业务了”第一个跳出来的问题就是这台master挂了怎么办标题里说的“扩容单机k8s服务到多个master节点”本质上就是把控制面从1个变成3个让所有控制面组件具备高可用。这篇文章我会按自己实际操作的路线把架构选型、前置准备、完整步骤、故障排查从头到尾过一遍。文章主要面向用kubeadm搭过单机集群、对K8s基础概念有了解但还没碰过多控制面的同学。整个过程我尽量按“为什么这么干”来讲而不是丢给你一串命令让你复制粘贴完事。1. 为什么单机K8s要扩成多Master先想清楚扩的是什么1.1 单机部署的控制面单点到底危在哪单机K8s集群里控制面四个核心角色全在一台机器上kube-apiserver所有组件和kubectl的入口。它挂了你连kubectl get nodes都敲不了。etcd整个集群的状态存储。它挂了集群的“记忆”就丢了即便apiserver还活着也读不到数据。kube-controller-manager负责维持期望状态比如Pod数量不够时补Pod。kube-scheduler负责把新Pod分到合适的节点。单机的场景下这四样东西只要有一台机器宕机或重启整个集群就变成“只读废墟”。业务Pod可能还在跑但你没法调度新Pod、没法改Deployment副本数、没法做任何写操作。对测试环境来说忍一忍也就过去了生产环境这就是不可接受的单点。很多人会觉得“哪有那么容易宕机”。但宕机不只是物理断电还有内核panic、磁盘满、云厂商宿主机维护、自动重启后kubelet没起来等等。我见过不止一次一个跑得好好的单机集群因为一次自动更新重启结果起不来了排查半天才发现是证书过期了。控制面高可用不是应急方案是让你在大半夜不用爬起来救火的基础设施。1.2 多Master之后哪些组件真正变高可用了扩到多个master之后不是所有组件都“自动”高可用要分清楚组件多Master后如何工作是否真正高可用kube-apiserver多实例独立运行前端用负载均衡分发请求是etcd多个副本组成etcd集群通过Raft选主是kube-controller-manager多实例通过leader election选出一个活跃leader是kube-scheduler多实例同样通过leader election选主是kubelet/容器运行时每个节点独立和控制面数量无关是本来就不是单点也就是说多master解决的是“控制面组件单点”的问题。apiserver从1个变成3个后客户端流量通过VIP或负载均衡进入任何一台master挂了剩下的仍然能处理请求。etcd从1个变成3个后只要多数派在线3个里至少2个就能正常选举和服务。这里要特别提醒多master不会让你的工作节点不挂。它不解决节点硬件故障也不解决业务Pod的高可用。要让业务高可用你还需要部署多副本、Pod反亲和、节点分布策略。控制面高可用是第一步不是全部。1.3 先确认你的集群是不是kubeadm可以扩的类型标题里“扩容单机k8s”有个隐含前提这个单机集群得是用kubeadm初始化的。判断方法很简单到master节点上看一眼/etc/kubernetes/manifests/目录如果里面有kube-apiserver.yaml、etcd.yaml这些静态Pod文件那就是kubeadm管理的形式可以继续。kubeadm提供了原生方式在你把新节点加入控制面时自动把etcd扩容进去这是整个流程能走通的关键。如果你用的是minikube、kind、Docker Desktop或者k3s那基本不存在“多master节点”的概念。这些工具的初衷是本地轻量环境不是生产高可用。如果你一定要高可用别想着“扩”而是用kubeadm重新搭一套三master集群再把业务迁移过去。另外还有一种情况云厂商的托管Kubernetes集群。这种集群的master面由平台托管你根本看不到控制面节点也不需要自己扩。标题说的是“单机k8s服务”所以我默认是你自己的裸机或虚拟机环境。2. 扩容架构与资源规划动手前把拓扑定下来2.1 堆叠etcd还是外部etcdKubernetes高可用控制面有两种常见拓扑堆叠etcd和外部etcd。kubeadm默认、也是单机集群最常见的模式是堆叠etcd——每个master节点本地跑一个etcd实例多个master上的etcd组成集群。扩容时kubeadm会自动把新master的etcd成员加入现有etcd集群你需要做的事最少。单机扩多master我建议你继续用堆叠etcd别中途改成外部etcd因为外部etcd意味着还要准备至少3台独立etcd服务器并且所有配置文件、证书、启动参数都要手动调整复杂度直接翻倍。外部etcd适合那种机器特别多、需要把etcd和master解耦的超大规模场景。对于一个从单机起步的小集群堆叠etcd完全够用而且后续升级路径更简单。不要在扩容时顺手把架构也重构了一次只做一件事成功率高很多。2.2 控制面入口的负载均衡怎么选多master部署里有个很容易被忽略的点apiserver有3个了客户端kubectl、worker节点的kubelet、kubeadm join工具到底连哪台答案是连一台负载均衡器由它把流量分发到三台master。负载均衡有两种选择第一种是云厂商的内网四层负载均衡。如果你在云上直接建一个内网LB监听TCP 6443后端挂三台master节点的6443端口。这是最省心的方案LB本身由云厂商保证高可用你只需要配置健康检查。第二种是自建HAProxy加Keepalived。HAProxy负责四层TCP转发Keepalived提供一个虚拟IPVIP两台LB节点做主备。VIP在LB间漂移客户端始终访问同一个IP。自建方案需要额外至少两台机器跑LB才能避免LB自己变成单点。如果只有一台空闲机器我宁愿上云LB。网上很多教程会省掉LB高可用这一步直接一台nginx或haproxy挂着结果master高可用了LB挂了照样全断。要扩就扩彻底别留一个更明显的单点。2.3 为什么最小规模是3个Master而不是2个很多第一次接触高可用的人会问两个master行不行答案是不要。etcd靠Raft协议选举需要大多数节点在场才能选出leader、才能提交写请求。3节点的容忍度是1台故障2节点时如果1台故障剩下1台达不到多数etcd直接进入只读状态。也就是说2个master和一个master一样任何一台故障集群就不可用还多了一把维护成本。生产实践里控制面节点通常是3个或5个。3个容忍1个故障5个容忍2个故障。考虑到单机扩容一般机器预算有限我建议直接扩到3个master。如果你凑不出3台机器做master那先别扩高可用需要实打实的机器资源。还有一个点节点数量最好保持奇数这是为了让节点划分时不会出现平票。比如3节点出现网络分区一边1台一边2台2台那边还能选主继续工作。如果4节点分区后两边各2台两边都凑不到多数集群就停摆。所以别贪偶数。2.4 环境规划清单与端口放行动手之前先把下面的规划表填好后面所有命令都围绕这个表执行。角色主机名IP地址说明原Master节点master1192.168.1.10已有集群控制面节点新Master节点master2192.168.1.11准备加入控制面新Master节点master3192.168.1.12准备加入控制面负载均衡VIP-192.168.1.20客户端统一访问入口所有节点的主机名必须不同而且不要用默认的localhost。IP也要用静态IP别用DHCP分配的动态地址否则节点重启后IP变了证书和kubeconfig里的地址对不上整个集群都会出问题。端口方面master节点之间需要放行端口用途6443kube-apiserver所有控制面流量入口2379etcd客户端通信2380etcd节点间通信这是新增节点能否成功扩etcd的关键10250kubelet10257kube-controller-manager10259kube-scheduler如果你有防火墙或云安全组先把这些端口配上。尤其是2380很多人漏了它导致join时etcd成员一直加不进去卡半天才发现是防火墙拦截。3. 扩容前准备这些前置条件不做后面全是坑3.1 新节点的系统与容器运行时初始化新master节点和已有的master节点在系统层面必须保持一致。我从实际踩坑中总结的清单是设置hostname并把它写进/etc/hosts包括三个master和VIP的映射。关闭swap。Kubelet对swap非常敏感不关会导致启动失败。加载br_netfilter内核模块并设置net.bridge.bridge-nf-call-iptables1、net.ipv4.ip_forward1、vm.swappiness0。安装容器运行时。如果原集群用containerd新节点也必须装containerd而且版本尽量一致。配置里重点确认SystemdCgroup true否则kubelet和容器运行时的cgroup驱动不一致Node会一直NotReady。安装kubeadm、kubelet、kubectl版本和master1完全一致。新节点kubelet版本比控制面高一两个minor versionjoin时可能出现不兼容的错误。这个过程没什么花哨的地方但每条都值得认真做。我见过有人skip了内核模块配置加上去后发现Pod无法通信也见过有人没装相同版本的kubeletjoin时报了一堆看不懂的TLS错误。3.2 检查并续期原Master的证书kubeadm从v1.26开始默认证书有效期为一年。如果你的单机集群已经跑了大半年证书剩余时间可能不够了。如果直接拿它去扩容新节点join成功之后很可能过几个月控制面就集体证书过期整个集群瘫痪。在master1上执行kubeadm certs check-expiration看输出的到期时间。如果剩余时间不足先续期再扩容kubeadm certs renew all命令执行完之后控制面静态Pod里的证书不会自动重载。稳妥的做法是逐个重启控制面容器或者把静态Pod的manifest文件移走再放回强制kubelet重建mv /etc/kubernetes/manifests/kube-apiserver.yaml /tmp/ sleep 30 mv /tmp/kube-apiserver.yaml /etc/kubernetes/manifests/对etcd、controller-manager、scheduler的manifest做同样操作。这个步骤看起来麻烦但比在扩容过程中碰到莫名的证书错误要省事得多。3.3 生成join所需的token与证书密钥在master1上生成加入控制面所需的信息kubeadm token create --print-join-command这条命令会输出类似这样的内容kubeadm join 192.168.1.20:6443 --token xxxxxx.xxxxxxxxxxxxxxxx --discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx注意join地址我会直接写VIP。如果你习惯在join命令里写原master IP后面扩完又要改kubeconfig多一道手续。不如从一开始就统一入口。控制面节点join还需要一个证书密钥用来解密控制面证书。先上传证书再拿到keykubeadm init phase upload-certs --upload-certs输出里会有一行Using certificate key: xxxxxxxxxxxxxxxxxxxx。这个key默认有效期两个小时token默认24小时。实际操作时把join命令和certificate key拼成最终命令后尽快执行不要拖到第二天。3.4 提前把控制面入口和kubeconfig切到VIP这是很多人忽视的一步。原始单机集群里kubeconfig里的server地址多半是master1的IP。例如/etc/kubernetes/admin.conf、kubelet.conf、controller-manager.conf、scheduler.conf全部指向https://192.168.1.10:6443。扩容后如果客户端还继续走master1 IP虽然也能工作但master1宕机时你的kubectl一样断。真正的意图是所有入口都走VIP让LB把流量分发到健康的master。所以扩容前我建议先把master1上所有kubeconfig的server改成VIPkubectl --kubeconfig/etc/kubernetes/admin.conf config set-cluster kubernetes --serverhttps://192.168.1.20:6443对/etc/kubernetes/kubelet.conf、controller-manager.conf、scheduler.conf重复执行。改完后重启kubelet再重启control-manager、scheduler对应的静态Pod容器。同时apiserver证书里必须有VIP地址否则任何通过VIP访问的客户端都会报TLS证书校验失败。如果原证书没有包含VIP在master1上重新生成apiserver证书kubeadm init phase certs apiserver --apiserver-cert-extra-sans192.168.1.20然后再按前面说的方式重启apiserver容器。这一步做完继续后续操作时你会发现一切顺利得多因为kubeadm join工具本身也是通过VIP来访问集群信息的。4. 完整实操从单Master到三Master的扩容全过程4.1 第一步部署负载均衡并接入第一个Master先在LB节点上装好HAProxy和Keepalived。HAProxy配置核心部分如下frontend k8s-control-plane bind *:6443 mode tcp option tcplog default_backend k8s-masters backend k8s-masters mode tcp option tcp-check balance roundrobin server master1 192.168.1.10:6443 check server master2 192.168.1.11:6443 check server master3 192.168.1.12:6443 check初始状态下master2和master3还没加入集群健康检查会失败但没关系只要master1是通的VIP就能正常转发。Keepalived负责把VIP绑定到主LB节点。配置里要设置一个相同优先级的virtual_ipaddress比如192.168.1.20。配好之后先在master1上测试VIP连通性curl -k https://192.168.1.20:6443/version能返回JSON版本信息说明LB和证书都通了。这一步成功后面所有join命令都指向VIP思路非常干净。4.2 第二步把第二个Master节点加入控制面master2节点系统初始化完成后进入持久化目录执行拼接好的完整命令。格式如下kubeadm join 192.168.1.20:6443 \ --token xxxxxx.xxxxxxxxxxxxxxxx \ --discovery-token-ca-cert-hash sha256:xxxxxxxxxxxx \ --control-plane \ --apiserver-advertise-address192.168.1.11 \ --certificate-key xxxxxxxxxxxxxxxxxxxx关键参数说明--control-plane告诉kubeadm这次不是join普通worker而是加入控制面。多了这一项kubeadm会部署控制面静态Pod、加入etcd成员、生成控制面相关证书。--apiserver-advertise-address新节点apiserver对外广播的地址一般填本机IP。--certificate-key前面upload-certs得到的证书密钥。执行后kubeadm会先通过VIP访问已有集群获取配置并验证token然后自动完成etcd成员加入。如果一切正常最后输出控制面节点已加入的提示。此时在master1上执行kubectl get nodes应该能看到master1和master2都是Ready。再看etcd podskubectl get pod -n kube-system -o wide | grep etcd正常情况下master2上已经有etcd实例并且会加入etcd集群。注意观察它的状态是不是Running、Ready。如果卡在ContainerCreating多半是镜像拉取慢或防火墙问题优先检查2380端口。4.3 第三步加入第三个Master然后验证etcdmaster3的操作和master2完全一致只需把--apiserver-advertise-address改成192.168.1.12其他token和certificate key都一样有效前提是前面三个小时内执行。如果超时回到master1重新执行一次kubeadm init phase upload-certs --upload-certs拿到新key即可。加入完成后在任意master上运行etcd成员列表验证export ETCDCTL_API3 etcdctl --endpointshttps://192.168.1.10:2379,https://192.168.1.11:2379,https://192.168.1.12:2379 \ --cacert/etc/kubernetes/pki/etcd/ca.crt \ --cert/etc/kubernetes/pki/etcd/healthcheck-client.crt \ --key/etc/kubernetes/pki/etcd/healthcheck-client.key \ member list你会看到三个成员状态分别是started。再用命令看健康状态etcdctl endpoint health \ --endpointshttps://192.168.1.10:2379,https://192.168.1.11:2379,https://192.168.1.12:2379 \ --cacert/etc/kubernetes/pki/etcd/ca.crt \ --cert/etc/kubernetes/pki/etcd/healthcheck-client.crt \ --key/etc/kubernetes/pki/etcd/healthcheck-client.key三个endpoint都返回is healthyetcd三副本就完成了。这一步是整个扩容里含金量最高的验证因为控制面高可用的核心就在etcd。4.4 第四步worker节点和kubectl都切到VIP三个master就绪后接下来把既有worker节点的kubelet连接地址切到VIP。worker节点上只需要改一个文件vi /etc/kubernetes/kubelet.conf把server: https://192.168.1.10:6443改成https://192.168.1.20:6443然后systemctl restart kubelet重启后确认worker状态仍为Ready。不要用重新join的方式去切worker那样会导致节点重建身份kubelet证书、名字都可能变化不如直接改kubeconfig干净。如果你在其它机器上有管理用的kubectl同样把kubeconfig的server地址改成VIP。改完后验证kubectl get nodes kubectl get pod -n kube-system -o wide这时能看到的控制面组件是三套分别在三个master上运行。4.5 第五步高可用验证与故障切换演练扩完了不代表真的高可用必须做一次故障切换测试。我习惯的做法是把master1直接停机或者模拟更真实的故障停掉kubelet并杀掉控制面容器。在master1上执行systemctl stop kubelet crictl stop $(crictl ps -q --name kube-apiserver) || true crictl stop $(crictl ps -q --name etcd) || true crictl stop $(crictl ps -q --name kube-controller-manager) || true crictl stop $(crictl ps -q --name kube-scheduler) || true然后回到你自己的电脑上通过VIP执行kubectl get nodes -w正常情况下apiserver请求会被LB转发到master2或master3命令依然秒回。你再创建几个Pod测试写路径kubectl create deployment nginx-test --imagenginx --replicas2 kubectl scale deployment nginx-test --replicas4只要调度正常说明控制面的写路径是通的。等测试做完恢复master1systemctl start kubelet观察master1重新Ready、etcd重新加入集群并变成follower。到这里整个扩容才算真正落地。5. 扩容中常见的坑与排查思路5.1 证书类报错扩容过程中最常遇见的错误是x509: certificate is valid for ... not ...。原因通常是apiserver证书的SANSubject Alternative Name里没有包含VIP或某个节点IP。解决方式就是前面第3.4节里说的用kubeadm init phase certs apiserver --apiserver-cert-extra-sans需要包含的IP/域名重新生成apiserver证书然后重启相关静态Pod。另一个证书类报错是join时提示certificate key不正确或超时。回到master1重新跑一次kubeadm init phase upload-certs --upload-certs拿新key再执行即可。5.2 kubelet起不来的两类原因新master节点加入后kubectl get nodes里该节点一直NotReady或者kubelet反复重启大概率是两类问题。第一类是cgroup driver不一致。查看kubelet日志journalctl -u kubelet -f如果看到类似failed to run Kubelet errfailed to create kubelet去检查containerd配置里是否设置了SystemdCgroup true没有就加上并重启containerd。第二类是系统缺少必要配置。比如swap没关、内核参数没设、/etc/hosts里没有本机hostname映射。这些都会导致kubelet启动后无法注册或注册后又被拉黑。5.3 etcd集群健康但Leader不稳定有时候etcd三副本都显示healthy但系统日志里频繁出现leader丢失选举。这种情况多半不是扩容命令的问题而是节点之间的网络或时间同步问题。先检查三个master节点时间是否一致偏差超过几百毫秒就会影响Raft选举chronyc tracking再检查master节点间到2380端口之间的网络延迟。Raft对网络抖动敏感如果中间有防火墙做限速或NAT会导致心跳超时。etcd所在的磁盘IO如果太慢fsync延迟一高也会造成选主不稳定。数据库目录尽量放在本地SSD上别放在网络存储或机械盘上。5.4 join报错速查表报错现象原因排查方向Failed to request cluster infotoken过期或错误重新生成tokenx509: certificate signed by unknown authoritydiscovery hash不对或证书被换过重新获取--discovery-token-ca-cert-hash[preflight] Some fatal errors里提示端口冲突新节点上还有旧集群残留清理/etc/kubernetes、/var/lib/kubelet、/var/lib/etcdFailed to get etcd cluster status无法连接master1的2379/2380检查防火墙和安全组failed to add etcd member新旧节点版本差异导致协议不兼容确保kubeadm和etcd版本一致kubelet: failed to run Kubeletcgroup driver不一致或swap未关闭检查containerd配置和swap状态这里特别提醒如果新节点之前用kubeadm初始化过或加入过别的集群残留文件会让preflight直接报错。处理方法是彻底清理kubeadm reset -f rm -rf /etc/kubernetes /var/lib/kubelet /var/lib/etcd /etc/cni/net.d清理完再重新join比手动删一堆未知文件更靠谱。6. 扩容后的日常维护个人经验收尾6.1 证书轮换和etcd备份要养成习惯多master集群并不是扩完就一劳永逸。我一直觉得证书轮换是K8s运维里最容易被忽略的定时炸弹。控制面证书默认一年过期每次升级、重启、加入节点都可能踩到。把kubeadm certs check-expiration写进监控脚本每月看一次别等到告警。etcd备份也要定期做。多master后etcd有三副本抗的是节点故障抗不了误删和恶意操作。万一有人手滑kubectl delete namespace或者应用里面有bug不断创建异常资源etcd快照就是最后的后悔药。快照命令在前面接近末尾部分已经贴过建议配个cron放到独立存储上别存在同一批master上。6.2 如果让我重做一次init时就指定controlPlaneEndpoint这次扩容做完我最大的体会是如果最初初始化单机集群时就顺手加上--control-plane-endpoint参数指向之后的VIP整个过程能省掉三分之一的工作。kubeadm init --control-plane-endpoint192.168.1.20:6443 --apiserver-advertise-address192.168.1.10这不会影响单机使用因为请求到VIP还是转发到本机。但以后扩容时所有kubeconfig、apiserver证书、join命令默认都走VIP不需要中途改证书SAN、不需要批量改kubeconfig。也就是说你今天哪怕只有一台机器也建议按将来要做高可用的方式初始化。给自己留条后路比事后返工划算得多。最后再分享一个小技巧每次做这类架构变更前先把etcd快照打好把变更步骤写成一个checklist每做完一步就用kubectl get nodes和etcdctl做一次验证。多master扩容本身不复杂大家遇到的坑也高度相似稳扎稳打顺序执行基本都能顺利落地。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询