[Kubernetes] 我启动了3个Pod,为什么它们都集中在一个节点上? (调度器的秘密) 🧐

在运营Kubernetes时,总会遇到“咦?这怎么会出现在这里?”的时刻。最常见的误解就是认为“Pod会均匀地(Round-Robin)分布到各个节点上”

今天,我将通过亲身经历的“Pod集中现象”,深入剖析Kubernetes调度器是如何实际评分并选择节点的,揭示其看不见的逻辑。🚀

image

0. 问题情况:“明明有多个节点,但…”

为了测试,我依次创建了MySQL、Nginx和httpd的Pod。我们理所当然的期望是“它们会友好地分配到1号节点、2号节点、3号节点…对吧?”

但现实并非如此。

$ kubectl get pod -o wide
NAME    READY   STATUS    RESTARTS   AGE   IP           NODE                               NOMINATED NODE
mysql   1/1     Running   0          66m   10.16.2.12   gke-cluster-1-default-pool-rr03    
nx      1/1     Running   0          16m   10.16.2.13   gke-cluster-1-default-pool-rr03    
nx1     1/1     Running   0          6s    10.16.2.14   gke-cluster-1-default-pool-rr03    
httpd   1/1     Running   0          4s    10.16.2.15   gke-cluster-1-default-pool-rr03    

正如您所看到的,mysql、nx,甚至不同的镜像httpd,都持续不断地创建在名为rr03的同一个节点上。而其他节点却空空如也!😱

这是为什么呢?调度器坏了吗?


1. 消除误解:调度器不懂ABC顺序 ❌

很多人认为调度器采用“轮询(Round-Robin)”方式,就会按照节点名称顺序(A->B->C)进行部署。但Kubernetes调度器是完全基于“分数(Score)”运作的。

  1. Filtering: 淘汰不符合条件的节点
  2. Scoring: 对剩余节点进行评分 (0~100分)
  3. Ranking: 选择得分最高的节点

也就是说,rr03节点之所以持续被选中,并非因为顺序,而是因为在调度器看来,rr03一直都是“最佳候选”


2. 罪魁祸首分析:为什么已满的节点会成为第一名? 🤔

在有空节点的情况下,一个已经有3个Pod的节点却成为第一名,这是两种设置共同作用的结果。

① 未设置资源请求(Requests)= 被视为“透明人”

我在创建Pod时没有设置resources.requests(CPU/内存请求量)。

  • 调度器的视角: “这个Pod使用0 CPU,0内存?”
  • 判断: 无论rr03节点上有100个还是1000个Pod,如果只有未设置Request的Pod,调度器认为该节点的利用率仍然是“0%”。
  • 结果: “空节点和rr03反正都一样空闲?(资源分数平局)”

② 决定性因素:镜像本地性(Image Locality)与层共享

这引出了一个疑问:“如果分数平局,应该随机选择啊,为什么偏偏去了rr03?”

罪魁祸首是ImageLocality加分项

  • 部署Nginx时: 因为mysql的镜像已经下载到rr03节点,去那里更快,所以选择了rr03(可以理解)
  • 部署Httpd时: “咦?这是一个新镜像啊?”
  • 但Docker镜像是分层(Layer)结构。
  • mysql、nginx、httpd虽然不同,但它们极有可能共享底层基础镜像(Debian、Alpine等)或公共库层
  • 最终判定:
  • 空节点: “需要从头开始下载所有基础层” -> 无加分
  • rr03节点: “咦?旁边的Pod用的基础层已经有了?赚到了!” -> 获得加分 (+)

最终,资源分数平局(0点影响),但在镜像缓存分数上rr03略胜一筹,从而成为了一个不断吸纳Pod的黑洞。🕳️


3. 解决方案:如何让Pod分散开来? 🛠️

如何阻止这种现象,让Pod均匀地分布到整个集群中呢?

✅ 方法1: 明确指定资源请求(Requests)(推荐)

给Pod贴上“我需要这么多资源!”的标签。

resources:
  requests:
    cpu: "200m" # 请求0.2核心

这样,调度器就会正常判断“啊,rr03已经满了。应该发送到空节点!”并启动负载均衡(Least Allocated)逻辑。

✅ 方法2: 设置反亲和性(Anti-Affinity)

这是一种强制设置“我不喜欢和跟我一样的Pod待在一起!”的方法。

podAntiAffinity:
  requiredDuringSchedulingIgnoredDuringExecution:
    - labelSelector: ...

使用此选项,无论分数计算如何,Pod都将强制不重叠地部署。


4. 结论 📝

  1. Kubernetes调度器不按ABC顺序部署。
  2. 如果不设置requests,调度器会将Pod的大小视为“0”
  3. 在这种情况下,即使只有少量镜像层已存在的节点也会获得加分,导致Pod集中在一个地方。
  4. 为了稳定运行,设置Resource Requests不是可选项,而是必需项

今天的摸索到此结束!希望这能帮助您理解这位高傲的调度器的心思。👋


Comments

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注