1 docker-tc
实现上可参考github上的 lukaszlach/docker-tc。lukaszlach/docker-tc 提供了一个通过监听 docker event 来为对应容器做限速的方法,但是只支持 bridge 模式下的下行限速。
本方案采用 golang 来是实现容器限速和流量采集,支持对 bridge host模式下容器限速,支持上行和下行限速,支持多网口限速。用户只需要在需要限速的容器配置对应的 --label "com.docker-tc.enabled=1"
启动参数即可。支持 NetworkMode
为 host
和 bridge
两种网络模式;支持配置限速的基础速率 rate
和最高速率 ceil
;支持对于配置了限速的容器做流量采集。
2 技术方案
2.1 主要技术栈
- 使用 docker golang sdk 来获取容器相关信息并且监听容器的事件(主要关注create 和 die 事件)
- 使用 tc-traffic control 来进行限速,使用
tc
的htb
策略进行限速。对应到容器的不同网络模式采用不同的限速方法。对于net=host
, 需要借助 cgroup 来区别不同的容器,并且在WAN
接口上进行限速,对于net=bridge
,找出容器对应的veth
,对veth
接口进行限速。 - 使用 ifb-Intermediate Functional Block 来将某个端口的流量
ingress
到ifb
接口,然后对ifb
接口做限速,从而实现下行速率的限制 - 使用 netlink 来实现各种网络相关的操作
- 使用 prometheus 来暴露流量的
metric
2.2 数据流
- 程序启动后,首先检查所有
running
状态的容器,并且根据--label "com.docker-tc.enabled=1"
的label
来确定是否要对这些容器配置限速策略。 - 启动一个循环,一直监听容器的事件,对于
create
事件,需要对容器应用限速策略;对于die
事件,需要删除容器的限速策略。 - 对于容器的限速需要区分
net=host
和net=bridge
,使用对应的限速方式来实现。 - 下行流量限速借助配置
ifb
接口来实现。
2.3 限速策略
bridge 模式下容器限速
# bridge 模式容器下行限速,veth6552aeb 是容器对应的 veth 接口
tc qdisc del dev veth6552aeb root
tc qdisc add dev veth6552aeb root handle 1: htb default 1
tc class add dev veth6552aeb parent 1: classid 1:1 htb rate 1000mbit ceil 1000mbit prio 2
tc class add dev veth6552aeb parent 1:1 classid 1:10 htb rate 4Mbit ceil 8Mbit prio 1 burst 96kbit
tc qdisc add dev veth6552aeb parent 1:10 handle 10: sfq perturb 10
tc filter add dev veth6552aeb protocol ip parent 1: prio 2 u32 match ip src 0.0.0.0/0 match ip dst 0.0.0.0/0 flowid 1:10
# bridge 模式容器上行限速,veth6552aeb 是容器对应的 veth 接口, ss是 container name,用 container name 起一个 ifb 接口
ip link add ss type ifb
ip link set ss up
tc qdisc add dev veth5d3d1d6 handle ffff: ingress
tc filter add dev veth5d3d1d6 parent ffff: protocol ip u32 match u32 0 0 action mirred egress redirect dev ss
tc qdisc add dev ss root handle 1: htb default 1
tc class add dev ss parent 1: classid 1:1 htb rate 1000mbit ceil 1000mbit prio 2
tc class add dev ss parent 1:1 classid 1:10 htb rate 8Mbit ceil 12Mbit prio 1 burst 96kbit
tc qdisc add dev ss parent 1:10 handle 10: sfq perturb 10
tc filter add dev ss protocol ip parent 1: prio 2 u32 match ip src 0.0.0.0/0 match ip dst 0.0.0.0/0 flowid 1:10
注意事项
- 需要处理 docker-tc 容器异常关闭、重启等场景
- 删除 tc 策略的顺序应该跟添加 tc 策略的顺序相反,应该先删除
filter
,再删除class
,不然可能出现RTNETLINK answers: Device or resource busy
的错误 - 为了配置下行限速,给对应网口配置了
ingress mirror
,比如ppp4014
的流量mirror
到了ifbppp4014
,如果直接删除ifbppp4014
,会导致ppp4014
的网络不通,需要有一个协程定期清理filter
中netlink.U32.RedirIndex == 0
的网口的ingress
策略 - 需要处理 WAN 口重启或删除导致 tc 策略丢失问题,所以需要监听网口up/down 等事件,并配置对应的 tc 策略。
3 使用说明
3.1 部署和启动该服务
在需要进行容器限速的每台服务器上部署该服务,可以使用 systemd 来启动服务,或者使用 docker
来部署该服务。如果使用 docker
来部署该服务,启动脚本如下:
docker run -d \
--name docker-tc \
--network host \
--privileged \
--pid=host \
--restart always \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /var/docker-tc:/var/docker-tc \
-v /opt/docker-tc/config:/docker-tc/config \
-v /sys/fs/cgroup/net_cls:/sys/fs/cgroup/net_cls \
docker-tc
由于 docker-tc
需要操作网络接口,需要配置 --network host
和 --cap-add NET_ADMIN
。另外 docker-tc
需要监听 docker
事件,所以需要配置 -v /var/run/docker.sock:/var/run/docker.sock
。
3.2 限速容器的配置
如果需要对某个容器做限速,需要配置对应的 label
参数, docker-tc
相关的 label
参数 如下:
-
com.docker-tc.enabled
配置该值=1时应用限速策略,其他值或者没有配置该label
值将不应用限速策略。 com.docker-tc.down.rate
下行限速的基础速率,这个速率范围的的带宽不会拿来跟其他容器的限速共享,但是当某个接口上所有限速策略的技术速率总和超过接口的总速率时,无法保证容器能够达到这个速率。速率单位参考 tc 速率单位如下:
- 以bit为单位:bit, kbit, mbit, gbit, tbit
- 以btye为单位: bps, kbps, mbps, gbps, tbps
-
com.docker-tc.down.ceil
下行限速的最高速率。 -
com.docker-tc.up.rate
上行限速的基础速率,这个速率范围的的带宽不会拿来跟其他容器的限速共享,但是当某个接口上所有限速策略的技术速率总和超过接口的总速率时,无法保证容器能够达到这个速率。 -
com.docker-tc.up.ceil
上行限速的最高速率。 -
com.docker-tc.up.cgroup
cgroup id,用于关联限速策略 -
com.docker-tc.biz
该容器的业务id,暴露流量采集 metric 时会用到该标签
3.3 测试
3.3.1 启动一个限速的容器
docker run -it --name xxxx \
--label "com.docker-tc.enabled=1" \
--label "com.docker-tc.cgroup=65552" \
--label "com.docker-tc.down.rate=2mbps" \
--label "com.docker-tc.down.ceil=10mbps" \
--label "com.docker-tc.up.rate=20mbps" \
--label "com.docker-tc.up.ceil=30mbps" \
--label "com.docker-tc.biz=test" \
nettool-centos7 bash
3.3.2 速率测试
docker exec
进入容器使用 wget
或 iperf
测试一下限速策略是否生效。