TT Lab
开始
学习 学习路径 课程

Kubernetes 运维实务

没人执行 drain,Pod 却不见了

在 TT Lab 中继续学习

目标

通过真实的驱逐,确认 NoSchedule 和 NoExecute 分别对已经在运行的 Pod 做了什么,按 Pod 计算由 tolerationSeconds 决定的驱逐时刻并制成表,然后连同依据一起为有状态的工作负载确定要使用的值。

为什么重要

运维人员被叫起来处理的事故,有一半是没有人敲任何命令、Pod 却动了的情况。节点一旦停止心跳,节点控制器就会以 NoExecute 加上 node.kubernetes.io/not-ready 或 node.kubernetes.io/unreachable 污点,从那一刻起,每个 Pod 的 tolerationSeconds 就开始计时。问题在于,大多数团队从来没有写过这个值。不写的话,准入会自动加入 300 秒——也就是说,所有工作负载都在使用“5 分钟后迁走”这同一个策略。对无状态前端来说太长,对本地状态庞大的数据工作负载来说太短。理解这个值并为每个工作负载分别选择,是节点事故应对的一半。

步骤

  1. 创建命名空间 ops-evict,并在 /root/ops-eviction/web.yaml 中写入 Deployment web——副本数 3,标签 app: web,镜像 nginx:1.27.3,并通过 nodeSelector 让它只落在 kubernetes.io/hostname: lab-node-0 上。应用它,并等待 3 个 Pod 全部变为 Running。
  2. web Pod 上一行容忍度都没有写。但实际对象上却带着容忍度。请任选一个 web Pod,用 -o json 确认,并把这些容忍度按每行一条,以 <키> 制表符 <효과> 制表符 <tolerationSeconds>(占位符依次为键、效果、tolerationSeconds 的值)的格式,按键名升序保存到 /root/ops-eviction/default-tolerations.tsv。不要放入其他行。
  3. 在 /root/ops-eviction/brittle.yaml 和 /root/ops-eviction/patient.yaml 中写入两个 Pod。两者的命名空间都是 ops-evict,镜像都是 nginx:1.27.3,nodeSelector 都是 kubernetes.io/hostname: lab-node-1。这两个 Pod 只有容忍度的效果不同——brittle 的标签为 role: brittle,有一条以 operator: Exists、effect: NoSchedule 容忍键 maint 的容忍度(没有 tolerationSeconds);patient 的标签为 role: patient,有一条以 operator: Exists、effect: NoExecute、tolerationSeconds: 3600 容忍键 maint 的容忍度。应用这两个 Pod,使它们在 lab-node-1 上变为 Running。
  4. 给 lab-node-1 加上污点 maint=planned:NoSchedule。然后把 brittle 和 patient 此刻的状态,按每行一条,以 <파드이름> 制表符 <노드이름> 制表符 <phase>(占位符依次为 Pod 名称、节点名称、phase)的格式,按 Pod 名称升序保存到 /root/ops-eviction/noschedule.tsv。
  5. 给同一个节点 lab-node-1 再加上污点 maint=planned:NoExecute。不要用固定 sleep,而是用等待条件的循环等到 brittle 消失,然后把结果保存为两行到 /root/ops-eviction/noexecute.tsv——brittle 制表符 gone,patient 制表符 Running。
  6. 写出 /root/ops-eviction/shortwait.yaml 和 /root/ops-eviction/longwait.yaml。两者的命名空间都是 ops-evict,镜像都是 nginx:1.27.3,nodeSelector 都是 kubernetes.io/hostname: lab-node-2。两个 Pod 都以 operator: Exists、effect: NoExecute 容忍键 linkdown,其中 shortwait 的 tolerationSeconds: 20,longwait 的 tolerationSeconds: 3600。标签分别是 role: shortwait、role: longwait。让两个 Pod 都在 lab-node-2 上变为 Running。
  7. 模拟链路中断的情况——给 lab-node-2 加上污点 linkdown=yes:NoExecute。用条件循环等到 shortwait 消失,然后把结果保存为两行到 /root/ops-eviction/partition.tsv——shortwait 制表符 gone,longwait 制表符 Running。
  8. 先在 /root/ops-eviction/forever.yaml 中创建 Pod forever——命名空间 ops-evict,标签 role: forever,镜像 nginx:1.27.3,nodeSelector 为 kubernetes.io/hostname: lab-node-0,容忍度不写键,只有一条 operator: Exists、effect: NoExecute(没有 tolerationSeconds)。然后创建 /root/ops-eviction/evict-plan.sh——读取 ops-evict 中的所有 Pod,对每个 Pod,按 <파드이름> 制表符 <값>(占位符依次为 Pod 名称与值)的格式,仅输出到标准输出它能容忍 linkdown 的 NoExecute 多少秒。值的规则是:没有能容忍的容忍度则为 0,有但没有 tolerationSeconds 则为 never,有则为该秒数。输出必须按 Pod 名称升序。把该输出保存到 /root/ops-eviction/evict-plan.tsv。
  9. 创建命名空间 ops-ledger,并在 /root/ops-eviction/ledger.yaml 中写入 Deployment ledger——命名空间 ops-ledger,副本数 2,标签 app: ledger,镜像 nginx:1.27.3。这个工作负载的节点本地状态很大,所以在短暂中断时撑住更好。以 operator: Exists、effect: NoExecute 容忍 node.kubernetes.io/not-ready 和 node.kubernetes.io/unreachable 两个键,并且请把两个容忍度的 tolerationSeconds 定为同一个值,在 900 到 3600 之间(含)。应用后让 2 个 Pod 变为 Running,再在 /root/ops-eviction/decision.tsv 中写两行——tolerationSeconds 制表符 <고른 값>(占位符为所选的值),reason 制表符 <40자 이상의 근거 한 문장>(占位符为不少于 40 个字符的一句依据)。

