1. CPU 管理基础:CPU 架构基础与调度原理

在容器运行时环境中,CPU 是竞争最激烈、隔离最敏感的硬件资源。要理解容器进程如何被分配、限制和绑定到特定 CPU,首先需要掌握现代 CPU 的物理拓扑、内存访问架构以及操作系统调度器的基本工作原理。本节将建立这些核心概念,为后续的绑核策略和性能调优打下坚实基础。

1.1 物理核、逻辑核与超线程

现代 CPU 通过超线程技术将一个物理核模拟为多个逻辑核,以提高指令级并行度。理解这三者的关系是 CPU 管理的第一步。

概念 定义 关键特征 容器视角
物理核 芯片上独立的计算单元,拥有完整的执行单元、L1/L2 缓存 真正的并行执行能力;每个物理核在同一时刻只能执行一个线程 容器绑核时,优先绑定到不同物理核可获得最佳性能隔离
逻辑核 操作系统看到的可调度单元(CPU 编号) 由超线程或物理核直接提供;/proc/cpuinfo 中的 processor 编号 容器 cpuset 配置直接操作逻辑核编号
超线程 一个物理核内共享执行资源的两个逻辑核 共享 L1/L2 缓存和 ALU;一个逻辑核空闲时另一核可借用资源 两个超线程逻辑核竞争资源,不适合延迟敏感型容器
性能调优提示: 在容器绑核场景中,应避免将两个容器进程绑定到同一物理核的两个超线程逻辑核上。这会导致严重的资源争抢和性能抖动。推荐使用 lscpu -e 命令查看 CPU 拓扑,确认哪些逻辑核共享同一物理核。
物理核与超线程结构示意图 物理核 0 L1/L2 缓存(共享) 执行单元(共享) 逻辑核 0 架构状态寄存器 逻辑核 1 架构状态寄存器 物理核 1 L1/L2 缓存(共享) 执行单元(共享) 逻辑核 2 架构状态寄存器 逻辑核 3 架构状态寄存器 关键说明 • 逻辑核 0 和 1 共享物理核 0 的资源 • 逻辑核 2 和 3 共享物理核 1 的资源 • 跨物理核绑核可获得真正并行

1.2 NUMA 架构简介

NUMA(非统一内存访问)是现代多路服务器的主流架构。在 NUMA 系统中,每个 CPU 插槽拥有本地内存,访问本地内存的速度远快于访问远端内存。容器绑核时必须考虑 NUMA 拓扑,否则会导致内存访问延迟显著增加。

  • NUMA 节点(Node): 每个节点包含一组 CPU 和本地内存。节点内访问延迟低(通常 60-100ns)。
  • 跨节点访问: 访问其他节点的内存需要经过 QPI/UPI 总线,延迟增加 1.5-2 倍(通常 120-200ns)。
  • 容器绑核原则: 容器进程应尽量绑定在同一 NUMA 节点内的 CPU 上,并分配该节点的本地内存。
特性 UMA(统一内存访问) NUMA(非统一内存访问)
内存访问延迟 所有 CPU 访问内存延迟一致 本地内存低延迟,远端内存高延迟
总线架构 单总线/交叉开关 每个节点独立内存控制器,通过互联总线通信
扩展性 CPU 数量增加时总线成为瓶颈 支持更多 CPU,扩展性好
容器影响 绑核只需考虑 CPU 缓存 绑核必须同时考虑 CPU 和内存节点亲和性
实战检查命令: 使用 numactl --hardware 查看 NUMA 节点拓扑;使用 lscpu | grep NUMA 查看节点与 CPU 的映射关系。在容器启动时,可通过 --cpuset-mems 参数指定内存节点。
NUMA 双节点架构示意图 NUMA Node 0 CPU 0-3 物理核 0,1 本地内存 延迟 ~80ns 快速 NUMA Node 1 CPU 4-7 物理核 2,3 本地内存 延迟 ~80ns 快速 QPI/UPI 互联 延迟 ~160ns 容器绑核建议:将容器进程和内存分配限制在同一 NUMA 节点内,避免跨节点访问

1.3 CPU 时间片与调度原理

