仮想化通信

日本仮想化技術株式会社の公式エンジニアブログ

Multus CNIでVLANモードを使う

Kubernetesの基本設計では一つのPodには一つのNICが提供され、外部からのアクセスや内部的な通信などを全てそのインターフェイスを経由して行います。

一方、KubernetesでMultus CNIを使うと、Podに対して複数のInterfaceを付与できます。 Multus CNIがセットアップされたクラスターでPodを作成すると、標準CNIによって一つのNICが割り当てられ、さらにNetwork Attachment Definitionによって定義したネットワークインターフェイスを使って、Podに別のインターフェイスを複数付与できます。

f:id:virtualtech:20210120162143p:plain
(上図は公式サイトより引用)

Multus CNIを使って、一つのVLANポートをPodに付与する

Multus CNIの使い方の一つに、Podに対してタグVLANを持つインターフェイスを追加する方法があるのですが、 赤帽ブログのMultusで遊ぶをみながらVLANモードでPodを作ったのですが、 こんな感じでエラーになり、うまくPodを作れませんでした。何か自分がこれまで設定した内容に間違いがあるのかもしれません。

Events:
  Type     Reason                  Age               From               Message
  ----     ------                  ----              ----               -------
  Normal   Scheduled               19s               default-scheduler  Successfully assigned default/vlan-192.168.100.211 to ubuntu1
  Warning  FailedCreatePodSandBox  13s               kubelet            Failed to create pod sandbox: rpc error: code = Unknown desc = failed to set up sandbox container "d5ad4d8a9b98754120533bf9cf13c10c3ccecf0dd0f27b2c9d17d5abf14aaee8" network for pod "vlan-192.168.100.211": networkPlugin cni failed to set up pod "vlan-192.168.100.211_default" network: [default/vlan-192.168.100.211:vlan100-conf]: error adding container to network "vlan100-conf": failed to create vlan: file exists
  Normal   SandboxChanged          6s (x2 over 12s)  kubelet            Pod sandbox changed, it will be killed and re-created.
  Warning  FailedCreatePodSandBox  6s                kubelet            Failed to create pod sandbox: rpc error: code = Unknown desc = failed to set up sandbox container "748c54177de2a3b01e60167b34b1047b4147dd157078a6a47e95a429d639e46c" network for pod "vlan-192.168.100.211": networkPlugin cni failed to set up pod "vlan-192.168.100.211_default" network: [default/vlan-192.168.100.211:vlan100-conf]: error adding container to network "vlan100-conf": failed to create vlan: file exists
  Warning  FailedCreatePodSandBox  0s                kubelet            Failed to create pod sandbox: rpc error: code = Unknown desc = failed to set up sandbox container "0bdf7ab44bf1aca3e699dbfec7dd0e729c47892f0c33b6732126a43eb5a18150" network for pod "vlan-192.168.100.211": networkPlugin cni failed to set up pod "vlan-192.168.100.211_default" network: [default/vlan-192.168.100.211:vlan100-conf]: error adding container to network "vlan100-conf": failed to create vlan: file exists

LinuxでタグVLANが必要な対向ノードと通信するにはVLAN用のインターフェイス設定ファイルを作る必要があり、今回はUbuntu 18.04を使っていたので次のような設定をしていました。 クラスターは enp30s0 のインターフェイスを使って動いています。ちなみに dhcp4: true になっていますが、 実際の環境は dhcp4: false に設定して固定のIPアドレスを設定しています。

 $ sudo vi /etc/netplan/99-my-config.yaml
network:
  ethernets:
    enp16s0f0:
      dhcp4: false
      optional: true 
    enp16s0f1:
      dhcp4: false
      optional: true 
    enp30s0:
      dhcp4: true
  vlans:
    vlan.100:
      id: 100
      link: enp16s0f0
      addresses: [192.168.100.100/24]
    vlan.200:
      id: 200
      link: enp16s0f1
      addresses: [192.168.200.100/24]
  version: 2

これを以下のように設定して再起動した以降はVLANモードによるPod作成がうまくいきました。

ちなみにNetplan.ioが利用されているUbuntuにおいては netplan apply コマンドでIPアドレスの設定が即時反映されますが、一度作成されたインターフェイスはそのコマンドでは削除されないため、ホストの再起動を行いました(再起動するまで同様のエラーが出てPodが作成できませんでした)。

 $ sudo vi /etc/netplan/99-my-config.yaml
network:
  ethernets:
    enp16s0f0:
      dhcp4: false
      optional: true 
    enp16s0f1:
      dhcp4: false
      optional: true 
    enp30s0:
      dhcp4: true

ホスト側でそれぞれのポートにVLANの設定がされてしまっていたので、Multus CNIがNetwork Attachment Definitionに従ってインターフェイスの設定をしようとしたらインターフェイスが使用中だったので「error adding container to network "vlan100-conf": failed to create vlan: file exists」というエラーを吐いていたようです。 サーバーにはタグVLANを設定したポートに繋いでおくだけで良かったようですね。

