使用 TC 模拟容器内网络延迟

场景

  • 模拟跨地域级别的请求,通过广域网的形式无法满足需求,因此使用 tc 来模拟请求链路上的延迟

前提

  • 基础镜像需要安装 iproute、nmap-ncat
sh-4.2# yum install -y iproute nmap-ncat

sh-4.2# tc
Usage: tc [ OPTIONS ] OBJECT { COMMAND | help }
       tc [-force] [-OK] -batch filename
where  OBJECT := { qdisc | class | filter | action | monitor | exec }
       OPTIONS := { -s[tatistics] | -d[etails] | -r[aw] | -p[retty] | -b[atch] [filename] | -n[etns] name |
                    -nm | -nam[es] | { -cf | -conf } path }
sh-4.2# nc 
Ncat: You must specify a host to connect to. QUITTING.

sh-4.2# nc -vz 10.2x.16.2x 22 
Ncat: Version 7.50 ( https://nmap.org/ncat )
Ncat: Connected to 10.2x.16.2x:22.
Ncat: 0 bytes sent, 0 bytes received in 0.01 seconds.

sh-4.2# nc -vz 10.2x.16.2x 22 
Ncat: Version 7.50 ( https://nmap.org/ncat )
Ncat: Connected to 10.2x.16.2x:22.
Ncat: 0 bytes sent, 0 bytes received in 0.01 seconds.

配置规则

临时配置

  • 添加规则,为了效果更明显这里我们设置为设置 100ms
[root@worker02 ~]# docker inspect `docker ps | grep centos | awk '{print $1}'` --format={{.State.Pid}}
299938
[root@worker02 ~]# nsenter -t 299938 -n tc qdisc add dev eth0 root handle 1: prio
[root@worker02 ~]# nsenter -t 299938 -n tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 match ip dst 10.2x.16.2x/32 match ip dport 22 0xffff flowid 2:1
[root@worker02 ~]# nsenter -t 299938 -n tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 match ip dst 10.2x.16.2x/32 match ip dport 22 0xffff flowid 2:1
[root@worker02 ~]# nsenter -t 299938 -n tc qdisc add dev eth0 parent 1:1 handle 2: netem delay 100ms
  • 查看规则是否添加成功
[root@worker02 ~]# nsenter -t 299938 -n tc -s qdisc show dev eth0
qdisc prio 1: root refcnt 2 bands 3 priomap  1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
 Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0) 
 backlog 0b 0p requeues 0 
qdisc netem 2: parent 1:1 limit 1000 delay 100.0ms
 Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0) 
 backlog 0b 0p requeues 0 

[root@worker02 ~]# nsenter -t 299938 -n tc -s filter show dev eth0
filter parent 1: protocol ip pref 1 u32 chain 0 
filter parent 1: protocol ip pref 1 u32 chain 0 fh 800: ht divisor 1 
filter parent 1: protocol ip pref 1 u32 chain 0 fh 800::800 order 2048 key ht 800 bkt 0 flowid 2:1 not_in_hw  (rule hit 0 success 0)
  match 0a1d101b/ffffffff at 16 (success 0 ) 
  match 00000016/0000ffff at 20 (success 0 ) 
filter parent 1: protocol ip pref 1 u32 chain 0 fh 800::801 order 2049 key ht 800 bkt 0 flowid 2:1 not_in_hw  (rule hit 0 success 0)
  match 0a1d1021/ffffffff at 16 (success 0 ) 
  match 00000016/0000ffff at 20 (success 0 ) 
  • 验证规则是否生效
