Ssoon

[8주차] Cilium CNI : Bandwidth Manager 본문

쿠버네티스 네트워크 스터디 3기

[8주차] Cilium CNI : Bandwidth Manager

구구달스 2024. 10. 21. 22:12
CloudNet@ 가시다님이 진행하는 쿠버네티스 네트워크 스터디 3기

Cilium과 속도 제한 기능

https://isovalent.com/blog/post/addressing-bandwidth-exhaustion-with-cilium-bandwidth-manager/

🧿 MQ (Multi-Queue)

멀티 큐(Multi-Queue)는 네트워크 트래픽을 여러 개의 큐로 분배하여 처리하는 방법입니다. 이 기술은 CPU 코어의 수에 맞춰 트래픽을 효율적으로 분산시키는 데 사용됩니다. 멀티 큐의 주요 특징은 다음과 같습니다:

  • 병렬 처리: 트래픽을 여러 큐에 나누어 각 큐가 별도의 CPU 코어에서 처리되므로 병렬로 작업할 수 있습니다.
  • 성능 향상: 트래픽 처리량과 지연 시간을 줄이는 데 도움을 줍니다. 특히, 대규모 네트워크 환경에서 유용합니다.

🧿 FQ (Fair Queueing)

공정 큐잉(Fair Queueing)은 네트워크 트래픽의 공정한 처리를 위해 설계된 큐잉 메커니즘입니다. 이 기술은 각 트래픽 흐름에 대한 대역폭을 공정하게 분배하여, 특정 흐름이 네트워크 자원을 독점하지 않도록 합니다. FQ의 주요 특징은 다음과 같습니다:

  • 공정성: 모든 트래픽 흐름이 균등하게 대역폭을 사용할 수 있도록 합니다. 특정 사용자가 다른 사용자보다 더 많은 자원을 사용하지 못하게 합니다.
  • 지연 감소: 공정하게 대역폭을 분배함으로써, 특정 흐름의 지연을 줄이고 전체적인 지연 시간을 감소시킵니다.

🧿 eBPF와 MQ, FQ의 통합

Cilium과 같은 현대의 네트워크 솔루션에서는 eBPF를 사용하여 멀티 큐와 공정 큐잉 기능을 구현합니다. eBPF 프로그램은 패킷을 실시간으로 모니터링하고, 트래픽을 멀티 큐로 분산시키며, 각 큐에서 FQ 알고리즘을 사용하여 공정하게 대역폭을 분배합니다. 이 조합은 높은 성능과 공정한 트래픽 처리를 보장합니다.

  • Earliest Departure Time(EDT)
    • 네트워크 패킷이 전송될 "가장 빠른 시간"을 결정하는 방법입니다. 이 기술은 혼잡을 줄이고 네트워크 성능을 최적화하기 위해 고안되었습니다. EDT는 각 패킷에 전송 가능한 가장 빠른 시간을 계산하여 지정함으로써 네트워크에 패킷이 한꺼번에 몰리는 것을 방지합니다.
  • Timing Wheel
    • 주로 네트워크 트래픽 제어와 패킷 스케줄링을 위해 사용되는 자료 구조입니다. 시간 기반의 작업을 효과적으로 관리하고 지연을 최소화하기 위한 방식으로, 특히 수많은 타이머를 효율적으로 처리해야 하는 경우에 유용합니다.

