KEEL · 龙骨 · A CURRICULUM FOR THE AI ERA
03 · 节点 — keel 龙骨
类型:衍生概念(服务于「Pod」) · 应用课对应:01 章 · 声明式与调谐
类型:衍生概念(服务于「Pod」) · 应用课对应:01 章 · 声明式与调谐
这一页只回答四件事:它为什么会出现、它是什么、它长什么样、它怎么工作。
一、它为什么会出现
两个具体现场,都需要"节点"这个词才讲得下去:
① 一个 Pod 卡在 Pending 不动,等多久都不会自己好
② 某天节点内存告急,被内核杀掉的是那个用得多、但其实没什么责任的 Pod
第一个现场里,"等多久都不会好"的原因是它要的东西在一台机器上放不下,
而机器不会因为你在等而变大。第二个现场里,被杀的原因是有人的容器突破了
自己能占的天花板,而那本账是几台机器共享的。
两个现场问的都是同一件事:"装得下"这件事,是拿 Pod 的什么去比什么的什么?
这个"什么"必须先存在,才能谈"装得下"。
二、它是什么
一句话:节点是集群里的一台机器——上面跑着让容器真正跑起来的那套组件,
并且提供出 CPU、内存、能放多少个 Pod 这些可被计算的容量。
它不是这两样东西——混淆会带来误判:
- 不是一台空着的、专门伺候你的机器。 节点上还跑着集群自己那一套东西,
它们也会占地方。你在"这台机器还剩多少"里看到的差值,已经扣过它们了。 - 不是"放在哪台"的决定者。 挑哪一台的是调度器;节点是被挑中的那个,
它自己不参与决定。这解释了一个很容易搞反的现象:Pod 一直Pending时,
你去挨个describe node是白费力气——该看的字段在 Pod 的describe里。
三、它长什么样
先看列表(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 还活着,它就一直在那台机器上——它不会中途换机器
第 ③ 步和第 ⑤ 步各解释一个现象:
Pending等不到头:第 ② 步比的是"可分配量减去已占用",而等待不会让这个差值变大。
要变,只能改小 requests、加节点、或者腾出资源——三件都是你的动作,不是它的。- Pod 从不搬家:所以"把一个 Pod 挪到另一台机器"这件事在集群里不存在;
你要的效果只能通过"重造一个"来实现,这也回到了上一页说的"只有重造,没有修复"。
五、它会带出哪些概念
- Pod:第 ⑤ 步"停留"说的是 Pod 的属性——落位之后不动,要换机器只能重造。→ 00 Pod
- 控制器:第 ① 步"有一个 Pod 被造出来"是它的活;它只决定该有几个,不决定放哪台。→ 01 控制器
- Service:转发规则落在每个节点上,所以"有几个节点"这件事直接决定 Service 的效果。→ 02 Service
requests与limits:第 ② 步比的只是requests,limits不进这本账——两者的使用者不同。→ 应用课 04 章
六、回到应用课
| 应用课里的那句话 | 现在你知道它为什么成立 |
|---|---|
| 01 章:"调度失败的原因是『没有任何节点满足这个 Pod 的 requests』,而节点容量不会因为等待而变大" | 第 ② 步比的是一个只减不增的差值(可分配量 − 已占用)。等待不会改变它,所以这是一条"永远重试但永远失败"的路径 |
| 01 章:"第 ⑥ 跳 kube-scheduler 按 requests 找装得下的节点" | "找"这个动作发生在调度器身上;节点只提供账本(Allocatable + 已占表),它不被咨询、也不做选择 |
04 章:"没写 limits 不是『不会触发限制』,而是『争抢时没有保护、且节点会被用到极限才触发』" |
因为节点这本账里的两栏是分开的:requests 决定能不能放,limits 决定放上去之后能被限制到哪。不写后者,账上就是 0 |
05 章:"发布卡住有两条完全不同的路径:Pending(资源)与 NotReady(探针)" |
这两条路径恰好卡在第 ③ 步和第 ④ 步:一个是没被指派,一个是指派了但没就绪。所以它们的排查方向从一开始就分开了 |
这一页改变了你的哪个判断:以后看到 Pod 卡住,先问它卡在第几步——
是没被指派(第 ③ 步,去看 requests 与节点的账),还是指派了没就绪(第 ④ 步,去看节点上的它怎么说)。
以及:看到"某台机器还剩多少"这个数字时,记住它不是"给你留了多少",
它是"所有 Pod 的 requests 合计之后剩下的"。
自测题
- 为什么 Pod 卡在
Pending时,挨个describe node是白费力气? - 一个没写
requests的容器,在节点的账本上占多少?这个数字和它实际用多少内存是什么关系?