ARTICLE DETAIL

资讯详情

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

kubeadm部署Kubernetes v1.26集群:从环境准备到网络验证全指南

kubeadm部署Kubernetes v1.26集群:从环境准备到网络验证全指南 这个系列的第一篇我们聊了 Kubernetes 的整体架构把 API Server、Controller Manager、Scheduler、etcd、kubelet 这些组件的关系理了一遍。这次我直接上手用 kubeadm 把一套集群完整拉起来。kubeadm 是官方推荐的集群部署工具不管你是想搭一套学习环境还是想在测试环境做前期验证用 kubeadm 初始化都是非常稳妥的路径。整篇文章我会站在一个实操者的角度从零开始部署一套 1 主 2 从的 Kubernetes v1.26.0 集群把每一个关键参数、每一段潜在报错、每一个容易踩坑的细节都掰开揉碎讲清楚。看完之后你完全可以对着文章一步步操作在半小时内得到一套能正常调度应用、能跨节点通信的集群。选题上我用的是 Ubuntu 22.04 LTS 作为宿主机系统这也是当前社区里最主流的选择网上遇到问题能搜到的案例最多。1. 部署前的整体设计与前置检查很多人一上来就急着下载安装包直接kubeadm init结果卡在 preflight 检查阶段反复报错。实际上 kubeadm 部署 Kubernetes 这件事前置准备占了整个工程量的四成系统层面的小问题往往比集群本身的错误更难排查。我先把思路理清楚再动手。1.1 架构方案选型单控制平面还是高可用kubeadm 支持两种部署模式单控制平面和多控制平面。单控制平面就是只有一台机器承担 etcd、kube-apiserver、kube-controller-manager、kube-scheduler 这些控制面组件工作节点只跑 kubelet 和容器运行时。它的优点是部署简单资源消耗小适合学习和开发测试环境。多控制平面则需要至少三台控制节点通过 keepalived 或云厂商负载均衡器暴露 kube-apiserver 的 VIPetcd 也要组成集群。这套方案部署复杂度明显上升而且三台控制平面至少需要 3 台 4C8G 以上的机器普通个人电脑或者入门级云主机根本扛不住。我的建议是第一次用 kubeadm 部署老老实实搭单控制平面即可。生产的场景后面校验业务稳定后可以再用同样的操作步骤横向扩展控制平面节点。而且单控制平面这套流程跑通之后你对 kubeadm 生成证书、分发 kubeconfig、管理静态 Pod 这些底层机制已经有了感知再去上手高可用方案会觉得顺理成章。1.2 主机规划与网络约定我这次准备了三台机器主节点和两个工作节点都位于同一个内网网段系统统一使用 Ubuntu 22.04 LTS。部署之前先列好一张 IP 规划表后续很多地方都要用到这些信息。角色主机名IP 地址规格系统控制平面k8s-master192.168.1.104C8GUbuntu 22.04 LTS工作节点k8s-worker1192.168.1.212C4GUbuntu 22.04 LTS工作节点k8s-worker2192.168.1.222C4GUbuntu 22.04 LTS主机名必须规范不能出现大写字母和特殊字符推荐用k8s-master、k8s-worker1这种带横线的命名方式。同时四台机器的/etc/hosts里要写上彼此的映射关系否则后续节点之间互相访问时依赖 DNS 解析可能因为环境限制而拖慢排查速度。关于网段设计我选择用192.168.1.0/24作为宿主机网段Pod 网段留给192.168.0.0/16这个网段给 Calico 用后面网络插件章节会说为什么这么选Service 网段用 Kubernetes 默认的10.96.0.0/12。1.3 系统初始化配置系统层面的准备主要在三个方面关闭 swap、加载内核模块、调整 sysctl 参数。先关闭 swap。Kubernetes 在 1.28 版本之前kubelet 不允许节点上存在 swap 分区否则 kubelet 会直接拒绝启动。v1.26.0 对 swap 的支持也在 alpha 阶段稳妥做法是一律关闭。sudo swapoff -a sudo sed -i / swap / s/^\(.*\)$/#\1/g /etc/fstab第一条命令是立即关闭系统 swap第二条是修改/etc/fstab注释掉 swap 挂载项这样重启之后也不会自动挂载。接下来配置内核模块。Kubernetes 的网络组件需要两个内核模块overlay用于容器镜像层文件系统br_netfilter让 iptables 能处理 Linux 网桥流量这是 kube-proxy 实现服务转发的基础。cat EOF | sudo tee /etc/modules-load.d/k8s.conf overlay br_netfilter EOF sudo modprobe overlay sudo modprobe br_netfilter写完/etc/modules-load.d/k8s.conf之后用modprobe立即加载。可以顺手用lsmod | grep br_netfilter验证一下模块有没有进内核。再调整内核参数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这里最核心的是net.ipv4.ip_forward 1它控制 Linux 是否转发 IP 数据包。Pod 之间的跨节点通信、Service 的流量转发都依赖这个开关。设置完成之后可以用sysctl net.ipv4.ip_forward确认输出为 1。2. 容器运行时与 kubeadm 工具链安装控制面组件 kube-apiserver 等是以容器方式运行的所以容器运行时必须先行装好。Kubernetes 从 1.24 版本开始已经全面转向 containerd 作为默认运行时Docker 里那个 dockershim 已经移除。所以直接装 containerd干净利落。2.1 安装 containerdUbuntu 22.04 的官方软件源里自带 containerd 包直接用 apt 安装sudo apt-get update sudo apt-get install -y containerd安装完成后生成一份默认配置sudo mkdir -p /etc/containerd sudo containerd config default | sudo tee /etc/containerd/config.toml这份默认配置是不能直接用的至少需要改两个地方。第一处是SystemdCgroup。containerd 默认的 cgroup 驱动是cgroupfs而 kubelet 更推荐使用 systemd 作为 cgroup 驱动二者不一致时 kubelet 会报错。找到配置里的这段内容把值改成true[plugins.io.containerd.grpc.v1.cri.containerd.runtimes.runc] ... [plugins.io.containerd.grpc.v1.cri.containerd.runtimes.runc.options] SystemdCgroup true第二处是sandbox_image。Kubernetes 每个 Pod 里最先启动的其实是 pause 容器它的作用就是持有 Pod 的网络命名空间。containerd 需要从镜像仓库拉取这个基础镜像默认指向registry.k8s.io/pause:3.6。如果你的机器拉取这个镜像比较慢可以换成国内公共镜像仓库地址sandbox_image registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.6改完配置后重启 containerdsudo systemctl restart containerd sudo systemctl enable containerd sudo systemctl status containerd2.2 安装 kubeadm、kubelet、kubectl这三个工具的安装包不在 Ubuntu 默认源里需要先配置 Kubernetes 的 apt 仓库。以 v1.26 为例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 kubelet kubeadm kubectl镜像仓库地址可以根据你的网络环境换成可访问的公共镜像站。安装完成之后顺手锁定版本sudo apt-mark hold kubelet kubeadm kubectlapt-mark hold是很多新手会忽略的一步。Kubernetes 工具链的版本必须和集群版本保持匹配如果某次系统升级自动把 kubelet 升到更新版本而集群还是 v1.26.0就很容易出现 kubelet 与 apiserver 手工沟通失败的问题。锁定之后可以放心更新系统其他软件包。2.3 版本一致性说明检查一下三个工具的版本保证一致kubeadm version kubelet --version kubectl version --client注意 kubectl 输出中的Kubernetes v1.26.0要与预期一致。在这个环节kubelet 启动时会读取/etc/kubernetes/kubelet.conf获取 apiserver 地址如果版本差异过大握手时会出现兼容性问题。3. 主节点初始化与输出日志解读所有前置配置都在主节点上完成之后开始执行初始化操作。这一步是整个部署的核心初始化失败的大多数问题也集中在这个环节。3.1 kubeadm init 关键参数详解我执行的实际命令是sudo kubeadm init \ --apiserver-advertise-address192.168.1.10 \ --kubernetes-versionv1.26.0 \ --pod-network-cidr192.168.0.0/16 \ --service-cidr10.96.0.0/12 \ --image-repositoryregistry.cn-hangzhou.aliyuncs.com/google_containers这四个核心参数逐一说明--apiserver-advertise-address控制平面节点上 kube-apiserver 对外通告的地址也就是工作节点连接 apiserver 所用的 IP。必须写主节点的内网 IP如果写 127.0.0.1工作节点无法访问控制平面。--kubernetes-version指定要部署的 Kubernetes 版本需要与 kubeadm 工具版本一致。--pod-network-cidrPod 网段用于给集群内的每个 Pod 分配 IP。这里我用 192.168.0.0/16这个网段必须与后面安装的网络插件预设网段完全一致否则网络插件创建的 IP 池和 apiserver 预期的网段对不上会直接导致节点 NotReady。--service-cidrService 网段默认就是 10.96.0.0/12我明确写出来是为了方便后面排查。--image-repository从哪个镜像仓库拉取 kube-apiserver、kube-controller-manager、kube-scheduler、etcd 等组件的镜像。注意如果这里使用阿里云仓库实际镜像中registry.cn-hangzhou.aliyuncs.com/google_containers后面的组件名与官方一致。如果你的网络环境可以直连 registry.k8s.io那--image-repository参数可以不传直接用默认值。3.2 初始化过程日志解读执行之后日志会按阶段推进[init] Using Kubernetes version: v1.26.0 [preflight] Running pre-flight checkspreflight阶段会做环境自检。如果报错一般集中在以下几个方面swap 未关闭、端口被占用、内核参数未设置、cgroup driver 不匹配。日志会直接告诉你具体失败点。比如[ERROR Swap]: running with swap on is not supported. Please disable swap那就回去执行swapoff -a。这个阶段通过后kubeadm 会依次生成 CA 证书、kubeconfig 文件、etcd 静态 Pod 清单、kube-apiserver 静态 Pod 清单随后拉起 kubelet 并把控制面组件以静态 Pod 方式运行起来。静态 Pod 这个概念值得说一下控制面组件不是通过 Deployment 资源创建的而是直接把 Pod 清单放在/etc/kubernetes/manifests目录下面kubelet 会定时扫描这个目录发现新清单就创建对应容器。这是一种自举机制在集群自身不可用之前控制面先由静态 Pod 方式支撑起来。当你在日志末尾看到Your Kubernetes control-plane has initialized successfully!说明控制平面已经初始化完成。此时日志里会给出两段关键信息一段是配置 kubectl 的三条命令另一段是kubeadm join命令后者需要妥善保存。3.3 配置 kubectlkubectl 需要通过 kubeconfig 文件来访问 apiserver。初始化完成后用 root 权限执行mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config/etc/kubernetes/admin.conf是 kubeadm 生成的管理员证书文件。这里有一个很容易踩的坑如果不复制这个文件直接kubectl get nodes会提示连接被拒绝或者找不到配置因为默认情况下 kubectl 只会在$HOME/.kube/config里找凭证。验证一下kubectl get nodes此时输出显示主节点 STATUS 是NotReady这是正常的因为集群此时还没有任何网络插件。接下来的重点就是给集群装上网络组件。4. CNI 网络插件选型与部署主节点初始化完成控制面组件运行正常但集群的网络功能还是空的。Pod 之间的跨节点通信需要 CNI 插件来实现。4.1 网络插件怎么选CNI 插件是 Kubernetes 网络模型的具体实现决定了一个 Pod 如何获得 IP、如何与集群外通信、如何执行网络策略。目前生产环境最常见的是 Calico 和 Flannel 两个阵营。Flannel 底层基于 VXLAN 封装实现简单、对底层网络要求低适合扁平网络环境性能有一定开销。Calico 默认基于 BGP 路由协议可以走 IPIP 隧道或者直接路由模式性能更好还支持 NetworkPolicy能实现更细致的网络隔离。从 2023 年的社区实践看Calico 的占比已经明显超过 Flannel是更多生产集群的选择。我这套集群用 Calico。Calico 默认的 Pod 网段就是 192.168.0.0/16正好和前面kubeadm init中指定的--pod-network-cidr对上不需要额外修改任何配置文件这是选型上的一个优势。4.2 部署 Calico 并验证网络从 Calico 官方仓库获取对应的 manifestcurl -L -o calico.yaml https://raw.githubusercontent.com/projectcalico/calico/v3.25.0/manifests/calico.yaml kubectl apply -f calico.yaml整个安装会创建 calico-namespace、RBAC 授权、calico-node DaemonSet、calico-kube-controllers Deployment 等对象。等待两到三分钟观察 Pod 状态kubectl get pods -n kube-system -o wide重点关注 calico-node 开头的 Pod。如果 STATUS 全部变成Running并且 READY 是1/1网络组件就绪了。再回头看节点状态kubectl get nodes -o wide这次主节点的 STATUS 应该已经变成Ready。关于启动慢的问题有一个检查点值得提一下。Calico 依赖BGP和ipset相关内核模块如果某些环境没有加载calico-node 日志里会提示Failed to establish a connection to...这样的错误。可以检查/var/log/calico下的日志定位。4.3 验证 DNS 与网络连通性为了确认网络真的通了可以部署一个测试 Podkubectl run test-pod --imagebusybox -- sleep 3600 kubectl exec -it test-pod -- sh如果能进入容器执行ping 192.168.1.21访问另一个节点的宿主机 IP这是跨节点流量验证。再测试一下集群内部 DNS 解析nslookup kubernetes.default.svc.cluster.localDNS 服务是 CoreDNS 提供的它同样以 Pod 形式运行在 kube-system 命名空间。解析成功说明 Service 网络和 DNS 链路都没有问题。5. 工作节点加入与集群验收全流程主节点 Ready 之后把两台工作节点接入集群。5.1 准备 Worker 节点工作节点需要重复第一、二章里的全部步骤关闭 swap、加载内核模块、设置 sysctl、安装 containerd 并修改配置、安装 kubeadm kubelet kubectl。一台一台来不要跳过任何一步。这里最常见的问题是工作节点的/etc/hosts里没有补上主节点的主机名映射。如果没写执行kubeadm join时可能因为解析不了主机名而连接失败。顺手在 worker1 和 worker2 上都加上echo 192.168.1.10 k8s-master | sudo tee -a /etc/hosts5.2 执行 kubeadm join回到主节点的初始化日志找到kubeadm join命令通常长这样sudo kubeadm join 192.168.1.10:6443 --token xxxxxx \ --discovery-token-ca-cert-hash sha256:xxxxxxxxxx在 worker 节点上执行。如果命令已经找不到了在主节点上重新生成kubeadm token create --print-join-command这个命令会生成一个新的 token 并直接打印完整的 join 命令非常方便。token 默认有效期是 24 小时如果提示 token 过期就用上面的命令重新生成。5.3 集群整体验收回到主节点执行kubectl get nodes -o wide正常情况下三台节点应该全部处于Ready状态。再检查关键系统组件kubectl get pods -n kube-system -o wide | grep -E calico|CoreDNS|kube-proxy想要更完整地确认集群状态可以跑一个 Deploymentkubectl create deploy nginx --imagenginx:alpine kubectl expose deploy nginx --port80 kubectl get svc nginx如果在任意工作节点上执行curl http://ServiceIP能拿到 Nginx 的响应Pod 到 Service 的负载链路就验证完毕了。工作节点加入后有几件事要留心。第一node 显示NotReady时先看该节点上的 kubelet 日志journalctl -u kubelet -f第二查看kubectl describe node 节点名里的 Conditions 和 Events 部分哪里有 Error 就直接暴露问题第三如果 calico-node 没有在 Worker 节点上起来多半是 containerd 的配置没有同步过来。6. 高频问题排查实录与个人经验用 kubeadm 部署一套集群顺利的话半小时能跑完但中间踩坑补洞的时间往往比操作时间还长。我把这次部署中遇到的高频问题整理成一张速查表按症状定位问题比一句一句翻日志快得多。6.1 六个典型问题的定位方法症状典型日志排查方向kubeadm init 卡住apiserver 没起来[kubelet] Waiting for the kubelet to be readyjournalctl -u kubelet -f看 kubelet 日志通常是镜像没拉下来节点一直 NotReadyCNI 网络插件没有安装检查 kube-system 里 calico/flannel 的 Pod 状态kubelet 启动失败cgroup driver: cgroupfs is different from systemd修改 containerd 配置里SystemdCgroup true并重启容器运行时找不到unable to find runtime containerdcontainerd 未启动或 socket 路径不对join 命令报 token 过期token is invalid用kubeadm token create --print-join-command重新生成Pod 无法跨节点通信calico Pod 一直 CrashLoopBackOff检查 Pod 网段与 Calico 配置是否一致检查内核模块其中最常见的是第一条镜像拉取超时。控制面组件镜像体积较大网络状况不好时就容易卡住。解决办法是执行kubeadm config images pull先手动拉取全部镜像拉成功后再执行kubeadm init。手动拉取时可以通过 containerd 的命令行工具查看进度sudo crictl images只要看到列表里出现kube-apiserver、kube-controller-manager、kube-scheduler、etcd等镜像说明拉取完成。6.2 一条命令清单与长期维护心得最后分享几个日常维护特别有用的命令# 查看 kubelet 实时日志 journalctl -u kubelet -f # 查看节点状态详情 kubectl describe node k8s-master # 查看系统组件容器状态 sudo crictl ps -a # 重新生成 join 命令 kubeadm token create --print-join-command # 临时移除一个节点 kubectl drain k8s-worker1 --delete-emptydir-data --force kubectl delete node k8s-worker1根据我个人的体会kubeadm 部署 Kubernetes 这件事最大的成本并不在于命令本身而在于对集群各组件间依赖关系的理解。你越清楚 kubelet 从静态 Pod 清单拉起控制面组件、CNI 插件负责 Pod 网络、kube-proxy 负责 Service 转发就越能从容地应对各种报错。我建议第一次照本宣科搭完之后再做两件额外的事第一是执行kubeadm reset把它拆掉第二次独立操作时不再复制粘贴直接手写命令第二是去读一遍生成出来的/etc/kubernetes/manifests/kube-apiserver.yaml看看一个真实控制面组件的启动参数长什么样。把这两个文件读懂了kubeadm 部署就不再是玄学而是你后续排查和扩展集群的真正起点。
返回列表