LabHub
学习 学习路径 课程

Kubernetes 运维实务

腾空节点并制定升级计划

在 LabHub 中继续学习

目标

亲手执行安全排空并恢复一台节点的流程,并把版本偏差规则与金丝雀发布计划制作成可验证的产物,而不仅是文档。

为什么重要

大多数升级事故并不是因为不知道命令,而是因为不知道顺序和范围。在不可逆操作(kubeadm upgrade apply)之前,必须先创建 etcd 快照。控制平面不支持降级,因此出错时的逃生路线不是“降回去”,而只能是“从快照恢复”。范围问题由版本偏差规则负责约束。kubelet 可以低于 apiserver,却不能高于它;只有 kubectl 允许高一个小版本。如果不了解这种不对称性,就会卡在“kubectl 可以用最新版,为什么 kubelet 不行”之类的问题上。最后,节点操作的三步(cordon → drain → uncordon)必须与 PDB 配合。没有 PDB,drain 可能使整个服务停机;PDB 过于严格,drain 又可能永远无法结束。

步骤

  1. 创建 /root/ops/upgrade/out/versions.json。顶层键为 server(格式为 v1.x.y 的服务器版本字符串)和 nodesnodeslab-node-0lab-node-1lab-node-2 为三个键,其值必须是各节点实际的 kubelet 版本字符串。
  2. 只 cordon lab-node-1,立即将节点列表保存到 /root/ops/upgrade/out/cordon.txt。文件中必须出现 lab-node-1SchedulingDisabled,而 lab-node-0 不得被禁止调度。再在 /root/ops/upgrade/out/cordon-note.txt 中用一行说明 cordon 不会影响已经运行的 Pod
  3. 创建命名空间 ops-upgrade,并在其中创建 replicas: 3 的 Deployment web(Pod 标签 app=web)。接着在同一命名空间创建 PodDisruptionBudget web-pdb,设置 spec.minAvailable: 2spec.selector.matchLabels.app: web不要使用 maxUnavailable
  4. drain lab-node-1,将输出保存到 /root/ops/upgrade/out/drain.txt。drain 完成后,把 ops-upgrade 命名空间中的 Pod 分布在哪些节点保存到 /root/ops/upgrade/out/after-drain.txt,至少三行。该文件中不得出现 lab-node-1,并且必须出现 lab-node-0lab-node-2
  5. uncordon lab-node-1,将输出保存到 /root/ops/upgrade/out/uncordon.txt。三个节点都必须恢复为可调度状态,Deployment web 的就绪副本数也必须重新达到 3。
  6. /opt/lab/fixtures/k8s/skew-template.yaml 复制到 /root/ops/upgrade/skew.yaml,填写 answers 下的问号。apiserver: "1.34" 是题目给定条件,保持不变。答案格式为 "1.31" 之类的小版本字符串,只有 downgrade_supported 使用 "yes""no"。需要填写的键为 min_kubeletmax_kubeletmax_kubectlmin_kubectlmax_controller_managernext_upgrade_targetdowngrade_supported
  7. 编写不少于 500 字节的 /root/ops/upgrade/runbook.md。每一项必须放在不同的行,并遵循以下顺序:(1) 备份 etcd 快照,(2) kubeadm upgrade plan,(3) 在第一个控制平面执行 kubeadm upgrade apply,(4) 在其余节点执行 kubeadm upgrade node;节点操作部分中,drain 必须先于 uncordon 出现。还要用句子说明不能跨越小版本升级的限制(例如“一次一个”)。
  8. 为三个节点添加 upgrade.labhub.io/stage 标签:lab-node-0canarylab-node-1batch1lab-node-2batch2。然后在 /root/ops/upgrade/out/rollout.json 中编写计划。stages 是长度为 3 的数组,每个元素包含 stagecanary/batch1/batch2)和 nodes(节点名称数组)。第一阶段必须是 canary,且恰好包含 1 个节点;三个节点都必须被分配到某个阶段。顶层还要包含 verify_between_stages 键,写明阶段之间要验证什么。

参考

收集控制平面与节点版本