🧿 Cilium은 다음과 같은 기술들을 활용하여 속도 제한을 적용합니다:

  • 모니터링: Cilium 에이전트가 파드의 annotations 을 모니터링합니다. 운영자는 동일한 annotations  (kubernetes.io/egress-bandwidth)을 사용하여 대역폭 제한을 설정할 수 있습니다.
  • eBPF 데이터 경로: Cilium 에이전트는 대역폭 요구 사항을 eBPF 데이터 경로로 전달합니다. 이는 데이터 패킷이 처리되는 경로입니다.
  • 물리적 인터페이스에서의 시행: 속도 제한은 가상 네트워크 인터페이스(veth)가 아닌 물리적 인터페이스에서 시행되어, bufferbloat  (데이터가 너무 많이 쌓여 지연되는 현상)를 방지하고 TCP의 전송 지연 관리를 개선합니다.
  • EDT 타임스탬프: Cilium 에이전트는 사용자가 제공한 대역폭 정책에 기반하여 Earliest Departure Time(EDT) 타임스탬프를 구현합니다.
  • MQ (Multi-Queue)   지원: Cilium은 multi-queue 를 인식하고 자동으로 multi-queue qdiscs(큐 디스크)를 설정합니다. 여기서 main queue (root) 로 들어오는 트래픽은 여러 sub queues (leaf) 로 분류됩니다.
  • FQ (Fair Queueing) : fair queues  timing wheel 메커니즘을 사용하여 트래픽을 패킷 타임스탬프에 따라 분배합니다.
  • Cilium은 IFB(인터페이스 가상 브리지)를 제거하여, CNI(컨테이너 네트워크 인터페이스) 플러그인 구현에서 발생했던 지연을 줄입니다.
  • Cilium은 멀티 코어와 multi queue 기능을 활용하여, 속도 제한이 성능에 나쁜 영향을 미치지 않도록 보장합니다.
  • Cilium은 Earliest Departure Time(EDT) 및 Timing Wheel  과 같은 최신 혼잡 회피 기술을 활용하여 지연을 줄입니다.

🧿 ens5 네트워크 인터페이스의 큐잇(queuing discipline, qdisc) 설정 확인 

  • qdisc mq 0: root:
    • mq(multi-queue)는 다중 큐 스케줄링을 나타내며, ens5 인터페이스에 루트 큐로 설정되어 있습니다. 이 설정은 여러 개의 큐를 사용하여 데이터 전송을 관리합니다.
  • qdisc fq_codel 0: parent :n (n은 1, 2, 3, 4):
    •  fq_codel 큐는 Fair Queueing Controlled Delay(지연 제어) 알고리즘을 사용하여 트래픽을 관리합니다.
    • parent :n은 각 큐가 mq 큐의 자식 큐임을 나타냅니다.
    • limit 10240p는 각 큐가 허용하는 최대 패킷 수를 설정합니다.
    • flows 1024는 큐가 처리할 수 있는 최대 동시 흐름 수를 설정합니다.
    • quantum 1514는 패킷을 전송할 때 사용하는 최소 크기입니다.
    • target 5ms와 interval 100ms는 지연을 제어하는 설정입니다. 이는 평균 지연 목표를 5ms로 설정하고, 이를 100ms 간격으로 측정합니다.
    • memory_limit 32Mb는 큐가 사용할 수 있는 최대 메모리 용량을 설정합니다.
    • ecn(Explicit Congestion Notification)과 drop_batch 64는 혼잡 제어를 위한 추가 설정입니다.
(⎈|CiliumLab:N/A) root@k8s-s:~# tc qdisc show dev ens5
qdisc mq 0: root
qdisc fq_codel 0: parent :4 limit 10240p flows 1024 quantum 1514 target 5ms interval 100ms memory_limit 32Mb ecn drop_batch 64
qdisc fq_codel 0: parent :3 limit 10240p flows 1024 quantum 1514 target 5ms interval 100ms memory_limit 32Mb ecn drop_batch 64
qdisc fq_codel 0: parent :2 limit 10240p flows 1024 quantum 1514 target 5ms interval 100ms memory_limit 32Mb ecn drop_batch 64
qdisc fq_codel 0: parent :1 limit 10240p flows 1024 quantum 1514 target 5ms interval 100ms memory_limit 32Mb ecn drop_batch 64

🧿 Cilium의 Helm 차트를 업그레이드

  • bandwidthManager.enabled 값을 true로 설정하는 과정은 Cilium의 대역폭 관리 기능을 활성화
