ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

kubeadm部署Kubernetes集群实战:从初始化到节点接入全流程

kubeadm部署Kubernetes集群实战:从初始化到节点接入全流程 kubeadm 是 Kubernetes 官方提供的集群初始化工具名字里就有画面感kube-admadm 是 administrator 的缩写意思是让管理员用命令把一个多节点集群从零拉起来。相比二进制方式逐个部署 kube-apiserver、kube-controller-manager、kube-scheduler、etcdkubeadm 把所有最繁琐的环节都自动化了控制平面组件的静态 Pod 清单、证书签发、kubeconfig 生成、bootstrap token 分发。这篇文章我以 v1.26.0 作为示例版本从环境规划开始带你一步步把一套1 个控制平面节点 2 个工作节点的集群搭起来再把实际部署中踩过的坑和排查思路一并说透。适合刚接触 Kubernetes、想在自己机器上复现一套可用集群的读者也适合已经用其他方式装过集群、想对比 kubeadm 思路的人。1. 部署前的整体规划先把选型和版本搞清楚动手敲命令之前我强烈建议你先花十分钟把用什么工具、装什么版本、分几台机器这三个问题想清楚。很多人部署翻车不是命令敲错了而是环境前提没想明白装到一半才发现版本不匹配或者网络插件选型冲突。1.1 为什么选 kubeadm官方出品的“半自动”方案先把 kubeadm 和其他方式做个对比。二进制部署是最原始的方式你需要自己下载各个组件的二进制包自己写 systemd unit 文件自己生成证书自己拼 kubeconfig一套流程走完基本要半天而且极易出错。kubeadm 相当于官方把这一套流程固化成了一组命令但它又不是一键傻瓜式——它只负责拉起控制平面 引导节点接入网络插件、存储、负载均衡这些组件仍然需要你自己装。这正是 kubeadm 的价值它是可控性和便捷性的平衡点。你在初始化时能看到每一步的输出[init]、[preflight]、[certs]、[control-plane] 这些分节的日志知道它到底做了什么而不是像某些脚本一样一把梭。出了问题也能从日志定位到具体阶段。对于学习 Kubernetes 内部结构、搭建测试环境、甚至生产环境起步kubeadm 都是最稳的选择。kind 和 minikube 我也用过它们更适合单机快速体验核心是帮你省掉多节点成本但和真实集群的网络模型、调度逻辑有差异。如果想练手生产级多节点集群kubeadm 依然是首选。1.2 版本选型与兼容性关系不只是版本号好看这里有个关键认知Kubernetes 集群不是一个单体软件而是 kubelet、kubeadm、kubectl、容器运行时、CNI 插件的组合。我这个环境用的版本组合是Kubernetes v1.26.0containerd 1.6.x作为 CRI 容器运行时Flannel 作为 CNI 网络插件Ubuntu 22.04 操作系统为什么选 1.26.0因为这个版本是一个相对成熟稳定的版本同时它正式移除了 dockershim意味着 containerd 这类 CRI 运行时成为绝对主流。kubeadm 在初始化时输出的第一行日志就是[init] using kubernetes version: v1.26.0它要求 kubelet、kubeadm、kubectl 三个组件的版本保持严格一致这是 kubeadm 的一个硬约束。实际操作中我建议你用一个公式先定 Kubernetes 版本再看 containerd 兼容版本最后看 CNI 插件的参考文档。kubeadm 自己的依赖很小真正容易出问题的是 containerd 和 CNI。containerd 至少需要 1.6.x 才能完整支持 1.26 的 CRI 接口如果用太老的 1.4/1.5kubelet 会直接报unable to connect to runtime。1.3 节点规划与网络规划给集群留好“床位”节点规划我习惯用一张表先列明白避免中途改架构。角色主机名IP 地址配置建议操作系统control-planek8s-master192.168.10.102C4G 起步Ubuntu 22.04workerk8s-node1192.168.10.112C2GUbuntu 22.04workerk8s-node2192.168.10.122C2GUbuntu 22.04控制平面节点建议内存不低于 2GB因为 etcd 和 apiserver 都比较吃内存kubeadm preflight 检查也要求可用内存至少 1700MB不满足会直接报 Error。工作节点 2C2G 跑普通测试应用足够了。网络规划里最容易埋雷的是Pod CIDR。Kubernetes 的 Pod 网段和 Service 网段是两个独立的虚拟网段不能和物理机网段重叠。Flannel 默认使用 10.244.0.0/16所以在 kubeadm init 时要通过--pod-network-cidr10.244.0.0/16显式声明。如果你用的是 Calico默认 Pod 网段是 192.168.0.0/16这个值对不上网络插件和 kube-proxy 会处于各说各话的状态后面所有 Pod 都无法互相通信。我见过不少人在这里栽跟头建议你把这张 IP 规划表贴在终端旁边。2. 环境初始化把三台机器调教成合格宿主Kubernetes 对宿主机有基础要求这些不处理好后面会以各种奇怪方式报错。这一节的操作要在三台机器上全部执行。2.1 第一刀swap、内核模块与 sysctl 参数首先是关闭 swap。为什么因为 Kubelet 默认的节点资源管理模型不支持 swap 参与调度开着 swap 会让 Pod 的内存限额失去意义kubelet 会反复报running with swap on is not supported。这一步执行完要顺手把 /etc/fstab 里的 swap 行注释掉否则重启后 swap 又回来了swapoff -a sed -i / swap / s/^/#/ /etc/fstab接着加载两个内核模块overlay 提供容器镜像分层挂载能力br_netfilter 让 Linux 网桥设备上的流量也能被 iptables 规则过滤这是实现 Pod 网络隔离和 Service 转发的前提cat EOF | sudo tee /etc/modules-load.d/k8s.conf overlay br_netfilter EOF sudo modprobe overlay sudo modprobe br_netfilter sudo sysctl --system再配一组 sysctl 参数写入 /etc/sysctl.d/k8s.confcat 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 --systemnet.ipv4.ip_forward 1尤其重要它决定节点是否愿意转发数据包。Kubernetes 的 Service 流量、Pod 之间的跨节点流量都要经过三层转发这个值不开kube-proxy 的转发规则再正确也传不过去。2.2 容器运行时 containerd装对不难配好才见功力v1.26 已经移除了 dockershim在系统里装好 containerd 并且让它能以 CRI 方式被 kubelet 调用是必须的一步。Ubuntu 22.04 的 apt 源里自带 containerd直接装即可sudo apt-get update sudo apt-get install -y containerd装完不要急着用先生成一份默认配置。containerd 的默认配置生成命令是containerd config default生成的配置里有两个关键坑第一个坑是SystemdCgroup。默认配置文件里SystemdCgroup false意味着运行时使用 cgroupfs 作为 cgroup 驱动。而 kubeadm 默认让 kubelet 使用 systemd 作为 cgroup 驱动两者不一致时kubelet 启动后 Pod 创建会失败日志里反复出现 cgroup 相关的报错。必须把这一项改成 truesudo mkdir -p /etc/containerd containerd config default | sudo tee /etc/containerd/config.toml sudo sed -i s/SystemdCgroup false/SystemdCgroup true/g /etc/containerd/config.toml sudo systemctl restart containerd为什么统一用 systemd在 cgroup v2 的现代 Linux 发行版下systemd 作为 cgroup 驱动是官方推荐的方案和 systemd 的资源管理模型天然一致。这个坑如果你不主动改遇到报错再去翻日志往往要折腾好几个小时。第二个坑是sandbox_image。配置文件里的sandbox_image默认值是registry.k8s.io/pause:3.91.26 系列实际拉取版本以kubeadm config images list的输出为准。如果你的网络环境拉取 registry.k8s.io 速度不理想国内云服务器很常见最好提前把它替换成你云厂商提供镜像加速地址里对应的 pause 镜像。这个操作要趁早做否则kubeadm init执行到一半会因为拉不到镜像超时退出。2.3 安装 kubeadm、kubelet、kubectl版本粒度要锁死三个二进制包都要从 Kubernetes 官方仓库装并且指定精确到 patch 的版本。我用的是 pkgs.k8s.io 的 apt 源sudo apt-get update sudo apt-get install -y apt-transport-https ca-certificates curl gpg curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.26/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg echo deb [signed-by/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.26/deb/ / | sudo tee /etc/apt/sources.list.d/kubernetes.list sudo apt-get update sudo apt-get install -y kubelet1.26.0-00 kubeadm1.26.0-00 kubectl1.26.0-00 sudo apt-mark hold kubelet kubeadm kubectl最后一步apt-mark hold是我强烈建议加的。apt 的自动升级策略会不定期把 kubelet 升级到新版本导致集群里节点版本不一致轻则 kubelet 和 apiserver 之间出现 REST 兼容问题重则整个节点登出集群。锁死版本意味着只有你明确操作时才升级这是生产环境的基本素养。安装完成后可以验证一下版本kubeadm version kubelet --version kubectl version --client三条命令输出的版本应该都是 v1.26.0。如果哪个不对现在回头修还来得及别等到集群初始化再碰运气。3. 控制平面初始化与节点接入全流程基础环境就绪后真正的高潮来了。我会在控制平面节点上操作工作节点先待命。3.1 kubeadm init 参数解析每一行都有讲究在控制平面节点上执行。先看一眼即将拉取的镜像清单确认网络能通kubeadm config images list --kubernetes-version v1.26.0这个命令会把 init 阶段需要拉取的镜像全部列出来包括 kube-apiserver、kube-controller-manager、kube-scheduler、kube-proxy、etcd、pause、coredns。建议先执行kubeadm config images pull --kubernetes-version v1.26.0把镜像提前拉好避免 init 过程中因为某个镜像超时而中断。然后正式初始化sudo kubeadm init \ --apiserver-advertise-address192.168.10.10 \ --pod-network-cidr10.244.0.0/16 \ --kubernetes-versionv1.26.0三个参数分别解释一下。--apiserver-advertise-address指定 apiserver 对外通告的地址这里就是控制平面节点的内网 IP。如果不写kubeadm 会取默认路由出去的 IP在多网卡机器上经常取错所以我习惯显式指定。--pod-network-cidr必须和后面 CNI 插件默认网段保持一致我选 Flannel所以用 10.244.0.0/16。--kubernetes-version锁死版本避免 kubeadm 去联网探测最新版。执行后你会看到类似这样的日志这就是网上经常见到的[init] using kubernetes version: v1.26.0和[preflight] running pre-flight checks[init] Using Kubernetes version: v1.26.0 [preflight] Running pre-flight checks [WARNING Service-Kubelet]: kubelet service is not enabled... [preflight] Pulling images required for setting up a Kubernetes cluster [preflight] This might take a minute or two... [certs] Generating certificates and keys... [control-plane] Creating static Pod manifest for kube-apiserver [control-plane] Creating static Pod manifest for kube-controller-manager [control-plane] Creating static Pod manifest for kube-scheduler [etcd] Creating static Pod manifest for local etcd [init] Waiting for the kubelet to boot up... [kubelet] Writing kubelet configuration [kubelet] Starting the kubelet [kubelet-check] Initial timeout of 40s passed... [addons] Applied essential addon: CoreDNS [addons] Applied essential addon: kube-proxy Your Kubernetes control-plane has been initialized successfully!3.2 控制面组件落地检查从 preflight 到 etcd看到 control-plane has been initialized successfully 并不代表万事大吉它只说明 kubeadm 把静态 Pod 清单写进了 /etc/kubernetes/manifests 目录。真正拉起这些组件的是节点的 kubelet。静态 Pod 是 kubelet 直接监听的目录apiserver、etcd、controller-manager、scheduler 都以这种方式运行所以这些组件不归 kubelet 以外的任何进程管理。等 30 秒到一分钟然后检查核心容器状态sudo crictl ps -a kubectl get pods -A正常情况下 kube-system 命名空间里应该看到 apiserver、etcd、controller-manager、scheduler 都是 Running。如果哪个容器反复重启基本都在 preflight 或者 cgroup 驱动上出了岔子按第 4 节的思路去查。顺带说一下[preflight]都查什么交换分区是否关闭、端口 6443/10250 是否被占用、内核版本是否过老、内存是否足够、主机名是否规范、cgroup 驱动是否一致。这些检查项全是 Kubernetes 运行的硬前提任何一个错误都会直接中断初始化。看到[WARNING]可以继续看到[Error]就必须停下。3.3 kubectl 配置与 Flannel 网络插件安装init 成功后会输出一个 kubeconfig 配置指令官方建议用普通用户操作 kubectlmkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/configadmin.conf 里的内容本质上是 apiserver 地址、客户端 CA 证书、访问权限的集合。没有这个文件kubectl 根本不知道往哪里发请求。配置完以后kubectl get nodes应该能看到控制面节点处于 NotReady这很正常因为网络插件还没装节点无法确认 Pod IP 路由能正常工作。安装 Flannel 网络插件kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.ymlFlannel 的工作方式是给每个节点分配一个子网然后通过 VXLAN 隧道在节点之间转发 Pod 流量它默认使用的 Pod 网段就是 10.244.0.0/16和 init 时声明的网段一致。装完等待 Flannel Pod 全部 Running再执行kubectl get nodes -o wide控制平面节点应该变为 Ready。如果一直 NotReady优先去看 kube-system 里的 Flannel Pod 日志第 4 节会详细展开。3.4 工作节点 jointoken 与证书指纹一起用控制平面现身后工作节点登场。kubeadm init 成功输出的末尾已经给了一条 join 命令形如kubeadm join 192.168.10.10:6443 --token token --discovery-token-ca-cert-hash sha256:hash这条命令有两个核心信息token 是节点的准入凭证有效期为 24 小时--discovery-token-ca-cert-hash是控制面 CA 证书的 SHA256 指纹用于防止节点连到仿冒的 apiserver。两者缺一不可。把这条命令在两台工作节点上依次执行。如果你已经错过 init 输出的命令窗口期不用慌在控制面上随时可以重新生成一条kubeadm token create --print-join-commandjoin 成功后在两个节点各自执行kubectl get nodes -o wide看到 node1、node2 都是 Ready这套1 主 2 从的集群就算立起来了。Ready 状态的背后是 kubelet、CNI、kube-proxy 全部就绪的联合确认。3.5 集群可用性验证跑一个小应用确认整条链路节点全 Ready 后我习惯用一个最小应用验证完整链路的可用性用 nginx 是最直接的kubectl create deployment nginx-demo --imagenginx:1.25 --replicas2 kubectl expose deployment nginx-demo --port80 --target-port80 --typeNodePort kubectl get svc nginx-demoNginx 能被调度到两个不同节点说明调度器工作正常两个副本能互相通信说明 Flannel 的隧道转发正常Service 分配出 NodePort 端口并能在任意节点上用curl http://192.168.10.11:nodeport访问到说明 kube-proxy 的 iptables 规则生效了。这一条链路跑通集群最核心的能力就算验证完毕。4. 常见问题与排查技巧实录这部分是我最想分享的内容。kubeadm 部署本身不复杂但真遇到问题网上答案碎片化很严重。我把实际部署中最高频的几类问题整理成了排查清单。4.1 节点 NotReady 与 kubelet CrashLoopBackOff节点一直 NotReady第一反应不要去看 kubectl 的状态kubectl 在大部分情况下帮不上忙它只能告诉你结果不对原因还要去节点上挖。定位思路分三步第一步看 kubelet 是否存活。kubelet 是节点的核心代理它挂了节点必掉线systemctl status kubelet journalctl -u kubelet -f --no-pager第二步如果 kubelet 处于 active 但反复退出最常见的原因是 cgroup 驱动不一致。日志会明确写 kubelet 的 cgroup driver 和运行时的不匹配。排查命令cat /etc/containerd/config.toml | grep SystemdCgroup看输出是不是 true不是就改回来重启 containerd。第三步检查 CNI 插件的二进制和配置目录。Flannel 或 Calico 的 Pod 会调用宿主机上的 /opt/cni/bin 下的插件如果这个目录为空或权限不对Pod 的 sandbox 建不出来节点也会 NotReady。这个问题在手动安装二进制时特别容易踩我用 kubeadm 部署因为都会通过 manifest 拉起基本不会碰这个但如果你改过 CNI 插件路径就要注意。4.2 Pod 卡在 Pending / ContainerCreating 的归因卡在 Pending先查是否有节点能调度再查是否有污点。控制平面节点默认有污点node-role.kubernetes.io/control-plane:NoSchedule普通 Pod 不会被调度上去属正常现象。你部署的东西如果在工作节点上也 Pending先看kubectl describe pod pod-nameEvents 字段会告诉你调度失败的具体原因比如资源不足、节点亲和不满足、或者持久化卷没准备好。Pending 的常见原因是内存不足我之前有台 1C2G 的工作节点跑三个 Pod 就触发资源不足。卡在 ContainerCreating重点查两件事镜像拉不下来或者挂载卷失败。镜像问题用kubectl describe pod和kubectl logs看日志里有 ImagePullBackOff 或 ErrImagePull 就直接定位到镜像地址错误或镜像仓库不可达。挂载问题看宿主机 /var/log 下的 kubelet 日志。4.3 token 过期与证书更新的处理join 命令里的 token 默认 24 小时过期这是很多人隔天再用旧命令 join 失败的直接原因。处理非常简单控制面上重新生成即可kubeadm token create --print-join-command证书的问题更隐蔽。kubeadm 签发的 CA 证书有效期默认是 10 年但 apiserver、kubelet 之间的服务证书只有 1 年。一年后你会突然发现 kubectl 连不上 apiserver 了报证书过期或未知颁发机构。检查命令kubeadm certs check-expiration过期后用以下命令续期并重启掉相关的静态 Pod 让新证书生效sudo kubeadm certs renew all续期后还要记得更新 kubeconfig 里的客户端证书sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config4.4 重建环境kubeadm reset 的正确姿势这个坑我提一句能帮不少人省时间。集群实验坏了或者想重新初始化很多人直接重装系统。其实不用kubeadm 提供了标准拆卸流程sudo kubeadm reset -f sudo rm -rf /etc/cni/net.d $HOME/.kube之后重新走 init、join 流程即可。注意kubeadm reset会清掉 kubelet 配置、etcd 本地数据、iptables 规则但 CNI 的残留配置有时不会删干净所以手动补一刀rm -rf /etc/cni/net.d否则残留的网段配置可能和新集群冲突。现象最快定位命令常见根因节点 NotReadysystemctl status kubeletcgroup 驱动不一致、swap 未关Pod Pendingkubectl describe pod资源不足、污点限制Pod ContainerCreatingkubectl describe pod 宿主机日志镜像拉取失败、CNI 未就绪join 报 token 错误kubeadm token create --print-join-commandtoken 过期kubectl 连不上 apiserverkubeadm certs check-expiration证书过期5. 一些我自己的实操体会这套 kubeadm 环境我前前后后搭过很多次给你三条实在建议。第一条每个阶段都做最小验证。不要一口气把所有命令敲完再检查尤其是 init 之后先等 30 秒看 apiserver 和 etcd 是否 Running再往下走。所有问题早暴露都比晚暴露好处理这一条在集群运维里永远成立。第二条版本和网段的一致性记在本子上。kubeadm 对版本 lock 的要求很高对 pod-network-cidr 和 CNI 网段的匹配要求更高。我见过太多人因为 Flannel 装成 Calico 的网段导致整个集群 Pod 网络瘫痪。写计划时就把这些值固定下来不要边装边想。第三条保留一份干净的安装命令序列。以后你要在客户环境、测试环境快速复制集群无脑按顺序执行就行。我自己的做法是把所有命令和参数维护在一个 Markdown 文档里每次部署直接复制比临时翻文档效率高一倍。kubeadm 部署这套流程踩过几次坑之后你会慢慢意识到它其实就两条主线一条是把控制平面组件的运行方式标准化了一条是把节点接入的安全机制标准化了。搞懂这两条主线再看 kubeadm 的每一步输出就不会觉得玄学排查问题时也能顺着日志快速摸到根因。
返回列表