RKE2ノードのCiliumを使ったeBPFな帯域制限をする話

この記事は MicroAd Advent Calendar 2023Kubernetes Advent Calendar 2023 の5日目の記事です。

オンプレあるあるな悩みとして、データセンターと外の通信にはインターネットを経由する都合、1つのアプリで契約している帯域を専有してしまいインターネット通信が輻輳1 (ふくそう)し、他のシステムの本番環境に影響させてしまう事があったりします。

今回は、RKE22 で構築したKubernetesノードにて、Pod発の通信に対して帯域制限をかける方法について紹介します。

環境

  • Kubernetes: RKE2 v1.23.17+rke2r1 ※Kubernetes v1.23以降なら大丈夫
  • CNI:RKE2標準の Cilium L2モード
  • OS: Ubuntu 20.04 LTS ※Linux Kernel v5.1.x 以降ならOK
  • Rancher: v2.6.8以降 ※無くてもOK

種明かし

帯域制限については、以下のCiliumのBandwidth Managerを使います。これはeBPFを使用して個々のPodの帯域を制限します。

docs.cilium.io

これを有効にすると、マニフェストに特定のアノテーションを追加するだけで、指定の帯域を上限として制限がかけられる優れものです。 ただ、残念ながら、Pod発の通信のみで、Pod着の通信については対象外です。

詳しい話は上記のドキュメントでも紹介されてますが、 KubeCon + CloudNativeCon North America 2022 の動画が課題感といった背景や仕組みについて話されているので面白いです。

www.youtube.com

利用できるか確認する

CiliumのBandwidth Managerは、Linux Kernel v5.1.x 以降ならOKです。 以下のようにして、Kernelのバージョンを確認して、v5.1以降なら利用できるはずです。

$ uname -r
5.4.0-164-generic

念のため、Base Requirements 及び Requirements for the Bandwidth Manager で挙げられているカーネルオプションが有効になっているかを確認します。値がnでなければ大丈夫です3

$ cat /boot/config-`uname -r` | grep -E '\b(CONFIG_BPF|CONFIG_BPF_SYSCALL|CONFIG_NET_CLS_BPF|CONFIG_BPF_JIT|CONFIG_NET_CLS_ACT|CONFIG_NET_SCH_INGRESS|CONFIG_CRYPTO_SHA1|CONFIG_CRYPTO_USER_API_HASH|CONFIG_CGROUPS|CONFIG_CGROUP_BPF|CONFIG_PERF_EVENTS|CONFIG_SCHEDSTATS|CONFIG_NET_SCH_FQ)\b'
CONFIG_CGROUPS=y
CONFIG_CGROUP_BPF=y
CONFIG_BPF=y
CONFIG_BPF_SYSCALL=y
CONFIG_PERF_EVENTS=y
CONFIG_NET_SCH_FQ=m
CONFIG_NET_SCH_INGRESS=m
CONFIG_NET_CLS_BPF=m
CONFIG_NET_CLS_ACT=y
CONFIG_BPF_JIT=y
CONFIG_CRYPTO_SHA1=y
CONFIG_CRYPTO_USER_API_HASH=m
CONFIG_SCHEDSTATS=y

これで利用出来るか確認出来たので、次に、実際にCiliumのBandwidth Managerを有効にします。

Nodeのセットアップ

今回は、RKE2に対して有効にしてきます。

RKE2のみの場合

RKE2周りのセットアップについては、去年のアドカレの記事をどうぞ。

yassan.hatenablog.jp

補足: ↑に書いてないことといえば、HTTP Proxyが関係するなら、ここ も必要です。

ただし、今回はデフォルトの設定だけではBandwidth Managerを有効に出来ません。 そこで、rke2-serverを起動する前に、 /etc/rancher/rke2/config.yaml に追加して、 /var/lib/rancher/rke2/server/manifests/rke2-cilium-config.yaml を作成して、以下のように記述します。

# /var/lib/rancher/rke2/server/manifests/rke2-cilium-config.yaml
---
apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
  name: rke2-cilium
  namespace: kube-system
spec:
  valuesContent: |-
    bandwidthManager:
      enabled: true

上記を記述したら、 systemctl enable --now rke2-server.service してRKE2を起動してください。

CNIのデフォルト設定の変更については以下を参考にしてください。

docs.rke2.io

Rancherを使ってクラスターをCreateする場合

Downstream User Cluster をCreateする際に、下図のように、Add-On-ConfigにてbandwidthManager.enabledfalsetrueにしてからCreateしてください。

CiliumのBandwidth Managerが有効になっているか確認

以下の様にして確認します。

$ kubectl -n kube-system exec ds/cilium -- cilium status | grep BandwidthManager
BandwidthManager:        EDT with BPF [CUBIC] [eth0]

また、有効になっていない場合は以下のようになるので、この場合はセットアップを見直してください。

$ kubectl -n kube-system exec ds/cilium -c cilium-agent -- cilium status | grep BandwidthManager
BandwidthManager:        Disabled

Podに帯域制限をかけるには?

帯域制限をかける場合は、アノテーションを以下のように付与します。

---
apiVersion: v1
kind: Pod
metadata:
  annotations:
    # Pod発の通信に対して 10Mbit/s の上限を用いたい場合
    kubernetes.io/egress-bandwidth: "10M"

実際に試してみてる

Ciliumのドキュメントにあるサンプルを使って実践してみます。
💡注意点としては、このサンプルは2ノード必要です。

以下を適当なファイルに保存して、applyします。

---
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

applyしたら、以下を実行すると、Throughput が10Mbit/sを下回らないことが分かります。

$ NETPERF_SERVER_IP=$(kubectl get pod netperf-server -o jsonpath='{.status.podIP}')
$ 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.18.3.247 (172.18.) port 0 AF_INET
Recv   Send    Send                          
Socket Socket  Message  Elapsed              
Size   Size    Size     Time     Throughput  
bytes  bytes   bytes    secs.    10^6bits/sec  

 87380  65536  65536    10.00       9.54   

ちなみにアノテーションなしの場合は以下のように、Throughput が10Mbit/sを簡単に超えます。

$ NETPERF_SERVER_IP=$(kubectl get pod netperf-server -o jsonpath='{.status.podIP}')
$ 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.18.3.163 (172.18.) port 0 AF_INET
Recv   Send    Send                          
Socket Socket  Message  Elapsed              
Size   Size    Size     Time     Throughput  
bytes  bytes   bytes    secs.    10^6bits/sec  

 87380  65536  65536    10.00     906.00 

最後に

いかがだったでしょうか。 他にも方法があったり改善点があれば是非コメントください。

以上、MicroAd Advent Calendar 2023Kubernetes Advent Calendar 2023 の5日目の記事でした。

さーて、明日、6日目の記事は、、、

MicroAd Advent Calendar 2023 は、PolarsのSQLで何ができるかをまとめる話。
そして、Kubernetes Advent Calendar 2023 は、AlloyDB Omni on Kuberentesのオペレーターを眺める話。

どちらも楽しみですね!

おまけ

以下の運営やってます。近々Rancher v2.8も出るので2024年の早めにはMeetupをやりたいと考えてます(オフサイトでやりたいなぁ、、会場、、、) ただ、コロナ前の様にもっとMeetupを開催したいのですが、アクティブに動ける運営が少ないので、興味ある方は @yassan168 までDMください!

rancherjp.connpass.com


  1. 輻輳」とは、ネットワークトラフィックが集中して、通信速度が低下すること
  2. docs.rke2.io
  3. What does m mean in kernel configuration file? - Stack Overflow