资讯动态

kubeadm部署Kubernetes 1.24实战:从containerd配置到节点接入全解析

发布时间:2026/10/14 12:56:40 来源:尧图企业网站定制
kubeadm是Kubernetes官方提供的集群引导工具从1.24版本开始由于dockershim的移除所有使用kubeadm部署的集群都必须选择containerd或CRI-O作为容器运行时。这篇文章就从我实际部署Kubernetes 1.24的经验出发完整记录kubeadm部署过程中的每一个关键步骤和踩过的坑。我在做这个部署的时候发现很多教程还停留在1.23甚至更早的版本直接用旧思路装1.24就会遇到一堆问题容器运行时不会配、kubelet起不来、节点一直NotReady。所以这篇文章我会把1.24版本特有的变化拎出来单独讲再把完整的部署流程从零走一遍最后把常见报错整理成速查表。整个教程面向的是有一定Linux基础、想自己搭一套K8s环境的开发者或运维同学。如果你之前只用过minikube或者云厂商的托管集群想搞清楚底层组件的协作关系这篇也很适合。我用的环境是Ubuntu 22.04但里面的思路和配置同样适用于CentOS 7.9等主流系统。1. 为什么用kubeadm部署以及1.24版本的关键变化1.1 kubeadm到底解决了什么问题Kubernetes集群最麻烦的部分其实是引导和初始化apiserver、etcd、scheduler、controller-manager这些组件之间要互相通信需要证书、token、kubeconfig一套完整的东西。手工部署的话哪怕熟练工程师也要半天到一天时间而且每一步都容易出错。kubeadm把这些全自动化了。它会负责生成证书、启动etcd、拉起apiserver、配置kubeconfig最后给出一份完整的节点接入方案。它不包含网络插件和存储插件那是你拿到集群之后要自己装的东西。这个边界要明确kubeadm管的是控制平面的“骨架”CNI和存储属于“血肉”。1.2 1.24版本为什么要改容器运行时1.24这个版本在K8s历史上有一个标志性事件dockershim正式被移除。之前K8s可以通过dockershim直接调用Docker来跑容器但Docker封装了一层自己的逻辑并不符合CRI标准维护成本很高。1.24之后所有CRI实现都必须直接暴露给kubelet比如containerd和CRI-O。如果你还在用Docker来跑集群容器1.24直接不认。这个变化带来的直接后果就是部署方式变了。以前你装完Docker再装kubeadm就行现在必须装containerd而且还要保证kubelet和containerd的cgroup驱动一致。cgroup驱动不一致会导致kubelet报错节点起不来这是新手最容易踩的坑后面我会专门演示怎么做。1.3 集群拓扑规划建议我这里部署的是一个小型测试集群规划了1个控制平面节点加1个工作节点。如果你是个人学习或者想跑一些轻量应用这种规模完全够用。如果是生产环境建议至少3个控制平面节点配合外部etcd那是另一个话题了。节点的操作系统我统一用了Ubuntu 22.04 LTS。这里说个经验所有节点的内核版本和系统版本尽量一致否则排查问题时可能会有莫名其妙的差异。节点角色主机名配置系统控制平面k8s-master2核4G/40GUbuntu 22.04工作节点k8s-node12核4G/40GUbuntu 22.04内存2G以上是底线再低的话etcd和kubelet都会跑得很艰难。磁盘建议留40G以上镜像一多起来空间消耗很快。2. 基础环境准备系统初始化与期待内核参数这一节的内容非常多但每一条都跟后面的成败直接相关。我按照自己实际操作时执行的顺序来写。2.1 主机名、hosts解析与时间同步第一步是改主机名。这一步很简单但是很多人会忘后面join节点的时候主机名不一致会导致kubelet报错。hostnamectl set-hostname k8s-master # 节点上执行 hostnamectl set-hostname k8s-node1然后修改每台机器的/etc/hosts把所有节点都加进去确保互相能解析到cat /etc/hosts EOF 192.168.1.10 k8s-master 192.168.1.11 k8s-node1 EOF时间同步容易被忽略。K8s的证书校验基于时间如果节点之间时间偏差太大会导致TLS握手失败。Ubuntu 22.04默认带systemd-timesyncd直接启用就行timedatectl set-ntp true timedatectl status2.2 内核模块与系统参数K8s的网络依赖iptables和ipvs还需要打开特定的内核模块。这里我直接一步到位执行cat EOF | tee /etc/modules-load.d/containerd.conf overlay br_netfilter EOF modprobe overlay modprobe br_netfilteroverlay是容器文件系统需要的br_netfilter用来让iptables正确处理网桥流量。两个模块不加载的话后面Pod网络会出各种奇怪问题。接着配置系统参数。这一步会让iptables转发请求、并且让流量能穿过网桥cat EOF | tee /etc/sysctl.d/99-kubernetes-cri.conf net.bridge.bridge-nf-call-iptables 1 net.ipv4.ip_forward 1 net.bridge.bridge-nf-call-ip6tables 1 EOF sysctl --system这里非常关键的是net.ipv4.ip_forward它必须为1否则Pod之间无法通信。每次我在新服务器上部署前都会先跑一下sysctl检查避免后面误判。2.3 部署containerd容器运行时前面讲了1.24移除了dockershim所以这里需要安装containerd。Ubuntu 22.04的官方软件源里已经有containerd但不一定是最新版本。我更习惯直接用Docker官方的源来安装因为它会把containerd一起装好版本也比较稳定。如果你不想走Docker的源也可以去containerd的GitHub release页直接下载二进制包但那样需要自己处理systemd service文件。用官方源最省事apt-get update apt-get install -y ca-certificates curl gnupg lsb-release install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg chmod ar /etc/apt/keyrings/docker.gpg echo deb [arch$(dpkg --print-architecture) signed-by/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable | tee /etc/apt/sources.list.d/docker.list /dev/null apt-get update apt-get install -y containerd.io安装完成后containerd的默认配置是有的但默认配置并不适合K8s使用。核心问题在于cgroup驱动系统默认是cgroupfs而K8s推荐用systemd。先重置配置mkdir -p /etc/containerd containerd config default | sed s/SystemdCgroup false/SystemdCgroup true/ | tee /etc/containerd/config.toml这一条命令做的事情就是生成默认配置然后把SystemdCgroup改成true。我见过太多人直接生成默认配置就开始用结果kubelet疯狂报failed to run kubelet的错。改完之后重启containerdsystemctl restart containerd systemctl enable containerd验证一下systemctl status containerd如果看到active (running)说明容器运行时已经就绪。这里还有一个实操心得如果你在中国大陆的服务器上部署拉取镜像会非常慢或者超时建议在containerd的配置里把sandbox_image改成国内镜像源。具体做法是在config.toml里找到pause镜像的地址替换成你所在的镜像加速服务提供的地址。否则kubeadm init会在拉取pause镜像这步卡很久。2.4 安装kubeadm、kubelet、kubectl这三个组件的版本必须一致至少主版本号和小版本号都要相同否则可能出现kubelet和apiserver版本不匹配的问题导致节点注册不上。我这次装的是1.24.4版本。先添加Kubernetes的apt源。有些同学直接用Google的官方源但国内网络访问不稳定我一般用国内镜像源apt-get update apt-get install -y apt-transport-https ca-certificates curl curl -fsSL https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | apt-key add - echo deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main | tee /etc/apt/sources.list.d/kubernetes.list apt-get update然后指定版本安装apt-get install -y kubelet1.24.4-00 kubeadm1.24.4-00 kubectl1.24.4-00这里说个细节很多教程把kubelet kubeadm kubectl拆开来装建议你一次装齐。否则kubectl版本和集群版本不一致虽然一般也能用但API字段可能有差异还是对齐更省心。装完以后先把kubelet服务设置成开机自启。记住一个重要的点现在kubelet还没法正常启动它需要kubeadm init之后生成的配置文件所以现在service状态是“启动失败”也没关系systemctl enable kubelet不要急着看状态kubelet在没有配置的情况下会一直重启这是正常的。至此基础环境准备完毕。如果你用的是CentOS命令上会有差异但思路完全一致无非就是把apt换成yum源地址换成对应的镜像源。3. 控制平面部署kubeadm init全流程实战3.1 init命令的参数逐项解析现在到了最关键的一步初始化控制平面节点。先说一下我用的命令然后一个一个参数解释为什么这么写kubeadm init \ --apiserver-advertise-address192.168.1.10 \ --image-repositoryregistry.aliyuncs.com/google_containers \ --kubernetes-versionv1.24.4 \ --pod-network-cidr10.244.0.0/16 \ --ignore-preflight-errorsSwap--apiserver-advertise-address指定apiserver绑定的IP。一般填控制平面节点的内网IP。如果你的机器有多块网卡必须显式指定否则kubeadm可能会选错网卡。--image-repository镜像仓库地址。默认是registry.k8s.io国内经常拉不动我改成阿里云的镜像仓库。这个参数在1.24版本里非常实用省掉了很多手动拉镜像的麻烦。--kubernetes-version指定版本。这里必须跟kubeadm的版本一致否则会报版本不匹配。--pod-network-cidrPod网段。这里选了10.244.0.0/16这个网段必须和后面要部署的CNI插件我用的Flannel的配置保持一致。如果这里填了192.168.0.0/16那后面Flannel的默认配置就能直接用如果填别的网段就要改Flannel配置。--ignore-preflight-errorsSwap跳过Swap检查。K8s要求关闭Swap但我觉得个人测试环境关掉Swap无所谓就先加了这个参数跳过检查。如果是生产环境建议还是按规范来关闭Swap。新的1.24版本还会检查CRI如果containerd没装好或者cgroup配置不对preflight会直接报错。如果遇到报错先解决报错再重启init。输入上面的命令之后整个初始化过程会经历这么几个阶段生成证书、写kubeconfig、启动etcd、启动控制平面组件、安装基础服务。这个过程通常要2到5分钟取决于镜像拉取速度。看到输出末尾类似这样的内容就代表成功了Your Kubernetes control-plane has initialized successfully!并且会给出一段用于加入节点的kubeadm join命令。这个命令很重要最好复制保存到本地后面加入节点时要用。如果丢了也没关系可以重新生成后面会讲。3.2 初始化后的kubeconfig配置与验证初始化成功后kubeadm会告诉你需要把kubeconfig放到$HOME/.kube目录下才能使用kubectl。我直接执行mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config这个文件的权限一定要设置好否则kubectl会对配置文件权限敏感。顺带说一句如果用的是root用户执行顺序稍微有点不一样但思路一样。配置完成后先跑一下最基础的健康检查kubectl get node你会发现NAME列有你的master节点但STATUS是NotReady。这个完全正常因为CNI还没装网络插件没有就绪节点自然处于NotReady状态。再查看一下Pod状态kubectl get pods -n kube-system会看到coredns的Pod处于Pending状态。这同样是因为CNI没装Pod无法分配到IP。下面去部署网络插件。3.3 部署CNI网络插件Flannel我选择Flannel因为配置简单对基础设施依赖少。但不管选哪个插件前提都是网络插件的网段必须和--pod-network-cidr一致。先下载Flannel的yaml文件wget https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml检查这个yaml中是否包含Pod网段信息。如果我在kubeadm init时指定的pod-network-cidr10.244.0.0/16和Flannel默认一致那么不需要修改直接应用kubectl apply -f kube-flannel.yml如果你的网段不一样那就要在yaml文件中找到NetConf部分把Network改成你自己的网段。等30秒到1分钟然后再看节点状态kubectl get node正常情况下STATUS就变成Ready了。如果还是NotReady先等一会镜像拉取需要时间。如果等了3分钟还不行那就要看kubelet日志了这个问题我放在后面第五节专门讲。3.4 控制平面功能验证节点Ready只是第一步我们还要验证控制平面的核心组件是否正常工作。我个人习惯按这个顺序检查kubectl get pods -n kube-system -o wide检查kube-system里的所有Pod是否都是Running或Completed状态。如果coredns全部Running说明集群内部DNS服务正常Pod之间能用域名互相访问。再检查一下etcdkubectl get pods -n kube-system -o wide | grep etcdetcd的Pod应该是Running状态。etcd是K8s的数据库它挂了整个集群就废了。另外我在部署完控制平面后还会做一个小测试在其他节点还没有加入之前先创建一下临时Pod确认调度器、kubelet都正常工作。不过这步不是必需可以等节点都就绪之后再测。4. 工作节点接入kubeadm join全流程实录4.1 Token管理找到并重新生成加入命令新节点加入集群靠的是token。kubeadm init成功后打印的那一大段信息里就包含了kubeadm join命令。如果你像我一样没保存可以在控制平面节点上重新获取kubeadm token list这个命令会列出当前的token。但需要注意token默认有效期24小时如果超过期限还要重新创建。完整join命令除了token还需要--discovery-token-ca-cert-hash这是用来验证控制平面证书的哈希值。获取方式openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2/dev/null | openssl dgst -sha256 -hex | sed s/^.* //其实还有一个更快的方式直接用一行命令重新生成完整的join命令kubeadm token create --print-join-command这条命令会生成一个新的token并直接打印出完整的kubeadm join命令复制粘贴就能用。4.2 节点执行join操作的全过程把kubeadm join命令拿到工作节点上执行。我这里模拟完整的命令kubeadm join 192.168.1.10:6443 --token xxxxxx --discovery-token-ca-cert-hash sha256:xxxxxx如果提示缺少token检查一下你复制的是不是完整的两部分参数。我经常看到同学只把token复制进来忘了带后面的hash这会导致节点加入失败。join过程主要做这么几件事连接apiserver、验证证书、把自己注册为节点、拉取必要镜像、启动kubelet。这过程一般1-2分钟。看到类似这样的提示就代表加入成功This node has joined the cluster: * Certificate signing request was sent to apiserver and a response was received. * The Kubelet was informed of the new secure connection details. Run kubectl get nodes on the control-plane to see this node join the cluster.4.3 加入后的状态确认与功能实测回到控制平面节点执行kubectl get nodes能看到两个节点并且工作节点状态逐渐变为Ready。如果工作节点一直NotReady先别急着去抓kubelet日志先确认一下节点上有没有拉取所需要的镜像。有时候就是镜像拉得慢。另外还要验证跨节点的通信能力。我通常会在控制平面节点上创建一个简单deployment然后扩展到多个副本让副本调度到工作节点上看看Pod是否能跑起来kubectl create deployment nginx --imagenginx kubectl scale deployment nginx --replicas3 kubectl get pods -o wide如果所有Pod都Running并且分布在两个节点上那说明集群的核心功能正常。这个测试很简单但很能说明问题调度器在正常工作、kubelet在正常工作、CNI网络也是通的。如果还想进一步测试跨节点通信可以进入一个Pod里ping另一个Pod的IPkubectl exec -it pod-name -- ping 另一个pod-ip这里提醒一下默认的nginx镜像里没有ping命令需要先安装iputils。不想装也没关系能启动、Running一般来说网络就没问题。5. 常见问题排查与避坑经验这里整理了我个人在这次部署中踩过的坑也包含了平时帮别人排查时遇到的高频问题。按从高到低的频率排序。5.1 kubelet起不来从日志里找答案kubelet的日志是排查一切问题的第一入口。先看日志再猜测这是铁律。journalctl -u kubelet -f如果报错类似failed to run Kubelet: failed to create kubelet: misconfiguration: kubelet cgroup driver: systemd is different from docker cgroup driver: cgroupfs这就说明cgroup驱动不一致。解决方案有两种把containerd的SystemdCgroup改成和kubelet一致或者反过来。不过推荐统一为systemd因为内存和CPU的QoS管理在systemd下更稳定。如果你没有改containerd配置可以在kubelet配置文件里改但那种做法比较绕不推荐。我自己处理这个问题的标准流程是# 1. 修改/etc/containerd/config.toml把SystemdCgroup设为true # 2. 重启containerd systemctl restart containerd # 3. 重启kubelet systemctl restart kubelet # 4. 再看日志 journalctl -u kubelet -f5.2 节点一直NotReady的几个原因节点NotReady90%和网络有关。常见原因有下面几个第一个是CNI插件没部署或者部署失败。确认一下kubectl get pods -n kube-system如果网络插件Pod启动失败拉出日志看看具体报错。我这遇到过一个情况Flannel在启动时一直报错原因是容器里缺少iptables。这是因为宿主机没有安装iptables。Ubuntu 22.04默认可能没装那就执行apt-get install -y iptables第二个是Pod网段冲突。如果你在kubeadm init时用了10.244.0.0/16但是Flannel里配置了别的网段那Pod永远拿不到正确IP节点自然不Ready。这个必须仔细复核。第三个是容器运行时的问题。kubelet虽然起来了但没办法和containerd通信检查一下containerd的服务状态systemctl status containerd还有一种诡异情况所有Pod都是Running但节点就是不Ready。遇到这种检查一下kubelet上报的条件kubectl describe node node-name在Conditions里面能看到具体是哪一项不满足比如MemoryPressure、DiskPressure等。我之前遇到一次是磁盘空间不够节点进入Pressure状态导致不可调度。5.3 重新部署kubeadm reset的正确用法如果部署过程中出了问题最干脆的办法就是推倒重来。kubeadm reset可以把kubeadm初始化生成的配置清干净。kubeadm reset -f这命令会重置但不会清除网络插件留下的残留。我还建议手动把CNI配置清掉rm -rf /etc/cni/net.d如果是重新init建议把/var/lib/kubelet也清掉rm -rf /var/lib/kubelet但注意这个目录不要随便删只有确认要完全重新部署的时候才这么操作。之后如果用的是containerd记得重启一下containerd清理掉已挂载的旧容器状态systemctl restart containerd5.4 证书过期怎么办K8s默认证书有效期是一年很多测试环境集群跑着跑着证书就过期了。kubeadm的证书续期命令很简单kubeadm certs renew all续期后需要重启相关控制平面组件最简单的方式是重启kubeletsystemctl restart kubelet另外1.24版本之后如果你装的kubeadm支持自动续期kubelet的证书会在每年到期前自动更新控制平面的证书需要手动renew。如果你只是用来学习没必要焊死在长证书上到期renew一次就行。5.5 镜像拉取超时怎么办如果你用了阿里云镜像仓库还是拉不动或者很慢可以先手工拉取一次需要的镜像。比如crictl pull registry.aliyuncs.com/google_containers/pause:3.7crictl是containerd的命令行客户端装完containerd之后会带。用crictl images能查看本机已有镜像。拉取完成后再跑一遍kubeadm init已经存在的镜像就不会重复拉取。还有一个常见问题在containerd里通过crictl看到的镜像tag可能与docker里的不一样这是正常现象不用管它。6. 集群日常维护与我的实操心得到这里集群已经能正常运转了。我再分享几点日常维护中积累的经验。第一关于节点维护。比如你要重启一个节点或者对宿主机做内核升级最好先执行kubectl drain node-name --ignore-daemonsets这个命令会把节点上的Pod安全驱逐到其他节点。处理完后再恢复调度kubectl uncordon node-name很多同学不知道这个操作直接重启节点结果Pod全部挂掉还要手动恢复非常被动。第二关于版本升级。1.24升1.25其实不算难但要注意升级前最好看官方changelog。比如1.25版本把PodSecurityPolicy移除了如果你的业务依赖PSP升级前先想好替代方案。我自己的习惯是先在测试环境走一遍升级确认没影响再上生产。第三关于Pod调度。如果你发现集群明明有两个节点但Pod总是只调度到一个节点上看看节点上有没有打了污点。K8s 1.24之后如果只有控制平面一个节点参与调度而工作节点一直NotReady也经常被误解成“只调度到master”。检查一下kubectl describe node node-name | grep -i taintMaster节点上通常有污点node-role.kubernetes.io/control-plane:NoSchedule这会让普通Pod不会调度到控制平面节点上。如果你希望单机环境也能跑Pod可以把污点去掉kubectl taint nodes --all node-role.kubernetes.io/control-plane-不过我一般建议即使去掉污点也别把重量级业务放到控制平面节点etcd和业务抢资源会影响集群稳定性。第四关于日志和监控。K8s集群不装监控等于盲人摸象。我建议至少在集群里装一个metrics-server不然kubectl top没法用kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml装完后对Pod的CPU、内存使用情况一目了然。很多新手问为什么kubectl top node没数据绝大多数情况就是因为没装metrics-server。最后再说一个我个人的习惯所有通过kubectl apply部署的yaml文件我都会找地方保留一份副本最好是放到git仓库里管理。这样如果哪天集群出问题要重建直接apply回来就行不用靠记忆重新写配置。K8s的yaml文件写起来简单但保存历史版本的价值往往被低估。这个部署方案到这里已经完整落地了。从准备节点、配置containerd、初始化控制平面、接入工作节点到最后的网络验证每一个步骤我都按自己实际操作时的顺序做了说明。希望这篇内容能帮你把1.24集群稳稳跑起来。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