(⎈|CiliumLab:N/A) root@k8s-s:~# helm upgrade cilium cilium/cilium --namespace kube-system --reuse-values --set bandwidthManager.enabled=true
Release "cilium" has been upgraded. Happy Helming!
NAME: cilium
LAST DEPLOYED: Mon Oct 21 22:02:27 2024
NAMESPACE: kube-system
STATUS: deployed
REVISION: 4
TEST SUITE: None
NOTES:
You have successfully installed Cilium with Hubble Relay and Hubble UI.

Your release version is 1.16.3.

For any further help, visit https://docs.cilium.io/en/v1.16/gettinghelp

🧿 Cilium의 설정을 조회하고, 그 중에서 대역폭과 관련된 항목만 출력

(⎈|CiliumLab:N/A) root@k8s-s:~# cilium config view | grep bandwidth
enable-bandwidth-manager                          true

🧿 Cilium에서 대역폭 관리자의 상태를 확인

  • 대역폭 관리자 EDT 방식으로 작동하고 있으며, BPF 기술을 사용하여 TCP 대역폭 제어 알고리즘인 CUBIC을 통해 ens5 인터페이스에서 대역폭을 관리하고 있습니다.
(⎈|CiliumLab:N/A) root@k8s-s:~# c0 status | grep  BandwidthManager
BandwidthManager:        EDT with BPF [CUBIC] [ens5]

🧿 Linux의 트래픽 제어(Traffic Control) 시스템에서 설정된 큐 디스크립터(Queue Disc) 상태

  • ens5 인터페이스는 다중 큐(mq) 정책을 사용하며, 여러 개의 Fair Queuing 를 통해 트래픽을 공정하게 분배합니다.
  • lo, cilium_net, cilium_host, lxc_health 인터페이스는 큐를 사용하지 않는(noqueue) 정책을 사용합니다.
    • limit 10000p: 큐의 최대 길이는 10,000 패킷입니다.
    • flow_limit 100p: 각 흐름에 대한 최대 패킷 수입니다
(⎈|CiliumLab:N/A) root@k8s-s:~# tc qdisc
qdisc noqueue 0: dev lo root refcnt 2
qdisc mq 8002: dev ens5 root
qdisc fq 8005: dev ens5 parent 8002:2 limit 10000p flow_limit 100p buckets 32768 orphan_mask 1023 quantum 18030b initial_quantum 90150b low_rate_threshold 550Kbit refill_delay 40ms timer_slack 10us horizon 2s horizon_drop
qdisc fq 8003: dev ens5 parent 8002:4 limit 10000p flow_limit 100p buckets 32768 orphan_mask 1023 quantum 18030b initial_quantum 90150b low_rate_threshold 550Kbit refill_delay 40ms timer_slack 10us horizon 2s horizon_drop
qdisc fq 8004: dev ens5 parent 8002:3 limit 10000p flow_limit 100p buckets 32768 orphan_mask 1023 quantum 18030b initial_quantum 90150b low_rate_threshold 550Kbit refill_delay 40ms timer_slack 10us horizon 2s horizon_drop
qdisc fq 8006: dev ens5 parent 8002:1 limit 10000p flow_limit 100p buckets 32768 orphan_mask 1023 quantum 18030b initial_quantum 90150b low_rate_threshold 550Kbit refill_delay 40ms timer_slack 10us horizon 2s horizon_drop
qdisc noqueue 0: dev cilium_net root refcnt 2
qdisc noqueue 0: dev cilium_host root refcnt 2
qdisc noqueue 0: dev lxc_health root refcnt 2

🧿 ens5 인터페이스에 설정된 큐 디스크립터(Queue Disc)의 상태