创建 /root/ops/upgrade/out/versions.json。顶层键为 server(格式为 v1.x.y 的服务器版本字符串)和 nodesnodeslab-node-0lab-node-1lab-node-2 为三个键,其值必须是各节点实际的 kubelet 版本字符串。

升级从准确了解当前版本开始。服务器版本和各节点的 kubelet 版本需要从不同位置读取。节点信息位于 status.nodeInfo 下。

只禁止一台节点调度

只 cordon lab-node-1,立即将节点列表保存到 /root/ops/upgrade/out/cordon.txt。文件中必须出现 lab-node-1SchedulingDisabled,而 lab-node-0 不得被禁止调度。再在 /root/ops/upgrade/out/cordon-note.txt 中用一行说明 cordon 不会影响已经运行的 Pod

原则是一次只处理一台。cordon 只阻止今后的调度,不会影响已经运行的 Pod;请把这一点记录下来。

创建保障可用性的 PDB

创建命名空间 ops-upgrade,并在其中创建 replicas: 3 的 Deployment web(Pod 标签 app=web)。接着在同一命名空间创建 PodDisruptionBudget web-pdb,设置 spec.minAvailable: 2spec.selector.matchLabels.app: web不要使用 maxUnavailable

声明三个副本中至少必须存活多少个。最小可用数和最大不可用数不能同时设置。

排空节点并确认 Pod 迁移

drain lab-node-1,将输出保存到 /root/ops/upgrade/out/drain.txt。drain 完成后,把 ops-upgrade 命名空间中的 Pod 分布在哪些节点保存到 /root/ops/upgrade/out/after-drain.txt,至少三行。该文件中不得出现 lab-node-1,并且必须出现 lab-node-0lab-node-2

drain 在 cordon 的基础上还会驱逐现有 Pod。如果没有处理 DaemonSet 和 emptyDir 的选项,命令会被拒绝。排空后请保存 Pod 所在节点的信息。

恢复完成维护的节点

uncordon lab-node-1,将输出保存到 /root/ops/upgrade/out/uncordon.txt。三个节点都必须恢复为可调度状态,Deployment web 的就绪副本数也必须重新达到 3。

如果忘记 uncordon,该节点就会默默闲置。三个节点都应恢复可调度状态,工作负载也应回到原来的数量。

填写版本偏差表

/opt/lab/fixtures/k8s/skew-template.yaml 复制到 /root/ops/upgrade/skew.yaml,填写 answers 下的问号。apiserver: "1.34" 是题目给定条件,保持不变。答案格式为 "1.31" 之类的小版本字符串,只有 downgrade_supported 使用 "yes""no"。需要填写的键为 min_kubeletmax_kubeletmax_kubectlmin_kubectlmax_controller_managernext_upgrade_targetdowngrade_supported

基准始终是 apiserver。kubelet 可以低 3 个小版本,控制器可以低 1 个小版本,只有 kubectl 可以上下相差 1 个小版本。小版本必须一次升级一个。

编写升级 runbook

编写不少于 500 字节的 /root/ops/upgrade/runbook.md。每一项必须放在不同的行,并遵循以下顺序:(1) 备份 etcd 快照,(2) kubeadm upgrade plan,(3) 在第一个控制平面执行 kubeadm upgrade apply,(4) 在其余节点执行 kubeadm upgrade node;节点操作部分中,drain 必须先于 uncordon 出现。还要用句子说明不能跨越小版本升级的限制(例如“一次一个”)。

不可逆操作之前必须先备份。还要写明第一个控制平面与其余节点使用的 kubeadm 子命令不同。

制定金丝雀发布计划并添加标签

为三个节点添加 upgrade.labhub.io/stage 标签:lab-node-0canarylab-node-1batch1lab-node-2batch2。然后在 /root/ops/upgrade/out/rollout.json 中编写计划。stages 是长度为 3 的数组,每个元素包含 stagecanary/batch1/batch2)和 nodes(节点名称数组)。第一阶段必须是 canary,且恰好包含 1 个节点;三个节点都必须被分配到某个阶段。顶层还要包含 verify_between_stages 键,写明阶段之间要验证什么。

不要让计划只停留在文档中,要通过节点标签将它写入集群。第一阶段必须只有一台节点,而阶段之间验证什么,才是金丝雀策略的核心。