1. 课程导论

在现代数据中心与云计算环境中,一台物理服务器或虚拟机通常承载着多个租户、多个应用进程甚至多个容器。当这些实体共享同一个Linux内核时,CPU资源分配的公平性与隔离性便成为系统稳定性的核心挑战。如果没有精细的调度控制,一个“吵闹的邻居”可能耗尽CPU时间片,导致其他关键服务响应延迟甚至超时。本课程导论将剖析多租户场景下的资源隔离痛点,并引出组调度与CPU带宽控制作为解决方案的必要性。

1.1 传统CPU调度的局限性

Linux内核默认的完全公平调度器(CFS)以进程为基本调度单位,其核心目标是让每个进程在宏观上获得等比例的CPU时间。然而,在多租户环境中,这种“进程级公平”存在明显缺陷:

  • 租户边界模糊:一个租户可能启动100个进程,另一个租户仅启动1个进程。CFS会平等对待这101个进程,导致第一个租户获得约99%的CPU,而第二个租户仅获得约1%。
  • 缺乏聚合控制:运维人员无法对一组进程(如一个容器、一个用户、一个服务)设置统一的CPU上限或保障。
  • 突发干扰不可控:某个租户的突发计算任务可能瞬间抢占大量CPU,影响同主机其他租户的尾延迟。
关键问题:进程级调度无法感知“组”的概念,导致资源分配与业务所有权脱节。

1.2 多租户场景下的典型资源隔离挑战

下表总结了多租户环境中常见的CPU资源冲突场景及其后果:

场景 问题描述 潜在后果
离线批处理与在线服务混部 批处理作业(如日志分析)占用大量CPU,导致在线服务(如Web API)响应变慢 服务SLA违约,用户体验下降
多容器共享宿主机 一个容器内的死循环或内存泄漏触发CPU饥饿 其他容器被无辜限流,业务中断
用户态与内核态资源争抢 某个租户频繁触发系统调用,消耗内核CPU时间 影响所有租户的系统调用延迟
突发流量与预留资源冲突 促销活动导致计算任务激增,挤占数据库查询的CPU份额 数据库连接超时,核心业务受损

1.3 组调度与CPU带宽控制:解决方案概览

Linux内核通过 cgroup(控制组) 与 组调度(Group Scheduling) 机制,将调度单位从“进程”提升到“进程组”,从而解决上述挑战。其核心思想包括:

  • 层级化分组:将进程组织成树形结构,每个节点代表一个控制组。
  • 组间公平调度:CFS在组之间分配CPU时间,而非在进程之间。组内再按策略分配给子组或进程。
  • 带宽上限控制:通过 cpu.cfs_quota_us 与 cpu.cfs_period_us 参数,限制一个组在给定周期内最多使用的CPU时间(微秒级)。
  • 权重分配:通过 cpu.shares 设置组之间的相对权重,实现弹性资源竞争。
核心价值:组调度让运维人员能够以业务维度(如“支付服务”、“日志系统”)而非进程维度管理CPU资源,实现真正的多租户隔离。

1.4 组调度与CFS的关系:内联SVG架构图

下图展示了引入组调度后,CFS调度决策的层级变化。传统调度直接面向进程(左),而组调度先按组分配时间片,再在组内调度(右)。

组调度与CFS层级架构对比 传统CFS(进程级) CFS 运行队列 进程 P1(租户A) 进程 P2(租户A) 进程 P3(租户B) 进程 P4(租户C) 问题:租户A有2个进程 租户B有1个进程 CFS给P1~P4各25% CPU 租户A获50%,租户B仅25% 组调度(cgroup感知) CFS 组调度层 组A(50%) 组B(25%) 组C(25%) P1 P2 P3 P4 改进:先按组分配CPU时间片 组A获50%,组B获25%,组C获25% 组内P1、P2再平分组A的50% 租户A总获50%,租户B获25%,公平

1.5 CPU带宽控制:硬限制与软限制

组调度提供了两种CPU控制模式,分别对应不同的隔离需求:

控制类型 接口文件 含义 典型场景
软限制(权重) cpu.shares 设置组之间的相对权重(默认1024)。CPU空闲时,组可以超出权重;竞争时按权重比例分配。 开发环境、非关键业务,允许弹性使用空闲资源。
硬限制(带宽) cpu.cfs_quota_us
cpu.cfs_period_us
在 period 微秒内,组最多使用 quota 微秒的CPU时间。超限则被节流(throttle)。 生产环境、SLA敏感服务,严格限制CPU使用上限。
注意:硬限制设置过小会导致组频繁被节流,引发CPU饥饿;设置过大则失去隔离意义。通常建议 quota / period 等于期望的CPU核心数(如 quota=200000, period=100000 表示2个核心)。

1.6 一个典型的多租户配置案例

假设一台4核服务器上运行三个容器:

  • 容器A:在线Web服务,要求最低2核保障,上限3核。
  • 容器B:离线数据分析,无严格保障,但上限1核。
  • 容器C:监控代理,保障0.5核,上限0.5核。

使用cgroup配置如下:

# 容器A:保障2核,上限3核
echo 2048 > /sys/fs/cgroup/cpu/container-A/cpu.shares
echo 300000 > /sys/fs/cgroup/cpu/container-A/cpu.cfs_quota_us
echo 100000 > /sys/fs/cgroup/cpu/container-A/cpu.cfs_period_us

# 容器B:无保障,上限1核
echo 1024 > /sys/fs/cgroup/cpu/container-B/cpu.shares
echo 100000 > /sys/fs/cgroup/cpu/container-B/cpu.cfs_quota_us
echo 100000 > /sys/fs/cgroup/cpu/container-B/cpu.cfs_period_us

# 容器C:保障0.5核,上限0.5核
echo 512 > /sys/fs/cgroup/cpu/container-C/cpu.shares
echo 50000 > /sys/fs/cgroup/cpu/container-C/cpu.cfs_quota_us
echo 100000 > /sys/fs/cgroup/cpu/container-C/cpu.cfs_period_us
效果:当所有容器同时满载时,CPU按权重(2048:1024:512)分配,约57%:29%:14%。但容器B和C受硬限制,实际最多使用25%和12.5%的CPU(1核和0.5核),剩余资源自动分配给容器A,实现“保障+弹性”的混合策略。

1.7 本章小结