前回の記事では、Multus CNI を既存 Kubernetes クラスタへ導入し、00-multus.conf が作成され、既存の Calico CNI が delegate として呼び出されることを確認しました。ただし、その時点では NetworkAttachmentDefinition を作成しておらず、Pod に追加 NIC は付与していませんでした。
今回は、Multus で Pod に primary CNI とは別の secondary network を接続し、KVM ホスト上の別 VM と L2 接続できるかを確認しました。試験した方式は bridge CNI、macvlan、ipvlan L2 です。
結論として、3 方式すべてで Pod と対向 VM の双方向通信に成功しました。一方で、最初に通信できなかった主因は Multus や macvlan / ipvlan ではなく、Calico eBPF が worker VM の secondary NIC に TCX program を attach していたことと、対向 VM 側の nftables がテスト subnet への出力を許可していなかったことでした。
検証の目的
目的は、管理用 Kubernetes クラスタで Multus を使い、Pod に primary CNI とは別の追加 NIC を付け、KVM ホスト上の別 VM と L2 接続できるか確認することです。
| 観点 | 確認内容 |
|---|---|
| Multus | NetworkAttachmentDefinition に基づき Pod に net1 を追加できるか |
| bridge CNI | worker VM 内の Linux bridge 経由で対向 VM と通信できるか |
| macvlan | worker VM の secondary NIC を master として Pod を L2 接続できるか |
| ipvlan L2 | ipvlan L2 でも対向 VM と通信できるか |
| Calico eBPF | primary NIC と secondary NIC の BPF attach 対象を分けられるか |
検証構成
公開記事ではホスト名を例示名に置き換えます。構成としては、KVM ホスト上にテスト用 Linux bridge を作り、worker VM と対向 VM の追加 NIC を同じ bridge へ接続しています。
KVM host
br-multus-test
|- mgmt worker VM
| `- enp5s0
| `- Multus Pod net1
`- peer VM
`- enp8s0: 192.0.2.1/24| 要素 | 例示名 | 役割 |
|---|---|---|
| KVM ホスト | kvm-host-01.example.local | テスト用 bridge を持つ物理ホスト |
| worker VM | k8s-worker-01.example.local | Multus Pod を配置する Kubernetes node |
| 対向 VM | peer-vm-01.example.local | Pod の secondary network から到達する相手 |
| worker secondary NIC | enp5s0 | Multus の bridge / macvlan / ipvlan で使用 |
| 対向 VM secondary NIC | enp8s0 | 192.0.2.1/24 を設定 |
KVM ホスト側の設定
KVM ホスト側では、テスト用 Linux bridge を作成し、worker VM と対向 VM に追加 NIC を hotplug します。以下は公開用にホスト名を置き換えた例です。
sudo ip link add br-multus-test type bridge
sudo ip link set br-multus-test up
sudo virsh attach-interface --domain k8s-worker-01.example.local --type bridge --source br-multus-test --model virtio --mac 52:54:00:aa:32:10 --live
sudo virsh attach-interface --domain peer-vm-01.example.local --type bridge --source br-multus-test --model virtio --mac 52:54:00:aa:32:01 --liveこの bridge は、Kubernetes の Service や Calico の Pod network とは別の、Multus secondary network の疎通確認用です。
worker VM 側の NIC 設定
worker VM 側では、追加 NIC として enp5s0 を使用します。macvlan / ipvlan では、この interface を直接 master として使います。
sudo ip link set enp5s0 up promisc onbridge CNI を試験する場合は、worker VM 内にも Linux bridge を作り、enp5s0 を bridge に収容しました。
sudo ip link add br-multus-test type bridge
sudo ip link set br-multus-test up
sudo ip link set enp5s0 master br-multus-test
sudo ip link set enp5s0 upbridge CNI の試験後、macvlan / ipvlan を再試験する際には、enp5s0 を bridge から外して戻しています。
sudo ip link set enp5s0 nomaster
sudo ip link del br-multus-test
sudo ip link set enp5s0 up promisc on対向 VM 側の設定
対向 VM 側では、追加 NIC enp8s0 に 192.0.2.1/24 を設定します。
sudo ip addr flush dev enp8s0
sudo ip addr add 192.0.2.1/24 dev enp8s0
sudo ip link set enp8s0 up途中で、対向 VM 側の nftables により 192.0.2.0/24 宛の出力が許可されていないことが分かりました。検証中は、一時的にテスト subnet への出力を許可しています。
sudo nft add element ip filter output_private_dst { 192.0.2.0/24 }Calico eBPF の対象 interface を限定する
今回の検証で最も重要だった設定は、Calico eBPF の attach 対象を primary NIC に限定することです。最初に通信できなかった時点では、worker VM の追加 NIC enp5s0 に Calico eBPF の TCX program が attach されていました。
enp5s0 tcx/ingress cali_tc_preamble
enp5s0 tcx/egress cali_tc_preambleこの状態では、Multus の secondary network として使いたい interface が Calico の BPF dataplane の対象にもなってしまいます。そこで、FelixConfiguration の bpfDataIfacePattern を設定し、Calico eBPF の対象を primary NIC である enp1s0 に限定しました。
kubectl --context k8s-mgmt.example.local patch felixconfiguration default --type merge -p '{"spec":{"bpfDataIfacePattern":"^enp1s0$"}}'設定後は、bpftool で BPF attach 状態を確認します。
sudo bpftool net | grep -E 'enp1s0|enp5s0'期待する状態は、enp1s0 には cali_tc_preamble が表示され、enp5s0 は表示されないことです。
bridge CNI の NetworkAttachmentDefinition
bridge CNI では、worker VM 内の br-multus-test を使います。IPAM は static にし、Pod annotation 側で IP アドレスを指定します。
apiVersion: k8s.cni.cncf.io/v1
kind: NetworkAttachmentDefinition
metadata:
name: kvm-bridge-test
namespace: default
spec:
config: |
{
"cniVersion": "0.3.1",
"type": "bridge",
"bridge": "br-multus-test",
"capabilities": { "ips": true },
"ipam": { "type": "static" }
}apiVersion: v1
kind: Pod
metadata:
name: multus-kvm-bridge-pod
namespace: default
annotations:
k8s.v1.cni.cncf.io/networks: |
[{"name":"kvm-bridge-test","interface":"net1","ips":["192.0.2.10/24"]}]
spec:
nodeName: k8s-worker-01.example.local
restartPolicy: Never
containers:
- name: netshoot
image: nicolaka/netshoot:latest
command: ["sleep", "3600"]macvlan の NetworkAttachmentDefinition
macvlan では、worker VM の enp5s0 を master として使い、mode は bridge にしました。
apiVersion: k8s.cni.cncf.io/v1
kind: NetworkAttachmentDefinition
metadata:
name: kvm-macvlan-test
namespace: default
spec:
config: |
{
"cniVersion": "0.3.1",
"type": "macvlan",
"master": "enp5s0",
"mode": "bridge",
"capabilities": { "ips": true },
"ipam": { "type": "static" }
}apiVersion: v1
kind: Pod
metadata:
name: multus-kvm-macvlan-pod
namespace: default
annotations:
k8s.v1.cni.cncf.io/networks: |
[{"name":"kvm-macvlan-test","interface":"net1","ips":["192.0.2.10/24"]}]
spec:
nodeName: k8s-worker-01.example.local
restartPolicy: Never
containers:
- name: netshoot
image: nicolaka/netshoot:latest
command: ["sleep", "3600"]ipvlan L2 の NetworkAttachmentDefinition
ipvlan では、同じく enp5s0 を master とし、mode は l2 にしました。Pod 側の IP は、bridge / macvlan と重複しないよう 192.0.2.11/24 にしています。
apiVersion: k8s.cni.cncf.io/v1
kind: NetworkAttachmentDefinition
metadata:
name: kvm-ipvlan-test
namespace: default
spec:
config: |
{
"cniVersion": "0.3.1",
"type": "ipvlan",
"master": "enp5s0",
"mode": "l2",
"capabilities": { "ips": true },
"ipam": { "type": "static" }
}apiVersion: v1
kind: Pod
metadata:
name: multus-kvm-ipvlan-pod
namespace: default
annotations:
k8s.v1.cni.cncf.io/networks: |
[{"name":"kvm-ipvlan-test","interface":"net1","ips":["192.0.2.11/24"]}]
spec:
nodeName: k8s-worker-01.example.local
restartPolicy: Never
containers:
- name: netshoot
image: nicolaka/netshoot:latest
command: ["sleep", "3600"]疎通確認
Pod から対向 VM へ ping し、逆方向として対向 VM から Pod の secondary network 側 IP へ ping します。以下は macvlan の例です。
kubectl --context k8s-mgmt.example.local -n default exec multus-kvm-macvlan-pod -- ping -c 5 -W 2 192.0.2.1
ping -I enp8s0 -c 5 -W 2 192.0.2.10ipvlan L2 の場合は、Pod IP を 192.0.2.11 として確認しています。
ping -I enp8s0 -c 5 -W 2 192.0.2.11| 方式 | Pod から対向 VM | 対向 VM から Pod | 結果 |
|---|---|---|---|
| bridge CNI | 192.0.2.10 から 192.0.2.1 | 192.0.2.1 から 192.0.2.10 | OK |
| macvlan | 192.0.2.10 から 192.0.2.1 | 192.0.2.1 から 192.0.2.10 | OK |
| ipvlan L2 | 192.0.2.11 から 192.0.2.1 | 192.0.2.1 から 192.0.2.11 | OK |
実測では、すべて 0% packet loss でした。これにより、Multus secondary network として bridge CNI、macvlan、ipvlan L2 のいずれも、同じ KVM bridge 上の対向 VM と L2 接続できることを確認できました。
最初に通信できなかった理由
最初に通信できなかった理由は、Multus の NetworkAttachmentDefinition や macvlan / ipvlan の選択そのものではありませんでした。大きく二つの要因がありました。
| 要因 | 内容 | 対応 |
|---|---|---|
| Calico eBPF | worker VM の secondary NIC enp5s0 に TCX program が attach されていた | bpfDataIfacePattern で primary NIC enp1s0 に限定 |
| 対向 VM の nftables | OUTPUT が default drop で、192.0.2.0/24 宛が許可されていなかった | 検証中はテスト subnet への出力を一時許可 |
この結果から、Calico eBPF 環境で Multus secondary network を使う場合、secondary NIC を Calico の BPF dataplane 対象から外す設計が必要だと分かりました。特に、primary NIC と secondary NIC が同じ worker VM に存在する構成では、Calico がどの interface を処理対象にするかを曖昧にしてはいけません。
まとめ
今回の検証では、Multus を使って Pod に primary CNI とは別の追加 NIC を付与し、KVM ホスト上の別 VM と L2 接続できることを確認しました。bridge CNI、macvlan、ipvlan L2 のいずれでも、Pod と対向 VM の双方向通信に成功しています。
一方で、通信できるかどうかは NetworkAttachmentDefinition の設定だけでは決まりません。worker VM の secondary NIC が KVM bridge へ正しく接続されていること、対向 VM の firewall が通信を許可していること、Calico eBPF が secondary NIC に干渉していないことを合わせて確認する必要があります。
今回の最重要設定は、FelixConfiguration の bpfDataIfacePattern: "^enp1s0$" です。Calico eBPF 環境で Multus secondary network を使うなら、Calico が primary NIC だけを BPF dataplane 対象にし、Multus 用の secondary NIC を処理対象から外す設計を最初から入れておくべきです。
参考書籍
書籍
Kubernetes 完全ガイド 第 2 版
Kubernetes の Pod、CNI、Service、運用設計を体系的に確認したい場合の参考書籍です。価格や在庫はリンク先で確認してください。
Amazon で見るこのリンクは Amazon アソシエイトリンクです。