# 验证 dst+port 的响应时长
[root@worker02 ~]# nsenter -t 299938 -n nc -vz 10.2x.16.2x 22 
Ncat: Version 7.50 ( https://nmap.org/ncat )
Ncat: Connected to 10.2x.16.2x:22.
Ncat: 0 bytes sent, 0 bytes received in 0.11 seconds.
[root@worker02 ~]# nsenter -t 299938 -n nc -vz 10.2x.16.2x 22 
Ncat: Version 7.50 ( https://nmap.org/ncat )
Ncat: Connected to 10.2x.16.2x:22.
Ncat: 0 bytes sent, 0 bytes received in 0.12 seconds.

# 验证非 dst+port 的响应时长
[root@worker02 ~]# nsenter -t 299938 -n nc -vz 10.2x.16.2x 22 
Ncat: Version 7.50 ( https://nmap.org/ncat )
Ncat: Connected to 10.2x.16.2x:22.
Ncat: 0 bytes sent, 0 bytes received in 0.01 seconds.

# 再次查看匹配 tc filter 规则,发现已经匹配成功
[root@worker02 ~]# nsenter -t 299938 -n tc -s filter show dev eth0
filter parent 1: protocol ip pref 1 u32 chain 0 
filter parent 1: protocol ip pref 1 u32 chain 0 fh 800: ht divisor 1 
filter parent 1: protocol ip pref 1 u32 chain 0 fh 800::800 order 2048 key ht 800 bkt 0 flowid 2:1 not_in_hw  (rule hit 15 success 5)
  match 0a1d101b/ffffffff at 16 (success 5 ) 
  match 00000016/0000ffff at 20 (success 5 ) 
filter parent 1: protocol ip pref 1 u32 chain 0 fh 800::801 order 2049 key ht 800 bkt 0 flowid 2:1 not_in_hw  (rule hit 10 success 5)
  match 0a1d1021/ffffffff at 16 (success 5 ) 
  match 00000016/0000ffff at 20 (success 5 ) 
  • 删除策略
tc -s qdisc del dev eth0 root

持久化配置

在 k8s 环境中,推荐使用 initContaiers 的 sidecar 模式来实现,yaml 如下:

kind: Deployment
apiVersion: apps/v1
metadata:
  name: centos-centos
  namespace: default
  labels:
    dce.daocloud.io/app: centos
spec:
  replicas: 1
  selector:
    matchLabels:
      dce.daocloud.io/component: centos-centos
  template:
    metadata:
      name: centos-centos
      creationTimestamp: null
      labels:
        dce.daocloud.io/app: centos
        dce.daocloud.io/component: centos-centos
      annotations:
        dce.daocloud.io/parcel.egress.burst: '0'
        dce.daocloud.io/parcel.egress.rate: '0'
        dce.daocloud.io/parcel.ingress.burst: '0'
        dce.daocloud.io/parcel.ingress.rate: '0'
        dce.daocloud.io/parcel.net.type: calico
        dce.daocloud.io/parcel.net.value: 'default-ipv4-ippool,default-ipv6-ippool'
    spec:
      initContainers:
        - name: init-centos-tc
          image: '10.2x.14x.1x/base/centos:7.9.2009'
          command:
            - /bin/sh
          args:
            - '-c'
            - >-
              yum install -y iproute nmap-ncat && tc qdisc add dev eth0 root
              handle 1: prio && tc filter add dev eth0 parent 1:0 protocol ip
              prio 1 u32 match ip dst 10.2x.16.2x/32 match ip dport 22 0xffff
              flowid 2:1 && tc filter add dev eth0 parent 1:0 protocol ip prio 1
              u32 match ip dst 10.2x.16.3x/32 match ip dport 22 0xffff flowid
              2:1 && tc qdisc add dev eth0 parent 1:1 handle 2: netem delay
              100ms
          resources: {}
          terminationMessagePath: /dev/termination-log
          terminationMessagePolicy: File
          imagePullPolicy: IfNotPresent
          securityContext:
            privileged: true
      containers:
        - name: centos-centos
          image: '10.2x.14x.1x/base/centos:7.9.2009'
          command:
            - sleep
          args:
            - '3600'
          resources:
            requests:
              cpu: '0'
              memory: '0'
          lifecycle: {}
          terminationMessagePath: /dev/termination-log
          terminationMessagePolicy: File
          imagePullPolicy: Always
      restartPolicy: Always
      terminationGracePeriodSeconds: 30
      dnsPolicy: ClusterFirst
      securityContext: {}
      imagePullSecrets:
        - name: centos-centos-10.2x.14x.1x
      schedulerName: default-scheduler
      dnsConfig:
        options:
          - name: single-request-reopen
            value: ''
          - name: ndots
            value: '2'
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 25%
      maxSurge: 25%
  revisionHistoryLimit: 10
  progressDeadlineSeconds: 600

参考文档