4. 节点资源模型与可分配资源计算
在 Kubernetes 中,节点上真正能被 Pod 使用的 CPU 和内存,并不是 kubectl describe node 里看到的 Capacity。Capacity 表示节点整机资源总量,而调度器实际参与计算的是 Allocatable。如果忽略这一层差异,就会出现“节点看起来还有资源,Pod 却调度不上去”或“节点内存充足,却频繁驱逐 Pod”的现象。理解 Allocatable 的推导过程,是生产资源池容量规划的基础。
Allocatable 的推导公式
kubelet 启动时会根据启动参数和节点配置,计算并向 API Server 上报三类资源值:Capacity、Allocatable 和已分配资源。其中 Allocatable 的推导关系为:
Allocatable = Capacity
- kube-reserved
- system-reserved
- eviction-threshold(仅内存)
CPU 与内存的差异在于:CPU 是可压缩资源,通常不设置驱逐阈值;内存是不可压缩资源,必须为 kubelet 的驱逐机制预留安全边界。因此内存的 Allocatable 会额外减去 --eviction-hard 中声明的阈值。
三类预留的职责边界
kube-reserved 用于为 Kubernetes 自身组件预留资源,典型对象包括 kubelet、容器运行时(containerd 或 Docker)、kube-proxy 等。这些进程不属于任何业务 Pod,但会持续消耗 CPU 和内存。若不预留,它们会与业务 Pod 争抢资源,在节点压力大时导致 kubelet 自身被 OOM 或响应迟缓。
system-reserved 用于为操作系统和系统守护进程预留资源,例如 sshd、systemd、journald、udev 以及内核线程。这部分资源同样不能被调度器分配给 Pod,否则系统层在内存回收时可能触发不可预期的行为。
eviction-threshold 是 kubelet 的驱逐触发线。当节点内存可用量低于该阈值时,kubelet 会按 QoS 等级驱逐 Pod。它本质上是一块“不可被 Pod 占用”的内存缓冲,用于给 kubelet 留出反应时间,避免节点直接进入 OOM Killer 全局杀进程的状态。
生产环境中的预留配置实践
预留值没有统一标准,需要结合节点规格、运行时类型和业务特征确定。以下是一组常见参考区间,属于经验性示例,实际值应通过监控节点上 kubelet、containerd、systemd 等进程的常驻内存峰值来校准。
| 节点内存规格 | kube-reserved 建议 | system-reserved 建议 | eviction-hard 建议 |
|---|---|---|---|
| 8 GiB | 1 GiB | 1 GiB | 1 GiB |
| 16 GiB | 1.5 GiB | 1.5 GiB | 1.5 GiB |
| 64 GiB | 2 GiB | 3 GiB | 2.5 GiB |
| 128 GiB | 3 GiB | 4 GiB | 3 GiB |
配置方式是在 kubelet 启动参数中显式声明。以 systemd 管理的 kubelet 为例,可在 /var/lib/kubelet/config.yaml 或 kubelet 的 EnvironmentFile 中设置:
# /var/lib/kubelet/config.yaml 片段
kubeReserved:
cpu: "500m"
memory: "2Gi"
ephemeral-storage: "2Gi"
systemReserved:
cpu: "500m"
memory: "3Gi"
ephemeral-storage: "2Gi"
evictionHard:
memory.available: "2.5Gi"
nodefs.available: "10%"
imagefs.available: "15%"
修改后需要重启 kubelet 使配置生效,然后通过 kubectl describe node <node-name> 查看 Allocatable 是否按预期下降。重点关注输出中的 Allocatable 与 Allocated resources 两段,前者是调度上限,后者是当前已分配量。
对生产资源池的实际影响
预留配置直接改变资源池的有效容量。假设一个集群有 20 台 64 GiB 节点,若每台预留 7.5 GiB,则整个资源池损失 150 GiB 可调度内存。如果业务侧按 Capacity 做容量规划,就会高估可用资源,导致调度失败率上升。因此容量规划必须以 Allocatable 为基准,并额外考虑 DaemonSet、系统组件等非业务 Pod 的占用。
另一个常见问题是 eviction-threshold 设置过小。当 memory.available 阈值只有 100 MiB 时,kubelet 可能在内存已经极度紧张时才触发驱逐,此时驱逐操作本身也可能因内存不足而失败,最终由内核 OOM Killer 介入,杀掉包括 kubelet 在内的任意进程。生产环境通常建议将 memory.available 设置在 1 GiB 到 3 GiB 之间,具体取决于节点规格和内存分配速度。
如何验证与持续校准
配置完成后,建议从三个层面验证。第一,检查 Allocatable 数值是否符合预期,可用以下命令快速对比:
kubectl get node <node-name> -o jsonpath='{.status.capacity.memory}{"\n"}{.status.allocatable.memory}{"\n"}'
第二,观察节点上 kubelet 和 containerd 的实际内存占用,确认 kube-reserved 是否足够。可通过 systemd-cgtop 或 crictl stats 查看。第三,在压测环境中主动制造内存压力,观察 kubelet 是否在预期阈值附近触发驱逐,以及被驱逐 Pod 的 QoS 等级是否符合预期顺序(BestEffort 优先,其次是 Burstable,最后是 Guaranteed)。
随着节点上运行的组件版本升级、容器运行时更换或业务 Pod 密度变化,预留值需要定期复核。建议将 Allocatable、已分配资源、节点实际可用内存三项指标纳入监控,当 Allocatable 与 Capacity 的差值长期偏离设计值时,及时调整预留参数,避免资源池容量与规划脱节。