Kubeadm实战:从零搭建高可用k8s集群及常见避坑指南

📅 发布时间:2026/7/11 8:00:21 👁️ 浏览次数:
Kubeadm实战:从零搭建高可用k8s集群及常见避坑指南
1. 环境准备别急着敲命令先想清楚这几件事每次准备动手搭建K8s集群你是不是也和我一样看到网上教程就迫不及待地开始复制粘贴命令我刚开始那会儿就是这么干的结果踩了一堆坑光是网络不通就折腾了两天。所以在真正执行kubeadm init之前花点时间把环境规划好绝对是磨刀不误砍柴工。首先你得想清楚你的集群要干嘛。是个人学习、开发测试还是准备上生产环境这直接决定了你的硬件配置和架构选择。如果是生产环境高可用High Availability, HA是必须考虑的这意味着你至少需要三个控制平面节点以防一个节点挂了导致整个集群“失联”。但如果是自己在家里的几台旧电脑上玩单控制节点也完全够用。我建议新手先从单控制节点多工作节点的架构开始把流程跑通理解了基本原理再挑战高可用架构。服务器规划是第一步。官方建议控制节点至少2核CPU、2GB内存工作节点至少1核CPU、1GB内存。但相信我这只是“能跑起来”的最低标准。在实际使用中尤其是要跑点像样的应用控制节点我推荐4核8G起步工作节点根据你的应用负载来定。我自己的开发集群三个节点都是4核8G跑几个微服务加中间件还算流畅。操作系统方面CentOS 7/8、Ubuntu 20.04/22.04、Rocky Linux 8 都是常见的选择我这次演示用的是 CentOS 7.9主要是大家对它比较熟悉。接下来是网络规划这是最容易出问题的地方。你得规划好三个网段物理机网段就是你服务器或虚拟机本身的IP地址段比如192.168.1.0/24。Pod 网段这是K8s集群内部每个Pod容器组获取IP的地址池。比如10.244.0.0/16。Service 网段这是K8s内部服务的虚拟IP地址段用于服务发现和负载均衡。比如10.96.0.0/12。千万记住这三个网段绝对不能重叠我见过有人把Pod网段设成了和家里路由器一样的192.168.1.0/24结果就是集群内部网络和外部网络混在一起Pod根本访问不了外网排查起来极其痛苦。一个简单的原则物理机用常见的局域网段如192.168.x.xPod和Service用不那么常见的私有地址段如10.x.x.x。为了方便后续操作我建议你像我一样先列个表格把每台机器的角色、IP、主机名、要安装的组件都写清楚。这样后面配置的时候就不会搞混了。集群角色IP地址主机名主要组件控制节点192.168.153.180k8s-masterkube-apiserver, etcd, kube-controller-manager, kube-scheduler, kubelet, kube-proxy, 容器运行时工作节点192.168.153.181k8s-node1kubelet, kube-proxy, 容器运行时 网络插件如Calico工作节点192.168.153.182k8s-node2kubelet, kube-proxy, 容器运行时 网络插件如Calico1.1 系统初始化这些坑我替你踩过了规划好了就可以开始初始化系统了。这一步的所有操作需要在所有节点控制节点和工作节点上执行。我会把容易出错的地方重点标出来。首先是配置静态IP和主机名。虚拟机用DHCP获取IP重启可能变这绝对不行。以CentOS 7为例编辑网络配置文件你的网卡名可能是ens33或eth0vim /etc/sysconfig/network-scripts/ifcfg-ens32关键是把BOOTPROTO改成static并配上IP、掩码、网关和DNS。配完后systemctl restart network重启网络。接着用hostnamectl set-hostname k8s-master这样的命令分别设置好主机名并记得在/etc/hosts文件里把所有节点的IP和主机名映射加上这样后面节点间互相访问用主机名就行不用记IP。然后是配置SSH免密登录。这不是K8s的强制要求但能极大提升效率。在控制节点上执行ssh-keygen生成密钥对然后通过ssh-copy-id k8s-master、ssh-copy-id k8s-node1把公钥分发到所有节点包括自己。这样以后从控制节点操作其他节点就不需要反复输密码了。接下来是几个必须做的内核调整。第一个是关闭Swap。K8s的设计理念认为内存不足就应该由调度器去处理比如杀掉不重要的Pod而不是让操作系统用Swap这会影响性能和稳定性。临时关闭用swapoff -a永久关闭则要注释掉/etc/fstab里swap那行然后重启。第二个是修改内核参数。创建文件/etc/sysctl.d/k8s.conf写入以下内容cat /etc/sysctl.d/k8s.conf EOF net.bridge.bridge-nf-call-ip6tables 1 net.bridge.bridge-nf-call-iptables 1 net.ipv4.ip_forward 1 EOF执行sysctl -p /etc/sysctl.d/k8s.conf生效。前两个参数是为了让iptables规则能对桥接的网络流量生效这是Calico等网络插件正常工作所必需的。第三个是开启IP转发这是容器网络通信的基础。最后是关闭防火墙和SELinux。在学习和测试环境为了避免不必要的端口拦截和权限问题通常直接关闭。生产环境则需要更精细的安全组策略。关闭命令很简单systemctl stop firewalld systemctl disable firewalld setenforce 0 sed -i s/^SELINUXenforcing$/SELINUXdisabled/ /etc/selinux/config做完这些别忘了配置时间同步。集群内节点时间不一致会导致证书错误、日志时间错乱等各种诡异问题。安装ntpdate或chrony并同步到公共NTP服务器是必须的。1.2 容器运行时与K8s组件安装选对版本很重要K8s本身不直接运行容器它需要一个容器运行时。从K8s 1.24版本开始官方不再内置对Docker的支持推荐使用containerd或CRI-O。我这里选择containerd它更轻量也是Docker的底层运行时。安装containerd前先配置好阿里云的Docker CE yum源因为containerd包在里面yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo然后安装指定版本建议与K8s版本兼容yum install -y containerd.io-1.6.6安装后需要生成默认配置并修改关键的两项containerd config default /etc/containerd/config.toml vim /etc/containerd/config.toml找到SystemdCgroup改为true让containerd使用systemd作为cgroup驱动与kubelet保持一致。找到sandbox_image将其值改为阿里云的镜像地址比如registry.aliyuncs.com/google_containers/pause:3.9避免从谷歌仓库拉取失败。接着配置镜像加速器编辑/etc/containerd/config.toml在[plugins.io.containerd.grpc.v1.cri.registry.mirrors]下添加国内镜像地址。最后启动并设置开机自启systemctl enable --now containerd。安装K8s三件套kubeadm集群引导工具、kubelet节点代理、kubectl集群管理命令行工具。同样先配置阿里云的K8s yum源cat /etc/yum.repos.d/kubernetes.repo EOF [kubernetes] nameKubernetes baseurlhttps://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/ enabled1 gpgcheck0 EOF然后安装指定版本强烈建议所有节点版本一致yum install -y kubelet-1.27.0 kubeadm-1.27.0 kubectl-1.27.0 systemctl enable kubelet # 注意先enable但先不要start等init之后再启动注意这里只enable了kubelet没有start。因为kubelet需要等kubeadm init生成相关配置文件后才能正常启动现在启动会不断报错。2. 集群初始化与高可用配置让控制平面稳如泰山环境准备好后激动人心的时刻到了——初始化集群。但如果你想要的是生产级的高可用集群那么直接kubeadm init是远远不够的。高可用核心在于消除单点故障对于K8s控制平面来说就是让kube-apiserver、etcd、controller-manager和scheduler这些核心组件在多台机器上都有备份。2.1 使用Kubeadm Init读懂配置文件是关键很多教程让你直接运行kubeadm init但我强烈建议你使用配置文件。这能让你的操作可重复、可审计也方便后续调整。先导出一份默认配置看看kubeadm config print init-defaults kubeadm-config.yaml打开这个kubeadm-config.yaml文件你会看到很多配置项。我们需要修改几个关键地方。下面是我修改后的配置文件核心部分已省略一些默认值apiVersion: kubeadm.k8s.io/v1beta3 kind: InitConfiguration localAPIEndpoint: advertiseAddress: 192.168.153.180 # 当前控制节点的IP bindPort: 6443 nodeRegistration: criSocket: unix:///var/run/containerd/containerd.sock # 容器运行时socket路径 imagePullPolicy: IfNotPresent name: k8s-master # 当前节点主机名 --- apiVersion: kubeadm.k8s.io/v1beta3 kind: ClusterConfiguration kubernetesVersion: v1.27.0 # 指定版本避免用最新版遇到兼容性问题 controlPlaneEndpoint: k8s-lb:6443 # 【高可用关键】负载均衡器的地址和端口 imageRepository: registry.cn-hangzhou.aliyuncs.com/google_containers # 使用国内镜像仓库 networking: podSubnet: 10.244.0.0/16 # 必须与后续安装的网络插件如Calico的默认网段匹配 serviceSubnet: 10.96.0.0/12 --- apiVersion: kubelet.config.k8s.io/v1beta1 kind: KubeletConfiguration cgroupDriver: systemd # 必须与containerd配置的cgroup驱动一致这里最重要的改动是controlPlaneEndpoint。对于高可用集群你不能让其他节点直接连到某一个具体的控制节点IP192.168.153.180:6443因为那个节点挂了就全完了。你需要一个负载均衡器比如HAProxy Keepalived或者云厂商的LB提供一个虚拟IPVIP所有节点都通过这个VIP来访问API Server。k8s-lb就是这个VIP对应的域名需要在hosts文件或DNS中解析。准备好配置文件后在第一个控制节点上执行初始化kubeadm init --configkubeadm-config.yaml --upload-certs--upload-certs参数非常有用它会自动将生成的证书加密上传这样后续其他控制节点加入时就可以自动获取这些证书而不用手动拷贝大大简化了高可用集群的搭建。初始化成功后你会看到几行非常重要的输出如何用kubectl连接集群的命令mkdir -p $HOME/.kube和cp /etc/kubernetes/admin.conf那两行。如何让工作节点加入集群的kubeadm join命令。如何让其他控制节点加入集群的kubeadm join命令带--control-plane和--certificate-key参数。立刻执行第一条配置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你应该能看到第一个控制节点但状态是NotReady这是因为还没装网络插件。2.2 部署高可用控制平面让集群“心脏”多跳几下现在我们有了第一个控制节点。接下来要让第二、第三个控制节点加入形成高可用。在第二个控制节点上执行初始化成功后输出的那个特别的kubeadm join命令它包含了--control-plane和从第一个节点上传的证书密钥kubeadm join k8s-lb:6443 --token token \ --discovery-token-ca-cert-hash sha256:hash \ --control-plane \ --certificate-key certificate-key加入成功后这个节点上也会自动生成/etc/kubernetes/admin.conf文件。你可以用同样的方式配置kubectl。第三个控制节点的操作完全相同。避坑指南这里最容易出错的是controlPlaneEndpoint的连通性。在执行join命令前务必确保所有节点都能通过k8s-lb这个域名或VIP访问到6443端口。你可以用telnet k8s-lb 6443或curl -k https://k8s-lb:6443来测试。如果不通检查你的负载均衡器HAProxy/Keepalived配置或者/etc/hosts文件是否正确。三个控制节点都加入后执行kubectl get nodes应该能看到三个控制节点角色都是control-plane。这时候即使其中一个控制节点宕机API Server的服务也不会中断因为负载均衡器会把流量导向剩余的健康节点。2.3 工作节点加入壮大你的计算军团控制平面高可用搭建完毕后加入工作节点就简单多了。在第一个控制节点上如果你忘记了之前的join token可以重新生成kubeadm token create --print-join-command这个命令会输出一个标准的kubeadm join命令。在每一个工作节点上直接运行这个命令即可kubeadm join k8s-lb:6443 --token token --discovery-token-ca-cert-hash sha256:hash加入成功后回到控制节点用kubectl get nodes查看所有节点应该都列出来了但状态依然是NotReady。别急我们还差最后一步——安装网络插件。3. 网络插件与核心组件打通集群的“任督二脉”节点都齐了但彼此还不认识因为缺少网络插件。K8s的网络模型要求每个Pod都有一个唯一的集群内IP并且所有Pod之间可以直接通信无论它们在哪台主机上。这个功能需要由CNI容器网络接口插件来实现Calico、Flannel、Cilium 都是流行的选择。我以最常用的Calico为例。3.1 安装Calico解决节点NotReady的终极法宝在控制节点上直接使用Calico官方提供的最新YAML文件部署这里以Calico v3.26.1为例curl https://raw.githubusercontent.com/projectcalico/calico/v3.26.1/manifests/calico.yaml -O kubectl apply -f calico.yaml这个yaml文件会创建一系列的资源Namespace、DaemonSet、Deployment等。稍等一两分钟执行kubectl get pods -n calico-system看到所有Pod都是Running状态就说明Calico安装成功了。此时再执行kubectl get nodes你会惊喜地发现所有节点的状态都变成了Ready恭喜你一个基础的K8s集群已经搭建完成。常见避坑如果某个节点长时间处于NotReady首先在该节点上执行journalctl -u kubelet -f查看kubelet日志。常见原因有1) 网络插件Pod没起来检查calico-nodePod2) 节点内存或CPU不足3) 防火墙或安全组阻止了节点间的通信Calico默认使用BGP端口179和IP-in-IP协议协议号4。3.2 服务发现与DNS让Pod之间“喊得应”集群网络通了但服务发现还是个问题。总不能每次访问另一个Pod都记它的IP吧这就需要CoreDNS。幸运的是如果你是用kubeadm安装的CoreDNS已经作为系统组件自动部署在kube-system命名空间下了。执行kubectl get pods -n kube-system -l k8s-appkube-dns就能看到它。我们来测试一下DNS是否正常工作。运行一个临时的BusyBox Podkubectl run busybox-test --imagebusybox:1.28 --restartNever --rm -it -- sh在BusyBox的shell里执行nslookup kubernetes.default。你应该能看到类似下面的输出解析出了K8s默认Service的集群IP10.96.0.1并且DNS服务器地址是10.96.0.10这就是CoreDNS的Service IP。Server: 10.96.0.10 Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local Name: kubernetes.default Address 1: 10.96.0.1 kubernetes.default.svc.cluster.local这证明集群内部的服务发现机制Service - DNS工作正常。这是微服务在K8s内相互通信的基础。3.3 配置镜像与存储为应用运行做好准备集群是跑起来了但要运行你自己的应用还得解决两个问题镜像拉取和数据持久化。对于镜像由于国内网络原因从Docker Hub拉取镜像很慢甚至失败。我们可以在containerd或docker的配置中为docker.io等仓库配置镜像加速器。之前安装containerd时我们已经配过了。你也可以在拉取镜像时手动替换为国内镜像例如docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/nginx:alpine。对于存储K8s提供了PersistentVolume (PV)和PersistentVolumeClaim (PVC)的抽象。在开发测试环境最简单的就是使用hostPath卷它直接将节点上的目录挂载给Pod。但生产环境需要考虑网络存储如NFS、Ceph、云盘等以实现数据的高可用和迁移性。你可以部署一个NFS服务器然后使用nfs-client-provisioner这样的工具来动态提供PV。4. 日常维护与故障排查从“能用”到“好用”集群搭建成功只是第一步让它稳定运行才是真正的挑战。下面分享几个我经常遇到的场景和排查思路。4.1 节点NotReady问题深度排查节点NotReady是最常见的问题。别慌按照以下步骤排查基本能解决90%的情况检查kubelet状态在出问题的节点上执行systemctl status kubelet。如果没运行就systemctl start kubelet并journalctl -u kubelet -f看日志。检查容器运行时执行crictl ps或ctr namespace list看容器运行时服务是否正常。如果containerd挂了重启它systemctl restart containerd。检查网络插件执行kubectl get pods -n calico-system -o wide看Calico的Pod是否都运行在该节点上并且状态是Running。如果calico-nodePod是CrashLoopBackOff用kubectl logs -n calico-system pod-name查看具体错误。常见原因是内核模块缺失需要modprobe ip_tables等或IP转发没开。检查节点资源执行kubectl describe node node-name在输出底部看是否有MemoryPressure或DiskPressure事件。内存或磁盘满了kubelet会驱逐Pod并标记节点为异常。检查网络连通性在故障节点上ping其他节点的IP以及CoreDNS的Service IP (10.96.0.10)。如果不通检查防火墙、安全组和网络插件的路由表。4.2 证书管理与续期别让集群一年后“猝死”用kubeadm安装的集群默认证书有效期是一年。这意味着如果一年后你不做任何操作集群API将无法访问。这不是危言耸听我就在线上环境遇到过。预防措施在初始化集群时可以通过修改kubeadm-config.yaml中的certificateValidityPeriod字段来延长证书有效期例如设为87600h即10年。但如果你已经初始化完了也有办法补救。手动续期在证书过期前# 续期所有证书 kubeadm certs renew all # 重启相关组件 systemctl restart kubelet # 对于控制平面节点还需要重启静态Pod # 可以简单通过删除Pod让其自动重建由kubelet控制 kubectl get pods -n kube-system | grep -E apiserver|controller-manager|scheduler | awk {print $1} | xargs -I {} kubectl delete pod -n kube-system {}过期后抢救如果证书已经过期上述命令可能失效。这时需要手动从其他健康控制节点拷贝证书文件/etc/kubernetes/pki/下的.crt和.key文件过来替换或者使用一些社区维护的证书更新脚本。最彻底但影响最大的办法是备份数据然后用kubeadm reset和kubeadm init重新搭建。4.3 集群备份与恢复给你的集群上“保险”生产环境备份是生命线。对于K8s集群你需要备份两大类东西资源配置即所有YAML文件。可以用kubectl get --all-namespaces -o yaml all-resources.yaml导出但更推荐用Velero这样的专业工具它支持定时备份、增量备份和跨云迁移。持久化数据即PV里的实际数据。这需要结合你使用的存储方案如对云硬盘做快照对自建Ceph做备份。etcd备份这是集群状态的核心。在任一控制节点上你可以手动备份etcd# 设置环境变量 export ETCDCTL_API3 # 执行备份 etcdctl --endpointshttps://127.0.0.1:2379 \ --cacert/etc/kubernetes/pki/etcd/ca.crt \ --cert/etc/kubernetes/pki/etcd/server.crt \ --key/etc/kubernetes/pki/etcd/server.key \ snapshot save /opt/etcd-backup-$(date %Y%m%d).db恢复时则需要先停止kube-apiserver等组件用etcdctl snapshot restore命令恢复数据再重启组件。搭建和维护一个高可用的K8s集群就像养一个孩子初期需要精心照料熟悉它的脾气。过程中遇到的每一个报错都是你深入理解其内部原理的好机会。别怕麻烦多动手实践遇到问题先看日志善用kubectl describe和kubectl logs这两个神器。当你能够从容应对节点故障、网络波动和证书过期时你就真正驾驭了这套强大的容器编排系统。