Phase 6 — VM Workload
架構說明
VM (10.10.100.100) → tap → k6t-bridge (pod內) → veth → vmbr0 → eth1 → Azure NAT GW → Internet
關鍵限制:
- Azure NIC 有 MAC filtering:只有已登記的 MAC+IP 組合才能通訊
- vmbr0 必須使用 eth1 的 registered MAC(
7c:1e:52:4d:3b:a4) - Azure NAT Gateway 不支援 ICMP(ping to internet 會失敗,TCP/UDP 正常)
Step 6-1:建立 vmworkloads namespace
kubectl create namespace vmworkloads
Step 6-2:VM YAML(ub24-01)
# /tmp/ub24-01-vm.yaml
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
name: ub24-01
namespace: vmworkloads
spec:
runStrategy: Always
template:
metadata:
annotations:
k8s.v1.cni.cncf.io/networks: vmnetwork/vmnet-100
spec:
domain:
cpu:
cores: 2
memory:
guest: 4Gi
devices:
disks:
- name: containerdisk
disk:
bus: virtio
- name: cloudinit
disk:
bus: virtio
interfaces:
- name: vmnet100
bridge: {}
networks:
- name: vmnet100
multus:
networkName: vmnetwork/vmnet-100
volumes:
- name: containerdisk
containerDisk:
image: quay.io/containerdisks/ubuntu:24.04
- name: cloudinit
cloudInitNoCloud:
userData: |
#cloud-config
hostname: ub24-01
ssh_pwauth: true
chpasswd:
expire: false
users:
- name: ubuntu
sudo: ALL=(ALL) NOPASSWD:ALL
shell: /bin/bash
lock_passwd: false
plain_text_passwd: ubuntu
runcmd:
# 設定 gateway static ARP(因為 vmbr0 bridge 無法回應 ARP)
- ip neigh replace 10.10.100.13 lladdr 7c:1e:52:4d:3b:a4 dev enp1s0 nud permanent
# 持久化(開機後自動執行)
- mkdir -p /etc/networkd-dispatcher/routable.d
- |
cat > /etc/networkd-dispatcher/routable.d/50-gw-arp.sh << 'SCRIPT'
#!/bin/bash
[ "$IFACE" = "enp1s0" ] && ip neigh replace 10.10.100.13 lladdr 7c:1e:52:4d:3b:a4 dev enp1s0 nud permanent
SCRIPT
- chmod +x /etc/networkd-dispatcher/routable.d/50-gw-arp.sh
networkData: |
network:
version: 2
ethernets:
enp1s0:
dhcp4: false
addresses: [10.10.100.100/24]
gateway4: 10.10.100.13
nameservers:
addresses: [8.8.8.8, 1.1.1.1]
kubectl apply -f /tmp/ub24-01-vm.yaml
kubectl get vmi ub24-01 -n vmworkloads
Step 6-3:Worker vmbr0 Policy Routing 設定
問題根因分析:
vmbr0 bridge 的網路流量有多個問題:
- vmbr0 不回應 ARP(Azure MAC filtering 造成)→ VM 無法找到 gateway MAC
- Policy routing 造成 routing loop(ICMP reply 被當成 forward packet)
- rp_filter 丟棄 asymmetric routing 封包
Worker 需要的完整設定(/etc/networkd-dispatcher/routable.d/50-vmbr0-policy-routing):
#!/bin/bash
# KubeVirt vmbr0 policy routing for 10.10.100.0/24 subnet
[ "$IFACE" = "vmbr0" ] || exit 0
# Disable rp_filter(允許 asymmetric routing)
sysctl -w net.ipv4.conf.vmbr0.rp_filter=0
sysctl -w net.ipv4.conf.all.rp_filter=0
# Disable ICMP redirect
sysctl -w net.ipv4.conf.vmbr0.send_redirects=0
sysctl -w net.ipv4.conf.all.send_redirects=0
# Table 100 subnet route(確保 intra-subnet 封包不走 default route)
ip route add 10.10.100.0/24 dev vmbr0 table 100 2>/dev/null || true
# Policy routing rules:
# Rule 97: 先查 local table(確保 vmbr0 IP local delivery)
ip rule show | grep -q 'priority 97' || ip rule add from 10.10.100.0/24 lookup local priority 97
# Rule 98: intra-subnet → main table
ip rule show | grep -q 'priority 98' || ip rule add from 10.10.100.0/24 to 10.10.100.0/24 lookup main priority 98
# Rule 99: VM outbound → table 100 → default via 10.10.100.1
ip rule show | grep -q 'priority 99' || ip rule add from 10.10.100.0/24 lookup 100 priority 99
ip route show table 100 | grep -q 'default' || ip route add default via 10.10.100.1 dev vmbr0 table 100
chmod +x /etc/networkd-dispatcher/routable.d/50-vmbr0-policy-routing
Sysctl 持久化(/etc/sysctl.d/99-kubevirt-vmbr0.conf):
net.ipv4.conf.vmbr0.rp_filter = 0
net.ipv4.conf.all.rp_filter = 0
net.ipv4.conf.vmbr0.send_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.ip_forward = 1
Step 6-4:MASQUERADE(iptables 持久化)
# /etc/iptables/rules.v4 — 已由 netfilter-persistent 管理
# 加入 MASQUERADE rule:VM 流量出去 vmbr0 後 SNAT
sudo iptables -t nat -A POSTROUTING -s 10.10.100.0/24 ! -d 10.10.100.0/24 -o vmbr0 -j MASQUERADE
sudo netfilter-persistent save
Step 6-5:驗證
# 從 master
virtctl console ub24-01 -n vmworkloads
# 從 worker ping VM
ping -c 3 10.10.100.100 # ✅
# SSH 進 VM(從 worker)
ssh ubuntu@10.10.100.100 # 密碼: ubuntu
# 在 VM 內確認外網(TCP 可通,ICMP 不通是正常的)
curl https://api.ipify.org # 返回 public IP ✅
ping 8.8.8.8 # ❌ Azure NAT GW 不支援 ICMP(正常)
踩坑記錄(Phase 6)
| 問題 | 原因 | 解法 |
|---|---|---|
| VM 無法 ping gateway | vmbr0 bridge 不回應 ARP(bridge L2 forwarding 與 L3 ARP reply 衝突) | cloud-init runcmd 設 static ARP: ip neigh replace 10.10.100.13 lladdr 7c:1e:52:4d:3b:a4 dev enp1s0 nud permanent |
| VM ping gateway 的 ARP reply 不通 | ARP request 進 vmbr0 但 bridge 不回應(疑似 Azure kernel 或 Cilium 干擾) | ebtables nat PREROUTING arpreply rule(或 cloud-init static ARP 繞過) |
| Worker ping VM 100% packet loss | Policy routing rule 99 把 VM reply (src 10.10.100.0/24) forward 出去而非 local deliver | 加 rule 97: from 10.10.100.0/24 lookup local priority 97(先查 local table) |
| Worker vmbr0 MAC 不對 | vmbr0 bridge 預設 MAC ≠ eth1 registered MAC → Azure 不回 ARP | netplan 設 macaddress: 7c:1e:52:4d:3b:a4 on vmbr0 |
| VM outbound 無法連外 | 10.10.100.0/24 subnet 未設定 NAT Gateway | Azure Portal → 建立 NAT Gateway + 關聯 subnet |
| ping 8.8.8.8 不通(VM 內) | Azure NAT Gateway 不支援 ICMP | 正常,改用 curl/TCP 驗證 |
VMI 卡在 Scheduled/CrashLoopBackOff,事件含 failed to configure vmi network ... pod link (...) is missing |
virt-handler 在設定 secondary network(Multus bridge)時找不到預期 pod link,常見於重建後網路狀態不同步 | 先確認 kube-multus-ds 與 multi-networkpolicy-ds-amd64 在 worker 正常,再重建 VM/VMI(kubectl delete vm ub24-01 -n vmworkloads && 重新 apply)並重新檢查 worker vmbr0 policy routing |