背景
以下の記事で、2台の物理サーバーに、2台ずつVMを作成しそれぞれをKubernets NodeとしてKubernetesクラスタを構築するという記事を書きました。
この時、VM間通信はブリッジアダプタを採用し、VMの仮想NICをホストの物理NICにブリッジし、同じ物理LAN上に載せることでVM同士が L2 で直接通信できるようにしています。
そして、VMの仮想NIC(ここでは enp0s8)には、物理LANのネットワーク上のIPアドレスを割り振っています。
このやり方で今回は問題ないですが、一般的にはネットワーク構成が物理LANに依存し、ネットワークのスケーラビリティが制限されてしまうという課題があります。
そこで、今回はVLANのスケーラビリティを克服するために開発された、トンネリングベースのオーバーレイ技術であるVXLANを用いてVM間通信を試みようと思います。
そして、本通信手法を用いてKubernetesクラスタを再構築します。
VXLANについて
VXLANを用いると、物理サーバがVMからのL2フレームをL3のUDPパケットにカプセル化し、別の物理サーバに送信することができます。受信側はそれを解除して、目的のVMに届けるといった形の流れになります。
複数の物理サーバで、遠く離れた環境だったとしてもL3ネットワーク設定を気にすることなく通信でき、VM同士は同一のL2上に存在するように振る舞うことができるという点がメリットです。
詳しくは、例えば以下の記事に書かれています。
VXLAN通信の設定
VXLANインターフェース
2台のホスト上で、以下のような設定を追加します。
ここでは、VXLANインターフェースと仮想L2スイッチの設定を行っており、仮想スイッチに接続されたVMからのパケットがVXLANトンネルを通り相手サーバのVMに送られるという経路を作成します。
REMOTE_HOST_IP="192.168.80.11" # server-2 IP
LOCAL_HOST_IP="192.168.80.10" # server-1 IP
UNDERLAY_IF="eno1" # 物理NIC
VNI=100
# VXLANインターフェース名
VX_NAME="vxlan${VNI}"
# 仮想L2スイッチ名
BR_NAME="br-vxlan"
# 仮想スイッチ作成
sudo ip link add name $BR_NAME type bridge
# VXLANトンネルインターフェースを作成
sudo ip link add $VX_NAME type vxlan id $VNI \
remote $REMOTE_HOST_IP local $LOCAL_HOST_IP dstport 4789 dev $UNDERLAY_IF
# ブリッジへ接続&有効化
sudo ip link set $VX_NAME master $BR_NAME
sudo ip link set $BR_NAME up
sudo ip link set $VX_NAME up
VM仮想NICの接続先変更
まず、既存の設定を確認します。
VMのNIC2 がホストの物理NIC eno1 に接続されていることが確認できます。(なお、NIC1はVMへのsshのために既に使用されています)
$ VBoxManage showvminfo "k8s-master" | grep "NIC 2"
NIC 2: MAC: 0800238162D2, Attachment: Bridged Interface 'eno1', Cable connected: on, Trace: off (file: none), Type: 82540EM, Reported speed: 0 Mbps, Boot priority: 0, Promisc Policy: deny, Bandwidth group: none
NIC2を eno1 に接続し通信できるようにしていましたが、そうではなく、先ほど作成した br-vxlan の仮想スイッチに接続するようにします。
これにより、VMが送信したパケットは br-vxlan の仮想スイッチに届くようになり、br-vxlan がこのパケットを vxlan100トンネルインターフェースに転送することで、VXLANで通信できるようになります。
VBoxManage modifyvm "k8s-master" --nic2 bridged --bridgeadapter2 "br-vxlan"
VBoxManage modifyvm "k8s-worker-1" --nic2 bridged --bridgeadapter2 "br-vxlan"
VBoxManage modifyvm "k8s-worker-2" --nic2 bridged --bridgeadapter2 "br-vxlan"
VBoxManage modifyvm "k8s-worker-3" --nic2 bridged --bridgeadapter2 "br-vxlan"
本設定を行った後、接続先が eno1 から br-vxlan に更新されていることを確認します。
server-1:~$ VBoxManage showvminfo "k8s-master" | grep "NIC 2"
NIC 2: MAC: 0800238162D2, Attachment: Bridged Interface 'br-vxlan', Cable connected: on, Trace: off (file: none), Type: 82540EM, Reported speed: 0 Mbps, Boot priority: 0, Promisc Policy: allow-all, Bandwidth group: none
プライベートIPを仮想NICに設定
各VMにおける既存の enp0s8 (NIC2) のIPアドレスは、アンダーレイネットワーク(物理LAN)のIPであり、VXLANはオーバーレイネットワークであるため、重複しないプライベートなIPネットワークに変更します。
/etc/netplan/01-bridged.yaml のadressを、以下のように修正しました。
- k8s-master
- 192.168.80.101/24→10.10.10.101/24
- k8s-worker-1
- 192.168.80.103/24→10.10.10.103/24
- k8s-worker-2
- 192.168.80.105/24→10.10.10.105/24
- k8s-worker-3
- 192.168.80.107/24→10.10.10.107/24
設定反映後、各VMの enps08 に割り当てられるIPアドレスがプライベートIPに変更させれていることを確認します。
$ ip a show enp0s8
3: enp0s8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 08:00:27:92:62:c2 brd ff:ff:ff:ff:ff:ff
inet 10.10.10.101/24 brd 10.10.10.255 scope global enp0s8
valid_lft forever preferred_lft forever
inet6 fe80::a00:27ff:fe92:62c2/64 scope link
valid_lft forever preferred_lft forever
ここまでの設定で、オーバーレイネットワークとしては以下の構成になります。
このように、VMは物理LANのIPアドレスを意識することなく、10.10.10.x というプライベートIPだけで通信できるのがVXLANの大きなメリットです。
なお、実際はパケットがカプセル化された状態で、物理LANのアンダーレイネットワークを通っている点を補足します。
動作確認
ping
まず、先ほど設定したプライベートIPを利用して、VM間でpingが通ることを確認します。
以下は、k8s-worker-3のVMからk8s-masterのVMにpingを飛ばした例で、通信できていることが確認できます。
$ ping 10.10.10.101
PING 10.10.10.101 (10.10.10.101) 56(84) bytes of data.
64 bytes from 10.10.10.101: icmp_seq=1 ttl=64 time=2.50 ms
64 bytes from 10.10.10.101: icmp_seq=2 ttl=64 time=1.44 ms
64 bytes from 10.10.10.101: icmp_seq=3 ttl=64 time=1.30 ms
^C
--- 10.10.10.101 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
tcpdumpによるVXLANの確認
上記のpingを実行しながら、k8s-master VMのホストであるserver-1上でtcpdumpを行いパケットを眺めます。
k8s-worker-3 (10.10.10.107) からの ping 要求が、server-2(192.168.80.11)によってカプセル化され、server-1(192.168.80.10)のポート 4789 宛に、VXLAN パケット(vni 100)として送信されていることがわかります。
$ sudo tcpdump -i eno1 -n udp port 4789
03:44:37.161493 IP 192.168.80.11.58425 > 192.168.80.10.4789: VXLAN, flags [I] (0x08), vni 100
IP 10.10.10.107 > 10.10.10.101: ICMP echo request, id 3, seq 22, length 64
本ログにより、オーバーレイネットワーク(10.10.10.x)上の ICMP通信が、アンダーレイネットワーク(192.168.80.x)上で UDP/4789 にカプセル化されている様子が確認できました。
外部ネットワークとの接続
nic2 (enp0s8)の接続先を、閉じた仮想ネットワークである br-vxlan に変更したため通信がネットワークに出れなくなってしまっていたという問題が発生しました。
そのため、インターネット専用の3つ目のNIC(NAT)を追加します。
VBoxManage modifyvm "k8s-master" --nic3 nat
VBoxManage modifyvm "k8s-worker-1" --nic3 nat
VBoxManage modifyvm "k8s-worker-2" --nic3 nat
VBoxManage modifyvm "k8s-worker-3" --nic3 nat
NIC3の設定を、以下のsudo vim /etc/netplan/02-nat.yaml で作成し、sudo netplan apply で反映します。
ここでは、各VM内部で、enp0s9 をDHCPで自動設定しています。つまり、NAT機能によりIPアドレスとデフォルトゲートウェイを自動で取得できるようにします。
network:
version: 2
renderer: networkd
ethernets:
enp0s9: # NIC3 に対応するインターフェース名
dhcp4: true
実行後、ping 8.8.8.8 が通ることを確認し、外部ネットワークに接続できているようになりました。
Kubernetesクラスタ再構築
VM間通信にVXLANを利用できるように設定したところで、Kubernetesクラスタを再構築したいと思います。主に、Node IPを変更する形になります。
swapoffの確認
sudo swapoff -a
Node IPの再設定
echo 'KUBELET_EXTRA_ARGS=--node-ip=10.10.10.101' | sudo tee /etc/default/kubelet
sudo systemctl restart kubelet
echo 'KUBELET_EXTRA_ARGS=--node-ip=10.10.10.103' | sudo tee /etc/default/kubelet
sudo systemctl restart kubelet
echo 'KUBELET_EXTRA_ARGS=--node-ip=10.10.10.105' | sudo tee /etc/default/kubelet
sudo systemctl restart kubelet
echo 'KUBELET_EXTRA_ARGS=--node-ip=10.10.10.107' | sudo tee /etc/default/kubelet
sudo systemctl restart kubelet
Master Nodeにて、kubeadmをリセットし再構築します。
sudo kubeadm reset -f
sudo systemctl restart containerd || true
sudo kubeadm init \
--apiserver-advertise-address=10.10.10.101 \
--pod-network-cidr=10.244.0.0/16
kubectl
mkdir -p $HOME/.kube
sudo cp /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
CNI(Flannel)
kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/v0.27.4/Documentation/kube-flannel.yml
各Worker Nodeでは、joinを行います。
sudo kubeadm reset -f
sudo systemctl restart kubelet
sudo kubeadm join 10.10.10.101:6443 --token <token> \
--discovery-token-ca-cert-hash sha256:<hash>
最後に、クラスタのNode構成を確認します。
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-master Ready control-plane 4h31m v1.31.13
k8s-worker-1 Ready <none> 4h20m v1.31.13
k8s-worker-2 Ready <none> 3h47m v1.31.13
k8s-worker-3 Ready <none> 3h41m v1.31.13
まとめ
本記事では、ブリッジアダプタを用いたVM間通信の構成から、VXLANを用いた通信に変更してみました。その上で、Kubernetesクラスタを再構築してみました。