(⎈|CiliumLab:N/A) root@k8s-s:~# tc qdisc show dev ens5
qdisc mq 8002: root
qdisc fq 8005: parent 8002:2 limit 10000p flow_limit 100p buckets 32768 orphan_mask 1023 quantum 18030b initial_quantum 90150b low_rate_threshold 550Kbit refill_delay 40ms timer_slack 10us horizon 2s horizon_drop
qdisc fq 8003: parent 8002:4 limit 10000p flow_limit 100p buckets 32768 orphan_mask 1023 quantum 18030b initial_quantum 90150b low_rate_threshold 550Kbit refill_delay 40ms timer_slack 10us horizon 2s horizon_drop
qdisc fq 8004: parent 8002:3 limit 10000p flow_limit 100p buckets 32768 orphan_mask 1023 quantum 18030b initial_quantum 90150b low_rate_threshold 550Kbit refill_delay 40ms timer_slack 10us horizon 2s horizon_drop
qdisc fq 8006: parent 8002:1 limit 10000p flow_limit 100p buckets 32768 orphan_mask 1023 quantum 18030b initial_quantum 90150b low_rate_threshold 550Kbit refill_delay 40ms timer_slack 10us horizon 2s horizon_drop

🧿 테스트를 위한 트래픽 발생 서버/클라이언트 파드 생성

  • netperf-server Pod는 egress 대역폭을 10Mbit/s로 제한하며, netperf-client Pod는 서버와 다른 노드에서 실행되도록 설정되었습니다.
(⎈|CiliumLab:N/A) root@k8s-s:~# cat <<EOF | kubectl apply -f -
---
apiVersion: v1
kind: Pod
metadata:
  annotations:
    # Limits egress bandwidth to 10Mbit/s.
    kubernetes.io/egress-bandwidth: "10M"
  labels:
    # This pod will act as server.
    app.kubernetes.io/name: netperf-server
  name: netperf-server
spec:
  containers:
  - name: netperf
    image: cilium/netperf
    ports:
    - containerPort: 12865
---
apiVersion: v1
kind: Pod
metadata:
  # This Pod will act as client.
  name: netperf-client
spec:
  affinity:
    # Prevents the client from being scheduled to the
    # same node as the server.
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app.kubernetes.io/name
            operator: In
            values:
            - netperf-server
        topologyKey: kubernetes.io/hostname
  containers:
  - name: netperf
    args:
    - sleep
    - infinity
    image: cilium/netperf
EOF
pod/netperf-server created
pod/netperf-client created

🧿 netperf-server Pod의 어노테이션(Annotations)을 조회

  • kubernetes.io/egress-bandwidth: 10M: 이 어노테이션은 netperf-server Pod가 나가는 트래픽(egress)의 대역폭을 10Mbit/s로 제한한다는 것을 의미합니다. 즉, 이 Pod에서 외부로 전송되는 데이터의 속도가 10Mbit/s를 넘지 않도록 설정되어 있습니다.
(⎈|CiliumLab:N/A) root@k8s-s:~# kubectl describe pod netperf-server | grep Annotations:
Annotations:      kubernetes.io/egress-bandwidth: 10M

🧿 Cilium이 활성화된 Kubernetes 환경에서 현재 대역폭 설정을 확인

  • c1의 경우: 대역폭 설정이 없으므로 c1 엔드포인트나 포드에 대해 특정 대역폭 구성이 적용되지 않았음을 의미합니다.
  • c2의 경우: c2 엔드포인트(아마도 netperf-server 포드)가 10 메가비트/초로 설정된 egress 대역폭 제한을 가지고 있습니다. 이는 이 포드에서 외부로 전송되는 데이터의 최대 속도를 제한합니다.
(⎈|CiliumLab:N/A) root@k8s-s:~# c1 bpf bandwidth list
No entries found.
(⎈|CiliumLab:N/A) root@k8s-s:~# c2 bpf bandwidth list
IDENTITY   EGRESS BANDWIDTH (BitsPerSec)
356        10M

🧿 Cilium 네트워크에서 활성화된 엔드포인트의 목록

(⎈|CiliumLab:N/A) root@k8s-s:~# c1 endpoint list
ENDPOINT   POLICY (ingress)   POLICY (egress)   IDENTITY   LABELS (source:key[=value])                                                  IPv6   IPv4           STATUS
           ENFORCEMENT        ENFORCEMENT
