1. 为什么你需要一个高可用的Kubernetes集群如果你刚开始接触Kubernetes可能觉得在单台机器上装个Minikube或者K3s玩玩就足够了。但一旦你想把K8s用到稍微正经点的开发测试环境甚至是为未来的生产环境做准备单点故障就成了一个绕不过去的大问题。想象一下你花了好几天部署好一套服务结果因为唯一的主节点服务器宕机或者需要重启整个集群就“失联”了所有服务都不可访问那种感觉绝对会让你抓狂。高可用High Availability, HA就是为了解决这个问题而生的。简单来说它通过冗余设计让集群里没有“单点”。在Kubernetes的高可用架构里核心是控制平面Control Plane的多个副本。通常我们会部署三个主节点Master Node它们之间通过选举机制协同工作。即使其中一个甚至两个主节点暂时挂掉剩下的节点依然能维持集群的调度和管理功能保证你的业务应用跑在工作节点上不受影响持续对外提供服务。这次我们要做的就是在最新的Ubuntu Server 24.04 LTS系统上搭建一个三主三从的高可用Kubernetes集群。听起来很复杂别担心我会把每一步都掰开揉碎了讲。而且针对国内开发者最头疼的“镜像拉取”问题我会全程使用阿里云等国内镜像源来替代那些访问缓慢甚至无法访问的谷歌源。从系统配置、容器运行时安装到负载均衡器设置、集群初始化和网络插件部署我都会给出详细的命令和配置确保你跟着做就能成功。我自己的测试环境也是这么搭起来的踩过的坑、验证过的方案都会分享给你。2. 动手之前环境规划与系统准备搭建集群就像盖房子图纸规划没画好后面全是坑。我们先来把“图纸”画清楚。2.1 集群拓扑与资源规划我们目标是构建一个由3个控制平面节点主节点、3个工作节点和2个负载均衡器节点组成的集群。负载均衡器采用Keepalived HAProxy的方案实现主节点API Server的高可用访问入口。这是目前非常经典和稳定的高可用方案。服务器规划请根据你的实际网络调整假设你的内网网段是172.18.0.0/16。我规划的IP和主机名如下表IP地址主机名角色最低配置建议作用172.18.88.80k8s-control-plane01控制平面节点12核4G内存运行 kube-apiserver, kube-scheduler, kube-controller-manager, etcd172.18.88.81k8s-control-plane02控制平面节点22核4G内存同上172.18.88.82k8s-control-plane03控制平面节点32核4G内存同上172.18.88.83k8s-apiserver-lb01负载均衡器节点12核2G内存运行 Keepalived HAProxy提供API Server虚拟IP172.18.88.84k8s-apiserver-lb02负载均衡器节点22核2G内存同上作为备用负载均衡器172.18.88.85k8s-worker01工作节点12核4G内存运行业务Pod172.18.88.86k8s-worker02工作节点22核4G内存运行业务Pod172.18.88.87k8s-worker03工作节点32核4G内存运行业务Pod虚拟IP (VIP):172.18.88.88这个IP不绑定在任何一台物理机上而是由Keepalived管理在lb01和lb02之间浮动。集群内所有节点包括其他主节点和工作节点都将通过这个VIP:6443端口来访问API Server这样就实现了API Server入口的高可用。注意主机名非常重要Kubernetes和很多系统组件都会用到它。请务必在安装系统时设置好或者之后通过hostnamectl命令修改并确保在/etc/hosts中有对应解析。2.2 基础系统配置所有节点以下操作需要在所有八台服务器上执行。我建议你准备好终端工具如Tabby、WindTerm可以同时向多台服务器发送相同命令能极大提升效率。1. 启用远程访问 (SSH)Ubuntu Server 24.04 最小化安装默认可能没有开启SSH服务。我们需要先安装并启动它。sudo apt update sudo apt install -y openssh-server sudo systemctl enable --now ssh安装完成后你就可以从你的本地电脑通过ssh username服务器IP的方式连接了后续操作都在远程终端进行会更方便。2. 关闭防火墙在内部实验或受信任的网络环境中为了避免网络策略的干扰我们可以直接关闭Ubuntu自带的UFW防火墙。生产环境请根据实际情况配置安全组或防火墙规则。sudo systemctl disable --now ufw3. 关闭Swap分区Kubernetes 1.8要求必须禁用Swap以确保kubelet正常工作。否则节点在资源压力下可能会被驱逐。# 临时关闭 sudo swapoff -a # 永久关闭注释掉 /etc/fstab 文件中所有包含 ‘swap’ 的行 sudo sed -i /swap/s/^/#/ /etc/fstab你可以用free -h命令验证看到Swap一行全部为0就对了。4. 配置时间同步集群节点间时间同步至关重要否则证书、日志等都会出问题。# 设置时区为上海 sudo timedatectl set-timezone Asia/Shanghai # 安装时间同步工具chrony是Ubuntu 24.04的默认选择比ntpdate更优 sudo apt install -y chrony # 启动并设置开机自启 sudo systemctl enable --now chronyd # 查看同步状态 chronyc sources -v看到带^*标记的源表示当前正在使用的同步源状态正常即可。5. 修改主机名与Hosts文件首先为每台服务器设置正确的主机名以control-plane01为例sudo hostnamectl set-hostname k8s-control-plane01 # 执行 bash 或重新登录生效然后在所有节点的/etc/hosts文件中添加所有节点的IP和主机名映射这样即使内网DNS不完善节点间也能通过主机名互相访问。sudo vim /etc/hosts添加如下内容172.18.88.80 k8s-control-plane01 172.18.88.81 k8s-control-plane02 172.18.88.82 k8s-control-plane03 172.18.88.83 k8s-apiserver-lb01 172.18.88.84 k8s-apiserver-lb02 172.18.88.85 k8s-worker01 172.18.88.86 k8s-worker02 172.18.88.87 k8s-worker036. 启用内核模块并修改sysctl参数这些是Kubernetes网络和流量转发的基础要求。# 加载 overlay 和 br_netfilter 模块 sudo modprobe overlay sudo modprobe br_netfilter # 设置sysctl参数使其持久化 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 # 应用配置 sudo sysctl --system执行sysctl net.ipv4.ip_forward检查输出为net.ipv4.ip_forward 1即表示成功。3. 安装容器运行时ContainerdKubernetes需要容器运行时来管理容器生命周期。Docker曾经是标准但现在更推荐使用更轻量、更专注的containerd。我们选择1.7.20这个稳定版本并从国内镜像站下载。3.1 下载与安装直接在服务器上使用wget或curl下载。如果下载速度慢可以先用迅雷等工具在本地下载好再上传到服务器。# 下载containerd安装包 wget https://github.com/containerd/containerd/releases/download/v1.7.20/cri-containerd-cni-1.7.20-linux-amd64.tar.gz # 解压到根目录这会自动将文件释放到 /usr/local/ 等正确位置 sudo tar -zxvf cri-containerd-cni-1.7.20-linux-amd64.tar.gz -C / # 验证安装 sudo containerd --version你应该能看到类似containerd containerd.io 1.7.20 ...的输出。3.2 关键配置替换Pause镜像源这是避免后续kubeadm init失败的关键一步默认配置里sandbox_image即Pause容器镜像指向的是k8s.gcr.io/pause:3.9国内无法拉取。生成默认配置文件sudo mkdir -p /etc/containerd containerd config default | sudo tee /etc/containerd/config.toml修改配置文件sudo vim /etc/containerd/config.toml找到sandbox_image这一行将其修改为阿里云镜像地址sandbox_image registry.aliyuncs.com/google_containers/pause:3.9同时为了更好的资源管理确保SystemdCgroup设置为true在[plugins.io.containerd.grpc.v1.cri.containerd.runtimes.runc.options]部分[plugins.io.containerd.grpc.v1.cri.containerd.runtimes.runc.options] ... SystemdCgroup true重启containerd服务sudo systemctl daemon-reload sudo systemctl enable --now containerd sudo systemctl status containerd看到状态是active (running)就说明服务启动成功了。4. 搭建负载均衡层Keepalived HAProxy高可用集群的“门面”就是负载均衡器。我们使用两台服务器做负载均衡通过Keepalived实现VIP漂移通过HAProxy将流量分发到后端的三个真实API Server。4.1 安装软件仅在lb01和lb02上执行sudo apt update sudo apt install -y keepalived haproxy4.2 配置KeepalivedKeepalived的作用是管理虚拟IPVIP。它会在lb01和lb02之间进行心跳检测将VIP绑定在优先级高的主节点上。如果主节点故障VIP会自动漂移到备节点。1. 主节点 (lb01) 配置编辑/etc/keepalived/keepalived.conf内容如下。请务必根据你的实际情况修改interface网卡名和virtual_ipaddressVIP。! Configuration File for keepalived global_defs { router_id LVS_DEVEL } vrrp_script check_apiserver { script /etc/keepalived/check_apiserver.sh interval 3 weight -2 fall 10 rise 2 } vrrp_instance VI_1 { state MASTER # 主节点为MASTER备节点为BACKUP interface ens160 # 使用 ip a 命令查看你的实际网卡名可能是ens33, eth0等 virtual_router_id 51 # 虚拟路由ID同一组Keepalived必须相同 priority 101 # 主节点优先级要高如101备节点低如100 authentication { auth_type PASS auth_pass XiaoHH # 认证密码主备节点需一致 } virtual_ipaddress { 172.18.88.88/24 # 你的虚拟IP地址 } track_script { check_apiserver } }2. 备节点 (lb02) 配置配置文件路径相同内容仅有三处不同state BACKUP、priority 100。state BACKUP priority 100其他部分与主节点完全一致。3. 健康检查脚本Keepalived需要检查本机的HAProxy服务是否正常。创建脚本/etc/keepalived/check_apiserver.sh主备节点内容相同#!/bin/bash errorExit() { echo *** $* 12 exit 1 } # 检查本地6443端口的HAProxy健康状态 curl --silent --max-time 2 --insecure https://localhost:6443/healthz -o /dev/null || errorExit Error GET https://localhost:6443/healthz给脚本添加执行权限sudo chmod x /etc/keepalived/check_apiserver.sh4.3 配置HAProxyHAProxy负责将访问VIP:6443的流量以TCP模式负载均衡到后端的三个API Server。编辑/etc/haproxy/haproxy.cfg在文件末尾添加以下配置主备节点配置完全相同#--------------------------------------------------------------------- # apiserver frontend which proxys to the control plane nodes #--------------------------------------------------------------------- frontend apiserver bind *:6443 # 监听所有IP的6443端口 mode tcp option tcplog default_backend apiserverbackend #--------------------------------------------------------------------- # round robin balancing for apiserver #--------------------------------------------------------------------- backend apiserverbackend mode tcp balance roundrobin # 使用轮询算法 option tcp-check # 将下面三个server的IP地址替换成你的三个主节点的真实IP server k8s-control-plane01 172.18.88.80:6443 check server k8s-control-plane02 172.18.88.81:6443 check server k8s-control-plane03 172.18.88.82:6443 check4.4 启动服务并验证# 启动HAProxy和Keepalived并设置开机自启 sudo systemctl enable --now haproxy sudo systemctl enable --now keepalived # 检查服务状态 sudo systemctl status haproxy keepalived --no-pager -l两个服务都应该是active (running)状态。关键验证步骤在lb01上执行ip a你应该能看到网卡上除了物理IP还绑定了虚拟IP172.18.88.88。在任意节点包括lb02上尝试curl -k https://172.18.88.88:6443/healthz。如果返回ok说明负载均衡层工作正常能够将请求转发到后端的API Server虽然它们还没启动但HAProxy的TCP检查能通过。如果返回curl: (7) Failed to connect to ... Connection refused可能是HAProxy配置的端口或后端IP不对请检查防火墙和配置。5. 安装Kubernetes核心组件现在我们开始在所有Kubernetes节点即3个主节点和3个工作节点上安装kubelet, kubeadm, kubectl。注意负载均衡器节点lb01, lb02不需要安装这些。5.1 添加Kubernetes Apt源并安装我们使用官方提供的国内友好镜像源由阿里云等维护来加速下载。# 1. 更新apt包索引并安装依赖 sudo apt-get update sudo apt-get install -y apt-transport-https ca-certificates curl gpg # 2. 下载并添加Kubernetes官方签名密钥 # 如果目录不存在则创建 sudo mkdir -p /etc/apt/keyrings curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.30/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg # 3. 添加Kubernetes apt仓库 (对应1.30版本) echo deb [signed-by/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.30/deb/ / | sudo tee /etc/apt/sources.list.d/kubernetes.list # 4. 更新并安装指定版本组件 sudo apt-get update sudo apt-get install -y kubelet1.30.* kubeadm1.30.* kubectl1.30.* sudo apt-mark hold kubelet kubeadm kubectl # 防止被意外升级apt-mark hold命令很重要它会把这三个包“锁住”避免在系统更新时自动升级导致集群版本不一致。安装完成后可以验证一下版本kubeadm version。5.2 预拉取集群镜像可选但推荐在初始化主节点之前可以先让kubeadm把需要的镜像从国内源拉取下来这样能加快初始化速度也便于排查镜像拉取问题。这个操作只需要在第一个主节点例如control-plane01上执行即可。sudo kubeadm config images pull --image-repositoryregistry.aliyuncs.com/google_containers --kubernetes-versionv1.30.4--image-repository参数指定了使用阿里云镜像仓库完美避开了谷歌镜像拉取问题。执行成功后你会看到一系列Pull complete的提示。6. 初始化高可用控制平面激动人心的时刻到了我们将创建集群的第一个主节点并生成其他节点加入集群所需的凭证。6.1 初始化第一个主节点在k8s-control-plane01上执行初始化命令。请仔细核对每个参数尤其是IP地址必须换成你自己的。sudo kubeadm init \ --apiserver-advertise-address172.18.88.80 \ --control-plane-endpoint172.18.88.88:6443 \ --upload-certs \ --image-repositoryregistry.aliyuncs.com/google_containers \ --kubernetes-versionv1.30.4 \ --service-cidr10.96.0.0/12 \ --pod-network-cidr10.244.0.0/16 \ --cri-socketunix:///run/containerd/containerd.sock参数详解--apiserver-advertise-address: 本机第一个主节点的IP地址。--control-plane-endpoint:这是关键填写我们之前设置的虚拟IP和端口。所有节点都将通过这个地址访问API Server。--upload-certs: 自动将证书上传到集群这样其他控制平面节点加入时就可以自动获取证书无需手动拷贝。--image-repository: 指定国内镜像源。--kubernetes-version: 指定版本与安装的版本一致。--service-cidr: 集群内部Service使用的网段不要与物理网络冲突。--pod-network-cidr: Pod使用的网段需要与后面安装的网络插件Calico的默认网段匹配。Calico默认是192.168.0.0/16但我们这里指定了10.244.0.0/16后面安装Calico时需要做相应修改。--cri-socket: 指定容器运行时接口的socket路径告诉kubeadm我们用的是containerd。命令执行需要一两分钟。如果一切顺利你会在最后看到如下成功信息并包含两条非常重要的kubeadm join命令Your Kubernetes control-plane has initialized successfully! To start using your cluster, you need to run the following as a regular user: mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config Alternatively, if you are the root user, you can run: export KUBECONFIG/etc/kubernetes/admin.conf You should now deploy a pod network to the cluster. Run kubectl apply -f [podnetwork].yaml with one of the options listed at: https://kubernetes.io/docs/concepts/cluster-administration/addons/ Then you can join any number of worker nodes by running the following on each as root: kubeadm join 172.18.88.88:6443 --token xxxxxx.xxxxxxxxxxxx \ --discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # 下面这条是用于加入控制平面节点的命令包含了 --control-plane 和 --certificate-key 参数 kubeadm join 172.18.88.88:6443 --token xxxxxx.xxxxxxxxxxxx \ --discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx \ --control-plane --certificate-key xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx务必把这两条命令保存下来特别是第二条带--control-plane的它有时效性默认2小时。如果丢失了可以用sudo kubeadm token create --print-join-command重新生成worker节点的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现在执行kubectl get nodes你应该能看到control-plane01节点状态是NotReady这是因为网络插件还没装。6.2 加入其他控制平面节点现在分别在k8s-control-plane02和k8s-control-plane03上执行初始化成功后输出的那条带--control-plane的kubeadm join命令。记得在命令末尾加上--cri-socketunix:///run/containerd/containerd.sock参数因为我们的环境是containerd。加入过程会持续几分钟节点会从etcd、控制平面组件拉取镜像并启动。加入成功后同样需要在每个新加入的控制平面节点上执行上面的kubectl配置命令复制admin.conf这样你就可以从任意主节点管理集群了。在所有控制平面节点加入后执行kubectl get nodes -o wide应该能看到三个主节点角色都是control-plane状态仍是NotReady。6.3 加入工作节点在工作节点 (k8s-worker01,k8s-worker02,k8s-worker03) 上执行初始化成功后输出的那条不带--control-plane的kubeadm join命令同样记得加上--cri-socket参数。加入成功后再次查看节点六个节点应该都列出来了但状态都是NotReady。7. 安装Calico网络插件节点就绪但网络不通。我们需要安装CNI容器网络接口插件。Calico是功能强大且流行的选择。由于官方镜像也在国外我们直接使用我已经修改好镜像地址的YAML文件。7.1 适配Pod网段并安装还记得我们初始化时指定的--pod-network-cidr10.244.0.0/16吗Calico默认的IP池是192.168.0.0/16我们需要在安装时修改它。这里我提供了一个预配置好的YAML文件。对于节点数少于50个的集群我们就是执行以下命令kubectl apply -f https://raw.githubusercontent.com/xiaohh-me/kubernetes-yaml/main/network/calico/calico-v3.28.1.yaml这个YAML文件我已经将里面的所有镜像地址替换为了国内可访问的镜像并将CALICO_IPV4POOL_CIDR设置为了10.244.0.0/16与你初始化集群时的配置匹配。7.2 验证网络插件安装安装命令会创建一堆Deployment和DaemonSet。稍等一两分钟然后检查Pod状态kubectl get pods -n kube-system -l k8s-appcalico-node kubectl get pods -n kube-system -l k8s-appcalico-kube-controllers当所有calico-node-xxxxxPod每个节点一个和calico-kube-controllers-xxxxxPod 的状态都变为Running时网络插件就安装成功了。此时再执行kubectl get nodes所有节点的状态应该都会变成Ready。恭喜你一个高可用的Kubernetes集群已经搭建完成8. 验证集群高可用性与部署测试应用集群建好了我们得验证它是否真的“高可用”并且能跑应用。8.1 验证高可用性验证API Server访问在任意节点包括工作节点上执行kubectl cluster-info。它应该能通过VIP172.18.88.88:6443成功获取集群信息。模拟主节点故障尝试关闭或重启control-plane01节点。在另一台主节点上频繁执行kubectl get nodes。你会观察到control-plane01的状态短暂变为NotReady后可能变为Unknown但集群的调度和管理命令如kubectl get pods依然可以正常执行因为另外两个主节点还在工作。等故障节点恢复后其状态会自动恢复为Ready。验证负载均衡器故障转移在lb01MASTER上停止Keepalived服务sudo systemctl stop keepalived。然后去lb02上执行ip a你会发现VIP172.18.88.88已经漂移到了lb02上。此时集群的所有API访问流量会自动切换到lb02整个过程对集群内部是透明的。8.2 部署一个测试应用让我们部署一个最简单的Nginx来检验集群的工作状态。# 部署一个包含2个副本的Nginx Deployment kubectl create deployment nginx-test --imageregistry.cn-hangzhou.aliyuncs.com/google_containers/nginx:1.25 --replicas2 # 创建一个NodePort类型的Service暴露服务 kubectl expose deployment nginx-test --port80 --typeNodePort查看部署情况kubectl get pods -o wide kubectl get svc nginx-test你会看到两个Nginx Pod被调度到不同的工作节点上运行并且Service获得了一个集群IP和一个随机的高位端口如30080。此时你可以通过浏览器或curl访问任意一个集群节点IP:NodePort例如http://172.18.88.85:30080都应该能看到Nginx的欢迎页面。这证明Pod网络、Service发现和负载均衡都工作正常。走到这一步你已经成功在Ubuntu 24.04上利用国内镜像源部署了一个功能完整的三主三从高可用Kubernetes集群。这个集群具备了抵抗单点故障的能力为后续部署更复杂的微服务应用打下了坚实的基础。在实际使用中你还可以根据需要安装Ingress Controller如Nginx Ingress、持久化存储插件如NFS CSI和监控系统如Prometheus Stack让集群变得更加完善和强大。