[译] Cilium:基于 BPF+EDT+FQ+BBR 更好地带宽网络管理
译者序
本文翻译自 KubeCon+CloudNativeCon Europe 2022 的一篇分享:Better Bandwidth Management with eBPF。
作者 Daniel Borkmann, Christopher, Nikolay 都来自 Isovalent(Cilium 母公司)。翻译时补充了一些背景知识、代码片段和链接,以方便理解。
由于译者水平有限,本文不免存在遗漏或错误之处。如有疑问,请查阅原文。
以下是译文。
1 问题描述
1.1 容器部署密度与(CPU、内存)资源管理
下面两张图来自 Sysdig 2022 的一份调研报告,
左图是容器的部署密度分布,比如 33% 的 k8s 用户中,每个 node 上平均会部署 16~25 个 Pod;
右图是每台宿主机上的容器中位数,可以看到过去几年明显在不断增长。
这两个图说明:容器的部署密度越来越高。这导致的 CPU、内存等资源竞争将更加激烈, 如何管理资源的分配或配额就越来越重要。具体到 CPU 和 memory 这两种资源, K8s 提供了 resource requests/limits 机制,用户或管理员可以指定一个 Pod 需要用到的资源量(requests)和最大能用的资源量(limits),
apiVersion: v1
kind: Pod
metadata:name: frontend
spec:containers:- name: appimage: nginx-slim:0.8resources:requests: # 容器需要的资源量,kubelet 会将 pod 调度到剩余资源大于这些声明的 node 上去 memory: "64Mi"cpu: "250m"limits: # 容器能使用的硬性上限(hard limit),超过这个阈值容器就会被 OOM killmemory: "128Mi"cpu: "500m"
kube-scheduler 会将 pod 调度到能满足 resource.requests 声明的资源需求的 node 上;如果 pod 运行之后使用的内存超过了 memory limits,就会被操作系统以 OOM (Out Of Memory)为由干掉。这种针对 CPU 和 memory 的资源管理机制还是不错的, 那么,网络方面有没有类似的机制呢?
1.2 网络资源管理:带宽控制模型
先回顾下基础的网络知识。下图是往返时延(Round-Trip)与 TCP 拥塞控制效果之间的关系,
结合 流量控制(TC)五十年:从基于缓冲队列(Queue)到基于时间戳(EDT)的演进(Google, 2018), 这里只做几点说明:
TCP 的发送模型是尽可能快(As Fast As Possible, AFAP)
网络流量主要是靠网络设备上的出向队列(device output queue)做整形(shaping)
队列长度(queue length)和接收窗口(receive window)决定了传输中的数据速率(in-flight rate)
“多快”(how fast)取决于队列的 drain rate
现在回到我们刚才提出的问题(k8s 网络资源管理), 在 K8s 中,有什么机制能限制 pod 的网络资源(带宽)使用量吗?
1.3 K8s 中的 pod 带宽管理
1.3.1 Bandwidth meta plugin
K8s 自带了一个限速(bandwidth enforcement)机制,但到目前为止还是 experimental 状态;实现上是通过第三方的 bandwidth meta plugin,它会解析特定的 pod annotation,
kubernetes.io/ingress-bandwidth=XX
kubernetes.io/egress-bandwidth=XX
然后转化成对 pod 的具体限速规则,如下图所示,
bandwidth meta plugin 是一个 CNI plugin,底层利用 Linux TC 子系统中的 TBF, 所以最后转化成的是 TC 限速规则,加在容器的 veth pair 上(宿主机端)。
这种方式确实能实现 pod 的限速功能,但也存在很严重的问题,我们来分别看一下出向和入向的工作机制。
在进入下文之前,有两点重要说明:
限速只能在出向(egress)做。为什么?可参考 《Linux 高级路由与流量控制手册(2012)》第九章:用 tc qdisc 管理 Linux 网络带宽;
veth pair 宿主机端的流量方向与 pod 的流量方向完全相反,也就是 pod 的 ingress 对应宿主机端 veth 的 egress,反之亦然。
1.3.2 入向(ingress)限速存在的问题
对于 pod ingress 限速,需要在宿主机端 veth 的 egress 路径上设置规则。例如,对于入向 kubernetes.io/ingress-bandwidth="50M" 的声明,会落到 veth 上的 TBF qdisc 上:
TBF(Token Bucket Filter)是个令牌桶,所有连接/流量都要经过单个队列排队处理,如下图所示:
在设计上存在的问题:
TBF qdisc 所有 CPU 共享一个锁(著名的 qdisc root lock),因此存在锁竞争;流量越大锁开销越大;
veth pair 是单队列(single queue)虚拟网络设备,因此物理网卡的 多队列(multi queue,不同 CPU 处理不同 queue,并发)优势到了这里就没用了, 大家还是要走到同一个队列才能进到 pod;
在入向排队是不合适的(no-go),会占用大量系统资源和缓冲区开销(bufferbloat)。
1.3.3 出向(egress)限速存在的问题
出向工作原理:
Pod egress 对应 veth 主机端的 ingress,ingress 是不能做整形的,因此加了一个 ifb 设备;
所有从 veth 出来的流量会被重定向到 ifb 设备,通过 ifb TBF qdisc 设置容器限速。
存在的问题:
原来只需要在物理网卡排队(一般都会设置一个默认 qdisc,例如 pfifo_fast/fq_codel/noqueue),现在又多了一层 ifb 设备排队,缓冲区膨胀(bufferbloat);
与 ingress 一样,存在 root qdisc lock 竞争,所有 CPU 共享;
干扰 TCP Small Queues (TSQ) 正常工作;TSQ 作用是减少 bufferbloat, 工作机制是觉察到发出去的包还没有被有效处理之后就减少发包;ifb 使得包都缓存在 qdisc 中, 使 TSQ 误以为这些包都已经发出去了,实际上还在主机内。
延迟显著增加:每个 pod 原来只需要 2 个网络设备,现在需要 3 个,增加了大量 queueing 逻辑。
1.3.4 Bandwidth meta plugin 问题总结
总结起来:
扩展性差,性能无法随 CPU 线性扩展(root qdisc lock 被所有 CPU 共享导致);导致额外延迟;占用额外资源,缓冲区膨胀。因此不适用于生产环境;
2 解决思路
这一节是介绍 Google 的基础性工作,作者引用了 Evolving from AFAP: Teaching NICs about time (Netdev, 2018) 中的一些内容;之前我们已翻译,见 流量控制(TC)五十年:从基于缓冲队列(Queue)到基于时间戳(EDT)的演进(Google, 2018), 因此一些内容不再赘述,只列一下要点。
译注。
2.1 回归源头:TCP “尽可能快”发送模型存在的缺陷
2.2 思路转变:不再基于排队(queue),而是基于时间戳(EDT)
两点核心转变:
每个包(skb)打上一个最早离开时间(Earliest Departure Time, EDT),也就是最早可以发送的时间戳;
用时间轮调度器(timing-wheel scheduler)替换原来的出向缓冲队列(qdisc queue)
2.3 3 EDT/timing-wheel 应用到 K8s
有了这些技术基础,我们接下来看如何应用到 K8s。
3 Cilium 原生 pod 限速方案
3.1 整体设计:基于 BPF+EDT 实现容器限速
Cilium 的 bandwidth manager,
基于 eBPF+EDT,实现了无锁 的 pod 限速功能;
在物理网卡(或 bond 设备)而不是 veth 上限速,避免了 bufferbloat,也不会扰乱 TCP TSQ 功能。
不需要进入协议栈,Cilium 的 BPF host routing 功能,使得 FIB lookup 等过程完全在 TC eBPF 层完成,并且能直接转发到网络设备。
在物理网卡(或 bond 设备)上添加 MQ/FQ,实现时间轮调度。
3.2 工作流程
在之前的分享 为 K8s workload 引入的一些 BPF datapath 扩展(LPC, 2021) 中已经有比较详细的介绍,这里在重新整理一下。
Cilium attach 到宿主机的物理网卡(或 bond 设备),在 BPF 程序中为每个包设置 timestamp, 然后通过 earliest departure time 在 fq 中实现限速,下图:
注意:容器限速是在物理网卡上做的,而不是在每个 pod 的 veth 设备上。这跟之前基于 ifb 的限速方案有很大不同。
从上到下三个步骤:
BPF 程序:管理(计算和设置) skb 的 departure timestamp;
TC qdisc (multi-queue) 发包调度;
物理网卡的队列。
如果宿主机使用了 bond,那么根据 bond 实现方式的不同,FQ 的数量会不一样, 可通过 tc -s -d qdisc show dev {bond} 查看实际状态。具体来说,
Linux bond 默认支持多队列(multi-queue),会默认创建 16 个 queue, 每个 queue 对应一个 FQ,挂在一个 MQ 下面,也就是上面图中画的;OVS bond 不支持 MQ,因此只有一个 FQ(v2.3 等老版本行为,新版本不清楚)。bond 设备的 TXQ 数量,可以通过 ls /sys/class/net/{dev}/queues/ 查看。物理网卡的 TXQ 数量也可以通过以上命令看,但 ethtool -l {dev} 看到的信息更多,包括了最大支持的数量和实际启用的数量。
3.3 数据包处理过程
先复习下 Cilium datapath,细节见 2020 年的分享:
egress 限速工作流程:
Pod egress 流量从容器进入宿主机,此时会发生 netns 切换,但 socket 信息 skb->sk 不会丢失;
Host veth 上的 BPF 标记(marking)包的 aggregate(queue_mapping),见 Cilium 代码;
物理网卡上的 BPF 程序根据 aggregate 设置的限速参数,设置每个包的时间戳 skb->tstamp;
FQ+MQ 基本实现了一个 timing-wheel 调度器,根据 skb->tstamp 调度发包。过程中用到了 bpf map 存储 aggregate 信息。
3.4 性能对比:Cilium vs. Bandwidth meta plugin
netperf 压测。
同样限速 100M,延迟下降:
同样限速 100M,TPS:
3.5 小结
主机内的问题解决了,那更大范围 —— 即公网带宽 —— 管理呢?
别着急,EDT 还能支持 BBR。
4 公网传输:Cilium 基于 BBR 的带宽管理
4.1 BBR 基础
想完整了解 BBR 的设计,可参考 (论文) BBR:基于拥塞(而非丢包)的拥塞控制(ACM, 2017)。译注。
4.1.1 设计初衷
4.1.2 性能对比:bbr vs. cubic
CUBIC + fq_codel:
BBR + FQ (for EDT):
效果非常明显。
4.2 BBR + K8s/Cilium
4.2.1 存在的问题:跨 netns 时,skb->tstamp 要被重置
BBR 能不能用到 k8s 里面呢?
BBR + FQ 机制上是能协同工作的;但是,
内核在 skb 离开 pod netns 时,将 skb 的时间戳清掉了,导致包进入 host netns 之后没有时间戳,FQ 无法工作.
问题如下图所示,
4.2.2 为什么会被重置
下面介绍一些背景,为什么这个 ts 会被重置。
几种时间规范:https://www.cl.cam.ac.uk/~mgk25/posix-clocks.html
对于包的时间戳 skb->tstamp,内核根据包的方向(RX/TX)不同而使用的两种时钟源:
Ingress 使用 CLOCK_TAI (TAI: international atomic time)
Egress 使用 CLOCK_MONOTONIC(也是 FQ 使用的时钟类型)
如果不重置,将包从 RX 转发到 TX 会导致包在 FQ 中被丢弃,因为 超过 FQ 的 drop horizon。FQ horizon 默认是 10s。
horizon 是 FQ 的一个配置项,表示一个时间长度, 在 net_sched: sch_fq: add horizon attribute 引入,
QUIC servers would like to use SO_TXTIME, without having CAP_NET_ADMIN,
to efficiently pace UDP packets.As far as sch_fq is concerned, we need to add safety checks, so
that a buggy application does not fill the qdisc with packets
having delivery time far in the future.This patch adds a configurable horizon (default: 10 seconds),
and a configurable policy when a packet is beyond the horizon
at enqueue() time:
- either drop the packet (default policy)
- or cap its delivery time to the horizon.
简单来说,如果一个包的时间戳离现在太远,就直接将这个包 丢弃,或者将其改为一个上限值(cap),以便节省队列空间;否则,这种 包太多的话,队列可能会被塞满,导致时间戳比较近的包都无法正常处理。内核代码如下:
static bool fq_packet_beyond_horizon(const struct sk_buff *skb, const struct fq_sched_data *q)
{return unlikely((s64)skb->tstamp > (s64)(q->ktime_cache + q->horizon));
}
译注。
另外,现在给定一个包,我们无法判断它用的是哪种 timestamp,因此只能用这种 reset 方式。
4.2.3 能将 skb->tstamp 统一到同一种时钟吗?
其实最开始,TCP EDT 用的也是 CLOCK_TAI 时钟。但有人在邮件列表 里反馈说,某些特殊的嵌入式设备上重启会导致时钟漂移 50 多年。所以后来 EDT 又回到了 monotonic 时钟,而我们必须跨 netns 时 reset。
我们做了个原型验证,新加一个 bit skb->tstamp_base 来解决这个问题,
0 表示使用的 TAI,
1 表示使用的 MONO, 然后,
TX/RX 通过 skb_set_tstamp_{mono,tai}(skb, ktime) helper 来获取这个值,
fq_enqueue() 先检查 timestamp 类型,如果不是 MONO,就 reset skb->tstamp 此外,
转发逻辑中所有 skb->tstamp = 0 都可以删掉了
skb_mstamp_ns union 也可能删掉了
在 RX 方向,net_timestamp_check() 必须推迟到 tc ingress 之后执行
4.2.4 解决
我们和 Facebook 的朋友合作,已经解决了这个问题,在跨 netns 时保留时间戳, patch 并合并到了 kernel 5.18+。因此 BBR+EDT 可以工作了,
4.3 Demo(略)
K8s/Cilium backed video streaming service: CUBIC vs. BBR
4.4 BBR 使用注意事项
如果同一个环境(例如数据中心)同时启用了 BBR 和 CUBIC,那使用 BBR 的机器会强占更多的带宽,造成不公平(unfaireness);
BBR 会触发更高的 TCP 重传速率,这源自它更加主动或激进的探测机制 (higher TCP retransmission rate due to more aggressive probing);
BBRv2 致力于解决以上问题。
5 总结及致谢
5.1 问题回顾与总结
K8s 带宽限速功能可以做地更好;
Cilium 的原生带宽限速功能(v1.12 GA)
基于 BPF+EDT 的高效实现
第一个支持 Pod 使用 BBR (及 socket pacing)的 CNI 插件 -- 特别说明:要实现这样的架构,只能用 eBPF(realizing such architecture only possible with eBPF)
6 Cilium 限速方案存在的问题(译注)
Cilium 的限速功能我们 在 v1.10 就在用了,但是使用下来发现两个问题,到目前(2022.11)社区还没有解决,
启用 bandwidth manager 之后,Cilium 会 hardcode somaxconn、netdev_max_backlog 等内核参数,覆盖掉用户自己的内核调优;
例如,如果 node netdev_max_backlog=8192,那 Cilium 启动之后, 就会把它强制覆盖成 1000,导致在大流量场景因为宿主机这个配置太小而出现丢包。
启用 bandwidth manager 再禁用之后,并不会恢复到原来的 qdisc 配置,MQ/FQ 是残留的,导致大流量容器被限流(throttle)。
例如,如果原来物理网卡使用的默认 pfifo_fast qdisc,或者 bond 设备默认使用 的 noqueue,那启用再禁用之后,并不会恢复到原来的 qdisc 配置。残留 FQ 的一 个副作用就是大流量容器的偶发网络延迟,因为 FQ 要保证 flow 级别的公平(而实际上很多场景下并不需要这个公平,总带宽不超就行了)。
查看曾经启用 bandwidth manager,但现在已经禁用它的 node,可以看到 MQ/FQ 还在,
$ tc qdisc show dev bond0qdisc mq 8042: rootqdisc fq 0: parent 8042:10 limit 10000p flow_limit 100p buckets 1024 quantum 3028 initial_quantum 15140qdisc fq 0: parent 8042:f limit 10000p flow_limit 100p buckets 1024 quantum 3028 initial_quantum 15140...qdisc fq 0: parent 8042:b limit 10000p flow_limit 100p buckets 1024 quantum 3028 initial_quantum 15140
是否发生过限流可以在 tc qdisc 统计中看到:
$ tc -s -d qdisc show dev bond0qdisc fq 800b: root refcnt 2 limit 10000p flow_limit 100p buckets 1024 orphan_mask 1023 quantum 3028 initial_quantum 15140 refill_delay 40.0msSent 1509456302851808 bytes 526229891 pkt (dropped 176, overlimits 0 requeues 0)backlog 3028b 2p requeues 015485 flows (15483 inactive, 1 throttled), next packet delay 19092780 ns2920858688 gc, 0 highprio, 28601458986 throttled, 6397 ns latency, 176 flows_plimit6 too long pkts, 0 alloc errors
要恢复原来的配置,目前我们只能手动删掉 MQ/FQ。根据内核代码分析及实际测试,删除 qdisc 的操作是无损的,
$ tc qdisc del dev bond0 root$ tc qdisc show dev bond0qdisc noqueue 0: root refcnt 2qdisc clsact ffff: parent ffff:fff1
推荐
A Big Picture of Kubernetes
Kubernetes入门培训(内含PPT)
原创不易,随手关注或者”在看“,诚挚感谢!
[译] Cilium:基于 BPF+EDT+FQ+BBR 更好地带宽网络管理相关推荐
- [译] Cilium:BPF 和 XDP 参考指南(2021)
Cilium:BPF和XDP参考指南_RToax-CSDN博客Table of ContentsBPF体系结构指令系统辅助功能地图对象固定尾叫BPF到BPF呼叫准时制硬化减负工具链开发环境虚拟机本文档 ...
- [译]Cilium:BPF和XDP参考指南,一同认识eBPF【概念篇】
当你真正认识一门科学时,你才会感受到它的魅力. eBPF技术已经不再小众.从Linux3.18的初次亮相,现在的生态算得上是"内核关联技术"里的翘楚.其中代表性的有BCC.libb ...
- Cilium:BPF和XDP参考指南
Table of Contents BPF体系结构 指令系统 辅助功能 地图 对象固定 尾叫 BPF到BPF呼叫 准时制 硬化 减负 工具链 开发环境 虚拟机 本文档部分针对希望深入了解BPF和XDP ...
- android 获取hdmi数据格式,一种基于Android系统HDMI同显时节省带宽的方法与流程
本发明涉及一种基于Android系统HDMI同显时节省带宽的方法,属于计算机领域. 背景技术: 通常很多移动设备都配备了小尺寸的LCD屏幕,有些使用场景为了能提供更好的交互控制操作,需要移动设备在大尺 ...
- 物联网通信之5G工业级路由器,基于高速率、低时延、大带宽快速组网
随着2019年工信部与三大运营商举行的5G商用启动仪式,我们正式的进入了5G 时代,至今已经是第5年了,基于5G技术的高速率.低时延.大带宽特点,5G网络被广泛应用在智慧交通.智慧城市.自助终端.工业 ...
- 基于机器学习的AI预测更智能?
人工智能的应用一方面在不断改变我们每个人的生活方式,另一方面也在逐渐改变着企业的经营模式.决策方式.越来越多的企业经营者.高级管理人员.决策者都在这场了解人工智能的竞赛中感到焦虑,他们意识到,人工智能 ...
- 【操作系统/OS笔记12】同步互斥的三种实现方法:禁用硬件中断、基于软件的解决方案、更高级的抽象
本次笔记内容: 9.5 临界区 9.6 方法1:禁用硬件中断 9.7 方法2:基于软件的解决方案 9.8 方法3:更高级的抽象 文章目录 临界区 管理临界区的方法 方法1:禁用硬件中断 方法2:基于软 ...
- CVPR 2022 | SwinTextSpotter:基于文本检测与识别更好协同的场景文本识别
点击下方卡片,关注"CVer"公众号 AI/CV重磅干货,第一时间送达 转载自:CSIG文档图像分析与识别专委会 本文简要介绍CVPR 2022录用的论文"SwinTex ...
- 基于树的模型的更好功能
When you understand how a model works, it becomes much easier to create successful features. It is b ...
最新文章
- 谷歌再一次打脸:纯注意力并没那么有用,Transformer组件很重要
- 【计算机网络】网络安全 : 防火墙 ( 简介 | 防火墙功能 | 防火墙分类 | 分组过滤路由器 | 应用网关 )
- 文档类CDocument、子框架类CFrameWnd及视图类CView的关系及如何相互调用
- java安全沙箱(二)之.class文件检验器
- BUUCTF-Reverse:[GKCTF2020]Check_1n
- linux指定内核位置,ARM linux内核启动时几个关键地址
- [ExtJS5学习笔记]第三十五条 sencha extjs 5 组件查询方法
- ZH奶酪:Ionic中(弹出式窗口)的$ionicModal使用方法
- oracle还原 ora 39111,在server2008上进行impdp引发的(ORA-39070)的问题
- python爬取歌词_利用Python网络爬虫抓取网易云音乐歌词
- 给服务器mongodb设置权限_认识 MongoDB
- linux文件目录类命令|--history指令
- 单元素的枚举类型已经成为实现Singleton的最佳方法
- 服务器配置文件设计,IM配置服务器概要的设计文件.doc
- 黑马vue实战项目-(四)商品分类页面的开发
- 分享雷军二十年前写的代码,全部都是汇编,来看看雷军的编程水平吧。
- 微信可以显示你的足迹了,快试试
- JavaScript中的get和set方法讲解
- k8s学习-CKA真题-Pod指定节点部署
- Component属性
热门文章
- 计算机化工应用答案,计算机化工应用习题与解答.pdf
- 超详细的卷积后大小的计算公式
- Privacy Protection in Distributed Fingerprint-based Authentication
- ibm x服务器硬盘检测,IBM System x 服务器自带RAID1的故障恢复
- textarea禁止拉伸样式
- 测试分类-软件开发阶段的各个测试
- ORA-00933: SQL 命令未正确结束处理办法
- Oracle命令结束,更新语句oracle - ORA-00933:SQL命令未正确结束
- throw与throws的区别
- 快速批量压缩照片到指定大小工具,照片图片批量压缩实现方法,批量压缩工具