一个计算机技术爱好者与学习者

0%

好好学K8S:Pod网络限速

1. 前言

为避免Pod打满带宽,有时需要对Pod进行限速。
本文中,我们就来学习一下Pod中的网络限速方法。

相关文档:

2. Pod网络限速思路

1、CNI插件需要支持带宽限速
2、Pod中配置注解

注意:这种限速方式,对内网和外网都有效。

3. CNI插件启用带宽限速

3.1. Flannel

1、编辑flannel插件的配置

1
kubectl edit cm -n kube-system kube-flannel-cfg

plugins 配置中,增加 bandwidth 配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: v1
data:
cni-conf.json: |
{
"name": "cb0",
"cniVersion":"0.3.1",
"plugins": [
{
"type": "bandwidth",
"capabilities": {"bandwidth": true}
}
]
}

2、重建flannel pod

1
kubectl -n kube-system delete pod -l app=flannel

3.2. Calico

1、编辑calico插件的配置

1
vim /etc/cni/net.d/10-calico.conflist

plugins 配置中,增加 bandwidth 配置:

1
2
3
4
5
6
7
8
9
10
{
"name": "k8s-pod-network",
"cniVersion": "0.3.1",
"plugins": [
{
"type": "bandwidth",
"capabilities": {"bandwidth": true}
}
]
}

2、重建calico pod

1
kubectl -n calico-system delete pod -l k8s-app=calico-node

4. Pod中配置注解

1、pod中添加限速注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
annotations:
kubernetes.io/ingress-bandwidth: "1M" # 限制入站带宽为 1 Mbps,单位bit
kubernetes.io/egress-bandwidth: "3M" # 限制出站带宽为 3 Mbps,单位bit
labels:
run: testpod
name: testpod
spec:
containers:
- image: debian:bookworm-slim
name: testpod
resources: {}
command: ["/bin/sh", "-c"]
args:
- tail -f /dev/null
status: {}

2、登录进入pod中

1
kubectl exec -it testpod -- /bin/bash

3、测试外网速度

1
2
3
4
5
apt update
apt install -y wget python3

wget https://raw.github.com/sivel/speedtest-cli/master/speedtest.py
python3 speedtest.py

4、测试内网速度
创建两个pod,其中一个启动 iperf server,另外一个启动 iperf client
(1)pod1启动iperf server:

1
2
apt install -y iperf
iperf -s

(2)pod2启动iperf client:

1
2
apt install -y iperf
iperf -c $POD1_IP -i 3

5. CNI插件带宽限速原理解析

本节以CNI插件Calico为例,学习Pod带宽限速的原理。

5.1. 限速原理

Calico 本身不直接处理带宽注解,而是依赖以下组件协同工作:

Kubernetes 网络组件:

  • kubelet 会读取这些注解
  • 通过 CNI 插件配置传递给底层网络

Linux Traffic Control (tc):

  • 实际带宽限制是通过 Linux 的流量控制子系统实现的
  • 使用 Hierarchical Token Bucket (HTB) 队列规则(Queueing Discipline)

CNI 插件:

  • Calico 会确保这些限制被应用到正确的网络接口

5.2. 出口限制实现

1、当 Pod 创建时,kubelet 检测到 egress-bandwidth 注解

2、通过 CNI 调用,在 Pod 的网络命名空间中设置规则

1
2
tc qdisc add dev eth0 root handle 1: htb default 10
tc class add dev eth0 parent 1: classid 1:1 htb rate 1mbit

所有从 Pod 发出的流量都会受到这个限制。

5.3. 入口限制实现

入口限制的实现更复杂,通常通过设置一个虚拟的 ifb (Intermediate Functional Block) 设备

1
2
3
4
5
6
7
8
9
10
11
# 创建 ifb 设备
ip link add ifb0 type ifb
ip link set dev ifb0 up

# 将入口流量重定向到 ifb
tc qdisc add dev eth0 handle ffff: ingress
tc filter add dev eth0 parent ffff: protocol ip u32 match u32 0 0 action mirred egress redirect dev ifb0

# 在 ifb 上应用限制
tc qdisc add dev ifb0 root handle 1: htb default 10
tc class add dev ifb0 parent 1: classid 1:1 htb rate 1mbit

5.4. 注解与底层限速规则的关联

已知pod中的限速注解为:

1
2
3
4
5
6
apiVersion: v1
kind: Pod
metadata:
annotations:
kubernetes.io/ingress-bandwidth: "1M" # 限制入站带宽为 1 Mbps,单位bit
kubernetes.io/egress-bandwidth: "3M" # 限制出站带宽为 3 Mbps,单位bit

创建pod后,查看pod在宿主机中的底层限速配置:

1
tc qdisc show

看到的内容格式如下:

1
2
3
qdisc tbf 1: dev cali47528e10e0c root refcnt 2 rate 1Mbit burst 27917286b lat 1924.2s
qdisc ingress ffff: dev cali47528e10e0c parent ffff:fff1 ----------------
qdisc tbf 1: dev bwpc6db82aef347 root refcnt 2 rate 3Mbit burst 62277024b lat 549.8s

出口限制内容解析:

  • 设备名:bwpc6db82aef347(Kubernetes 为带宽限制创建的虚拟接口)
  • 类型:tbf(Token Bucket Filter)
  • 速率:rate 3Mbit → 对应 egress-bandwidth: 3M
  • 作用方向:root 表示这是出口限制
  • 突发流量限制:burst是为了应对突发流量,避免丢包,burst 62277024b,b表示bytes,允许短时间内突发最多传输 59.4MB 数据,这部分数据可以超过rate限制

入口限制内容解析:

  • 设备名:cali47528e10e0c(Calico 的 Pod 接口)
  • 类型:tbf + ingress 队列
  • 速率:rate 1Mbit → 对应 ingress-bandwidth: 1M
  • 特殊实现:Kubernetes 通过将入口流量 重定向到虚拟接口 的方式实现入口限制;实际生效的是 tbf 规则(虽然显示在 root 位置,但因重定向而作用于入口)
  • 突发流量限制:burst 27917286b,b表示bytes,允许短时间内突发最多传输 26.6MB 数据,这部分数据可以超过rate限制

查看tc规则详情:

1
2
tc -s qdisc show dev bwpc6db82aef347
tc -s qdisc show dev cali47528e10e0c

6. 后记

实际测试发现,Pod中配置注解的方式确实可以实现限制带宽,测速时的 带宽量级 会随着 限速量级 的变化而变化。
但是,限速效果不太好,测出的 带宽数值 与设置的 限速数值 有较大差距,并且多次测试结果波动很大。
整体来说,Pod中配置注解的方式可以限速,但是不可以精确限速。