Linux 内核的 CFS(完全公平调度器)是容器 CPU 管理的核心。CFS 通过时间片分配和虚拟运行时间(vruntime)机制,确保所有进程公平获得 CPU 资源。容器运行时(如 runc、containerd)通过 cgroup 的 cpu 子系统与 CFS 交互。

1.3.1 CFS 调度核心概念

概念 说明 容器调优意义
时间片(sched_latency) CFS 保证每个进程在一个调度周期内至少运行一次的时间窗口,默认 6ms 容器 CPU 限制(quota)基于此计算
虚拟运行时间(vruntime) 进程实际运行时间经过优先级加权后的值;CFS 总是选择 vruntime 最小的进程运行 nice 值调整影响容器进程的 CPU 份额
调度周期(sched_period) CFS 重新计算所有进程 vruntime 并选择下一个运行进程的间隔 影响容器 CPU 分配的细粒度
最小粒度(min_granularity) 进程被调度后至少运行的时间,默认 0.75ms,防止频繁上下文切换 对延迟敏感型容器,过小会导致切换开销

1.3.2 容器 CPU 限制的 cgroup 接口

容器运行时通过 cgroup v1 的 cpu 子系统或 cgroup v2 的 cpu 控制器来管理 CPU。以下是核心参数:

# cgroup v1 示例(/sys/fs/cgroup/cpu/docker/<container-id>/)
cpu.cfs_period_us    # 调度周期,默认 100000(100ms)
cpu.cfs_quota_us     # 在周期内允许的 CPU 时间,-1 表示无限制
                     # 例如 quota=50000, period=100000 表示使用 0.5 个 CPU
cpu.shares           # CPU 权重(相对份额),默认 1024
cpu.rt_runtime_us    # 实时调度运行时间(通常不用于容器)

# cgroup v2 示例(/sys/fs/cgroup/system.slice/docker-<id>.scope/)
cpu.max              # 格式:quota period,例如 "50000 100000"
cpu.weight           # CPU 权重,范围 [1, 10000],默认 100
常见陷阱: 当 cpu.cfs_quota_us 设置过小(例如 10000us)且容器进程数较多时,会导致严重的调度延迟和 throttling(节流)。使用 /sys/fs/cgroup/cpu/<group>/cpu.stat 中的 nr_throttled 字段可以监控容器是否被过度节流。

1.3.3 调度流程与容器上下文切换

  1. 时钟中断触发: 内核定时器(通常 1000Hz 或 250Hz)触发调度器检查。
  2. vruntime 更新: 当前运行进程的 vruntime 增加(实际运行时间 × 优先级权重因子)。
  3. 红黑树选择: CFS 从红黑树中选取 vruntime 最小的进程作为下一个运行进程。
  4. 上下文切换: 保存当前进程状态(寄存器、页表、TLB),加载新进程状态。
  5. 容器 cgroup 检查: 如果进程属于容器 cgroup,内核检查 quota 是否耗尽;若耗尽则进程进入 throttled 状态,直到下一个周期。
性能调优关键指标: 使用 perf sched 或 trace-cmd 可以分析容器进程的调度延迟和上下文切换频率。对于绑核容器,上下文切换应尽量发生在同一物理核上,避免跨核迁移导致缓存失效。
CFS 调度与容器 CPU 限制流程 1. 时钟中断 1000Hz 触发 2. 更新 vruntime 加权计算 3. 红黑树选择 最小 vruntime 4. 上下文切换 保存/加载状态 5. 容器 cgroup quota 检查 判断是否 throttled 正常运行 继续执行 Throttled 等待下一周期 quota 充足 继续调度 quota 耗尽 强制休眠

1.4 本节核心要点总结

  • 物理核 vs 逻辑核: 超线程逻辑核共享物理核资源,容器绑核时应避免绑定到同一物理核的两个逻辑核上。
  • NUMA 亲和性: 容器进程和内存分配应尽量限制在同一 NUMA 节点内,使用 numactl 和 cpuset-mems 控制。
  • CFS 调度: 通过 vruntime 红黑树实现公平调度;容器 cgroup 的 quota/period 控制 CPU 使用上限。
  • 监控关键指标: nr_throttled(节流次数)、context switches(上下文切换)、cpu migrations(CPU 迁移)是判断容器 CPU 性能瓶颈的核心指标。