512        Disabled           Disabled          4          reserved:health                                                                     172.16.1.159   ready
1004       Disabled           Disabled          39166      k8s:io.cilium.k8s.namespace.labels.kubernetes.io/metadata.name=kube-system          172.16.1.221   ready
                                                           k8s:io.cilium.k8s.policy.cluster=default
                                                           k8s:io.cilium.k8s.policy.serviceaccount=coredns
                                                           k8s:io.kubernetes.pod.namespace=kube-system
                                                           k8s:k8s-app=kube-dns
1031       Disabled           Disabled          42840      k8s:app.kubernetes.io/name=hubble-ui                                                172.16.1.79    ready
                                                           k8s:app.kubernetes.io/part-of=cilium
                                                           k8s:io.cilium.k8s.namespace.labels.kubernetes.io/metadata.name=kube-system
                                                           k8s:io.cilium.k8s.policy.cluster=default
                                                           k8s:io.cilium.k8s.policy.serviceaccount=hubble-ui
                                                           k8s:io.kubernetes.pod.namespace=kube-system
                                                           k8s:k8s-app=hubble-ui
1085       Disabled           Disabled          18203      k8s:io.cilium.k8s.namespace.labels.kubernetes.io/metadata.name=default              172.16.1.62    ready
                                                           k8s:io.cilium.k8s.policy.cluster=default
                                                           k8s:io.cilium.k8s.policy.serviceaccount=default
                                                           k8s:io.kubernetes.pod.namespace=default
2158       Disabled           Disabled          39166      k8s:io.cilium.k8s.namespace.labels.kubernetes.io/metadata.name=kube-system          172.16.1.210   ready
                                                           k8s:io.cilium.k8s.policy.cluster=default
                                                           k8s:io.cilium.k8s.policy.serviceaccount=coredns
                                                           k8s:io.kubernetes.pod.namespace=kube-system
                                                           k8s:k8s-app=kube-dns
2400       Disabled           Disabled          44886      k8s:app.kubernetes.io/name=hubble-relay                                             172.16.1.78    ready
                                                           k8s:app.kubernetes.io/part-of=cilium
                                                           k8s:io.cilium.k8s.namespace.labels.kubernetes.io/metadata.name=kube-system
                                                           k8s:io.cilium.k8s.policy.cluster=default
                                                           k8s:io.cilium.k8s.policy.serviceaccount=hubble-relay
                                                           k8s:io.kubernetes.pod.namespace=kube-system
                                                           k8s:k8s-app=hubble-relay
2851       Disabled           Disabled          1          reserved:host                                                                                      ready
(⎈|CiliumLab:N/A) root@k8s-s:~# c2 endpoint list
ENDPOINT   POLICY (ingress)   POLICY (egress)   IDENTITY   LABELS (source:key[=value])                                              IPv6   IPv4           STATUS
           ENFORCEMENT        ENFORCEMENT
210        Disabled           Disabled          1          reserved:host                                                                                  ready
356        Disabled           Disabled          5323       k8s:app.kubernetes.io/name=netperf-server                                       172.16.2.180   ready
                                                           k8s:io.cilium.k8s.namespace.labels.kubernetes.io/metadata.name=default
                                                           k8s:io.cilium.k8s.policy.cluster=default
                                                           k8s:io.cilium.k8s.policy.serviceaccount=default
                                                           k8s:io.kubernetes.pod.namespace=default
1169       Disabled           Disabled          4          reserved:health                                                                 172.16.2.73    ready

🧿 netperf를 사용하여 TCP 성능 테스트를 수행

  • netperf-server로부터 데이터를 전송하는 데 10.04초가 걸렸고, 평균 전송 속도는 9.88 Mbps
(⎈|CiliumLab:N/A) root@k8s-s:~# NETPERF_SERVER_IP=$(kubectl get pod netperf-server -o jsonpath='{.status.podIP}')
(⎈|CiliumLab:N/A) root@k8s-s:~# kubectl exec netperf-client -- netperf -t TCP_MAERTS -H "${NETPERF_SERVER_IP}"
MIGRATED TCP MAERTS TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 172.16.2.180 (172.16.) port 0 AF_INET
Recv   Send    Send
Socket Socket  Message  Elapsed
Size   Size    Size     Time     Throughput
bytes  bytes   bytes    secs.    10^6bits/sec