参考

在不去动的节点上放置对比组

创建命名空间 ops-evict,并在 /root/ops-eviction/web.yaml 中写入 Deployment web——副本数 3,标签 app: web,镜像 nginx:1.27.3,并通过 nodeSelector 让它只落在 kubernetes.io/hostname: lab-node-0 上。应用它,并等待 3 个 Pod 全部变为 Running。

本实验在后面会故意破坏 lab-node-1 和 lab-node-2。没有对比组,就无法区分 Pod 消失的原因是污点还是其他因素。新命名空间的 default ServiceAccount 需要过一会儿才会生成,所以如果失败,请过几秒再应用。

没有人写过的容忍度已经加上了

web Pod 上一行容忍度都没有写。但实际对象上却带着容忍度。请任选一个 web Pod,用 -o json 确认,并把这些容忍度按每行一条,以 <키> 制表符 <효과> 制表符 <tolerationSeconds>(占位符依次为键、效果、tolerationSeconds 的值)的格式,按键名升序保存到 /root/ops-eviction/default-tolerations.tsv。不要放入其他行。

是准入控制器加的。创建 Pod 时,如果没有亲自写这两个键,就会自动填充;亲自写了,就不会填充。选哪个 web Pod,答案都一样。列之间用制表符分隔,所以用 jq 的 @tsv 会很方便。

同一个节点上,两个仅容忍时间不同的 Pod

在 /root/ops-eviction/brittle.yaml 和 /root/ops-eviction/patient.yaml 中写入两个 Pod。两者的命名空间都是 ops-evict,镜像都是 nginx:1.27.3,nodeSelector 都是 kubernetes.io/hostname: lab-node-1。这两个 Pod 只有容忍度的效果不同——brittle 的标签为 role: brittle,有一条以 operator: Exists、effect: NoSchedule 容忍键 maint 的容忍度(没有 tolerationSeconds);patient 的标签为 role: patient,有一条以 operator: Exists、effect: NoExecute、tolerationSeconds: 3600 容忍键 maint 的容忍度。应用这两个 Pod,使它们在 lab-node-1 上变为 Running。

容忍度是在宣告“即使加上这个污点,我也能撑住”。operator 为 Exists 时不看值,但效果必须匹配——即使键相同,效果不同就无法容忍。两个 Pod 的差别恰恰就在于此,后面的步骤会看到这个差别造成了什么。

NoSchedule 不会动已经在运行的 Pod

给 lab-node-1 加上污点 maint=planned:NoSchedule。然后把 brittle 和 patient 此刻的状态,按每行一条,以 <파드이름> 制表符 <노드이름> 制表符 <phase>(占位符依次为 Pod 名称、节点名称、phase)的格式,按 Pod 名称升序保存到 /root/ops-eviction/noschedule.tsv。

它的名字叫“不要调度”是有原因的。这个效果只阻止放置新的 Pod。patient 并不能容忍这个效果,但它保持原样才是正常的——确认什么都没有发生,就是这一步的目的。

加上 NoExecute,Pod 真的会消失

