Phase 5 — KubeVirt 平台層

Step 5-1:確認 Worker 支援 Nested Virtualization

mansion-kubevirt-worker 執行:

egrep -c '(vmx|svm)' /proc/cpuinfo
# 預期:> 0(Azure Dv4 支援 nested virt)

ls /dev/kvm
# 預期:/dev/kvm

Step 5-2:安裝 KubeVirt Operator(在 mansion-kubevirt-master)

KUBEVIRT_VERSION=v1.5.0

kubectl apply -f \
  https://github.com/kubevirt/kubevirt/releases/download/${KUBEVIRT_VERSION}/kubevirt-operator.yaml

驗證 operator 啟動:

kubectl get pods -n kubevirt -l kubevirt.io=virt-operator
# 預期:virt-operator Running(可能需要 1-2 分鐘)

Step 5-3:建立 KubeVirt CR

KubeVirt 管理面(virt-api/virt-controller)排程到 Infra node:

cat > /tmp/kubevirt-cr.yaml <<'EOF'
apiVersion: kubevirt.io/v1
kind: KubeVirt
metadata:
  name: kubevirt
  namespace: kubevirt
spec:
  certificateRotateStrategy: {}
  # 整合 Prometheus 監控:virt-operator 自動建立 ServiceMonitor + PrometheusRules
  monitorNamespace: monitoring
  monitorAccount: kube-prometheus-stack-prometheus
  configuration:
    developerConfiguration:
      featureGates:
        - DataVolumes
        - LiveMigration
        - Sidecar
  customizeComponents:
    patches:
      # virt-api:排到 Infra
      - resourceType: Deployment
        resourceName: virt-api
        patch: '[{"op":"add","path":"/spec/template/spec/tolerations","value":[{"key":"node-role.kubernetes.io/infra","operator":"Exists","effect":"NoSchedule"}]},{"op":"add","path":"/spec/template/spec/nodeSelector","value":{"kubevirt-management":"true"}}]'
        type: json
      # virt-controller:排到 Infra
      - resourceType: Deployment
        resourceName: virt-controller
        patch: '[{"op":"add","path":"/spec/template/spec/tolerations","value":[{"key":"node-role.kubernetes.io/infra","operator":"Exists","effect":"NoSchedule"}]},{"op":"add","path":"/spec/template/spec/nodeSelector","value":{"kubevirt-management":"true"}}]'
        type: json
      # virt-handler:只部署在 worker node(DaemonSet)
      - resourceType: DaemonSet
        resourceName: virt-handler
        patch: '[{"op":"add","path":"/spec/template/spec/nodeSelector","value":{"kubevirt-workload":"true"}}]'
        type: json
EOF

kubectl apply -f /tmp/kubevirt-cr.yaml

Step 5-4:等待 KubeVirt 就緒

kubectl wait kv kubevirt -n kubevirt \
  --for=condition=Available \
  --timeout=10m

kubectl get pods -n kubevirt -o wide
# 預期:
# virt-operator-*    Running  mansion-kubevirt-infra
# virt-api-*         Running  mansion-kubevirt-infra
# virt-controller-*  Running  mansion-kubevirt-infra
# virt-handler-*     Running  mansion-kubevirt-worker(DaemonSet + nodeSelector kubevirt-workload=true)

Step 5-5:安裝 virtctl

mansion-kubevirt-master 執行:

KUBEVIRT_VERSION=$(kubectl get kubevirt -n kubevirt kubevirt \
  -o jsonpath='{.status.observedKubeVirtVersion}')

curl -L -o /tmp/virtctl \
  https://github.com/kubevirt/kubevirt/releases/download/${KUBEVIRT_VERSION}/virtctl-${KUBEVIRT_VERSION}-linux-amd64
sudo install /tmp/virtctl /usr/local/bin/virtctl

驗證:

virtctl version

Step 5-6:Worker 設定 vmbr0 Bridge(在 mansion-kubevirt-worker)

臨時設定(立即生效)

sudo ip link add vmbr0 type bridge
sudo ip link set eth1 master vmbr0
sudo ip link set vmbr0 up
sudo ip link set eth1 up
ip link show vmbr0

永久設定(netplan,開機持久化)

# 查看 eth1 的 MAC address
ip link show eth1

sudo tee /etc/netplan/60-kubevirt-bridge.yaml << 'EOF'
network:
  version: 2
  ethernets:
    eth1:
      dhcp4: false
      dhcp6: false
  bridges:
    vmbr0:
      interfaces: [eth1]
      dhcp4: false
      dhcp6: false
      parameters:
        stp: false
        forward-delay: 0
