Koordlet 的核心地位
3.1 What - Koordlet 是什么?
Koordlet 是 Koordinator 在每个 Kubernetes Node 上运行的 DaemonSet,是节点侧的核心执行引擎。
核心职责(指标采集 → 策略计算 → 资源执行的闭环):
- 指标采集:收集节点和容器的性能指标(CPU、内存、IO、干扰信号等)
- 状态同步:监听 Pod、Node、NodeSLO 的变化,维护本地缓存
- 策略计算:根据指标和 NodeSLO 配置,计算资源调控策略
- 资源执行:实际修改 CGroup 参数,落地调控策略
- 指标上报:聚合指标数据,上报给控制面供调度和管理决策
3.2 Why - 为什么需要 Koordlet?
问题 1:原生系统无法感知干扰程度
原生 kubelet 的局限:
├─ 只能看到 Pod 是否 OOM(Binary:是/否)
├─ 无法量化 BE 任务对 LS 应用的干扰程度
├─ 无法判断应该采取什么行动(抑制/驱逐?多少程度?)
└─ 导致不得不采取保守的资源隔离策略
Koordlet 的方案:
├─ 采集底层干扰指标:CPU schedule latency、缓存未命中率等
├─ 精确量化干扰程度(0-100%)
├─ 根据干扰程度动态调整 CPU quota(从 4 core → 1.2 core)
└─ 实现精细化的混部调度
问题 2:容器启动后无法修改资源配置
原生方案:
├─ Pod 启动时指定 CPU request/limit
├─ 一旦创建,这些值就固定不变
└─ 无法根据集群资源压力动态调整
Koordlet 的方案:
├─ 运行时动态修改 CGroup 的 cpu.max(CPU 配额)
├─ 运行时动态修改 memory.high(内存限制)
├─ 平时宽松,紧急时严格
└─ 实现真正的动态资源管理
问题 3:无法精细化隔离 LS 和 BE
原生 CGroup 的隔离机制:
├─ CPU share:比例式分配(有限制条件下)
├─ Memory limit:硬限制(容易 OOM)
└─ 无法区分优先级(高优先级和低优先级混合在一起)
Koordlet 的创新:
├─ CPU group identity:内核级的优先级(比 share 更优先)
│ └─ groupIdentity = 0(LSR)优先级最高
│ └─ groupIdentity = -1(BE)优先级最低
├─ Memory QoS:三级保护(min/low/high)
│ └─ LSR: min=100%, low=100% → 即使系统紧张也很难被回收
│ └─ BE: min=0%, low=0% → 优先被回收
├─ LLC isolation:缓存隔离
│ └─ 防止 BE 的大容量工作集污染 LS 的热数据
└─ 多维度的隔离组合,远强于原生方案
分 - Koordlet 的七大核心模块
3.3 完整架构与模块组成
┌─────────────────────────────────────────────────────────────┐
│ Koordlet DaemonSet │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 1️⃣ StatesInformer (状态同步与维护) │ │
│ │ │ │
│ │ 职责:维护本地资源状态视图 │ │
│ │ ├─ Watch 并缓存所有本节点 Pod │ │
│ │ ├─ Watch 本节点信息 │ │
│ │ ├─ Watch NodeSLO 配置 │ │
│ │ └─ 提供高效的查询接口 │ │
│ │ │ │
│ │ 数据规模(200 Pod 节点): │ │
│ │ ├─ Pod 缓存:~50 MB │ │
│ │ ├─ Watch 延迟:< 1 秒(通常) │ │
│ │ └─ 查询延迟:< 1 ms │ │
│ └──────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 2️⃣ MetricAdvisor (指标采集框架) │ │
│ │ │ │
│ │ 职责:采集节点和容器的性能指标 │ │
│ │ ├─ NodeCollector:节点级指标 │ │
│ │ │ ├─ CPU/Memory/IO 使用率 │ │
│ │ │ ├─ CPI(Cycles Per Instruction) │ │
│ │ │ ├─ Cache miss ratio │ │
│ │ │ └─ 采集源:cgroup、procfs、BPF、perf │ │
│ │ │ │ │
│ │ ├─ ContainerCollector:容器级指标 │ │
│ │ │ ├─ 各容器 CPU/Memory/IO 使用 │ │
│ │ │ ├─ Throttled 时间(被抑制) │ │
│ │ │ └─ 采集源:cgroup、Docker stats、CRI API │ │
│ │ │ │ │
│ │ └─ BECPUSuppressionCollector:BE 干扰检测 │ │
│ │ ├─ CPU schedule latency(通过 BPF) │ │
│ │ ├─ LLC miss ratio(通过 perf) │ │
│ │ └─ 用于检测干扰程度 │ │
│ │ │ │
│ │ 采集周期: │ │
│ │ ├─ 默认:60 秒 │ │
│ │ ├─ 可配置:10-600 秒 │ │
│ │ └─ 紧急情况:可加速到 10 秒 │ │
│ │ │ │
│ │ 数据规模(200 Pod 节点,1 min 采集): │ │
│ │ ├─ 单次采集:~100 KB │ │
│ │ ├─ 每小时:~100 KB × 60 = ~6 MB │ │
│ │ └─ 保留 7 天:~1 GB │ │
│ └──────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 3️⃣ MetricCache (指标缓存与查询) │ │
│ │ │ │
│ │ 职责:缓存历史指标,支持查询和聚合 │ │
│ │ ├─ 数据结构:时间序列数据库(TSDB) │ │
│ │ ├─ 存储方式:内存或 SQLite │ │
│ │ ├─ 缓存时间:保留 24-72 小时 │ │
│ │ └─ 支持查询: │ │
│ │ ├─ 当前值 │ │
│ │ ├─ P50/P95/P99 百分位值 │ │
│ │ ├─ 平均值、最大值 │ │
│ │ └─ 趋势(上升/下降) │ │
│ │ │ │
│ │ 查询延迟:< 10 ms │ │
│ └──────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 4️⃣ QOSManager (QoS 策略执行框架) │ │
│ │ │ │
│ │ 职责:计算和执行 QoS 策略 │ │
│ │ ├─ 周期:每秒执行一次 │ │
│ │ ├─ Plugin 架构(可扩展): │ │
│ │ │ ├─ CPUSuppress:动态调整 CPU quota │ │
│ │ │ │ └─ 降低 BE Pod 的 CPU quota │ │
│ │ │ ├─ MemoryEvict:内存驱逐 │ │
│ │ │ │ └─ 选择并驱逐低优先级 Pod │ │
│ │ │ ├─ CPUBurst:CPU 动态提升 │ │
│ │ │ │ └─ 在有空闲时允许超额使用 │ │
│ │ │ ├─ Resctrl:缓存隔离 │ │
│ │ │ │ └─ 配置 LLC CAT(Cache Allocation Tag) │ │
│ │ │ ├─ BlkIO:磁盘限流 │ │
│ │ │ │ └─ 限制 IO 吞吐量和 IOPS │ │
│ │ │ └─ CGReconcile:CGroup 参数协调 │ │
│ │ │ └─ 解决多个 Plugin 的参数冲突 │ │
│ │ │ │ │
│ │ └─ 执行流程: │ │
│ │ ├─ 收集指标 → 判断压力等级 → 执行 Plugin │ │
│ │ ├─ 合并冲突 → 批量提交 ResourceExecutor │ │
│ │ └─ 上报状态 │ │
│ │ │ │
│ │ 性能指标: │ │
│ │ ├─ 周期延迟:通常 < 100 ms │ │
│ │ ├─ 能同时管理:500+ Pod │ │
│ │ └─ CPU 开销:< 5% 单核 │ │
│ └──────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 5️⃣ ResourceExecutor (资源执行器) │ │
│ │ │ │
│ │ 职责:实际修改 CGroup 参数 │ │
│ │ ├─ 操作类型: │ │
│ │ │ ├─ 创建 CGroup │ │
│ │ │ ├─ 更新 CGroup(cpu.max、memory.high 等) │ │
│ │ │ ├─ 删除 CGroup │ │
│ │ │ └─ 查询 CGroup │ │
│ │ ├─ 优化: │ │
│ │ │ ├─ 批量提交(减少系统调用) │ │
│ │ │ ├─ 错误重试(自动恢复) │ │
│ │ │ └─ 幂等性(多次调用结果相同) │ │
│ │ └─ 支持 CGroup v1 和 v2 │ │
│ │ │ │
│ │ 延迟:< 10 ms(通常) │ │
│ └──────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 6️⃣ RuntimeHooks (运行时钩子) │ │
│ │ │ │
│ │ 职责:在容器生命周期中注入资源策略 │ │
│ │ ├─ 工作方式: │ │
│ │ │ ├─ CRI Proxy 模式:代理 kubelet → 容器运行时 │ │
│ │ │ └─ NRI 模式:使用新的 Node Resource Interface │ │
│ │ ├─ 钩子点: │ │
│ │ │ ├─ Pre-create:容器创建前,计算 CGroup 参数 │ │
│ │ │ ├─ Post-start:容器启动后,注入策略 │ │
│ │ │ └─ Pre-kill:容器销毁前,清理资源 │ │
│ │ └─ 优势: │ │
│ │ └─ 容器启动时就已经应用正确的隔离策略 │ │
│ │ (相比 QOSManager 的延迟应用更早) │ │
│ │ │ │
│ │ 成功率:> 99%(除非容器运行时不支持) │ │
│ └──────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 7️⃣ PredictServer (负载预测,可选) │ │
│ │ │ │
│ │ 职责:预测未来的资源需求 │ │
│ │ ├─ 功能: │ │
│ │ │ ├─ 历史数据分析 │ │
│ │ │ ├─ 峰值预测 │ │
│ │ │ └─ 趋势分析 │ │
│ │ ├─ 应用: │ │
│ │ │ ├─ 调度器提前感知可能的需求增长 │ │
│ │ │ ├─ 避免调度新 Pod 到即将高峰的节点 │ │
│ │ │ └─ 提前触发重调度避免冲突 │ │
│ │ └─ 预测准确率:通常 70-80%(取决于工作负载) │ │
│ │ │ │
│ │ 成熟度:Beta(功能完善但仍在优化) │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
↓
┌──────────────────────────────────────────────────────────────┐
│ Linux Kernel & CGroup │
│ ├─ cpu.max / cpu.max_burst (CPU 配额) │
│ ├─ memory.min / memory.low / memory.high (内存保护) │
│ ├─ resctrl (缓存隔离) │
│ └─ blkio (磁盘限流) │
└──────────────────────────────────────────────────────────────┘
Koordlet 的完整工作流程
3.4 秒级工作循环
Koordlet 每秒的工作流程(周期 = 1 秒):
T=0ms
│
├─→ StatesInformer.Sync()
│ └─ 同步 Pod/Node/NodeSLO 的最新状态
│ └─ 延迟:< 1 秒
│
├─→ MetricAdvisor.Collect()(周期 60s,但每秒检查一次)
│ ├─ 如果本周期需要采集,从 cgroup/procfs 收集指标
│ └─ 延迟:0-10 ms(取决于是否采集)
│
├─→ MetricCache.Update()
│ └─ 如果有新指标,写入缓存
│ └─ 延迟:< 1 ms
│
├─→ QOSManager.Run()
│ ├─ 分析 → 决策 → 执行(核心步骤)
│ │
│ ├─ Step 1:分析资源压力
│ │ ├─ cpuUsagePercent = 当前 CPU 使用率
│ │ ├─ memUsagePercent = 当前内存使用率
│ │ └─ cpuScheduleLatency = CPU 调度延迟
│ │
│ ├─ Step 2:判断压力等级
│ │ ├─ 如果 cpuUsagePercent < 65% → NONE(无压力)
│ │ ├─ 如果 65% ≤ cpuUsagePercent < 80% → MID(中等压力)
│ │ └─ 如果 cpuUsagePercent ≥ 80% → HIGH(高压力)
│ │
│ ├─ Step 3:执行各 Plugin(并行)
│ │ │
│ │ ├─ CPUSuppress Plugin
│ │ │ ├─ 根据压力等级计算 BE Pod 的新 CPU quota
│ │ │ │ ├─ NONE: quota = request(全速)
│ │ │ │ ├─ MID: quota = request × 0.7(70%)
│ │ │ │ └─ HIGH: quota = request × 0.3(30%)
│ │ │ └─ 输出:{pod_xxx: {cpu.max: 1200m}, ...}
│ │ │
│ │ ├─ MemoryEvict Plugin
│ │ │ ├─ 如果 memUsagePercent > 75%
│ │ │ │ ├─ 选择低优先级 Pod(按优先级和内存占用排序)
│ │ │ │ └─ 标记为驱逐候选
│ │ │ └─ 输出:{evict: [pod_aaa, pod_bbb], ...}
│ │ │
│ │ ├─ CPUBurst Plugin
│ │ │ ├─ 计算是否有空闲 CPU
│ │ │ │ └─ 如果 cpuUsagePercent < 60% → 允许 Burst
│ │ │ └─ 输出:{pod_yyy: {cpu.max_burst: 50000}, ...}
│ │ │
│ │ └─ ... 其他 Plugin ...
│ │
│ ├─ Step 4:合并冲突
│ │ ├─ 如果多个 Plugin 对同一个 Pod 有不同的决策
│ │ ├─ 合并策略:取最严格的值(保护 LS)
│ │ └─ 输出:最终的 CGroup 更新列表
│ │
│ └─ Step 5:提交给 ResourceExecutor
│ └─ 延迟:< 50 ms
│
├─→ ResourceExecutor.Update()
│ ├─ 批量更新 CGroup
│ │ └─ 写入 cpu.max、memory.high 等参数
│ └─ 延迟:< 20 ms(批量提交优化)
│
├─→ Metrics Report(可能,周期 60s)
│ ├─ 如果本周期需要上报
│ ├─ 将聚合的指标转换为 NodeMetric 格式
│ └─ 上报给 API Server
│ └─ 延迟:< 500 ms(网络)
│
T=1000ms(1 秒后,循环重复)
3.5 生产中的实际场景案例
案例:1024 核集群,突发流量导致的动态抑制
初始状态(T=0):
├─ LS Pod:400 个,每个 2 core request,平均使用 1.5 core
│ └─ 总使用:600 core
├─ BE Pod:200 个,每个 4 core request,平均使用 2 core
│ └─ 总使用:400 core
├─ 节点总 CPU:1024 core
└─ 集群利用率:(600+400)/1024 = 98%,但资源仍然有序
T=10s:突然大量用户请求涌入(突发流量)
├─ LS Pod 的实际 CPU 使用快速上升
└─ MetricAdvisor 采集到数据(每 10s 一次,这次碰上了)
T=20s(采集周期到):MetricAdvisor 发现异常
├─ CPU 使用率:(700+400)/1024 = 107% ❌ 溢出!
│ └─ 不对,应该是 <100%,表示 CPU 被过度使用
├─ 实际情况:用户请求导致 LS 需要 750 core,但只有 650 core 可用
│ └─ 原因:BE Pod 占用了 400 core
└─ 上报到 MetricCache
T=21s:QOSManager 分析并执行
├─ CPUSuppress Plugin 评估
│ ├─ CPU 使用率 > 80% → 高压力
│ └─ BE Pod CPU quota:4 core × 0.3 = 1.2 core
├─ 计算释放资源:200 BE Pod × (4-1.2) core = 560 core
│ └─ 释放后,BE 总占用:200 × 1.2 = 240 core
├─ 新的资源分配:
│ ├─ LS 需要 750 core → 现在有 1024-240 = 784 core ✅
│ ├─ LS 实际使用:750 core(充足)
│ └─ 延迟恢复正常
└─ ResourceExecutor 批量更新所有 BE Pod 的 cpu.max
T=22s:Effect 立刻生效
├─ LS Pod 获得充分 CPU,延迟从 50ms 降回 5ms
├─ BE Pod 被限制,但仍在正常运行(只是变慢)
└─ 系统恢复稳定
T=30s(采集周期再到):继续监控
├─ 流量是否继续上升 → 继续调整
├─ 流量是否下降 → 逐步放松抑制
└─ 保持动态平衡
预期指标:
✅ LS P99 延迟:5ms(保证 SLO)
✅ BE 任务运行时间:增加 30%(被抑制,但仍在进行)
✅ 集群利用率:仍达 90%+ (比不混部的 65% 高很多)
总 - 与其他组件的协作
3.6 Koordlet 与 Control Plane 的交互
koord-manager
│ (1) 计算并创建/更新 NodeSLO
│ └─ 根据 ConfigMap 和节点特性
│ └─ 每当 ConfigMap 变化时更新
│
↓
NodeSLO CRD(节点级 QoS 参数)
│
├──────→ Koordlet (StatesInformer Watch)
│ │
│ ├─→ (2) 应用 NodeSLO 配置到本节点
│ │ └─ 更新 QOSManager 的参数
│ │
│ └─→ (3) 上报 NodeMetric
│ └─ 聚合采集的指标
│
↓
NodeMetric CRD(节点实时指标)
│
├──────→ koord-scheduler
│ │ (4) 读取 NodeMetric
│ │
│ ├─→ LoadAware Plugin
│ │ └─ 根据节点负载做出调度决策
│ │ └─ 避免调度到高负载节点
│ │
│ └─→ 其他 Plugin
│ └─ 检查资源充足性等
│
├──────→ koord-manager (NodeResource Controller)
│ │ (5) 读取 NodeMetric
│ │
│ └─→ 聚合计算可超卖资源量
│ └─ 更新各节点的可分配资源
│
└──────→ koord-descheduler
│ (6) 读取 NodeMetric
│
└─→ 发现过载节点
└─ 触发重调度
时间线:
T=0s Koordlet 启动
└─ 监听 NodeSLO、Pod、Node 变化
T=1-60s Koordlet 采集指标
└─ 每 1s 执行 QOSManager,每 60s 采集新指标
T=60s Koordlet 上报 NodeMetric
└─ koord-scheduler 和其他组件获得最新信息
T=60-120s koord-scheduler 基于新的 NodeMetric 做出调度决策
└─ 新 Pod 避免调度到高负载节点
持续的闭环和反馈
Koordlet 的性能与可靠性
3.7 生产环境的性能指标
| 指标 | 期望值 | 实际范围 | 备注 |
|---|---|---|---|
| 内存占用 | 500 MB | 200-1000 MB | 取决于 Pod 数量和缓存配置 |
| CPU 使用 | < 5% | 2-8% | 单核(采集周期有关) |
| QOSManager 周期延迟 | < 100 ms | 50-150 ms | 通常 < 100 ms |
| CGroup 更新延迟 | < 20 ms | 10-50 ms | 批量提交优化 |
| NodeMetric 上报延迟 | < 1 s | 500-1500 ms | 网络延迟 |
| Watch 延迟 | < 1 s | 100-1000 ms | 取决于 API Server 负载 |
| MetricCache 查询延迟 | < 10 ms | 1-5 ms | 内存操作 |
| 最大管理 Pod 数 | > 500 | 500-2000 | 取决于内存和 CPU |
3.8 常见问题与故障排查
问题 1:Koordlet 内存占用过高
症状:Koordlet Pod 内存占用 > 2 GB
可能原因:
1. 节点 Pod 过多(> 1000 个)
2. MetricCache 保留数据过多
3. 内存泄漏(某个模块持续增长)
诊断方法:
$ kubectl logs -f pod/koordlet-xxx -n koordinator-system | grep memory
$ kubectl top pod koordlet-xxx -n koordinator-system
$ kubectl exec -it pod/koordlet-xxx -n koordinator-system -- \
curl localhost:6060/debug/pprof/heap > heap.dump
解决方案:
1. 减少 MetricCache TTL:
--metric-cache-ttl=1800s(从 3600s 改为 1800s)
2. 启用定期 GC:
--gc-interval=30m
3. 查找内存泄漏:
分析 heap.dump 找出持续增长的对象
问题 2:CPU 抑制不生效
症状:BE Pod 被设置了 cpu.max,但 CPU 使用仍然很高
可能原因:
1. CGroup v2 未启用(系统使用 CGroup v1)
2. Kernel 版本不支持 cpu.max
3. Pod 的 limit 已经比 cpu.max 更小
4. 更新失败(权限不足或路径不对)
诊断方法:
$ kubectl get cgroup version
v2 # 应该是 v2
$ cat /proc/cmdline | grep cgroup
cgroup_enable=memory # 检查是否启用了 cgroup2
$ kubectl describe node <node> | grep -i cgroup
$ # 检查实际的 cgroup 限制
$ cat /sys/fs/cgroup/pods/<pod-id>/cpu.max
# 应该显示正确的值,如 "1200000 100000"
解决方案:
1. 升级系统和内核(需要 >= Linux 5.4)
2. 启用 CGroup v2
3. 确保 Koordlet 有权限写入 cgroup
$ # 查看 Koordlet 日志
$ kubectl logs pod/koordlet-xxx -n koordinator-system | \
grep "cpu.max\|error\|fail" | tail -20
问题 3:NodeMetric 上报延迟大
症状:NodeMetric 的 updateTime 远后于当前时间
可能原因:
1. API Server 负载过高
2. 网络延迟
3. Koordlet 采集/上报的周期太长
诊断方法:
$ kubectl get nodemetric node-001 -o yaml | grep updateTime
updateTime: "2024-01-01T12:00:00Z" # 应该是最近的时间
$ # 查看 Koordlet 采集是否正常
$ kubectl logs pod/koordlet-xxx -n koordinator-system | \
grep "collect\|report" | tail -10
解决方案:
1. 减少上报周期:
--nodemetric-report-interval=30s(从 60s 改为 30s)
2. 增加 API Server 的资源限制
3. 检查网络连接:
$ kubectl exec pod/koordlet-xxx -n koordinator-system -- \
curl -v https://kubernetes.default.svc/api/v1/
总结 - 章节关键要点
3.9 快速参考表
| 模块 | 周期 | 输入 | 输出 | 关键参数 |
|---|---|---|---|---|
| StatesInformer | 实时 | Watch Pod/Node/NodeSLO | 状态缓存 | watch_timeout=30s |
| MetricAdvisor | 60s | cgroup/procfs/BPF | 指标数据 | collect_interval=60s |
| MetricCache | 实时 | 指标数据 | 可查询的缓存 | ttl=3600s |
| QOSManager | 1s | 指标+NodeSLO | CGroup 更新计划 | qos_interval=1s |
| ResourceExecutor | 按需 | CGroup 更新计划 | 修改后的参数 | batch_size=100 |
| RuntimeHooks | 生命周期 | Pod 创建事件 | 注入的参数 | hook_timeout=5s |
| PredictServer | 60s | 历史指标 | 预测值 | predict_window=3600s |
3.10 后续深入学习
下一章(第 4 章)将深入 StatesInformer 的实现细节:
- 如何高效地维护 Pod/Node 状态缓存
- Watch 机制的优化
- 状态一致性保证
本章核心收获: 理解 Koordlet 的 7 大核心模块及职责 掌握每秒工作循环的完整流程 学会从生产案例分析动态调控过程 了解性能指标和故障排查方法 理解 Koordlet 与 Control Plane 的协作机制