拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

Kubernetes集群部署实战:kubeadm搭建与管理避坑指南

Kubernetes集群部署实战:kubeadm搭建与管理避坑指南 带过几轮 Kubernetes 实验课之后我越来越确定一件事同一个实验指导书有人能在半小时内把集群拉起来有人却对着同一个屏幕盯上两个小时。差别不在于手速而在于部署之前是不是把决策做完了。这篇文章是一份经过实战检验的 Kubernetes 集群部署与管理实验作业指导书我把指导书里经常省略掉的“为什么”补全也把实验过程中最容易踩的坑和排查思路完整写出来。适合正在做课程实验、准备自己搭一套集群环境练手、或者刚开始接触生产级 K8s 的同学。整份实验我建议按“部署前决策、节点初始化、控制平面搭建、工作节点加入、CNI 网络插件、部署后的管理实验、故障修复练习”这条链路来做。前四步属于“搭起来”后面三步属于“会管理”两部分都不能省。1. 实验开始前先把三个决策做掉很多同学拿到指导书第一件事就是敲kubeadm init我一般会拦一下。实验环境的选型、容器运行时选型和组件版本选型这三个决策会在后面大约两个小时的实验里反复影响你。它们不是“高阶选项”而是部署前的必修课。1.1 实验规模单机、双节点、还是多节点先看你能拿到多少机器。如果只是为了体验 Kubernetes 的基本功能Minikube或者Kind就够了一条命令能把控制平面和工作节点都跑在一台机器上。但实验作业的意义通常不是“把集群跑起来”而是理解集群的几个核心机制节点加入、工作负载调度到不同节点、节点故障后 Pod 如何迁移。这些场景在单机环境里根本没法真正验证。我的建议是实验作业至少用一主一从两台节点。如果条件允许一主二从更好。三节点能做的实验边界比二节点大很多比如你可以把其中一个工作节点 cordon 掉观察另一个节点的调度行为也可以模拟一个节点宕机看看已有的 Pod 会不会被重新调度。这些动作在二节点集群里虽然也能演示但效果打了折扣。先看一张选型对比表方案推荐工具适合场景能覆盖的实验点单机体验Minikube / Kind只想熟悉 kubectl 命令应用发布、Service、配置管理一主一从kubeadm 手动搭建标准课程实验节点加入、CNI 网络、节点管理一主二从kubeadm 手动搭建进阶实验、故障演练节点驱逐、故障迁移、多副本调度高可用三主多从kubeadm keepalived接近生产环境控制平面高可用、etcd 备份恢复实验环境不够也没关系还有折中方案把控制平面节点打上污点taint不让业务 Pod 调度上去只让它在集群里承担管理职责。这样一台控制平面节点加一台工作节点也能撑起大部分实验。1.2 容器运行时containerd 是当前默认选择Kubernetes 1.24 版本把dockershim从 kubelet 里移除之后Docker 就不再是 kubelet 直接支持的容器运行时了。现在最常见的运行时是containerd也有团队用CRI-O。实验环境里我推荐直接用 containerd因为它是当前部署工具默认配好的选择踩坑资料也最多。你在初始化节点的时候只需要把 containerd 装好kubelet 会通过 CRIContainer Runtime Interface接口去调用它。实验里容易翻车的点有三个一是 containerd 版本太老和 kubelet 的 CRI 版本对不上kubelet 日志里会直接报unknown service runtime.v1.RuntimeService二是 containerd 的SystemdCgroup参数没改导致 Pod 内的 cgroup 资源统计异常三是 pause 镜像registry.k8s.io/pause拉取失败Pod 一直卡在ContainerCreating。实验前可以手动验证一下运行时是否正常# 查看 containerd 是否在跑 systemctl status containerd # 用 crictl 检查 CRI 是否可用 crictl info # 手动拉一下 pause 镜像确认镜像仓库连通 crictl pull registry.k8s.io/pause:3.9crictl是实验阶段最常用的诊断工具之一它可以查看节点上的容器、镜像和 Pod 沙箱状态。很多你从 kubectl 里看不到的信息在crictl里一眼就能看出来。1.3 组件版本与安装方式为什么实验里要“锁版本”Kubernetes 部署涉及的组件很多kubeadm、kubelet、kubectl、etcd、apiserver、controller-manager、scheduler、coredns再加上网络插件。它们之间的版本组合有兼容关系。最省心的做法是在所有节点上安装相同版本的kubeadm/kubelet/kubectl并且通过系统包管理工具把版本“锁住”。比如在 Debian/Ubuntu 上安装时可以直接指定版本号apt-get install -y kubeadm1.28.2-00 kubelet1.28.2-00 kubectl1.28.2-00不建议使用apt-get install -y kubeadm这种不带版本号的写法因为你无法控制它会拉到什么版本。实验中经常出现的“kubelet 起来了一秒又退出”“apiserver 容器不断重启”很多就是因为 kubeadm 和 kubelet 的小版本不一致。至于安装方式kubeadm是当前手动搭建集群的主流选择它把控制平面组件的生命周期拉到了容器里你只需要关心初始化和加入节点的动作。二进制方式能让你看到每个组件的启动参数但对实验时间成本太高脚本一键部署方式虽然快却把太多细节隐藏了出了问题反而不好排查。实验作业我更推荐 kubeadm这也是目前社区资料最丰富、遇到问题最容易搜到答案的方式。Kubernetes 版本建议选当前维护期内的小版本不要选太老也不要选刚发布的开发版本。做实验的阶段稳定优先。2. kubeadm 部署实操链路从节点准备到集群可用这一整段是实验的核心操作链路。我会把每个步骤背后的原因讲清楚而不是只给一段命令。因为你一旦明白了这一步“到底在解决什么问题”后续出故障时就能快速定位到具体是哪一层没做好。2.1 基础配置阶段那些“看起来没用”的操作其实都在保命在安装 Kubernetes 组件之前每个节点都要做一组基础配置。这部分看起来琐碎但实验中大部分“初始化失败”都源于此。先设置主机名并写进/etc/hosts。kubelet 会拿主机名作为节点的标识如果两台节点的主机名重复节点注册时会互相覆盖集群里会出现一台机器“消失”的情况。# 控制平面节点 hostnamectl set-hostname k8s-master # 工作节点 hostnamectl set-hostname k8s-worker1 # 在每台节点上都配好 hosts echo 192.168.100.10 k8s-master /etc/hosts echo 192.168.100.11 k8s-worker1 /etc/hosts接下来是关闭 swap。Kubernetes 在默认配置下要求节点必须关掉 swap因为 kubelet 的 QoS服务质量模型在 swap 启用时无法正确估算 Pod 的内存占用。临时关闭命令是swapoff -a但为了重启后不失效最好把/etc/fstab里的 swap 行注释掉。然后加载内核模块并调整系统参数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这三个系统参数为什么不能省net.ipv4.ip_forward控制 IP 转发Pod 跨节点通信时宿主机需要转发数据包不开这个会直接导致跨节点的 Pod 网络不通。net.bridge.bridge-nf-call-iptables则让桥接流量也能经过 iptables 规则kube-proxy给 Service 做的 DNAT/SNAT 才能对容器流量生效。实验里最常见的“Service 能通Pod 之间不通”很多就出在这两个参数漏配。还有时间同步。节点之间时间差超过一定范围组件之间的 TLS 证书会因为“证书有效期判断失败”而无法建立连接。实验阶段装好 chrony 或 ntpdate至少保证所有节点时钟源一致。最后是防火墙端口。如果实验环境里有防火墙需要放行一组端口。最关键的是控制平面节点的6443apiserver、2379/2380etcd、10250kubelet以及工作节点的30000-32767NodePort 范围。如果只是课程实验且网络环境隔离最简单的办法是关闭防火墙但要清楚这是“实验妥协”不是生产环境正确做法。2.2 控制平面初始化kubeadm init 的每个参数都在管什么事基础配置做完后安装 kubeadm、kubelet、kubectl然后就可以初始化控制平面了。以 Kubernetes 1.28 为例初始化命令大概长这样kubeadm init \ --kubernetes-versionv1.28.2 \ --control-plane-endpointk8s-master:6443 \ --pod-network-cidr10.244.0.0/16 \ --apiserver-advertise-address192.168.100.10这几个参数要逐一说清楚。--control-plane-endpoint设置的是控制平面的统一入口地址。单控制平面实验环境里它就是本机的主机名和 apiserver 端口做高可用实验时这个地址通常是一个虚拟 IP由 keepalived 或云负载均衡器提供。实验代里如果不写这个参数kubeadm 会默认使用第一个网络接口的 IP如果机器有多个网卡很容易指向错误地址。--pod-network-cidr是 Pod 网络地址池。它必须和后面要装的 CNI 插件保持一致。比如 Flannel 默认用10.244.0.0/16Calico 的默认地址池是192.168.0.0/16。你在初始化时指定的网段如果和 CNI 期望的不一样后面网络插件会拒绝工作或者给 Pod 分配出和 Service/ClusterIP 冲突的地址。--apiserver-advertise-address明确告诉 apiserver 对外宣告的 IP 地址。在多网卡实验机上这个参数能避免 apiserver 选错地址。初始化过程会拉取一组镜像kube-apiserver、kube-controller-manager、kube-scheduler、kube-proxy、etcd、coredns、pause。网络条件不理想时可以指定一台镜像加速仓库用--image-repository参数。初始化成功后会显示两段关键信息一是配置 kubectl 的命令二是工作节点加入集群的kubeadm join命令。保存好 join 命令尤其是 token 和--discovery-token-ca-cert-hash。token 默认 24 小时过期如果过期了也不用重新初始化用kubeadm token create --print-join-command重新生成即可。初始化完成后先配置 kubectlmkdir -p $HOME/.kube cp -i /etc/kubernetes/admin.conf $HOME/.kube/config chown $(id -u):$(id -g) $HOME/.kube/config不执行这一步kubectl 就无法连接集群会报connection refused或者提示找不到 kubeconfig。2.3 网络插件与工作节点加入决定集群能否立刻可用的最后一步控制平面初始化完成后集群实际上还没有 Ready。你执行kubectl get nodes会看到控制平面节点的状态是NotReady原因就是缺少 CNI 网络插件。CNI 插件的选择在实验阶段主要有两个Calico 和 Flannel。Flannel 配置简单适合快速搭环境但 NetworkPolicy网络安全策略支持有限Calico 功能更全也是很多生产环境的选择。我的建议是实验作业里直接用 Calico哪怕只是把它的默认配置跑起来也能顺便学习一下网络策略怎么配。安装 Calico 最简单的方式kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.27/manifests/calico.yaml安装后观察它的运行状态kubectl get pods -n kube-system -w等到calico-node和calico-kube-controllers变成 Running控制平面节点通常就会变成 Ready。这时候再让工作节点加入成功率会高很多。顺序很重要先让控制平面节点的网络可用再 join 工作节点否则工作节点加入后因为没有网络插件核心 Pod 无法和 apiserver 通信。工作节点上执行控制平面初始化完成后输出的 join 命令kubeadm join 192.168.100.10:6443 \ --token token \ --discovery-token-ca-cert-hash sha256:hash控制平面节点上验证kubectl get nodes如果两个节点都显示 Ready说明集群已经跑起来了。再跑一遍kubectl get pods -A看到kube-system命名空间里的 Pod 基本都是 Running这个部署实验才算真正通关。3. 部署实验最容易翻车的三个环节一次完整的排查思路演练部署过程中最痛苦的不是敲命令而是集群状态一切正常、但有一个 Pod 或者节点就是不对劲。实验指导书里往往只有“常见问题列表”却没有教你怎么一步步定位。这一节我把排查思路完整走一遍。3.1 kubelet 异常先看日志而不是先怀疑网络kubelet 是每个节点上的核心代理它负责和容器运行时交互、向 apiserver 上报节点状态。很多节点级问题根因都在 kubelet但表面症状会表现为“节点 NotReady”或者“Pod 调度不上去”。我建议把排查命令固定成一套组合拳systemctl status kubelet journalctl -u kubelet --since 10 minutes ago -f crictl ps crictl logs container-id先看 kubelet 有没有跑起来。很多实验里 kubelet 装完后根本没启动或者启动一下就退出systemctl status会直接告诉你。然后看日志kubelet 的日志信息量巨大常见错误会在里面直接出现。举个例子如果你看到日志里有failed to get sandbox image registry.k8s.io/pause:3.9说明是 kubelet 调容器运行时创建沙箱时拉不到 pause 镜像。这种情况不用重启集群手动把 pause 镜像拉到本机就行crictl pull registry.k8s.io/pause:3.9另一个高频错误是Failed to run kubelet ... cgroup ... swap is enabled这种就是 swap 没关干净去检查/etc/fstab和当前swapon -s输出。还有一个容易被忽略的原因--cgroup-driver和容器运行时的 cgroup 驱动不一致。实验环境里如果都用 systemd 作为 cgroup 驱动在 containerd 的配置文件里确认SystemdCgroup为 true。3.2 Pod 一直 Pending事件记录比“猜原因”有效率得多部署网络插件或者跑业务应用时经常遇到 Pod 一直 Pending 或者 ContainerCreating。这时候不要盯着kubectl get pods看事件记录才是最快的入口kubectl describe pod pod-name -n namespace kubectl logs pod-name -n namespacedescribe输出的 Events 区域会告诉你调度器为什么不调度、容器为什么没起来。我整理了几个高频事件的对应原因事件关键字常见原因下一步动作0/1 nodes are available没有满足调度条件的节点检查节点状态、污点和 labelFailedCreatePodSandBoxCNI 网络插件未就绪或运行时异常查看 kubelet 日志检查 calico/flannel 状态ImagePullBackOff镜像拉取失败检查镜像名称、仓库连通性、私有仓库凭据Back-off restarting failed container容器启动后立即退出用kubectl logs看应用日志0/1 nodes are available这类调度问题结合kubectl describe node看节点上的污点和资源情况通常能马上找到原因。比如工作节点上有node.kubernetes.io/unschedulable污点说明节点被 cordon 了去掉即可。FailedCreatePodSandBox是最常见也最复杂的它可能由 CNI 插件没装好、containerd 启动异常、网络内核参数不对等多种因素触发。看到这个事件后先去kubectl get pods -n kube-system检查网络插件 Pod 是否 Running再回到节点上crictl ps看有没有沙箱容器被创建。这两个动作能过滤掉至少一半问题。3.3 “照着指导书敲集群仍然 NotReady”的版本与配置问题还有一类翻车最让人头大所有命令都照着挂了节点还是 NotReadyPod 也起不来。这种情况多半是版本组合或 CIDR 配置出了问题。先说版本组合。kubeadm 初始化时如果指定的--kubernetes-version和命令行工具版本偏差过大kubeadm 会尝试拉取指定版本的组件镜像而 kubelet 还是旧版本两边 CRI 调用协议对不上日志里会出现CRI v1 runtime API is not implemented。解决办法是统一三个组件的版本不要只固定了 kubelet 而初始化时传了另一个版本。再看 CIDR。如果你初始化时写了--pod-network-cidr10.244.0.0/16但 Calico 的默认配置期望是192.168.0.0/16Calico 就会报告 IP 池冲突Pod 无法获得地址。修复方式不是重新初始化集群而是修改 Calico 清单文件里的CALICO_IPV4POOL_CIDR字段再重新 apply。还有一个隐蔽问题控制平面节点初始化后如果这个节点的 IP 地址发生变化比如 DHCP 重新分配apiserver 的证书里记录的 IP 就失效了kubelet 连不上 apiserver。实验环境建议在初始化前给节点配置静态 IP 或者 DHCP 保留地址省得后面折腾证书。4. 把“部署之后”变成实验内容管理作业的实验设计集群搭起来只完成了一半实验作业要求的“管理”部分同样可以设计成可操作、可验证的任务。这里给一套可以直接用在实验课上的管理模块。4.1 节点维护实验cordon、drain 和污点的边界在哪管理作业里我建议先让学员做节点维护实验因为它是理解调度器行为的最直观方式。先执行kubectl cordon k8s-worker1cordon的效果是标记节点为不可调度但它只影响后续 Pod 的调度已经在节点上运行的 Pod 不迁移。想验证这个区别可以在 cordon 后用kubectl create deployment部署一个新应用然后kubectl get pods -o wide观察 Pod 是否都落在控制平面节点上。节点上已存在的 Pod 原样运行不受影响。然后执行kubectl drain k8s-worker1 --ignore-daemonsetsdrain会把节点上的 Pod 驱逐到其他可用节点但 DaemonSet 部署的 Pod比如 calico-node不会被驱逐所以要加--ignore-daemonsets。学员如果不加这个参数命令会卡住或者直接报错这也是理解 DaemonSet 调度逻辑的好机会。维护完成后执行kubectl uncordon k8s-worker1节点恢复调度。这个三步操作可以做成一个完整实验让学员在 worker 节点上部署一个带副本数的应用然后执行 cordon/drain/uncordon记录 Pod 的变化。记录结果比单纯敲命令更有价值。污点taint实验也值得做。给节点打污点kubectl taint node k8s-worker1 dedicatedexperiment:NoSchedule之后新调度的 Pod 不会跑到这个节点上除非 Pod 显式带对应的容忍toleration。这个机制在生产环境里用来隔离专用节点。实验作业可以让学生自己设计一个“只有指定应用能调度到某节点”的场景。4.2 应用发布实验从 Deployment 到 Ingress 的一条完整链路管理实验的另一个重点是应用发布它贯穿 Deployment、Service、Ingress 三个对象。我建议让学员最终实现一个“通过域名访问应用”的目标而不是停留在kubectl run跑个 Pod 就结束。先创建 Deploymentkubectl create deployment nginx --imagenginx:1.25 --replicas3 kubectl scale deployment nginx --replicas5 kubectl rollout status deployment/nginx滚动更新和回滚是实验中很关键的动作kubectl set image deployment/nginx nginxnginx:1.26 kubectl rollout status deployment/nginx kubectl rollout undo deployment/nginx然后创建 Servicekubectl expose deployment nginx --port80 --target-port80 --typeNodePort到这里就能通过任意节点IP:NodePort访问应用了。但实验要求如果要更接近生产还得加一层 Ingress。先把 Ingress Controller推荐ingress-nginx装好再定义一个 Ingress 资源让实验域名指向这个 ServiceapiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: nginx-ingress spec: ingressClassName: nginx rules: - host: nginx.lab.local http: paths: - path: / pathType: Prefix backend: service: name: nginx port: number: 80最后在实验机/etc/hosts里把nginx.lab.local指向 Ingress Controller 所在节点 IP能通过域名访问应用这条链路就完整了。学员在这个过程里会自然理解ClusterIP、NodePort、LoadBalancer这三种 Service 类型的区别以及 Ingress 和 Service 的分层关系。4.3 故障演习与安全加固实验作业里就该有的“拆装练习”管理实验如果只有“部署-访问-删除”对故障处理完全没有训练价值。我建议实验作业增加一个故障演习环节制造一个可控故障让学生自己恢复。最简单的故障演习是等应用跑起来后手动停掉工作节点的 kubeletsystemctl stop kubelet观察这个节点的状态变成 NotReady之前运行在这个节点上的 Pod 会在一段时间后被调度到其他节点。等学生记录完现象再systemctl start kubelet恢复节点。这个实验的成本很低但对理解“自愈”机制非常有效。再进阶一点是 etcd 备份恢复。etcd 是集群的“数据库”所有资源数据都存在那里。实验里可以让学生做一次快照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 /backup/etcd-snapshot.db恢复的流程相对复杂实验课上建议用一个单独的虚拟机或者恢复到临时集群不要直接在原有集群上反复试。这个过程主要让学员明白etcd 快照是集群的“最后救命稻草”很多不可恢复的误删除操作只要有快照就能回到过去某个时间点。安全加固方面实验环境里最容易出现的问题是 apiserver 的6443端口直接暴露。Kubernetes 的未授权访问漏洞在现实里多次成为安全事故入口根因通常是 apiserver 没有做访问控制或者把管理端口暴露到了不该暴露的网络。实验作业里至少应该完成的动作是在防火墙层面限制只有管理网段能访问 6443 端口给集群开启 RBAC用最小权限的 ServiceAccount 和 Role 做授权而不是长时间使用默认的 cluster-admin。带过几轮实验之后我个人最大的体会是部署失败的几个原因其实高度集中要么是网络层没通要么是容器运行时和 kubelet 之间没对上报要么是版本之间的兼容陷阱。把这些点提前讲透学生“照着敲”才真的敲得通否则只会让他们觉得 Kubernetes 很难而不是让他们觉得这个调度系统很有趣。最后再分享一个小技巧实验操作比较密集的时候可以把初始化命令、join 命令、token、节点 IP 这些参数统一放到一个 shell 变量文件里每次开新终端先source一下能省掉大量复制粘贴的时间和因为手误产生的错误。我自己带实验课时还会让每个学员把自己执行过的命令记录成一个 pty 日志文件后面遇到诡异问题时直接翻历史命令比回忆可靠得多。
返回列表