给同一个节点 lab-node-1 再加上污点 maint=planned:NoExecute。不要用固定 sleep,而是用等待条件的循环等到 brittle 消失,然后把结果保存为两行到 /root/ops-eviction/noexecute.tsv——brittle 制表符 gone,patient 制表符 Running。

NoExecute 对已经在运行的 Pod 也有效。brittle 也有 maint 容忍度,但效果是 NoSchedule,所以无法容忍这个污点。等待的循环是这样的形状——for i in $(seq 1 60); do kubectl -n ops-evict get pod brittle >/dev/null 2>&1 || break; sleep 2; done

为网络中断做准备的两个 Pod

写出 /root/ops-eviction/shortwait.yaml 和 /root/ops-eviction/longwait.yaml。两者的命名空间都是 ops-evict,镜像都是 nginx:1.27.3,nodeSelector 都是 kubernetes.io/hostname: lab-node-2。两个 Pod 都以 operator: Exists、effect: NoExecute 容忍键 linkdown,其中 shortwait 的 tolerationSeconds: 20,longwait 的 tolerationSeconds: 3600。标签分别是 role: shortwait、role: longwait。让两个 Pod 都在 lab-node-2 上变为 Running。

linkdown 是我们自己定义的自定义键。不使用内置键(not-ready、unreachable)是有原因的——这两个键的污点由节点控制器根据节点状况直接管理,手动加到 Ready 的节点上,会被立刻撤走(已经亲自确认过)。驱逐规则本身与键无关,是相同的。

让节点看起来像停止了响应,并测量时刻

模拟链路中断的情况——给 lab-node-2 加上污点 linkdown=yes:NoExecute。用条件循环等到 shortwait 消失,然后把结果保存为两行到 /root/ops-eviction/partition.tsv——shortwait 制表符 gone,longwait 制表符 Running。

如果是真正的集群,节点停止心跳之后,节点控制器会加上 node.kubernetes.io/unreachable,从那时起同样的规则就开始运转。这里的节点是 kwok 创建的模拟节点,无法切断心跳,而手动加内置键又会被节点控制器立刻撤走,所以用自定义键来做同样的事。要过 20 秒才会消失,所以请用条件循环等待。

让机器来计算驱逐时间表

先在 /root/ops-eviction/forever.yaml 中创建 Pod forever——命名空间 ops-evict,标签 role: forever,镜像 nginx:1.27.3,nodeSelector 为 kubernetes.io/hostname: lab-node-0,容忍度不写键,只有一条 operator: Exists、effect: NoExecute(没有 tolerationSeconds)。然后创建 /root/ops-eviction/evict-plan.sh——读取 ops-evict 中的所有 Pod,对每个 Pod,按 <파드이름> 制表符 <값>(占位符依次为 Pod 名称与值)的格式,仅输出到标准输出它能容忍 linkdown 的 NoExecute 多少秒。值的规则是:没有能容忍的容忍度则为 0,有但没有 tolerationSeconds 则为 never,有则为该秒数。输出必须按 Pod 名称升序。把该输出保存到 /root/ops-eviction/evict-plan.tsv。

键为空的容忍度表示容忍所有键,effect 为空表示容忍所有效果——漏掉这两种情况,forever 就不会得到 never。如果脚本直接写文件,评分器再次运行时会覆盖学员的产出,所以请只通过标准输出输出。

为有状态的工作负载确定值并留下依据

创建命名空间 ops-ledger,并在 /root/ops-eviction/ledger.yaml 中写入 Deployment ledger——命名空间 ops-ledger,副本数 2,标签 app: ledger,镜像 nginx:1.27.3。这个工作负载的节点本地状态很大,所以在短暂中断时撑住更好。以 operator: Exists、effect: NoExecute 容忍 node.kubernetes.io/not-ready 和 node.kubernetes.io/unreachable 两个键,并且请把两个容忍度的 tolerationSeconds 定为同一个值,在 900 到 3600 之间(含)。应用后让 2 个 Pod 变为 Running,再在 /root/ops-eviction/decision.tsv 中写两行——tolerationSeconds 制表符 <고른 값>(占位符为所选的值),reason 制表符 <40자 이상의 근거 한 문장>(占位符为不少于 40 个字符的一句依据)。

增大这个值,可以减少在短暂断开的节点上白白迁移的情况,但在真正死掉的节点上,服务也会相应地空缺那么久。依据中必须包含得到了什么、失去了什么。只有写在文件里的数字与实际应用到集群里的数字一致,才能通过。