3. 资源配额概念:Linux CGroup v2 与网络资源隔离
在 eBPF 网络观测与配额校准场景中,理解 Linux CGroup v2 的资源隔离机制是性能调优与故障排查的基础。CGroup v2 提供了统一的层级结构,通过 io、memory、cpu 以及 net 控制器实现精细化的资源配额管理。本章聚焦于网络资源隔离,重点解析带宽、连接数、数据包速率等核心配额指标及其在 eBPF 观测中的校准方法。
3.1 CGroup v2 网络控制器架构
CGroup v2 将网络资源隔离抽象为两个主要维度:带宽控制(通过 net_prio 和 net_cls 的演进)与 连接/包速率控制(通过 eBPF 辅助的 cgroup 钩子)。与 CGroup v1 不同,v2 采用单一层级树,所有进程归属于唯一的 cgroup 节点,避免了 v1 中多层级冲突的问题。
net_prio 和 net_cls 控制器,转而推荐使用 eBPF 程序挂载到 cgroup 套接字钩子(BPF_CGROUP_INET_INGRESS / BPF_CGROUP_INET_EGRESS)实现精细化的网络流量控制与统计。
3.2 核心网络配额指标
网络资源配额通常从三个维度进行度量:带宽(吞吐量)、连接数(并发与新建速率)、数据包速率(PPS)。每个指标对应不同的性能瓶颈与校准策略。
| 配额指标 | 单位 | 典型限制场景 | eBPF 观测方法 | 校准目标 |
|---|---|---|---|---|
| 带宽 | bps / Bps | 容器出向带宽上限、跨 AZ 流量限速 | BPF_CGROUP_INET_EGRESS 统计字节数 |
避免拥塞丢包,保障 QoS |
| 连接数 | 个(并发)/ 个/s(新建) | 四层负载均衡连接表溢出、短连接风暴 | BPF_CGROUP_INET_SOCK_CREATE 跟踪新建 |
防止连接耗尽,控制内存开销 |
| 数据包速率 | pps | DDoS 小包攻击、高频心跳包限速 | BPF_CGROUP_INET_INGRESS 统计包数 |
保护 CPU 免于软中断过载 |
3.3 带宽配额:令牌桶实现原理
带宽配额的核心是控制单位时间内通过的数据量。CGroup v2 本身不直接提供带宽限制接口,但 eBPF 程序可以在 cgroup 套接字钩子中实现令牌桶逻辑。以下是一个简化的 eBPF 带宽配额伪代码:
// 伪代码:eBPF 带宽令牌桶
struct _bucket {
__u64 rate; // 字节/秒
__u64 burst; // 突发大小(字节)
__u64 tokens; // 当前令牌数
__u64 last_time; // 上次更新时间(ns)
};
// 在 BPF_CGROUP_INET_EGRESS 钩子中调用
static inline bool consume_(struct _bucket *tb, __u32 bytes) {
__u64 now = bpf_ktime_get_ns();
// 计算新增令牌
__u64 elapsed = now - tb->last_time;
__u64 new_tokens = (elapsed * tb->rate) / NSEC_PER_SEC;
tb->tokens = min(tb->tokens + new_tokens, tb->burst);
tb->last_time = now;
// 检查是否足够
if (tb->tokens >= bytes) {
tb->tokens -= bytes;
return true; // 允许通过
}
return false; // 限速丢包或延迟
}
bpf_ktime_get_ns() 调用开销较高(约 50-100ns/次)。在高 PPS 场景下,建议使用 bpf_jiffies64 或批量更新时间戳,避免每次包都触发高精度时间读取。
3.4 连接数配额:并发与新建速率
连接数配额分为两个子指标:并发连接数(当前活跃连接总数)和新建连接速率(每秒新建连接数,CPS)。eBPF 通过挂载到 BPF_CGROUP_INET_SOCK_CREATE 钩子来拦截新建连接请求,并在 BPF_CGROUP_INET_SOCK_DESTROY 钩子中释放计数。
3.5 数据包速率配额:漏桶与 PPS 限制
数据包速率(PPS)限制通常用于防御小包攻击或控制 CPU 软中断负载。漏桶算法以恒定速率处理数据包,超出部分被丢弃或排队。eBPF 实现时,在 BPF_CGROUP_INET_INGRESS 钩子中维护一个包计数器和时间窗口。
// 伪代码:eBPF 数据包速率漏桶
struct leaky_bucket {
__u64 pps_limit; // 每秒允许的包数
__u64 last_sec; // 上一秒的时间戳(秒级)
__u64 pkt_count; // 当前秒内的包计数
};
static inline bool check_pps(struct leaky_bucket *lb) {
__u64 now_sec = bpf_ktime_get_sec(); // 秒级时间
if (now_sec != lb->last_sec) {
// 新的一秒,重置计数
lb->last_sec = now_sec;
lb->pkt_count = 0;
}
if (lb->pkt_count >= lb->pps_limit) {
return false; // 超过 PPS 限制,丢包
}
__sync_fetch_and_add(&lb->pkt_count, 1);
return true;
}
requests 和 limits 对齐。例如,设置 bandwidth: 100Mbps 对应令牌桶的 rate = 12.5MB/s,burst = 12.5MB(允许 1 秒突发)。连接数配额建议设置为 Pod 预期最大并发连接数的 1.5 倍,避免误杀正常流量。
3.6 配额校准的常见故障模式
当网络配额设置不合理或 eBPF 程序存在缺陷时,会出现以下典型故障:
- 带宽配额过小:导致 TCP 吞吐量下降,重传率升高,应用层超时。eBPF 观测指标:
tcpi_delivery_rate远低于链路容量。 - 连接数配额耗尽:新建连接被
-EPERM拒绝,客户端出现connection refused错误。eBPF 观测指标:BPF_CGROUP_INET_SOCK_CREATE返回码非零。 - PPS 限速过于激进:导致正常小包业务(如 DNS、健康检查)被丢包,服务出现间歇性不可用。eBPF 观测指标:
ingress_packets_dropped计数器持续增长。 - 时间戳精度不足:使用
bpf_jiffies64或秒级时间戳时,在高速率场景下出现配额计算偏差,导致限速不准确。
| 故障现象 | 可能原因 | eBPF 校准方法 | 修复建议 |
|---|---|---|---|
| TCP 吞吐量低 | 带宽令牌桶 rate 设置过小 | 观测 tokens 是否频繁耗尽 |
增大 rate 或 burst 值 |
| 连接被拒绝 | 并发连接数达到上限 | 检查 concurrent_count map 值 |
提高连接数上限或优化连接复用 |
| 小包丢包率高 | PPS 漏桶限速过严 | 统计 pkt_count 与 pps_limit 比值 |
增大 pps_limit 或启用 burst 模式 |
| 配额波动大 | 时间戳更新粒度太粗 | 对比纳秒与毫秒级时间戳的精度 | 改用 bpf_ktime_get_ns 并缓存 |
bpftool cgroup list 可以查看当前挂载的 eBPF 程序及其关联的 cgroup 路径。