k8s~避免调度资源碎片化

调度资源碎片化详解

调度资源碎片化是指:由于每个Pod的Request设置不合理,导致节点上虽然实际物理内存还有大量空闲,但因为Request的"虚占"导致无法调度新Pod的现象。

这类似于硬盘文件系统的碎片化——明明总空闲空间很大,但因为没有连续的大块空间,无法存储大文件。


核心概念:Request是"预留",不是"占用"

# 关键理解
resources:
  requests:
    memory: 8Gi   # ← 这是"预留车位",不是"实际停车"
  limits:
    memory: 16Gi  # ← 这是"最大允许停的车辆数"

类比:停车场管理

┌─────────────────────────────────────────────────────┐
│  停车场总车位:32个                                   │
├─────────────────────────────────────────────────────┤
│  每个公司可以"预定"车位(Request)                    │
│  但实际停了几辆车(实际使用)没人管                    │
│                                                      │
│  问题:                                               │
│  - 公司A预定8个车位,实际停了9.5辆车(超停)          │
│  - 公司B预定6个车位,实际停了3辆车                    │
│  - 总预定车位:8+6+... = 26个                        │
│  - 剩余可预定:32-26=6个                             │
│                                                      │
│  实际物理空闲:还有其他6个车位空着?                   │
│  不对!因为公司A超停了1.5辆,实际只空4.5个车位         │
│  但调度系统只看预定数,认为只能再接收6个车位的预定      │
└─────────────────────────────────────────────────────┘

具体例子演示

场景设定

# 节点配置
节点内存:32Gi

# 现有Pod
Pod1: Request=4Gi, Limit=8Gi,  实际使用=3Gi
Pod2: Request=6Gi, Limit=12Gi, 实际使用=5Gi  
Pod3: Request=8Gi, Limit=16Gi, 实际使用=2Gi
你的Pod: Request=8Gi, Limit=16Gi, 实际使用=9.5Gi  # ← 问题Pod

调度器视角(只看Request)

# 调度器计算可调度内存
总节点内存: 32Gi
已分配Request总和: 4+6+8+8 = 26Gi
剩余可调度Request: 32 - 26 = 6Gi

# 调度器结论
"节点只剩下6Gi的调度配额,只能调度Request≤6Gi的Pod"

实际情况(物理内存)

# 真实物理内存使用
Pod1实际: 3Gi
Pod2实际: 5Gi
Pod3实际: 2Gi
你的Pod实际: 9.5Gi
实际总使用: 3+5+2+9.5 = 19.5Gi

# 物理空闲内存
物理空闲: 32 - 19.5 = 12.5Gi

# 矛盾!
物理空闲: 12.5Gi
可调度配额: 6Gi  ← 怎么只有一半?

可视化对比

物理内存实际使用(真实情况):
┌──────────────────────────────────────────────────┐
│ 32GB 物理内存                                      │
├──────────────────────────────────────────────────┤
│ ████ Pod1 3G                                      │
│ ██████ Pod2 5G                                    │
│ ██ Pod3 2G                                        │
│ ██████████ 你的Pod 9.5G                           │
│ ░░░░░░░░░░░░░░░░░░░░░░░░░░░ 空闲 12.5G            │
└──────────────────────────────────────────────────┘

调度器眼中的分配(Request视角):
┌──────────────────────────────────────────────────┐
│ 32GB 总容量                                       │
├──────────────────────────────────────────────────┤
│ ████ Pod1 Request 4G (实际只用3G,虚占1G)         │
│ ██████ Pod2 Request 6G (实际只用5G,虚占1G)       │
│ ████████ Pod3 Request 8G (实际只用2G,虚占6G) ← 严重虚占│
│ ████████ 你的Pod Request 8G (实际用9.5G,倒欠1.5G)│
├──────────────────────────────────────────────────┤
│ 已分配Request总和: 26G                            │
│ 剩余可调度: 6G                                     │
└──────────────────────────────────────────────────┘

关键:12.5G物理空闲 ≠ 6G可调度配额
差值 6.5G 就是碎片化浪费!

为什么会产生6.5G的碎片?

碎片来源1:Request高于实际使用(向上虚占)

Pod3: Request=8G, 实际=2G
虚占: 8G - 2G = 6G  ← 这6G物理内存是空闲的,但被"预留"了

碎片来源2:Request低于实际使用(向下透支)

你的Pod: Request=8G, 实际=9.5G  
透支: 9.5G - 8G = 1.5G  ← 这1.5G是"偷"了其他Pod预留的空间

净效应

# 正确计算
物理总内存: 32G
实际使用: 3+5+2+9.5 = 19.5G
物理空闲: 12.5G

Request总和: 4+6+8+8 = 26G
调度剩余配额: 32-26 = 6G

碎片化浪费 = 物理空闲 - 调度剩余配额 
           = 12.5G - 6G 
           = 6.5G

这6.5G物理内存明明空闲,但调度器认为已经用完了!

碎片化的后果

后果1:无法调度中等规格的Pod

# 现在有个新Pod需要 Request=8G(例如标准的Java微服务)
节点剩余调度配额: 6G  ← 不足8G,无法调度

# 但实际上物理内存空闲 12.5G,完全可以运行!
# 导致这个Pod必须调度到其他节点,增加跨节点通信成本

后果2:节点利用率低

# 节点实际利用率
实际使用: 19.5G / 32G = 61%

# 但调度器认为
Request占用: 26G / 32G = 81%  ← 看起来快满了

# 结果:节点看起来"满了",实际还很空闲
# 导致整个集群需要更多节点,增加云成本

后果3:触发不必要的Pod驱逐

# 当节点内存压力控制器(kubelet)检测到:
物理内存使用率 > 85% 时开始驱逐Pod

# 你的节点实际使用19.5G,使用率61%,完全安全
# 但因为你的Pod透支了1.5G,可能触发其他Pod被驱逐

# 例如:Pod3实际只用了2G,但K8s以为它需要8G
# 当节点内存紧张时,会按优先级驱逐Pod

如何避免碎片化?

原则1:Request应该接近实际使用

# 错误
resources:
  requests:
    memory: 8Gi    # 猜测值
  limits:
    memory: 16Gi

# 正确(基于监控数据)
resources:
  requests:
    memory: 10Gi   # 基于实际使用9.5G + 5%缓冲
  limits:
    memory: 12Gi   # 实际使用 + 20%缓冲

原则2:定期检查并调整

# 查看Pod实际内存使用(过去7天的P99)
kubectl exec -it pod-name -- cat /sys/fs/cgroup/memory/memory.usage_in_bytes

# 使用VPA自动调整(谨慎使用)
kubectl apply -f vpa.yaml

原则3:使用Guaranteed QoS

# Request = Limit 时,QoS为Guaranteed
# 这种Pod不会被驱逐,且调度更稳定
resources:
  requests:
    memory: 10Gi
  limits:
    memory: 10Gi   # 等于Request

总结一句话

调度资源碎片化:Pod的Request就像在节点上"画地为牢",即使物理内存空闲,但Request总量满了,新Pod就无法调度,导致明明有空地却无法建新房的尴尬局面。

posted @ 2026-05-06 09:10  张占岭  阅读(0)  评论(0)    收藏  举报