PodにPingを実施してみる

Podを作って、パケットが到達するか確認してみます。 基本的な設定は赤帽ブログのMultusで遊ぶの「vlanで追加ネットワークに接続」と同様にしています(違うのは標準CNIとしてCalicoを使っている点、インターフェイスとVLAN ID、IPアドレスくらいです)。

//Podを作る
# kubectl create -f vlan-pod1.yaml

//Podが作られたか確認
# kubectl get -f vlan-pod1.yaml
NAME                   READY   STATUS    RESTARTS   AGE
vlan-192.168.100.211   1/1     Running   0          34m

//PodのIPアドレスを見てみる
# kubectl exec -it vlan-192.168.100.211 -- 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
3: eth0@if14: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default 
    link/ether 2a:c9:40:80:79:d5 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.244.25.159/32 brd 10.244.25.159 scope global eth0
       valid_lft forever preferred_lft forever
4: net1@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 00:15:17:75:c0:8e brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.100.211/24 brd 192.168.100.255 scope global net1
       valid_lft forever preferred_lft forever

//PodでtcpdumpをMultus CNIで追加したインターフェイス側で実行
# kubectl exec -it vlan-192.168.100.211 -- tcpdump -i net1
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes

//外部からpingを実行してみる(当然タグVLANが使える環境から)
$ ping -c3 192.168.100.211

//対向ノードからPodにpingを実行し、パケットがPodに到達するのを確認
//対向: 192.168.100.200  Pod: 192.168.100.211
# kubectl exec -it vlan-192.168.100.211 -- tcpdump -i net1
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on net1, link-type EN10MB (Ethernet), capture size 262144 bytes
03:51:29.443436 IP 192.168.100.200 > vlan-192.168.100.211: ICMP echo request, id 29492, seq 0, length 64
03:51:29.443493 IP vlan-192.168.100.211 > 192.168.100.200: ICMP echo reply, id 29492, seq 0, length 64
03:51:30.038088 STP 802.1w, Rapid STP, Flags [Learn, Forward], bridge-id 8064.00:19:e8:93:31:40.8001, length 42
03:51:30.448592 IP 192.168.100.200 > vlan-192.168.100.211: ICMP echo request, id 29492, seq 1, length 64
03:51:30.448645 IP vlan-192.168.100.211 > 192.168.100.200: ICMP echo reply, id 29492, seq 1, length 64
03:51:31.452776 IP 192.168.100.200 > vlan-192.168.100.211: ICMP echo request, id 29492, seq 2, length 64
03:51:31.452829 IP vlan-192.168.100.211 > 192.168.100.200: ICMP echo reply, id 29492, seq 2, length 64
03:51:32.038198 STP 802.1w, Rapid STP, Flags [Learn, Forward], bridge-id 8064.00:19:e8:93:31:40.8001, length 42

Multus CNIを使って、複数のVLANポートをPodに付与する

複数のVLANポートをPodに付与するには、もう一つNetwork Attachment Definitionを作って...

# kubectl get network-attachment-definition
NAME           AGE
vlan100-conf   98m
vlan200-conf   82s

次のように複数のインターフェイスを指定したPod YAMLを作って作成すれば良いようです。

# cat vlan-pod-multi.yaml
---
apiVersion: v1
kind: Pod
metadata:
#  generateName: vlan-
  name: vlan-multi
  labels:
    app: multus-vlan
  annotations:
    k8s.v1.cni.cncf.io/networks: '[
            { "name": "vlan100-conf",
              "ips": [ "192.168.100.211/24" ]
            },
            {  
              "name": "vlan200-conf",
              "ips": [ "192.168.200.211/24" ]  
            }
    ]'
spec:
  containers:
  - name: centos-tools
    image: docker.io/centos/tools:latest
    command:
    - /sbin/init
    securityContext:
      privileged: true

それぞれのIPアドレスに対してPingが通るはずです。

$ ping -c1 192.168.100.211
PING 192.168.100.211 (192.168.100.211): 56 data bytes
64 bytes from 192.168.100.211: icmp_seq=0 ttl=64 time=0.669 ms

--- 192.168.100.211 ping statistics ---
1 packets transmitted, 1 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 0.669/0.669/0.669/0.000 ms

$ ping -c1 192.168.200.211
PING 192.168.200.211 (192.168.200.211): 56 data bytes
64 bytes from 192.168.200.211: icmp_seq=0 ttl=64 time=0.832 ms

--- 192.168.200.211 ping statistics ---
1 packets transmitted, 1 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 0.832/0.832/0.832/0.000 ms

以上は次の構成で確認した内容です。

  • Kubernetes 1.20.2
  • Calico 3.16.6
  • Multus CNI 3.6