KEEL · 龙骨 · A CURRICULUM FOR THE AI ERA

03 · 节点 — keel 龙骨

类型:衍生概念(服务于「Pod」) · 应用课对应:01 章 · 声明式与调谐

类型:衍生概念(服务于「Pod」) · 应用课对应:01 章 · 声明式与调谐

这一页只回答四件事:它为什么会出现、它是什么、它长什么样、它怎么工作。

一、它为什么会出现

两个具体现场,都需要"节点"这个词才讲得下去:

① 一个 Pod 卡在 Pending 不动,等多久都不会自己好
② 某天节点内存告急,被内核杀掉的是那个用得多、但其实没什么责任的 Pod

第一个现场里,"等多久都不会好"的原因是它要的东西在一台机器上放不下,
而机器不会因为你在等而变大。第二个现场里,被杀的原因是有人的容器突破了
自己能占的天花板,而那本账是几台机器共享的。

两个现场问的都是同一件事:"装得下"这件事,是拿 Pod 的什么去比什么的什么?
这个"什么"必须先存在,才能谈"装得下"。

二、它是什么

一句话:节点是集群里的一台机器——上面跑着让容器真正跑起来的那套组件,
并且提供出 CPU、内存、能放多少个 Pod 这些可被计算的容量。

它不是这两样东西——混淆会带来误判:

三、它长什么样

先看列表(kubectl get nodes -o wide,Kubernetes v1.37.0):

NAME                     STATUS   ROLES           AGE   VERSION   INTERNAL-IP   EXTERNAL-IP   OS-IMAGE                       KERNEL-VERSION                             CONTAINER-RUNTIME
keel-lab-control-plane   Ready    control-plane   8m    v1.37.0   172.18.0.2    <none>        Debian GNU/Linux 13 (trixie)   6.6.87.2-microsoft-standard-WSL2 (amd64)   containerd://2.3.4

STATUS 是 Ready 才说明它现在能收活。ROLES 那一列写着 control-plane,
说明这台机器同时还承担着集群自己的职责——它不是一台只给你用的机器。

再把它自己的账本打开(kubectl describe node <名字>):

Capacity:
  cpu:                32
  memory:             16210040Ki
  pods:               110
Allocatable:
  cpu:                32
  memory:             16210040Ki
  pods:               110
Non-terminated Pods:          (11 in total)
  Namespace   Name                    CPU Requests  CPU Limits  Memory Requests  Memory Limits  Age
  ---------   ----                    ------------  ----------  ---------------  -------------  ---
  default     web-565bc6668-c9g8d     0 (0%)        0 (0%)      0 (0%)           0 (0%)         3m37s
  default     web-565bc6668-wkm7t     0 (0%)        0 (0%)      0 (0%)           0 (0%)         3m58s

这三段就是"装得下"的全部依据:能提供多少(Allocatable)、已经被占了什么
(下面那张按 Pod 列的请求表)。中间那个 0 (0%) 是本章最该读准的一格——
没写 requests 的容器,在这本账上占的是 0,而它实际上要用内存。这正是上面第二个现场。

还有一件事只能在节点这一层看见:集群自己也在这些节点上。
kubectl -n kube-system get pods 会列出一整排:

coredns-559f6c778d-5tg8h                         1/1   Running
etcd-keel-lab-control-plane                      1/1   Running
kube-apiserver-keel-lab-control-plane            1/1   Running
kube-controller-manager-keel-lab-control-plane   1/1   Running
kube-scheduler-keel-lab-control-plane            1/1   Running
kube-proxy-rdx6w                                 1/1   Running

这些名字就是前面两页提过的那些角色。 它们不是飘在空中的概念,
它们也是跑在节点上的东西——kube-controller-manager 就是你上一页看见的那个控制器,
kube-scheduler 就是决定"放哪台"的那个。

四、它怎么工作

一个 Pod 落位,五步:

① 待命   有一个 Pod 被造出来了,但还没落在任何机器上
② 比账   调度器拿它的 requests,去比每台节点的 Allocatable 减去已占用的那部分
③ 指派   找到一台装得下的,把这个 Pod 指定给它;找不到就一直等(这就是 Pending)
④ 落地   被指派的那台机器上的 kubelet 接到它,去把容器造出来、启动、并持续报状态
⑤ 停留   只要这个 Pod 还活着,它就一直在那台机器上——它不会中途换机器

第 ③ 步和第 ⑤ 步各解释一个现象:

五、它会带出哪些概念

六、回到应用课

应用课里的那句话 现在你知道它为什么成立
01 章:"调度失败的原因是『没有任何节点满足这个 Pod 的 requests』,而节点容量不会因为等待而变大" 第 ② 步比的是一个只减不增的差值(可分配量 − 已占用)。等待不会改变它,所以这是一条"永远重试但永远失败"的路径
01 章:"第 ⑥ 跳 kube-scheduler 按 requests 找装得下的节点" "找"这个动作发生在调度器身上;节点只提供账本(Allocatable + 已占表),它不被咨询、也不做选择
04 章:"没写 limits 不是『不会触发限制』,而是『争抢时没有保护、且节点会被用到极限才触发』" 因为节点这本账里的两栏是分开的:requests 决定能不能放,limits 决定放上去之后能被限制到哪。不写后者,账上就是 0
05 章:"发布卡住有两条完全不同的路径:Pending(资源)与 NotReady(探针)" 这两条路径恰好卡在第 ③ 步和第 ④ 步:一个是没被指派,一个是指派了但没就绪。所以它们的排查方向从一开始就分开了

这一页改变了你的哪个判断:以后看到 Pod 卡住,先问它卡在第几步——
是没被指派(第 ③ 步,去看 requests 与节点的账),还是指派了没就绪(第 ④ 步,去看节点上的它怎么说)。
以及:看到"某台机器还剩多少"这个数字时,记住它不是"给你留了多少",
它是"所有 Pod 的 requests 合计之后剩下的"。

自测题

  1. 为什么 Pod 卡在 Pending 时,挨个 describe node 是白费力气?
  2. 一个没写 requests 的容器,在节点的账本上占多少?这个数字和它实际用多少内存是什么关系?

进入 keel 阅读