首页 / 资讯中心 / 文章详情

kubeadm实战:从零搭建Kubernetes集群的完整指南

kubeadm实战:从零搭建Kubernetes集群的完整指南 ★ FEATURED ARTICLE
如果你已经习惯在某云厂商控制台里点几下鼠标十分钟拿到一个托管版Kubernetes集群那这篇文章可能会让你稍微回到“石器时代”——但相信我这趟石器时代走完之后你对Kubernetes的理解会完全不一样。标题写的是“kubernetes 入门实践-搭建集群”我就按最扎实的方式来做这件事用kubeadm从零搭一套真正能跑业务的集群把每条核心命令、每个关键参数、每次踩坑背后的逻辑都讲清楚而不是给你一份复制粘贴就算完事的脚本。这篇文章适合三种人刚看完Kubernetes概念、准备动手但不知道第一步做什么的小白已经在云上用过托管集群、想搞明白底层组件怎么协作的开发者以及公司有离线环境、私有化部署需求不得不自己维护集群的人。我会把环境准备、容器运行时、控制平面初始化、网络插件、节点加入、冒烟测试和排错链路全部走一遍全程基于我在实际搭建中反复试错、确认过可靠的方案。1. 为什么自己从零搭集群而不是直接开云托管1.1 托管集群与自建集群的本质差异托管集群的好处是省心控制平面由云厂商维护你只需要关注节点池和应用。但问题也出在这里——很多人在云上用了两三年Kubernetes还是说不清kube-apiserver、etcd、kubelet之间到底怎么通信Pod网段和Service网段是什么关系为什么节点加入要带一个token。因为这些东西全被托管了你根本没有机会碰到它们。自己用kubeadm搭集群最大的收获不是“我搭起来了”而是把整个控制链路亲手走了一遍。kubeadm把复杂度降到了“每条命令有明确用途”的程度它不会替你隐藏架构反而会通过init、join两个核心动作让你直观看到控制平面各组件是怎么被编排起来的。另外还有一层很现实的原因很多公司和项目组有离线环境、内网部署的需求。托管集群进不去你必须在裸机或虚拟机里手工搭建。早一点把这套流程摸熟真到需要离线部署的时候会从容很多。我建议每个做云原生相关工作的人都应该至少完完整整亲手搭一次集群哪怕你平时根本不负责运维。1.2 这套演示适合哪类人我这次用的方案是三节点标准拓扑一个控制平面节点master节点两个工作节点worker节点。控制平面节点运行kube-apiserver、etcd、kube-scheduler、kube-controller-manager工作节点只运行kubelet和容器运行时。这是Kubernetes最经典、最能反映真实生产环境形态的入门架构。当然你也可以用单节点控制平面和工作负载混跑但那种拓扑会掩盖掉很多网络和调度上的细节比如Pod跨节点通信是怎么走的、污点是怎么阻止普通Pod调度到master的。所以我建议有条件就至少准备三台机器资源不够的话用虚拟机也是可以的只要网络互通就行。这次我用的是三台Debian系虚拟机主机名规划如下node-master控制平面节点2核4GB内存node-worker1工作节点2核2GB内存node-worker2工作节点2核2GB内存内存是硬指标2GB是kubelet和系统组件能稳定运行的下限4GB给控制平面更稳妥。如果你只有两台机器可以把控制平面和其中一个工作节点合并但我不推荐把这个作为首次搭建方案排查问题时你会分不清到底是控制平面的问题还是调度的问题。2. 动手前的规划至少三台机器和一串预留网段2.1 节点角色与硬件底线在敲第一条命令之前先想清楚你的节点角色。生产环境里控制平面和工作节点通常是分开的控制平面要跑etcd、多个控制器资源需求明显更高。我这次做演示控制平面给了2核4GB工作节点2核2GB跑一般的Demo应用完全够用。操作系统建议统一为同一个发行版和版本不要混用。我选择Debian系是因为包管理简单、内核版本较新对overlay文件系统和网络模块支持友好。如果你熟悉RHEL系也没问题但务必保持三台机器的环境一致否则后面排查问题时你会被“为什么这台有报错那台没有”这种问题折磨。主机名规划好后第一时间写入各节点的/etc/hosts。这一步很多人跳过实际运行中非常影响体验# 三台节点都执行IP换成你自己的实际地址 192.168.31.101 node-master 192.168.31.102 node-worker1 192.168.31.103 node-worker2这样配置之后后续部署网络插件、生成证书、节点join时组件之间可以通过主机名互相访问避免IP变动带来的连锁故障。2.2 网络架构的顶层设计Kubernetes一上来就要规划三个网段很多新人在这一步彻底懵掉宿主机网络你的节点所在局域网比如192.168.31.0/24Service网段用于ClusterIP是一个虚拟网段不直接对应任何物理设备Pod网段用于给每个Pod分配IP同样是一个虚拟网段这三个网段不能重叠。我在演示中选择的是Pod网段10.244.0.0/16Service网段10.96.0.0/12。请注意Pod网段不是随便选的它必须和你之后部署的CNI网络插件匹配这是新手最容易栽的地方——后面我在网络插件章节会专门展开。为什么Service要用10.96.0.0/12而不是其他网段因为这是kubeadm的默认值和kube-proxy配置中默认的子网划分兼容性最好。非必要不修改除非你的内网真的和这些网段冲突否则用默认值能省掉大量配置联调成本。2.3 操作系统层的统一预处理在安装任何Kubernetes组件之前三台节点都要做同样的系统准备。这些操作看起来琐碎但每一件都直接影响集群能否正常建立。第一件事关闭交换分区。Kubernetes从设计上就不允许在swap启用的情况下运行因为调度器基于节点可用资源做调度决策如果内存可以换出到磁盘资源评估就失真了。执行以下命令并永久生效# 立即关闭 swapoff -a # 永久注释掉 /etc/fstab 中的 swap 条目 sed -i /swap/s/^/#/ /etc/fstab # 验证 free -h第二件事加载内核模块并配置网络转发。Kubernetes依赖iptables处理流量依赖overlay文件系统支撑容器镜像分层。这两项不配好后面kubelet会起不来或容器运行时异常。cat EOF | tee /etc/modules-load.d/k8s.conf overlay br_netfilter EOF modprobe overlay modprobe br_netfilter cat EOF | 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第三件事确保节点时间同步。证书验证、日志时间戳都依赖准确的时间强烈建议配置NTP同步。时间偏移过大会直接导致kubeadm init或join时报证书相关错误这类问题隐蔽且难排查最好提前杜绝。3. 容器运行时的坑containerd的SystemdCgroup与非默认配置3.1 为什么现在默认使用containerdKubernetes从1.24版本开始废弃了对Docker作为运行时socket的直接支持这不是说不能用Docker镜像了而是kubelet不再通过Docker来启动容器而是直接通过容器运行时接口CRI和containerd通信。containerd本身可以独立管理容器Docker镜像的格式它完全兼容所以现在的主流方案就是安装containerd作为底层运行时。很多新手对这一步的理解停留在“装一个containerd就行”但实际操作中至少有两个配置必须手动修改否则节点加入集群后要么无法启动Pod要么启动后反复重启。3.2 安装后必须修改的两处配置安装containerd我这里以Debian系为例包管理器路径可能有差异并生成默认配置# 安装后先生成默认配置文件 containerd config default | tee /etc/containerd/config.toml然后打开/etc/containerd/config.toml修改两个关键位置。第一个把SystemdCgroup设置为true。containerd默认的cgroup驱动是cgroupfs而kubelet默认使用systemd作为cgroup驱动。两者不一致时kubelet会报“failed to run Kubelet: cgroup driver错误。这是一条非常典型的配置项我每次帮别人排查集群问题时第一站永远是检查这里[plugins.io.containerd.grpc.v1.cri.containerd.runtimes.runc] ... [plugins.io.containerd.grpc.v1.cri.containerd.runtimes.runc.options] SystemdCgroup true第二个将pause镜像的Sandbox镜像地址改为你网络环境能拉取到的地址。containerd的默认配置里写的是registry.k8s.io/pause版本号国内网络环境直连这个地址常常超时。你需要改成可用的公共镜像仓库地址。这一步不调整后续初始化集群时每个节点拉取pause基础镜像都会卡住表现为Pod一直处于ContainerCreating状态。改完后重启并验证systemctl restart containerd systemctl enable containerd ctr version3.3 验证运行时是否就绪一个容易被忽略的细节是修改了/etc/containerd/config.toml后最好先用containerd config check检查语法和配置项确认关键值已经生效。启动后可以用crictl验证CRI接口是否正常crictl是独立的调试工具常用于手动查看容器和镜像状态crictl version crictl info如果crictl没有默认连接需要检查/etc/crictl.yaml如果存在里是否配置了endpoint。containerd的CRI默认监听unix:///var/run/containerd/containerd.sock。配置好之后crictl info能看到容器运行时的版本、cgroup驱动等信息确认无误再进入下一步。提示cgroup驱动一致性是集群能否稳定运行的基础。请确保containerd的SystemdCgrouptrue且后续kubelet配置不额外指定cgroup-driver为cgroupfs否则它们会互相冲突。4. 控制平面初始化kubeadm init的每个参数都有用4.1 kubeadm、kubelet、kubectl的版本对齐三台节点都要安装这组工具版本必须统一。Kubernetes集群对版本差异非常敏感控制平面和节点之间的版本差距不能超过一个小版本。我这次用的是一个稳定的Kubernetes v1.29系列版本你没有必要追最新版选一个已经发布几个月的稳定版本即可。安装后建议锁定版本避免apt或yum意外升级导致控制平面和节点版本不一致。Debian系可以用如下方式锁定不同发行版命令不同apt-mark hold kubeadm kubelet kubectl请你务必养成这个习惯。我见过不止一次生产事故是因为节点上一个apt upgrade -y把kubelet升级到了和apiserver不兼容的版本整个集群直接报警。4.2 init命令参数全拆解所有前置条件准备就绪后在控制平面节点node-master上执行初始化。先执行以下命令它会清理环境中可能的残留配置并把运行时要用的镜像预先拉取到本地kubeadm config images pull --image-repository你的公共镜像仓库地址你可能会问为什么不直接执行kubeadm init因为init过程中如果镜像拉取失败会直接中断你很难分辨是镜像源的问题还是配置的问题。先单独拉取镜像把源的问题提前暴露掉。这一步同时也是验证containerd配置是否正确的快捷方式如果拉取成功说明容器运行时的网络和配置都没问题。接下来正式初始化kubeadm init \ --apiserver-advertise-address192.168.31.101 \ --image-repository你的公共镜像仓库地址 \ --kubernetes-versionv1.29.0 \ --pod-network-cidr10.244.0.0/16 \ --service-cidr10.96.0.0/12逐项解释一下--apiserver-advertise-address指定kube-apiserver对外广播的地址也就是控制平面节点的IP。这一步是为了让apiserver生成证书时把该IP加入SAN同时让工作节点能通过这个IP访问API。注意必须使用节点的实际IP不能用127.0.0.1。--image-repository指定镜像仓库前缀因为我们把拉取源改为国内公共镜像仓库。--pod-network-cidrPod网段必须和后续CNI插件要求的网段一致。这里我写10.244.0.0/16对应后面要部署的flannel。--service-cidrService网段保持默认10.96.0.0/12即可。有些教程会加了--control-plane-endpoint参数这是为高可用多控制平面准备的这里不展开。单控制平面集群没有这个参数也能正常工作。如果init成功Kubernetes会输出一段信息包括控制平面已初始化、加入工作节点的完整kubeadm join命令、以及配置kubeconfig的方法。请保存好这段join命令后面要用。4.3 初始化后的kubeconfig与环境配置init完成后当前用户还不能直接用kubectl因为kubeconfig文件生成在/etc/kubernetes/admin.conf普通用户默认没有权限读取。需要把它复制到当前用户的~/.kube目录mkdir -p $HOME/.kube cp -i /etc/kubernetes/admin.conf $HOME/.kube/config chown $(id -u):$(id -g) $HOME/.kube/config然后验证控制平面组件状态kubectl get pods -n kube-system正常情况你可以看到apiserver、etcd、scheduler、controller-manager都进入Running状态但此时集群还是“残疾”的——你会发现coredns一直处于Pending状态节点status也是NotReady。原因在于缺少网络插件没有CNIPod之间无法创建网络通道。这正是下一节要解决的问题。5. 网络插件部署Pod网段与CNI必须二选一对准5.1 flannel与calico的实际差异Kubernetes本身不提供Pod间网络的实现方式它把网络交给了CNI插件。两个最主流的选择是flannel和calico我从实际使用体验的角度说一下区别。flannel的特点是简单省心使用VXLAN隧道技术对网络环境要求低普适性好。代价是网络性能有一些损耗并且只关注三层网络连通性不支持NetworkPolicy。calico使用BGP路由协议直接在各节点间路由Pod流量性能更好支持NetworkPolicy功能强大但对网络环境要求更高在某些云环境或复杂网段里需要额外配置。我这次演示选flannel原因只有一个它是初学者失败率最低的方案。大部分人的第一次集群搭建目标应该是“让它跑起来”而不是一开始就上最复杂的网络方案。等你熟悉了整个链路再换calico也不迟。5.2 部署flannel并验证flannel的部署文件是一个YAML清单里面包含DaemonSet、ServiceAccount、ConfigMap等资源。你可以从官方仓库获取部署清单文件并应用。部署前第一件事是检查清单里的Pod网段是否和init参数匹配。默认情况下flannel的配置里写的是10.244.0.0/16所以你在init时指定这个网段就是为了和它对齐。如果你的环境必须用其他Pod网段一定要同步修改flannel YAML里的ConfigMap配置。应用清单kubectl apply -f kube-flannel.yml然后观察状态kubectl get pods -n kube-flannel -o wide kubectl get nodes正常情况下几秒钟到几分钟内每个节点上的flannel Pod会变为Running节点状态会从NotReady变为Ready。如果节点一直NotReady问题大概率出在容器运行时配置或Pod网段不一致上。这个状态转换是集群能不能继续走的关键节点值得盯紧一点。5.3 换网络方案时的清理顺序很多人会搭到一半想从flannel换成calico或反过来。此时最忌讳的做法是“先apply一个再看情况把旧的删掉”这会导致每个节点上残留两个CNI配置kubelet会随机选择一个最终结果就是Pod网络时通时不通。正确的清理顺序是先删除旧插件的DaemonSet和相关资源然后检查并清理所有节点上/etc/cni/net.d/目录下的残留配置文件确认接口清理干净后再应用新的CNI插件。每更换一次网络插件建议直接从kubectl delete走到节点重建而不是在同一个节点上反复覆盖。6. 工作节点加入与集群冒烟验证6.1 生成join命令的两种方式刚才kubeadm init输出里有一串join命令里面包含一个token。如果当时没保存或token过期了可以在控制平面节点上重新生成kubeadm token create --print-join-command这会在输出一个完整的kubeadm join命令包含鉴权token和ca证书的哈希值。在每一个工作节点上执行该命令即可。如果你有多个工作节点可以在控制平面生成一次命令然后在所有工作节点上重复执行。这里有个细节值得说明kubeadm join的本质是让新节点用token向apiserver发起CSR证书签名请求然后获取自己的kubelet客户端证书。默认token有效期是24小时如果你第二天再来加入节点会发现token已过期。企业环境里可以通过配置更长的token有效期但出于安全考虑我建议还是按需生成用完即失效。6.2 节点加入后的状态排查在工作节点上执行join后回到控制平面查看节点kubectl get nodes如果节点状态是NotReady典型的排查路径是确认flannel或CNI插件是否已经覆盖到新节点。新节点加入后CNI的DaemonSet默认会在所有节点上新调度一个Pod检查这个Pod是否正常运行。如果CNI Pod也在Pending或CrashLoopBackOff查看其日志。最常见的原因是拉取pause镜像失败以及前面讲过的SystemdCgroup配置问题。如果在控制平面看不到新节点出现检查该节点上的kubelet服务和容器运行时是否正常运行systemctl status kubelet journalctl -u kubelet -f6.3 冒烟测试部署一个临时应用验证链路集群搭建完成后先别急着去跑真实业务用一个最简单的Deployment验证整条链路是更明智的做法。运行一个轻量级的Web服务比如nginxkubectl create deployment nginx-smoke --imagenginx:latest kubectl expose deployment nginx-smoke --port80 --typeNodePort kubectl get svc nginx-smoke然后拿到NodePort映射的端口从任意节点访问curl http://节点IP:映射端口能返回nginx的默认页面说明Pod调度、容器启动、Service负载均衡、节点间路由全部正常集群已经达到“可用”状态。再深入一层可以测试一下DNS功能kubectl run curl-test --imagebusybox --rm -it --restartNever -- nslookup kubernetes.default如果DNS解析能返回Service IP说明coredns工作正常集群内部服务发现机制也确认可用。7. 我踩过的坑和完整排查链路7.1 kubelet反复崩溃cgroup驱动不一致这个问题我在不同环境里遇到过至少五六次而且每次的报错信息都不太一样。有时是“Failed to start Kubelet”有时是容器运行时启动失败有时节点永远NotReady。最后追根究底几乎都是同一个根源containerd的SystemdCgroup没有设为true或者kubelet启动参数里指定了一个和containerd不一致的cgroup驱动。排查路径是这样的先看kubelet日志定位到cgroup相关关键词。然后在控制平面或节点上检查运行时配置看看实际生效的cgroup驱动是什么。这里有个实操技巧直接在节点上检查containerd日志或在crictl info中确认Runtime配置通常可以发现SystemdCgroup是否为true。如果确认不一致修改containerd配置并重启服务再重启kubelet节点状态通常一两分钟之内就会恢复Ready。7.2 节点一直NotReady容器运行时未注册另一个高频问题是kubelet起来后报类似“Failed to get node info: node node-worker1 not found”的错误。这种情况下你要检查的不是网络插件而是kubelet是否成功向apiserver注册了自己。常见的坑有kubelet配置里指定的容器运行时socket不对、节点上存在旧的kubelet缓存、证书过期。快速处理方式停止kubelet删除/var/lib/kubelet目录下可能的缓存和证书文件再重新启动kubelet让它重新走一遍注册流程。更理智的做法是在节点上执行kubeadm reset然后重新执行join。kubeadm reset是处理节点加入失败问题的“一键重置”方案可以重新来过。不少教程把reset当作兜底方案但对于刚入门的人我反而建议更早使用reset——这比在一堆残留配置文件里猜来猜去高效得多。7.3 证书与Token问题证书类问题通常发生在两个时间点集群刚搭的时候和运行一段时间后。刚搭建时遇到证书问题八成是系统时间偏差导致。排查方法与前面说的NTP同步有关。我曾经在一组节点上遇到apiserver的HTTPS证书验证失败排查了所有配置都没发现问题最后发现是其中一台工作节点的系统时间和控制平面差了十几分钟。运行一段时间后遇到的证书问题则是kubeadm管理的证书过期。Kubernetes控制平面组件的证书有效期默认是一年可以通过以下命令查看kubeadm certs check-expiration如果发现即将到期可以通过kubeadm certs renew all配合kubeadm certs renew --use-api等方式续期。续期后需要重启相关组件或节点这块属于运维范畴但作为入门实践至少要能看懂证书列表里哪些证书需要关注。7.4 一条内核参数引发的网络黑洞我最后一次分享的坑来自一个小细节只配置了net.bridge.bridge-nf-call-iptables但没开启net.ipv4.ip_forward或者反过来。Kubernetes的Pod网络依赖宿主机IP转发如果ip_forward为0Pod可以创建成功、Service也可以创建但跨节点的Pod间通信会直接“黑洞化”表现为Pod能起来但ping不通或者curl超时。排查这类问题可以从两端下手先在源节点上ping目标Pod的IP观察是否通如果通说明路由正常问题可能在Service层如果不通重点检查目标节点的iptables规则和内核转发参数。一个简单而有效的验证命令是在节点上执行sysctl net.ipv4.ip_forward只要返回值不是1就说明转发被禁用。这一类基础系统参数问题比任何组件配置问题都更隐蔽因为它不会直接报错只会让你看到“网络不正常”的模糊现象。我在搭建这套集群的过程中踩过不少类似坑坦白讲每次解决完都发现是某个小细节没有按规范处理好。Kubernetes集群搭建这件事的难点并不在于某个组件的配置有多复杂而在于它要求所有组件、所有配置、所有系统参数都同时处于正确状态。所以我的建议是严格按本篇文章的步骤走每一步的验证都确认通过后再进下一步不要想着“先搭起来后面再修复”。集群搭建的试错成本永远是事后修复比事中验证高得多。这也是为什么我把所有命令的参数含义和系统层面的注意事项都写了出来——你理解了每一步在做什么才能真正掌握这套体系而不是只获得一个“碰巧跑起来的集群”。
阅读完成 · 觉得有帮助?
咨询建站