做信创环境下的Kubernetes多主高可用一定是绕不开的话题。大多数人入门的时候都用单节点或者minikube把集群跑起来觉得“能跑就行”但真到了生产环境控制面只有一台机器是绝对不够的apiserver、etcd、scheduler全部挤在一起这台机器一挂整个集群就瘫痪了虽然工作负载还在运行但你没法调度、没法扩缩容、没法发布新版本等于集群处于“看得见摸不着”的失控状态。信创环境又比普通环境更特殊操作系统、硬件、容器运行时都是国产化生态很多坑是通用教程里不会提到的。这篇文章我从一个一线运维的视角把信创Kubernetes多主高可用架构从原理到实操完整拆一遍。目标是让只会搭单节点集群的初学者照着这份指南能在一个真实的信创环境里从零搭出一套经得起故障演练、能够容忍单台控制面节点宕机的高可用集群。文章会先讲清楚架构为什么这么设计再给出一套我用过很多次的部署方案最后附上实战中的踩坑记录和故障演练方法。1. 多主高可用到底解决了什么问题1.1 单控制面节点的致命缺陷Kubernetes的控制面由四个核心组件构成kube-apiserver、etcd、kube-scheduler、kube-controller-manager。其中apiserver是所有请求的统一入口所有kubectl命令、所有组件之间的内部通信最终都要经过它etcd则是整个集群唯一的状态存储集群的所有数据、配置、调度结果都保存在里面。打个比方apiserver就是公司前台etcd就是档案室scheduler和controller-manager是具体干活的业务部门。单控制面节点意味着前台和档案室都在同一间屋子里这个屋子一着火公司虽然还有员工在办公室坐着但所有对外业务联系全断了也接不到新订单也没法调整组织架构只能干瞪眼。在单节点场景下还有一个容易被忽略的问题etcd和apiserver之间的延迟。这两个组件放同一台机器上时运维会为了省事直接用localhost通信这样配置虽然简单但也就意味着这台机器同时承担了“存储数据”和“处理请求”两个重活负载一高两个组件互相影响整个控制面性能都会塌方。1.2 信创环境下的特殊约束信创环境下的部署和普通x86加Ubuntu的标准环境相比多出来的麻烦主要在这几个方面操作系统层面国产化Linux发行版虽然大多基于开源Linux内核但systemd版本、内核安全模块、防火墙默认策略都可能有差异。比如有的系统默认开启了强制访问控制模块容器里的进程读写文件或者绑定低端口会被拦截如果你没意识到这一点部署完发现Pod一直CrashLoopBackOff排查半天都不知道是安全策略在捣乱。容器运行时层面信创环境普遍用containerd但containerd与内核、runc之间的版本兼容性在不同架构上表现并不一致。尤其是ARM架构的机器部分指令集差异会导致镜像运行异常。还有cgroup驱动的问题Kubernetes默认推荐的cgroup驱动是systemd但很多国产系统的containerd默认配置还停留在cgroupfs这两个不一致会导致kubelet报错甚至无法启动。镜像获取层面很多信创环境是内网隔离的无法直接访问公共镜像仓库。这就需要提前规划好内网镜像仓库或者在一台能联网的机器上把镜像打好包传进去。初学者往往意识不到镜像准备不充分是信创环境部署K8s时最常见的“隐形杀手”。硬件层面如果集群是ARM架构所有镜像、所有工具、所有基础组件都得选ARM版本这个看起来很简单但实际操作中经常出现“某组件x86的结果混进去了”的情况然后Pod起不来查半天才发现架构不对。2. 架构拆解多主高可用是怎么实现的2.1 控制面、数据面与接入层先梳理一下多主高可用架构的整体层次。整个集群分成三层控制面、数据面、接入层。控制面包括kube-apiserver、etcd、kube-scheduler、kube-controller-manager。在多主架构中这些组件不再是“单点”而是多个实例同时运行。apiserver本身就是无状态的多实例同时对外提供服务没有任何问题scheduler和controller-manager内部通过选主机制保证同一时刻只有一个实例真正干活其他实例处于待命状态etcd则通过Raft协议在多个节点之间复制数据保证数据一致性。数据面就是真正跑业务负载的工作节点包括kubelet、kube-proxy和容器运行时。数据面本身是分布式的每个节点上的kubelet都独立管理本节点的Pod所以数据面没有“高可用”问题节点挂了Pod可以被调度到其他节点重建。接入层是很多初学者会忽略的部分。多个apiserver同时存在kubelet和kubectl该连哪个如果固定写死某一个节点的IP那这个节点挂了所有外部请求又断了高可用就白做了。所以需要在前方加一层负载均衡通常用HAProxy配合Keepalived对外提供一个虚拟IPVIP所有请求都打到VIP上由HAProxy把流量分发到后端的多个apiserver。VIP本身通过Keepalived在多个节点间漂移即使某一台负载均衡节点挂了VIP也能自动转移到其他节点保证入口永远可用。2.2 为什么etcd必须用奇数节点etcd的高可用依赖Raft共识算法。Raft算法的核心是“多数派”原则一个Raft集群要正常工作必须有一半以上节点存活并且能够互相通信。3节点集群最多容忍1个节点故障5节点集群最多容忍2个节点故障以此类推。初学的时候经常有人问为什么必须3个节点2个不行吗答案是2个节点没法形成稳定的多数派。2个节点中如果有1个挂了剩下的1个节点只有1票达不到“超过半数”的要求集群会进入只读模式无法写入数据。3个节点中挂1个剩下的2个节点有2票超过3的一半以上集群还能正常读写。从资源角度看3个etcd节点已经能够覆盖“容忍单点故障”的需求节点再多也只是提高容错上限但会带来更多的同步开销和更高的时延。所以信创环境最常见的做法是3个控制面节点每个节点上跑一个etcd成员既保证了etcd的多数派又让apiserver和etcd同机部署省去了外部etcd集群的额外复杂度。需要注意的是etcd对磁盘IO延迟非常敏感这是信创环境最容易踩坑的地方。如果控制面节点的磁盘性能不行或者用了很慢的网络存储etcd的写入时延会明显上升反过来拖垮整个控制面的响应速度。实测中etcd所在磁盘的建议是每秒至少几百次随机写IOPS至少用固态硬盘千万别用机械盘或者性能差的网络存储。2.3 负载均衡层选型对比多主架构的接入层有很多种实现方式我整理一个对比表格方便初学者根据自己的环境选型方案优点缺点信创环境适用性HAProxy Keepalived完全开源、配置灵活、可同时承担四层转发与VIP漂移需要自己维护有一定的学习成本很适用物理机和虚拟机都可以跑云厂商负载均衡免运维、自带健康检查与公网能力依赖厂商、信创私有环境往往没有不适用信创环境大多是私有化部署硬件负载均衡设备性能强、稳定贵、交付周期长、配置依赖厂商可用但性价比低在控制面节点上用static Pod部署HAProxy节省机器资源端口冲突问题需要额外处理适用但初学者建议先独立部署对于初学者我最推荐独立部署两台轻量机器哪怕是两台虚拟机跑HAProxy Keepalived。这样架构最清晰控制面节点专心跑K8s负载均衡节点专心做转发出了故障也容易排查。等真正理解了整个链路之后再考虑把负载均衡塞进控制面节点也不迟。3. 部署方式选型kubeadm还是二进制还是自动化平台3.1 kubeadm是初学者的最优解Kubernetes集群搭建方式主要有三种kubeadm、二进制部署、自动化平台。kubeadm是官方提供的集群初始化工具它会自动完成证书签发、kubeconfig生成、控制面组件静态Pod编排、etcd集群初始化等所有复杂操作。而且kubeadm天然支持多主集群先初始化第一个控制面节点然后用一条join命令把其他控制面节点加进来kubeadm会帮你把etcd成员扩展和证书分发都搞定。这是初学者最应该选择的方案。二进制部署是把kube-apiserver、kube-scheduler等组件的二进制包下载下来自己写systemd单位文件逐个启动。这种方式的优点是透明可控但也正因为它透明每个组件是干什么的、证书怎么配、参数怎么填都必须自己搞定。配置量非常大任何一个证书或者参数写错排查起来都很痛苦初学者不要碰。自动化平台不管是商业产品还是开源工具确实省事点几个按钮集群就出来了但问题在于黑盒太多。高可用架构的核心在于“知道哪里会挂、怎么恢复”用自动化平台搭出来的集群遇到故障时你对底层机制缺乏感知反而更难排查。所以我的建议是初学者至少要用kubeadm手搓一遍理解了之后再考虑用工具提升效率。3.2 版本选型与兼容性信创环境里的版本选型需要额外小心。Kubernetes版本不是越新越好而是要和容器运行时、操作系统、内核版本匹配。我的建议是选择一个已经发布半年以上的稳定小版本比如1.28或1.29系列的最新patch版本。太新的版本可能对旧内核有兼容问题太老的版本则可能缺少一些必要的稳定性修复。安装完成后要把三个组件的版本锁死这一点尤其重要。kubeadm、kubelet、kubectl的版本必须保持一致如果软件源更新后自动升级了其中某一个组件版本漂移会导致集群无法正常工作。在实际操作中我遇到过好几次因为源更新把kubelet悄悄升级到新版本、而apiserver还是旧版本导致节点反复注册失败的情况。网络插件方面也提前规划好。calico功能全面但对内核模块要求较多flannel简单易用但功能少cilium性能好但对内核要求更高。信创环境如果内核版本比较保守建议先用flannel或calico的旧版本先把集群跑起来再谈性能优化。4. 一步一步搭起多主集群4.1 环境规划与系统初始化我以一个实际部署过的配置为例来说明。整个集群规划如下角色主机名IP地址配置要求负载均衡节点1lb01192.168.10.212C4G跑HAProxy和Keepalived负载均衡节点2lb02192.168.10.222C4G跑HAProxy和Keepalived控制面节点1master01192.168.10.114C8G跑控制面组件和etcd控制面节点2master02192.168.10.124C8G跑控制面组件和etcd控制面节点3master03192.168.10.134C8G跑控制面组件和etcd工作节点1worker01192.168.10.318C16G跑业务负载工作节点2worker02192.168.10.328C16G跑业务负载虚拟IPVIP192.168.10.100由Keepalived管理对外暴露所有节点安装某国产Linux发行版建议内核版本不低于4.19磁盘都用固态硬盘。拿到机器之后第一步不是装K8s而是做系统初始化。初始化清单如下关闭swap。kubelet对swap的支持非常有限开启swap会导致节点状态异常。执行swapoff -a后还要修改/etc/fstab把swap行注释掉否则重启之后swap又回来了。配置hosts文件。把上面表格里的所有IP和主机名对应关系写进/etc/hosts特别要注意主机名不要有下划线也不要用带点号的域名格式Kubernetes组件对主机名格式有隐性要求我之前遇到过主机名带点号导致证书生成错误。时间同步必须做。用chrony配置好内网时间同步源所有节点时间偏差控制在100毫秒以内。这个很重要Kubernetes和etcd对时钟偏移都很敏感时间不同步会导致组件之间认证失败。加载内核模块并调整内核参数。执行modprobe br_netfilter然后设置网络转发和网桥过滤参数cat EOF | sudo tee /etc/sysctl.d/k8s.conf net.bridge.bridge-nf-call-iptables 1 net.bridge.bridge-nf-call-ip6tables 1 net.ipv4.ip_forward 1 EOF sysctl --system如果你的机器防火墙默认开启需要放行Kubernetes组件的端口6443apiserver、2379和2380etcd通信、10250kubelet、10259scheduler、10257controller-manager、8443HAProxy、以及Keepalived的VRRP协议。但更稳妥的做法是直接调整安全策略把Kubernetes节点的相关协议放行不要跟它较劲。4.2 容器运行时与K8s组件安装容器运行时以containerd为例。安装完containerd后必须修改配置文件把SystemdCgroup设为truemkdir -p /etc/containerd containerd config default | sudo tee /etc/containerd/config.toml然后编辑/etc/containerd/config.toml找到SystemdCgroup相关的配置改成true。这个配置决定容器使用systemd cgroup管理器还是cgroupfsKubernetes默认采用systemd驱动两边不一致就会出问题。同时如果环境无法直接访问公共镜像仓库需要在config.toml中配置内网镜像仓库地址或者提前把镜像导出到离线包。信创环境大部分都是内网隔离这一步不做后面拉取Pod依赖的镜像时会出现长时间ImagePullBackOff。接下来安装kubeadm、kubelet、kubectl。这里用包管理工具安装版本选定的方式如下# 查看可用版本 dnf list available kubeadm kubelet kubectl # 安装指定版本 dnf install -y kubeadm-1.29.0 kubelet-1.29.0 kubectl-1.29.0安装完成后确认版本一致kubeadm version kubelet --version kubectl version --client三个版本必须一致这是硬要求。然后设置kubelet开机自启但现在先不启动等集群初始化完成再启动否则它会一直报错找apiserver。4.3 HAProxy与Keepalived配置在负载均衡节点上安装HAProxy和Keepalived。HAProxy做四层转发把来自VIP的流量分发到三个控制面节点的apiserver端口。Keepalived负责VIP的漂移两台负载均衡节点一主一备主节点挂了VIP自动切换到备节点。HAProxy配置如下重点看前端监听端口和后端服务器列表global log /dev/log local0 chroot /var/lib/haproxy maxconn 20000 user haproxy group haproxy defaults mode tcp log global option tcplog option dontlognull retries 3 timeout connect 5s timeout client 120s timeout server 120s frontend k8s-api bind *:8443 default_backend k8s-apiserver backend k8s-apiserver balance roundrobin option httpchk GET /livez http-check expect status 200 server master01 192.168.10.11:6443 check inter 3s fall 3 rise 2 server master02 192.168.10.12:6443 check inter 3s fall 3 rise 2 server master03 192.168.10.13:6443 check inter 3s fall 3 rise 2这里有个关键点HAProxy监听8443而不是6443。因为apiserver自身监听6443如果HAProxy也在同一台机器上监听6443会有端口冲突。用8443作为统一入口对外通过VIP的8443访问即可。Keepalived配置更简单一些重点是用VRRP协议管理VIPvrrp_instance k8s-vip { state MASTER interface eth0 # 改成实际网卡名 virtual_router_id 51 priority 100 # 备节点设为90 advert_int 1 authentication { auth_type PASS auth_pass k8s-ha } virtual_ipaddress { 192.168.10.100/24 } }两台负载均衡节点的配置几乎一样区别在于state和priority主节点priority高一些。配置完成后启动HAProxy和Keepalived然后在任意节点上验证VIP是否正常ping 192.168.10.100 curl -k https://192.168.10.100:8443/healthz如果curl能返回ok说明负载均衡层已经通了。4.4 初始化第一个控制面节点在master01上创建kubeadm配置文件。这是整个搭建过程中最重要的一个文件直接决定集群的控制面参数apiVersion: kubeadm.k8s.io/v1beta3 kind: ClusterConfiguration kubernetesVersion: v1.29.0 controlPlaneEndpoint: 192.168.10.100:8443 networking: podSubnet: 10.244.0.0/16 serviceSubnet: 10.96.0.0/12 apiServer: certSANs: - 192.168.10.100 - 192.168.10.11 - 192.168.10.12 - 192.168.10.13 - 127.0.0.1 etcd: local: dataDir: /var/lib/etcdcontrolPlaneEndpoint是核心必须填VIP加8443端口这样所有控制面节点join时都会通过负载均衡入口访问apiserver。certSANs里要把VIP和所有控制面节点IP都加进去否则用这些地址访问apiserver时证书校验会失败。确认配置没问题后执行初始化kubeadm init --configkubeadm-config.yaml --upload-certs注意--upload-certs参数它会把控制面证书上传到etcd加密存储后续其他控制面节点join时不用手动分发证书文件。初始化完成后会输出两条join命令一条是控制面节点用的一条是工作节点用的先保存下来。按提示配置kubectlmkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config然后安装网络插件。这里用flannel为例kubectl apply -f https://xxx/flannel.yml注意flannel的Pod网段要跟kubeadm配置里的podSubnet一致默认正好是10.244.0.0/16所以不用额外修改。等flannel Pod起来之后master01应该已经是Ready状态了。4.5 加入其余控制面节点和工作节点在master02和master03上执行控制面join命令。命令大概长这样kubeadm join 192.168.10.100:8443 --token xxx --discovery-token-ca-cert-hash sha256:xxx --control-plane --certificate-key xxx这里再次强调join的地址填的是VIP加8443整个集群所有节点接入都走负载均衡层这是多主高可用的关键。三个控制面节点都Ready之后再在工作节点上执行join命令这次不带--control-planekubeadm join 192.168.10.100:8443 --token xxx --discovery-token-ca-cert-hash sha256:xxx工作节点只需要从负载均衡入口拿配置然后启动kubelet注册自己就完成了。全部join完之后在任意节点上验证集群状态kubectl get nodes正常情况下3个控制面节点和2个工作节点都应该是Ready状态。再看一下控制面组件是否健康kubectl get pods -n kube-system所有Pod都应该是Running或Completed状态。5. 高可用验收怎么证明集群真的高可用5.1 基础验证清单集群搭建完成不等于高可用高可用是需要验证的。先把基础验证清单过一遍通过VIP访问apiserver证明负载均衡层正常curl -k https://192.168.10.100:8443/healthz kubectl --serverhttps://192.168.10.100:8443 get nodes检查etcd集群健康状态。在任意控制面节点上执行kubectl -n kube-system exec -it etcd-master01 -- etcdctl endpoint health --cluster --cacert/etc/kubernetes/pki/etcd/ca.crt --cert/etc/kubernetes/pki/etcd/server.crt --key/etc/kubernetes/pki/etcd/server.key3个etcd成员都应该是healthy。检查控制面组件的选主状态scheduler和controller-manager同一时刻只有一个leader可以看日志确认kubectl -n kube-system logs -l componentkube-scheduler --tail20日志里会周期性出现“successfully acquired lease”或者类似的选主记录确认leader选举机制在工作。5.2 故障演练实操高可用架构必须做故障演练这一步不能省。我的建议是至少做三组演练。第一组停掉一台负载均衡节点。直接关闭lb01的Keepalived服务VIP应该自动漂移到lb02上。验证方式是对VIP做持续ping只会有极短的丢包然后从kubectl访问VIP依旧正常。第二组停掉一个控制面节点。直接在master02上执行shutdown然后观察集群状态kubectl get nodesmaster02状态会变成NotReady但这不影响集群操作调度和etcd读写都正常。等master02恢复重新变回Ready之后再测试Pod能否正常调度到各个节点。第三组最关键测试“同时挂掉一台控制面节点和一台负载均衡节点”这种组合故障。理论上只要etcd还有多数派在线集群就依然可用。实测下来3控制面2负载均衡的架构能轻松应对这种组合故障。有一个非常重要的演练注意事项绝对不要在演练中模拟同时挂掉两台控制面节点因为3节点etcd只剩1台就意味着失去多数派整个集群会进入只读状态这不是高可用架构能解决的问题需要额外容灾方案。5.3 常见坑点速查表把实战中常见的问题整理成一张速查表遇到对应症状直接查症状可能原因排查思路kubeadm init卡在等待apiserver启动controlPlaneEndpoint填错或负载均衡层没通先curl VIP:8443/healthz不通就检查HAProxy和Keepalivedjoin时报certificate-key错误--certificate-key格式不对或已过期重新执行kubeadm init --upload-certs生成新key节点状态一直NotReadyflannel/calico网络插件没装好检查网络插件Pod日志、内核模块、Pod网段是否冲突Pod创建长时间Pending网络插件没就绪或节点资源不足kubectl describe pod看事件优先查flannel相关Podetcd成员数不足某控制面节点挂了或者etcd容器异常进etcd容器查看endpoint health通过VIP访问apiserver证书报错certSANs没加VIP重新生成apiserver证书或者修改配置后重新initKeepalived VIP不漂移防火墙拦截了VRRP协议放行VRRP协议通常是协议112检查优先级配置containerd启动的容器无法读写安全策略限制调整安全策略放行容器运行时或修改SELinux相关配置6. 信创环境避坑实操记录6.1 环境准备阶段的坑信创环境的第一道坑就在系统层面。我在某次实际部署中装好系统后执行modprobe br_netfilter发现内核模块不存在。原因是这个国产发行版的内核开启了一些额外安全加固选项把部分内核模块默认禁用了。解决办法是检查内核模块列表找到对应的模块文件手动加载或者换一个已经启用该模块的内核。另外一个容易被忽略的是网卡命名。国产发行版有时候会使用基于固件的网卡命名规则不同机器上网卡名可能是eno1、ens3f0、eth0混着来。如果你在Keepalived配置里写死网卡名换一台机器就要改一次非常容易漏。建议统一通过ip link命令确认每台机器的网卡名再写入配置。系统初始化阶段还有一个高频问题sysctl配置不生效。net.bridge.bridge-nf-call-iptables这个参数在一些系统上加载br_netfilter之前是不存在的必须先modprobe br_netfilter再sysctl --system顺序反了就会报“No such file or directory”。很多初学者卡在这里其实问题很简单。6.2 组件安装阶段的坑Kubernetes组件安装阶段最大的坑是软件源里的版本漂移。我遇到过一台机器kubelet被自动升级到1.30另外两台还是1.29结果节点反复注册失败。解决方案有两个一是在软件源配置里锁定版本二是安装后用dnf/yum的versionlock插件锁住三个组件的版本。containerd的配置坑在于SystemdCgroup这个参数。不同发行版的containerd默认配置模板不一样有的版本默认模板里已经带了SystemdCgrouptrue有的没带。最好的办法是用containerd config default生成一份全新配置然后再手动确认不要直接拿网上的一份配置来用版本不同字段名可能都不一样。还有一个坑是镜像仓库的TLS证书。内网镜像仓库如果用的是自签名证书containerd默认会拒绝连接。需要在config.toml里配置好证书信任或者直接把仓库地址配置成允许跳过TLS验证。这个在信创环境特别常见因为很多内网仓库都是自建的。6.3 调度与高可用的隐性坑集群搭好之后除了验证故障转移还有几个隐性坑值得提前注意。控制面组件选主异常。kube-scheduler和kube-controller-manager的选主机制依赖etcd的租约正常情况下没有问题但如果etcd响应变慢选主请求超时就会出现“频繁切换leader”的现象。表现为Pod迁移异常、控制面日志里大量leader选举消息。这个问题的根因多半在etcd磁盘IO上把etcd数据目录换到性能更好的磁盘或者调整etcd的快照策略能明显改善。还有一个我踩过的坑是kubelet的证书轮换。控制面节点的kubelet证书默认是一年有效期到期后如果kubelet没有自动轮换成功节点会进入NotReady状态而表面上所有证书看起来都没过期。排查起来很隐蔽。建议集群搭建完成后设置定期巡检证书有效期不要等到生产事故了再补救。6.4 调度高可用的补充验证高可用不是“配置完就结束”而是一个持续验证的过程。我的习惯是集群搭建完成后做一次包含以下步骤的完整联调先创建几个测试Deployment并暴露成Service然后在运行测试应用的节点上执行drain操作观察Pod是否被平滑迁移到其他节点再把管理VIP的负载均衡节点直接关机观察VIP漂移时间和服务连续性最后检查etcd备份是否完整至少确认一份周期性能用的备份数据是可恢复的。这些联调做完这个集群才算真正交付。最后分享一点个人体会信创环境的Kubernetes多主高可用难点从来不在Kubernetes本身而在环境适配。每换一套操作系统、每换一种芯片架构都可能冒出新问题。所以不要指望一份配置能在所有环境里“一键复制”而是要理解每个配置背后的原理。比如你知道了为什么etcd要奇数节点为什么VIP不放在控制面节点上为什么HAProxy端口要和apiserver错开那么换一个环境你都能自己排查问题。我在实际部署中踩坑最多的时候往往不是初次搭建而是过了一段时间后的故障处理很多问题都是环境升级、组件版本漂移、证书过期这类隐蔽因素引起的。所以如果让我给初学者一句话建议我会说多主高可用搭起来只是开始把巡检脚本、故障演练和版本锁定养成习惯才是这套架构真正稳定运行的核心。