EOF

sudo netplan apply
ip link show vmbr0
# 預期:vmbr0: <BROADCAST,MULTICAST,UP,LOWER_UP>

Step 5-7:建立 NetworkAttachmentDefinition(在 mansion-kubevirt-master)

kubectl create namespace vmnetwork

cat > /tmp/vmnet-100-nad.yaml <<'EOF'
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
  name: vmnet-100
  namespace: vmnetwork
spec:
  config: '{
    "cniVersion": "0.3.1",
    "name": "vmnet-100",
    "type": "bridge",
    "bridge": "vmbr0"
  }'
EOF

kubectl apply -f /tmp/vmnet-100-nad.yaml

驗證:

kubectl get network-attachment-definitions -n vmnetwork
# 預期:vmnet-100

Step 5-8:安裝 multus-networkpolicy

注意: GitHub raw URL 可能 404(路徑已改變),改用本機 clone 後 scp 到 master。

# 在 Mac 本機 clone(若尚未有)
git clone https://github.com/k8snetworkplumbingwg/multus-networkpolicy /tmp/multus-networkpolicy

# scp deploy.yml 到 master
scp -i ~/.ssh/id_ed25519 /tmp/multus-networkpolicy/deploy.yml \
  ubuntu@<master-ip>:/tmp/multus-networkpolicy-deploy.yml

# 在 mansion-kubevirt-master 執行
kubectl apply -f /tmp/multus-networkpolicy-deploy.yml

驗證:

kubectl get pods -n kube-system | grep multi-networkpolicy
# 預期:multi-networkpolicy-ds-amd64-* Running(三台 node 各一個)

Step 5-9:最終驗證

# KubeVirt 完整狀態
kubectl get kubevirt -n kubevirt
# 預期:PHASE = Deployed

# 所有 KubeVirt pods
kubectl get pods -n kubevirt -o wide
# 預期:
# virt-api       → mansion-kubevirt-infra(2 replicas)
# virt-controller → mansion-kubevirt-infra(2 replicas)
# virt-handler   → 只在 mansion-kubevirt-worker(DaemonSet + nodeSelector kubevirt-workload=true)
# virt-operator  → mansion-kubevirt-infra(2 replicas)

# NAD 確認
kubectl get network-attachment-definitions -n vmnetwork
# 預期:vmnet-100

# multus-networkpolicy DaemonSet
kubectl get ds multi-networkpolicy-ds-amd64 -n kube-system
# 預期:DESIRED=3 CURRENT=3 READY=3

# virtctl 版本
virtctl version --client

踩坑記錄

問題 原因 解法
kubeadm init 失敗 — sandbox image 不一致 CRI-O 預設已對應正確 pause image,通常不會出現此問題 若出現,檢查 /etc/crio/crio.confpause_image
CoreDNS Pending 未安裝 CNI 先裝 Cilium,CoreDNS 才會 Running
virt-api/controller 一直排程到 worker Infra 節點未標記或無 toleration 在 KubeVirt CR 的 customizeComponents 加 toleration + nodeSelector (kubevirt-management=true)
virt-api Pending(didn't match Pod's node affinity/selector KubeVirt 內建 affinity 要求 control-plane/master,但自訂又加了 kubevirt-management=true;若只有 infra 有此 label,條件交集為空 補上 kubectl label node mansion-kubevirt-master kubevirt-management=true,或改為不與內建 affinity 衝突的 selector
Worker bridge 模式流量被丟棄 Azure NIC MAC filtering Portal → NIC (eth1) → Enable IP Forwarding

實際結果

kubectl get nodes
NAME         STATUS   ROLES           AGE   VERSION
mansion-kubevirt-infra    Ready    infra           -     v1.31.x
mansion-kubevirt-master   Ready    control-plane   -     v1.31.x
mansion-kubevirt-worker   Ready    worker          -     v1.31.x

kubectl get pods -n kubevirt -o wide
NAME                              READY   STATUS    NODE
virt-api-xxx                      1/1     Running   mansion-kubevirt-infra
virt-controller-xxx               1/1     Running   mansion-kubevirt-infra
virt-handler-xxx (worker)         1/1     Running   mansion-kubevirt-worker
virt-operator-xxx                 1/1     Running   mansion-kubevirt-infra