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

K8s集群升级避坑:kubeadm与apt-mark unhold全流程

K8s集群升级避坑:kubeadm与apt-mark unhold全流程 ★ FEATURED ARTICLE
k8s 集群升级这道 CKA 真题看起来只是几条 apt 命令加一次 kubeadm upgrade真上手做才知道坑全藏在细节里。标题里那个apt-mark unhold kubeadm括号注释恰恰是很多人第一次做这道题时卡住的地方——明明apt-get install敲下去了终端却回一句软件包已经是 locked 状态或者干脆装了个旧版本上去。这篇文章就把这道题从头到尾拆一遍先讲清楚这道题在考什么、评分点落在哪里再给出控制平面节点和工作节点各自的完整操作序列最后整理一份排查速查表。不管你是准备考证的考生还是日常要维护测试集群的运维都可以照着这个顺序走一遍。文中涉及的版本号、包后缀、命令参数都基于常见实践补充你在自己的环境里落地时把版本号替换成实际目标即可。1. 真题拆解这道升级题到底在考什么1.1 任务描述还原与评分点分布这类题目的标准形态大致是这样给定一个已经跑起来的小型集群包含一到两个控制平面节点和一到两个工作节点要求把整组节点从当前小版本升级到指定的小版本只允许使用 kubeadm 工具链完成升级完成后所有节点状态必须是 Ready且kubectl get nodes里显示的 kubelet 版本要和目标版本一致。题目通常会明确写出目标版本号比如从 1.28.x 升到 1.29.x同时给出待升级的节点清单。评分点通常拆得很细常见的有这么几块kubeadm 包是否升级到目标版本、kubeadm upgrade plan是否执行过、控制平面节点的kubeadm upgrade apply是否成功、工作节点的 kubeadm 与 kubelet 是否同步升级、节点是否正确执行了 drain 和 uncordon、升级后 kubelet 服务是否处于 active 状态。有些题目还会额外给分给升级后节点未被污染也就是没有被随手改动过标签、污点或者调度的状态。这里有个容易被忽视的点很多人只盯着 kubelet 版本忘了 kubectl。题目里如果写了kubectl 也要升级到目标版本就必须在控制平面节点上单独处理 kubectl 包并且用kubectl version --client或者kubectl version --short确认版本号。缺这一步扣分是静默的你根本感觉不到自己漏了。1.2 版本偏差策略为什么升级顺序不能乱Kubernetes 的组件版本不是随便配的官方有一套版本偏差策略核心约束是kube-apiserver 永远是最新版本的那一个其他组件不允许比它更新。具体到几个关键组件kubelet 允许比 kube-apiserver 落后当前策略允许落后三个小版本kube-proxy 跟着 kubelet 走偏差范围一致kubectl 允许与 kube-apiserver 相差正负一个小版本kube-scheduler 和 kube-controller-manager 则不允许超过 kube-apiserver 的版本。这套约束直接决定了操作顺序必须先把控制平面的 kubeadm 升上去再执行 upgrade apply 把 apiserver、controller-manager、scheduler、etcd 这些静态 Pod 拉起来然后才能在各个节点上升级 kubelet。如果你反过来先在节点上把 kubelet 升到目标版本那么 kubelet 版本就会短暂地高于 apiserverkubectl get nodes里会看到版本号跳了但控制平面那边的 preflight 检查很可能会报错甚至出现 kubelet 无法正常上报状态的情况。用生活化的类比apiserver 是公司里的主管kubelet 是组员。组员的版本可以比主管旧一点但不能比主管还新——否则主管看不懂组员提交的汇报格式。理解了这个关系你就不用死记硬背升级顺序自己就能推导出先控制面、后工作节点的流程。1.3 包管理器锁apt-mark hold 这个细节为什么是高频坑Debian 系发行版上kubeadm、kubelet、kubectl 这三个包在安装时通常会被apt-mark hold锁住。加锁的初衷是防止系统例行apt upgrade时把集群组件顺手升级导致版本失控——这在生产环境里是好事但在考试或者计划内升级时就成了障碍。具体现象是你执行apt-get install -y kubeadm目标版本apt 会告诉你该软件包被 hold 住了要么跳过要么直接失败。更迷惑的一种情况是命令执行完没有明显报错但kubeadm version一看还是旧版本。原因就是 apt 在遇到 held 包时的行为差异加上有些环境会配置--allow-change-held-packages之类的参数导致你以为装上了。所以标题里那句apt-mark unhold kubeadm不是废话它是整个升级流程的第一步。操作上要先apt-mark unhold kubeadm装完新版本再apt-mark hold kubeadm把锁加回去避免后续被自动升级。kubelet 和 kubectl 同理只是它们的 unhold 时机分布在不同节点、不同阶段。记住一个原则谁要升级就先给谁解锁装完立刻加锁锁的状态是这道题的隐性检查项。2. 升级前的准备把不确定性收敛到最小2.1 环境体检五条命令摸清家底动手之前先做体检这一步花五分钟能省掉后面半小时的排错。我习惯跑这么几条命令按顺序看kubectl get nodes -o wide kubectl version --short kubeadm version apt-mark showhold cat /etc/os-release第一条看节点列表、角色、状态和当前 kubelet 版本顺便记下哪些节点是控制平面、哪些是工作节点。第二条看客户端和服务端的版本确认 kubectl 与 apiserver 的偏差在允许范围内。第三条确认 kubeadm 的当前版本。第四条列出当前被锁住的软件包这就是你后面要逐个解锁的对象清单。第五条确认发行版和版本代号决定你用 apt 还是 yum/dnf 的语法。体检时还有两个地方值得顺手看一眼kubectl -n kube-system get pods确认核心组件都健康没有 CrashLoopBackOff 的 Pod 挂着kubectl get pdb -A看看有没有 PodDisruptionBudget这个直接影响后面 drain 节点会不会卡住。如果集群里跑着带 PDB 的业务负载drain 时可能被拦住得提前想好是临时调整 PDB 还是接受排水失败后手工处理。2.2 备份etcd 快照和配置目录双保险升级是有回退需求的虽然考试环境往往不要求你真回滚但备份动作本身是职业习惯也是防止手滑的安全网。两件事etcd 快照和 /etc/kubernetes 目录拷贝。etcd 快照的命令在控制平面节点上执行路径和证书按实际情况替换ETCDCTL_API3 etcdctl snapshot save /opt/etcd-backup-$(date %F-%H%M).db \ --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执行完用etcdctl snapshot status看一眼文件大小和哈希确认快照是可用的而不是个空壳。配置目录的备份更简单cp -a /etc/kubernetes /opt/kubernetes-backup-$(date %F-%H%M)把整个目录连同 kubeconfig、静态 Pod 清单、PKI 证书一起打包。这个目录不大但一旦升级过程中静态 Pod 清单被改坏它就是最快的恢复来源。注意备份目录不要放在会被 drain 影响的本地临时路径上也不要把快照文件塞进 /tmp某些系统重启会清理。选一个固定目录命名带上时间戳方便回查。2.3 仓库与版本号确认别让 apt 帮你选版本升级前必须确认目标版本在软件源里确实存在。用apt-cache madison kubeadm可以列出仓库里所有可安装的版本输出里会带上类似1.29.0-1.1这样的包后缀。这个后缀很重要Kubernetes 的 deb 包版本号是上游版本-打包修订号的格式你写apt-get install kubeadm1.29.0可能匹配不上得写全kubeadm1.29.0-1.1。另外要确认软件源指向的仓库地址与你当前集群的版本线匹配。如果源里只有旧版本apt-get update之后仍然看不到目标版本那就得先调整源配置再继续。这一步在考试环境里通常不用操心题目会给好源但在自建环境里源配置错误导致的版本不存在报错非常常见而且报错信息不直观容易让人以为是网络问题。还有一个细节apt-get update要单独执行一次不要指望 install 命令自动刷新索引。仓库索引过期时你看到的可用版本列表是旧的install 会告诉你找不到指定版本实际原因只是索引没更新。3. 控制平面节点升级实操全流程3.1 第一步解锁并安装目标版本的 kubeadm控制平面节点的第一件事永远是 kubeadm 本身。命令序列如下注意 unhold 和 hold 的配对apt-mark unhold kubeadm apt-get update apt-get install -y kubeadm1.29.0-1.1 apt-mark hold kubeadm kubeadm version最后那条kubeadm version是必须的验证动作输出里会明确写着GitVersion:v1.29.0。看不到这个版本号后面的步骤全都白搭。我见过有人 unhold 之后忘了apt-get update结果装回去的还是旧版本然后对着kubeadm upgrade plan的输出发懵以为集群状态有问题。这里解释一下为什么先只升 kubeadm不把 kubelet 和 kubectl 一起装。原因在于 kubeadm 是升级动作的执行者它需要先具备识别目标版本、生成新静态 Pod 清单、执行 preflight 检查的能力。而 kubelet 是被升级的对象必须等控制平面的 apiserver 先升上去、版本偏差允许之后才能动。kubectl 则是纯客户端工具放最后处理没有风险。3.2 第二步读透 kubeadm upgrade plan 的输出kubeadm upgrade plan是只读操作不改任何东西但它的输出信息量很大。典型输出会包含三块内容当前集群版本与 kubeadm 版本、可升级到的目标版本列表、以及各核心组件的版本对照比如 etcd、CoreDNS、kube-proxy 分别会升到什么版本。读这块输出的时候重点关注两件事。一是目标版本是否出现在可以升级到的列表里如果没有说明 kubeadm 的版本或者仓库里的版本信息不对。二是输出末尾有没有 preflight 相关的警告比如 kubelet 版本与目标版本不匹配、某个节点不可达、swap 未关闭之类。这些警告在upgrade apply阶段会变成阻塞项。如果 plan 阶段报出 the kubelet version is higher than the control plane version 这类错误说明之前有人把某个节点的 kubelet 提前升上去了得先把控制平面升到位再处理。如果是 cannot determine the node name 之类的报错通常是 kubeconfig 指向不对检查/etc/kubernetes/admin.conf或者当前用户的~/.kube/config是否可用。提示plan 的输出建议用| tee存一份到文件里升级出问题时可以对照着看哪些组件本该升级却没升。3.3 第三步apply 执行与静态 Pod 滚动重建确认 plan 没问题执行 applykubeadm upgrade apply v1.29.0 -y-y参数省掉交互确认考试环境里有用因为有些终端会把确认提示吞掉导致看起来卡住。执行过程中会看到它依次做这些事检查集群健康状态、拉取新版本的控制平面镜像、更新/etc/kubernetes/manifests下的静态 Pod 清单、等待 apiserver、controller-manager、scheduler、etcd 依次重启并就绪、升级 CoreDNS 和 kube-proxy。中间会出现一段等待的时间因为静态 Pod 重建后 kubelet 需要重新拉起容器apiserver 重启期间 kubectl 会短暂连不上。不要在这个阶段慌着敲命令等它自己走完。全流程走完会输出 SUCCESS! Your cluster was upgraded to v1.29.0 之类的字样。如果集群有多个控制平面节点第一个节点用kubeadm upgrade apply其余控制平面节点用kubeadm upgrade node。后者只做本节点的静态 Pod 清单和 kubelet 配置更新不执行集群级别的升级动作。顺序上每个控制平面节点都要走一遍unhold kubeadm、装新版本、hold、upgrade node这套组合。3.4 第四步kubelet 与 kubectl 收尾控制平面的 kubeadm 升级完成后接着处理本节点的 kubelet 和 kubectlapt-mark unhold kubelet kubectl apt-get install -y kubelet1.29.0-1.1 kubectl1.29.0-1.1 apt-mark hold kubelet kubectl systemctl daemon-reload systemctl restart kubelet systemctl status kubelet --no-pagerdaemon-reload不能省因为 kubelet 的 systemd unit 文件或者配置可能在新包里发生了变化不重载的话重启会用到旧配置。重启后看状态是否 active (running)同时用kubectl get nodes确认控制平面节点的 kubelet 版本已经刷新。这里有个实操心得kubelet 重启后节点状态会短暂变成 NotReady 或者 SchedulingDisabled等十几秒会自动恢复 Ready。如果你在读秒阶段看节点状态就急着重启服务反而可能把一次正常的滚动弄成反复重启。我的做法是重启完sleep 20再看或者用kubectl get nodes -w盯着状态变化。4. 工作节点升级与集群回归验证4.1 drain 的正确姿势与 PDB 拦截处理工作节点升级前必须排水让上面的业务 Pod 迁移到其他节点。标准命令是kubectl drain node01 --ignore-daemonsets --delete-emptydir-data--ignore-daemonsets不能省因为每个节点上都有 DaemonSet 类型的 Pod比如 kube-proxy 和网络插件drain 默认会拒绝处理这些 Pod。--delete-emptydir-data用于清理使用 emptyDir 的 Pod在较新版本里这个参数替代了旧的--delete-local-data写错参数名会直接报错。drain 卡住是最常见的问题之一。表现是命令一直不返回或者报 Cannot evict pod as it would violate the pods disruption budget。遇到 PDB 拦截先kubectl get pdb -A找到对应的预算对象看它的 minAvailable 和当前健康 Pod 数。如果集群里副本本来就少PDB 会拦住驱逐这时候要么临时调低 PDB要么接受这个节点暂时无法完全排空。考试场景里一般不会有复杂业务负载但日常环境里这一步值得多留个心眼。还有一个容易忽略的点如果节点上有裸 Pod没有控制器管理的 Poddrain 会直接拒绝提示需要加--force。加--force意味着这个 Pod 会被直接删除且不会重建操作前得确认它是不是关键负载。4.2 工作节点的三步升级序列排水完成后工作节点上的操作顺序是升 kubeadm、跑 upgrade node、升 kubelet 和 kubectl。具体命令apt-mark unhold kubeadm apt-get update apt-get install -y kubeadm1.29.0-1.1 apt-mark hold kubeadm kubeadm upgrade node注意工作节点用的是kubeadm upgrade node不是apply。这个子命令在工作节点上的作用是更新 kubelet 的配置文件和证书不会去动静态 Pod。执行完会看到类似 kubelet configuration updated 的输出。接着处理 kubelet 和 kubectlapt-mark unhold kubelet kubectl apt-get install -y kubelet1.29.0-1.1 kubectl1.29.0-1.1 apt-mark hold kubelet kubectl systemctl daemon-reload systemctl restart kubelet工作节点上装不装 kubectl 取决于题目要求有些环境只在控制平面节点保留 kubectl。如果题目没要求装了也没坏处但别忘了跟着锁回去。节点恢复调度用kubectl uncordon node01然后在控制平面节点上确认节点状态和版本。这里要注意uncordon 之前先确认 kubelet 已经是 active 状态否则节点会以 NotReady 的状态回到调度池后续调度上去的 Pod 会启动失败。4.3 升级后的回归验证清单全部节点走完之后做一轮验证清单如下检查项命令期望结果节点版本kubectl get nodes -o wide所有节点 kubelet 版本为目标版本节点状态kubectl get nodes全部 Ready无 SchedulingDisabled核心组件kubectl -n kube-system get pods控制平面组件和 CoreDNS 均 Running客户端版本kubectl version --short客户端与服务端版本符合偏差策略kubeadm 版本kubeadm version目标版本包锁状态apt-mark showholdkubeadm、kubelet、kubectl 均在锁列表中证书状态kubeadm certs check-expiration关键证书未过期表格里最后一项容易被忽略。升级过程本身不会自动续期证书但如果集群运行时间较长升级后恰好赶上证书临近过期后续操作会莫名其妙失败。顺手检查一次成本很低。另外kubectl get pods -A看一遍全局 Pod 状态确认没有因为升级导致的异常重启网络插件和存储插件这类依赖 kubelet 接口的组件尤其值得关注。5. 常见问题与排查速查表5.1 版本与包管理类报错这类问题的根子大多在 hold 状态和版本号写法上。典型表现是 install 成功但版本没变或者直接报找不到指定版本。排查顺序先apt-mark showhold看锁再apt-cache madison kubeadm看可用版本最后核对包后缀是否写全。版本号里少写一个-1.1后缀apt 就会认为你要装的是一个不存在的版本。另一类是版本偏差报错。比如在控制平面还没升级时就去升工作节点的 kubeletkubeadm upgrade node会报 kubelet 版本高于控制平面的错误。处理办法是把顺序倒回来先完成控制平面升级。5.2 镜像拉取与静态 Pod 启动失败kubeadm upgrade apply过程中如果卡在拉镜像通常是镜像仓库不可达。先手动执行kubeadm config images pull --kubernetes-version v1.29.0看具体哪个镜像失败再检查仓库地址配置。kubeadm 的配置存在名为kubeadm-config的 ConfigMap 里可以用kubectl -n kube-system get cm kubeadm-config -o yaml查看当前的 imageRepository 设置。静态 Pod 起不来还有一个隐蔽原因清单文件权限或路径被改动。检查/etc/kubernetes/manifests下四个核心清单是否存在、属主是否正确。kubelet 只认这个默认目录路径改了它就不管了。5.3 证书、配置与节点状态异常升级后节点长时间 NotReady先看 kubelet 日志journalctl -u kubelet -n 100 --no-pager。常见原因有 kubelet 配置文件里的 cgroup driver 与容器运行时不一致、证书过期、以及 kubelet 无法连接 apiserver。cgroup driver 不一致的表现是 Pod 能调度上去但起不来日志里会有明显的驱动名称对比。如果kubectl get nodes显示的版本号还是旧的但节点上的kubelet --version已经是新的那多半是 kubelet 没重启或者 apiserver 缓存未刷新。先确认服务状态再等半分钟重新查询。下面这张表把高频问题集中列一下方便对照现象可能原因处理方向install 后版本未变包被 hold 住apt-mark unhold后重装找不到指定版本包后缀写错或索引过期补全-1.1后缀重跑apt-get updateupgrade apply 卡住镜像拉取失败手动 pull 镜像检查仓库配置drain 无法完成PDB 拦截或存在裸 Pod调整 PDB 或加--force节点 NotReadykubelet 未启动或配置错误看 journalctl核对 cgroup driver节点版本未刷新kubelet 未重启daemon-reload后重启服务静态 Pod 缺失清单目录被改动检查/etc/kubernetes/manifests6. 我在实操里总结的几条经验6.1 建立一个版本心智模型而不是背命令刚接触这道题的时候我习惯把命令序列抄在纸上按顺序敲。这种方式在环境干净时能用一旦中间某步报错就完全懵了因为不知道当前处在流程的哪个位置。后来我换了个思路把集群想象成一条从 apiserver 往下的版本链条apiserver 在最顶端kubelet 在下面kubectl 是旁挂的客户端。升级的本质是把这条链条从下往上、从执行者到被执行者逐段替换。带着这个模型很多判断就变得自然为什么先升 kubeadm因为它是执行升级的工具为什么控制平面先于工作节点因为 apiserver 必须先就位为什么 kubectl 放最后因为它不参与集群运行只影响你敲命令的体验。遇到报错时也能快速定位报错说版本偏差那就是链条顺序错了报错说包被锁那就是执行者没解锁。这种理解方式比死记命令可靠得多换个大版本或者换个发行版也能迁移过去。6.2 考场节奏先保流程完整再抠细节考试环境有时间压力这道题如果按部就班做顺利的话十几分钟能完成但如果卡在某个报错上很容易把后面的题时间挤掉。我的建议是先把主流程走通控制平面 apply 成功、工作节点 upgrade node 成功、kubelet 全部重启、节点全部 Ready。这四件事做完基本分已经拿到了。然后再回头补细节比如检查包锁状态、确认 kubectl 版本、看一眼证书有效期。另一个节奏上的经验是每个节点升级完立刻验证不要攒到最后一起查。节点多了之后你很难记住哪台机器上做过哪一步而kubectl get nodes -o wide是随时可以跑的低成本检查。发现某台机器版本没刷新当场补一条 restart比最后统一排查省事得多。还有一个容易被忽略的收尾动作升级完成后把 shell 的 kubectl 补全重新加载一次。大版本升级后补全脚本可能变化不重载会出现命令补全失效或者补全到已废弃子命令的情况虽然不影响功能但在紧张的考试节奏里挺烦人。执行source (kubectl completion bash)就行几秒钟的事。
阅读完成 · 觉得有帮助?
咨询建站