はじめに
k8sでpodにVFのNICを割り当てて通信できるかを試してみました。
以下の実施内容の記録です。
- SR-IOV環境の構築
- SR-IOVワークロードのデプロイ
- SR-IOVネットワーク経由の通信確認
- ネットワークスループット計測
- Serviceを使ってSR-IOVネットワーク経由で通信する方法の検証
SR-IOVとは
- Single Root I/O Virtualizationの略
- PCIデバイス(NICやRAIDカード)側で仮想化をサポートする規格
- 従来,複数の仮想マシンから送られてきた1つのPCIデバイスへのI/O要求は,ハイパーバイザが調停して要求を一本化し,デバイスから返ってきた結果を仮想マシンに振り分けるソフト処理が必要だった。
しかし,SR-IOVに対応したデバイスがあれば,この処理をPCIデバイス側にオフロードできる。
ハードウエアによる処理であるため,パフォーマンスの大幅な向上が期待できる。 - VMwareのサイトには、「単一のルートポートにある単一の PCIe (Peripheral Component Interconnect Express) 物理デバイスを、ハイパーバイザーやゲスト OS に対して、複数の別個の物理デバイスとして認識されるようにする仕様です。」と説明されている。
k8sでSR-IOVを使うには
SR-IOV Network Device Pluginを使えば、k8sでSR-IOVを使うことができる。
以下のCNIプラグインが必要。
- Any CNI meta plugin supporting Device Plugin based network provisioning
- Multus CNI, or DANM
- SR-IOV CNI
- uring Pod creation, plumbs allocated SR-IOV VF to a Pods network namespace using VF information given by the meta plugin
- On Pod deletion, reset and release the VF from the Pod
環境
- サーバー
- 機種:FUJITSU Server PRIMERGY RX2530 M5
- 10G NIC:Intel® Ethernet Controller XL710(controleplane: × 2, worker: × 4)
- OS:Ubuntu 20.04.2
- kubernetes v1.20.4
- containerd v1.3.3
- CNI
- Calico v3.18.0
- SR-IOV Network Device Plugin v3.3.1
- Multus v3.4.1
ネットワーク
用途 | サブネット | VLAN ID |
---|---|---|
構築(SSH等) | 10.110.0.0/24 | 180 |
k8s nodes | 172.16.90.0/24 | 181 |
SR-IOV | 172.16.91.0/24 | 182 (untag) |
本記事ではSR-IOVネットワークをuntag VLANとしましたが、後日tag VLANでも試してうまくいきました。
tag VLANを使う場合は、NetworkAttachmentDefinitionにVLAN IDを指定する必要があります。
構築
前提条件
- k8sクラスタが構築済であること
- Worker nodeがSR-IOV対応のNICを有すること
Quick Startに従って、作業を進める。
SR-IOV Virtual Functions(VF)作成
VF作成は、device pluginの機能に含まれないため、事前に作成しておく必要がある。
本記事では、VFを3つ作成する。
worker01
worker01:~$ cat /sys/class/net/enp24s0f2/device/sriov_numvfs
0
worker01:~$ cat <<EOF | sudo tee /sys/class/net/enp24s0f2/device/sriov_numvfs
> 3
> EOF
3
worker01:~$ cat /sys/class/net/enp24s0f2/device/sriov_numvfs
3
worker01:~$ lspci | grep "Virtual Function"
18:0a.0 Ethernet controller: Intel Corporation Ethernet Virtual Function 700 Series (rev 02)
18:0a.1 Ethernet controller: Intel Corporation Ethernet Virtual Function 700 Series (rev 02)
18:0a.2 Ethernet controller: Intel Corporation Ethernet Virtual Function 700 Series (rev 02)
NICも確認してみる。
worker01:~$ ip l show enp24s0f2
6: enp24s0f2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc mq state UP mode DEFAULT group default qlen 1000
link/ether 40:a6:b7:18:34:ca brd ff:ff:ff:ff:ff:ff
vf 0 link/ether 00:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff, spoof checking on, link-state auto, trust off
vf 1 link/ether 00:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff, spoof checking on, link-state auto, trust off
vf 2 link/ether 00:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff, spoof checking on, link-state auto, trust off
worker01:~$ ip a show enp24s0f2v0
18: enp24s0f2v0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 6a:26:e3:6e:0f:82 brd ff:ff:ff:ff:ff:ff
worker01:~$ ip a show enp24s0f2v1
20: enp24s0f2v1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether ca:45:9b:1b:10:39 brd ff:ff:ff:ff:ff:ff
worker01:~$ ip a show enp24s0f2v2
19: enp24s0f2v2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether be:3d:19:83:c6:40 brd ff:ff:ff:ff:ff:ff
この設定はマシンの再起動でリセットされてしまうので、設定を永続化するためには/etc/rc.local
に上記コマンドを指定する必要があるとのこと。
worker02
worker02:~$ lspci | grep "Virtual Function"
worker02:~$ cat <<EOF | sudo tee /sys/class/net/enp24s0f2/device/sriov_numvfs
> 3
> EOF
3
worker02:~$ lspci | grep "Virtual Function"
18:0a.0 Ethernet controller: Intel Corporation Ethernet Virtual Function 700 Series (rev 02)
18:0a.1 Ethernet controller: Intel Corporation Ethernet Virtual Function 700 Series (rev 02)
18:0a.2 Ethernet controller: Intel Corporation Ethernet Virtual Function 700 Series (rev 02)
worker02:~$ ip l show enp24s0f2
6: enp24s0f2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc mq state UP mode DEFAULT group default qlen 1000
link/ether 40:a6:b7:18:37:ea brd ff:ff:ff:ff:ff:ff
vf 0 link/ether 00:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff, spoof checking on, link-state auto, trust off
vf 1 link/ether 00:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff, spoof checking on, link-state auto, trust off
vf 2 link/ether 00:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff, spoof checking on, link-state auto, trust off
worker02:~$ ip a show enp24s0f2v0
22: enp24s0f2v0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 8e:6b:a3:5e:91:4d brd ff:ff:ff:ff:ff:ff
worker02:~$ ip a show enp24s0f2v1
21: enp24s0f2v1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether ca:a0:3b:3f:68:99 brd ff:ff:ff:ff:ff:ff
worker02:~$ ip a show enp24s0f2v2
20: enp24s0f2v2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 8e:d4:33:5f:2e:06 brd ff:ff:ff:ff:ff:ff
SR-IOV CNIのビルド
controlplane:~$ git clone https://github.com/k8snetworkplumbingwg/sriov-cni.git
Cloning into 'sriov-cni'...
remote: Enumerating objects: 87, done.
remote: Counting objects: 100% (87/87), done.
remote: Compressing objects: 100% (56/56), done.
remote: Total 20703 (delta 36), reused 37 (delta 27), pack-reused 20616
Receiving objects: 100% (20703/20703), 29.67 MiB | 7.01 MiB/s, done.
Resolving deltas: 100% (7504/7504), done.
controlplane:~$ cd sriov-cni/
controlplane:~/sriov-cni$ sudo apt install make
(省略)
controlplane:~/sriov-cni$ make
Running gofmt...
/bin/sh: 1: go: not found
Building golint...
make: go: Command not found
make: *** [Makefile:66: /home/xxxxxx/sriov-cni/bin/golint] Error 127
golangが入ってないので、インストールする。
controlplane:~$ wget https://golang.org/dl/go1.16.linux-amd64.tar.gz
(省略)
controlplane:~$ sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.16.linux-amd64.tar.gz
controlplane:~$ export PATH=$PATH:/usr/local/go/bin
controlplane:~$ go version
go version go1.16 linux/amd64
再度、ビルドを実行
controlplane:~/sriov-cni$ make
(省略)
controlplane:~/sriov-cni$ cp build/sriov /opt/cni/bin
ドキュメントにはないが、パーミションを他のCNIプラグインと合わせておく。
controlplane:~$ sudo chown root:root /opt/cni/bin/sriov
controlplane:~$ sudo chmod 755 /opt/cni/bin/sriov
controlplane:~$ sudo ls -l /opt/cni/bin/sriov
-rwxr-xr-x 1 root root 3894356 Mar 10 04:58 /opt/cni/bin/sriov
ドキュメントに記載ないが、全ノードにCNIプラグインが必要と思われるため、ビルドしたバイナリをworker nodeにも配置する。
SR-IOV network device pluginのビルドとデプロイ
イメージはDocker Hubのものを使用するのでビルドはしない。
device pluginをk8sにデプロイする。
ConfigMapにVFの定義が必要。
今回使用するデバイスのベンダーコード、デバイスドライバ、デバイスコードは以下の通り。
worker01:~$ dmesg |grep enp24s0f2 |grep 18:0a.0
[346246.258461] iavf 0000:18:0a.0 enp24s0f2v0: renamed from eth2
(省略)
worker01:~$ lspci -nn | grep 18:0a.0
18:0a.0 Ethernet controller [0200]: Intel Corporation Ethernet Virtual Function 700 Series [8086:154c] (rev 02)
今回使用する環境の場合、デフォルトの定義で問題なそうなので、マニフェストを編集せずにConfigMapをapplyする。
controlplane:~/sriov-network-device-plugin$ k apply -f deployments/configMap.yaml
configmap/sriovdp-config created
controlplane:~/sriov-network-device-plugin$ k apply -f deployments/k8s-v1.16/sriovdp-daemonset.yaml
serviceaccount/sriov-device-plugin created
daemonset.apps/kube-sriov-device-plugin-amd64 created
daemonset.apps/kube-sriov-device-plugin-ppc64le created
daemonset.apps/kube-sriov-device-plugin-arm64 created
controlplane:~/sriov-network-device-plugin$ k -n kube-system get pod -l app=sriovdp -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
kube-sriov-device-plugin-amd64-2fvtq 1/1 Running 0 2m5s 172.16.90.3 worker02 <none> <none>
kube-sriov-device-plugin-amd64-fpptr 1/1 Running 0 2m5s 172.16.90.2 worker01 <none> <none>
kube-sriov-device-plugin-amd64-q99tf 1/1 Running 0 2m5s 172.16.90.1 controlplane <none> <none>
CNI meta pluginのインストール
MultusかDANMをインストールする必要がある。
今回はMultusを使用。(Multusは少しだけ知ってるので)
MultusのQuickstart Installation Guideに従ってインストールする。
- 前提条件
- k8sクラスタにCNIプラグインがインストール済であること
今回の場合は、Calicoをインストール済
- k8sクラスタにCNIプラグインがインストール済であること
controlplane:~$ k apply -f https://raw.githubusercontent.com/intel/multus-cni/v3.6/images/multus-daemonset.yml
customresourcedefinition.apiextensions.k8s.io/network-attachment-definitions.k8s.cni.cncf.io created
clusterrole.rbac.authorization.k8s.io/multus created
clusterrolebinding.rbac.authorization.k8s.io/multus created
serviceaccount/multus created
configmap/multus-cni-config created
daemonset.apps/kube-multus-ds-amd64 created
daemonset.apps/kube-multus-ds-ppc64le created
controlplane:~$ k get pod -l app=multus -A -o wide
NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
kube-system kube-multus-ds-amd64-9wc5g 1/1 Running 0 60s 172.16.90.2 worker01 <none> <none>
kube-system kube-multus-ds-amd64-g4xgv 1/1 Running 0 60s 172.16.90.3 worker02 <none> <none>
kube-system kube-multus-ds-amd64-mmwsf 1/1 Running 0 60s 172.16.90.1 controlplane <none> <none>
各worker nodeでVFが認識されているかの確認。
controlplane:~$ kubectl get node worker01 -o jsonpath='{.status.allocatable}' |jq -r '."intel.com/intel_sriov_netdevice"'
3
controlplane:~$ kubectl get node worker02 -o jsonpath='{.status.allocatable}' |jq -r '."intel.com/intel_sriov_netdevice"'
3
SR-IOV Network CRDの作成
テンプレートをベースにサブネット、ゲートウェイを環境に合わせて変更する。
kind: NetworkAttachmentDefinition
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
name: sriov-net1
annotations:
k8s.v1.cni.cncf.io/resourceName: intel.com/intel_sriov_netdevice
spec:
config: '{
"type": "sriov",
"cniVersion": "0.3.1",
"name": "sriov-network",
"ipam": {
"type": "host-local",
"subnet": "172.16.91.0/24",
"routes": [{
"dst": "0.0.0.0/0"
}],
"gateway": "172.16.91.1"
}
}'
controlplane:~$ k apply -f sriov-network-device-plugin/deployments/sriov-crd.yaml
networkattachmentdefinition.k8s.cni.cncf.io/sriov-net1 created
controlplane:~$ k get network-attachment-definitions.k8s.cni.cncf.io
NAME AGE
sriov-net1 2m6s
SR-IOVワークロードのデプロイ
以下のマニフェストを使用する。
apiVersion: v1
kind: Pod
metadata:
name: testpod1
annotations:
k8s.v1.cni.cncf.io/networks: sriov-net1
spec:
containers:
- name: appcntr1
image: centos/tools
imagePullPolicy: IfNotPresent
command: [ "/bin/bash", "-c", "--" ]
args: [ "while true; do sleep 300000; done;" ]
resources:
requests:
intel.com/intel_sriov_netdevice: '1'
limits:
intel.com/intel_sriov_netdevice: '1'
metadata.annotation
にk8s.v1.cni.cncf.io/networks: sriov-net1
、spec.containers.resources
にpodに割り当てるVFポート数を指定すれば、SR-IOVのNICをもつpodをデプロイできるようである。
上記マニフェストにspec.nodeName
の定義を追加してworker01にpodをスケジューリングする。
controlplane:~$ k apply -f pod-tc1.yaml
pod/testpod1 created
controlplane:~$ k get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
testpod1 1/1 Running 0 5s 192.168.5.27 worker01 <none> <none>
とりあえずrunning
となった。
つづいてpodのIPを確認してみる。
controlplane:~$ k exec -it testpod1 -- ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
3: eth0@if21: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default
link/ether 92:40:9e:d4:af:84 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 192.168.5.27/32 brd 192.168.5.27 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::9040:9eff:fed4:af84/64 scope link
valid_lft forever preferred_lft forever
20: net1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 6e:c6:50:5f:c1:66 brd ff:ff:ff:ff:ff:ff
inet 172.16.91.24/24 brd 172.16.91.255 scope global net1
valid_lft forever preferred_lft forever
inet6 fe80::6cc6:50ff:fe5f:c166/64 scope link
valid_lft forever preferred_lft forever
NICが2つ付いていることを確認できた。
192.168.5.27/32
がCallicoのネットワークで、もう一つの172.16.91.24/24
はSR-IOVのネットワーク。
どのVFが使用されているかを確認する。
PCIデバイスはコンテナの環境変数で確認できる。
controlplane:~$ k exec -it testpod1 -- env |grep PCIDEVICE_
PCIDEVICE_INTEL_COM_INTEL_SRIOV_NETDEVICE=0000:18:0a.1
コンテナにVFポートが割り当てられていることを確認できた。
次に、podのホストであるworker01
nodeのVFを確認する。
worker01:~$ ip a | grep enp24s0f2v
18: enp24s0f2v0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
19: enp24s0f2v2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
enp24s0f2v1
がworker nodeから見えなくなった。
VFがコンテナのnamespaceになったためと思われる。
SR-IOV NICをもつpodの通信確認
まずは、先程デプロイしたpodtestpod1
からPFに割り振ったIPアドレス(ゲートウェイ)に通信できるか確認
controlplane:~$ k exec -it testpod1 -- ping -c 3 172.16.91.1
PING 172.16.91.1 (172.16.91.1) 56(84) bytes of data.
64 bytes from 172.16.91.1: icmp_seq=1 ttl=64 time=0.332 ms
64 bytes from 172.16.91.1: icmp_seq=2 ttl=64 time=0.153 ms
64 bytes from 172.16.91.1: icmp_seq=3 ttl=64 time=0.133 ms
--- 172.16.91.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2034ms
rtt min/avg/max/mdev = 0.133/0.206/0.332/0.089 ms
通信できた。
次にpod間通信を確認するため、testpod1
と同様のpodtestpod2
をworker02
にスケジューリングする。
controlplane:~$ k get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
testpod1 1/1 Running 0 10m 192.168.5.27 worker01 <none> <none>
testpod2 1/1 Running 0 4s 192.168.30.73 worker02 <none> <none>
controlplane:~$ k exec -it testpod2 -- ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
3: eth0@if31: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default
link/ether c2:1e:ef:19:51:b0 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 192.168.30.73/32 brd 192.168.30.73 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::c01e:efff:fe19:51b0/64 scope link
valid_lft forever preferred_lft forever
22: net1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 8e:6b:a3:5e:91:4d brd ff:ff:ff:ff:ff:ff
inet 172.16.91.5/24 brd 172.16.91.255 scope global net1
valid_lft forever preferred_lft forever
inet6 fe80::8c6b:a3ff:fe5e:914d/64 scope link
valid_lft forever preferred_lft forever
testpod1
=> testpod2
へ通信が通るか試してみる。
controlplane:~$ k exec -it testpod1 -- ping -c 3 172.16.91.5
PING 172.16.91.5 (172.16.91.5) 56(84) bytes of data.
64 bytes from 172.16.91.5: icmp_seq=1 ttl=64 time=0.336 ms
64 bytes from 172.16.91.5: icmp_seq=2 ttl=64 time=0.179 ms
64 bytes from 172.16.91.5: icmp_seq=3 ttl=64 time=0.121 ms
--- 172.16.91.5 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2036ms
rtt min/avg/max/mdev = 0.121/0.212/0.336/0.090 ms
疎通できた。
ネットワークスループット計測
pod間のデータ転送速度を、calico, SR-IOVネットワークのそれぞれを経由する場合で計測する。
まずは、iperf3で計測される側のpodiperf3-server
をworker02
に、計測する側のpodiperf3-client
をworker01にデプロイする。
apiVersion: v1
kind: Pod
metadata:
name: iperf3-server
annotations:
k8s.v1.cni.cncf.io/networks: sriov-net1
spec:
nodeName: worker02
containers:
- name: iperf3-server
image: networkstatic/iperf3
imagePullPolicy: IfNotPresent
command: [ "iperf3", "-s" ]
resources:
requests:
intel.com/intel_sriov_netdevice: '1'
limits:
intel.com/intel_sriov_netdevice: '1'
apiVersion: v1
kind: Pod
metadata:
name: iperf3-client
annotations:
k8s.v1.cni.cncf.io/networks: sriov-net1
spec:
nodeName: worker01
containers:
- name: iperf3-client
image: networkstatic/iperf3
imagePullPolicy: IfNotPresent
command: [ "sleep", "infinity" ]
resources:
requests:
intel.com/intel_sriov_netdevice: '1'
limits:
intel.com/intel_sriov_netdevice: '1'
controlplane:~$ k get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
iperf3-client 1/1 Running 0 14m 192.168.5.29 worker01 <none> <none>
iperf3-server 1/1 Running 0 4s 192.168.30.77 worker02 <none> <none>
testpod1 1/1 Running 0 150m 192.168.5.27 worker01 <none> <none>
testpod2 1/1 Running 0 140m 192.168.30.73 worker02 <none> <none>
controlplane:~$ k exec -it iperf3-client -- ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
3: eth0@if23: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default
link/ether 8a:f4:d9:75:0d:a6 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 192.168.5.29/32 brd 192.168.5.29 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::88f4:d9ff:fe75:da6/64 scope link
valid_lft forever preferred_lft forever
19: net1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 96:3d:6c:f7:e4:5a brd ff:ff:ff:ff:ff:ff
inet 172.16.91.26/24 brd 172.16.91.255 scope global net1
valid_lft forever preferred_lft forever
inet6 fe80::943d:6cff:fef7:e45a/64 scope link
valid_lft forever preferred_lft forever
controlplane:~$ k exec -it iperf3-server -- ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
3: eth0@if35: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default
link/ether 6e:57:bf:f4:d5:b3 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 192.168.30.77/32 brd 192.168.30.77 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::6c57:bfff:fef4:d5b3/64 scope link
valid_lft forever preferred_lft forever
21: net1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether ca:a0:3b:3f:68:99 brd ff:ff:ff:ff:ff:ff
inet 172.16.91.9/24 brd 172.16.91.255 scope global net1
valid_lft forever preferred_lft forever
inet6 fe80::c8a0:3bff:fe3f:6899/64 scope link
valid_lft forever preferred_lft forever
calicoネットワーク経由の計測
controlplane:~$ k exec -it iperf3-client -- iperf3 -c 192.168.30.77
Connecting to host 192.168.30.77, port 5201
[ 5] local 192.168.5.29 port 48086 connected to 192.168.30.77 port 5201
[ ID] Interval Transfer Bitrate Retr Cwnd
[ 5] 0.00-1.00 sec 527 MBytes 4.42 Gbits/sec 2236 527 KBytes
[ 5] 1.00-2.00 sec 534 MBytes 4.47 Gbits/sec 2458 601 KBytes
[ 5] 2.00-3.00 sec 545 MBytes 4.58 Gbits/sec 1234 633 KBytes
[ 5] 3.00-4.00 sec 544 MBytes 4.56 Gbits/sec 1035 696 KBytes
[ 5] 4.00-5.00 sec 535 MBytes 4.49 Gbits/sec 1358 538 KBytes
[ 5] 5.00-6.00 sec 562 MBytes 4.72 Gbits/sec 1530 717 KBytes
[ 5] 6.00-7.00 sec 548 MBytes 4.59 Gbits/sec 1893 496 KBytes
[ 5] 7.00-8.00 sec 579 MBytes 4.86 Gbits/sec 978 640 KBytes
[ 5] 8.00-9.00 sec 592 MBytes 4.97 Gbits/sec 1655 564 KBytes
[ 5] 9.00-10.00 sec 509 MBytes 4.27 Gbits/sec 1203 601 KBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-10.00 sec 5.35 GBytes 4.59 Gbits/sec 15580 sender
[ 5] 0.00-10.03 sec 5.34 GBytes 4.58 Gbits/sec receiver
iperf Done.
SR-IOVネットワーク経由の計測
controlplane:~$ k exec -it iperf3-client -- iperf3 -c 172.16.91.9
Connecting to host 172.16.91.9, port 5201
[ 5] local 172.16.91.26 port 54806 connected to 172.16.91.9 port 5201
[ ID] Interval Transfer Bitrate Retr Cwnd
[ 5] 0.00-1.00 sec 1.10 GBytes 9.43 Gbits/sec 0 1.35 MBytes
[ 5] 1.00-2.00 sec 1.10 GBytes 9.41 Gbits/sec 0 1.42 MBytes
[ 5] 2.00-3.00 sec 1.09 GBytes 9.41 Gbits/sec 0 1.58 MBytes
[ 5] 3.00-4.00 sec 1.10 GBytes 9.42 Gbits/sec 0 1.58 MBytes
[ 5] 4.00-5.00 sec 1.10 GBytes 9.42 Gbits/sec 0 1.58 MBytes
[ 5] 5.00-6.00 sec 1.10 GBytes 9.41 Gbits/sec 0 1.58 MBytes
[ 5] 6.00-7.00 sec 1.10 GBytes 9.41 Gbits/sec 4 964 KBytes
[ 5] 7.00-8.00 sec 1.09 GBytes 9.41 Gbits/sec 9 959 KBytes
[ 5] 8.00-9.00 sec 1.10 GBytes 9.42 Gbits/sec 8 938 KBytes
[ 5] 9.00-10.00 sec 1.10 GBytes 9.42 Gbits/sec 4 1.21 MBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-10.00 sec 11.0 GBytes 9.42 Gbits/sec 25 sender
[ 5] 0.00-10.00 sec 11.0 GBytes 9.41 Gbits/sec receiver
iperf Done.
SR-IOVの方がCalicoよりも2倍程度の転送速度となった。
Serviceを使ってSR-IOVネットワーク経由で通信する方法の検証
SR-IOVネットワークを使用することで高スループットが得られた。
しかし、SR-IOVのネットワークで通信するためにはIPアドレスを指定する必要があり、serviceを使えなそう。
k8sではpodはつくって消してを繰り返すのが前提であり、その度にpodのIPアドレスが変わるため、serviceが使えないとなるとなかなか不便そう。
Headless Serviceを使用すればkube-proxyを介さずpodに通信できるので、SR-IOVネットワーク経由で通信できるかもしれないので試してみる。
まずは、SR-IOVのNICをもつpodをデプロイする。
apiVersion: v1
kind: Pod
metadata:
name: hello-world
annotations:
k8s.v1.cni.cncf.io/networks: sriov-net1
labels:
app: hello-world
spec:
containers:
- name: hello-worlde
image: gcr.io/google-samples/node-hello:1.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
protocol: TCP
resources:
requests:
intel.com/intel_sriov_netdevice: '1'
limits:
intel.com/intel_sriov_netdevice: '1'
controlplane:~$ k apply -f pod-helloworld.yaml
pod/hello-world created
controlplane:~$ k get pod -l app=hello-world
NAME READY STATUS RESTARTS AGE
hello-world 1/1 Running 0 78s
controlplane:~$ k exec -it hello-world -- ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
3: eth0@if36: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default
link/ether a2:16:bd:b6:2f:8f brd ff:ff:ff:ff:ff:ff
inet 192.168.30.78/32 brd 192.168.30.78 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::a016:bdff:feb6:2f8f/64 scope link
valid_lft forever preferred_lft forever
20: net1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 8e:d4:33:5f:2e:06 brd ff:ff:ff:ff:ff:ff
inet 172.16.91.10/24 brd 172.16.91.255 scope global net1
valid_lft forever preferred_lft forever
inet6 fe80::8cd4:33ff:fe5f:2e06/64 scope link
valid_lft forever preferred_lft forever
つづいて、headless serviceを作成する。
controlplane:~$ k apply -f service-helloworld.yaml
service/hello-world created
controlplane:~$ k get endpoints hello-world
NAME ENDPOINTS AGE
hello-world 192.168.30.78:8080 2m5s
Calicoネットワークのendpointしか作成されなかった。
headless serviceを使っても、SR-IOVネットワーク経由の通信はできなかった。
Multusでは現状、デフォルトネットワークしかserviceに対応していないようである。
所感
k8sでSR-IOVを使うことにより高スループットを得られるが、反面、以下のデメリットがあることがわかった。
- Worker nodeのSR-IOV用のNIC(PV)でbondを組めないため、ネットワークを冗長化できない
- k8sのserviceはデフォルトネットワークにしか対応していないため、SR-IOVで通信するにはserviceではなくIPアドレスを指定する必要がある
なのでk8sでSR-IOVを使うには、割り切りが必要ということがわかった。
参考
- https://xtech.nikkei.com/it/article/Keyword/20100115/343227/
- https://github.com/k8snetworkplumbingwg/sriov-network-device-plugin
- https://docs.vmware.com/jp/VMware-vSphere/7.0/com.vmware.vsphere.networking.doc/GUID-CC021803-30EA-444D-BCBE-618E0D836B9F.html
- https://golang.org/doc/install
- https://github.com/intel/multus-cni#quickstart-installation-guide
- https://metonymical.hatenablog.com/entry/2019/12/15/121251
- https://www.na3.jp/entry/20140121/p1
- https://gihyo.jp/admin/serial/01/ubuntu-recipe/0539?page=2
- https://hub.docker.com/r/networkstatic/iperf3
- https://kubernetes.io/ja/docs/concepts/services-networking/service/#headless-service
- https://github.com/k8snetworkplumbingwg/multus-cni/issues/256