131072  16384  16384    10.04       9.88

🧿 netperf-server 포드의 Egress 대역폭을 변경하고, 다시 네트워크 성능 테스트

  • 대역폭이 10M에서 5M로 감소하면서 성능 테스트의 전송 속도가 9.88 Mbps 에서 4.91 Mbps 로 감소했습니다. 
(⎈|CiliumLab:N/A) root@k8s-s:~# kubectl get pod netperf-server -o json | sed -e 's|10M|5M|g' | kubectl apply -f -
pod/netperf-server configured
(⎈|CiliumLab:N/A) root@k8s-s:~# c1 bpf bandwidth list
No entries found.
(⎈|CiliumLab:N/A) root@k8s-s:~# c2 bpf bandwidth list
IDENTITY   EGRESS BANDWIDTH (BitsPerSec)
356        5M
(⎈|CiliumLab:N/A) root@k8s-s:~# kubectl exec netperf-client -- netperf -t TCP_MAERTS -H "${NETPERF_SERVER_IP}"
MIGRATED TCP MAERTS TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 172.16.2.180 (172.16.) port 0 AF_INET
Recv   Send    Send
Socket Socket  Message  Elapsed
Size   Size    Size     Time     Throughput
bytes  bytes   bytes    secs.    10^6bits/sec

131072  16384  16384    10.04       4.91

🧿 netperf-server 포드의 Egress 대역폭을 변경하고, 다시 네트워크 성능 테스트

  • 대역폭이 5M에서 20M로 증가하면서 성능 테스트의 전송 속도가 4.91 Mbps 에서 19.80  Mbps 로 증가했습니다. 
(⎈|CiliumLab:N/A) root@k8s-s:~# kubectl get pod netperf-server -o json | sed -e 's|5M|20M|g' | kubectl apply -f -
kubectl exec netperf-client -- netperf -t TCP_MAERTS -H "${NETPERF_SERVER_IP}"
MIGRATED TCP MAERTS TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 172.16.2.180 (172.16.) port 0 AF_INET
Recv   Send    Send
Socket Socket  Message  Elapsed
Size   Size    Size     Time     Throughput
bytes  bytes   bytes    secs.    10^6bits/sec

131072  16384  16384    10.02      19.80

🧿 리눅스의 트래픽 제어(tc) 시스템을 통해 설정된 큐 관리(qdisc) 상태

(⎈|CiliumLab:N/A) root@k8s-s:~# tc qdisc show dev ens5
qdisc mq 8002: root
qdisc fq 8005: parent 8002:2 limit 10000p flow_limit 100p buckets 32768 orphan_mask 1023 quantum 18030b initial_quantum 90150b low_rate_threshold 550Kbit refill_delay 40ms timer_slack 10us horizon 2s horizon_drop
qdisc fq 8003: parent 8002:4 limit 10000p flow_limit 100p buckets 32768 orphan_mask 1023 quantum 18030b initial_quantum 90150b low_rate_threshold 550Kbit refill_delay 40ms timer_slack 10us horizon 2s horizon_drop
qdisc fq 8004: parent 8002:3 limit 10000p flow_limit 100p buckets 32768 orphan_mask 1023 quantum 18030b initial_quantum 90150b low_rate_threshold 550Kbit refill_delay 40ms timer_slack 10us horizon 2s horizon_drop
qdisc fq 8006: parent 8002:1 limit 10000p flow_limit 100p buckets 32768 orphan_mask 1023 quantum 18030b initial_quantum 90150b low_rate_threshold 550Kbit refill_delay 40ms timer_slack 10us horizon 2s horizon_drop

🧿 삭제

(⎈|CiliumLab:N/A) root@k8s-s:~# kubectl delete pod netperf-client netperf-server
pod "netperf-client" deleted
pod "netperf-server" deleted
Comments