主题
K3s + KubeVirt 部署与运维指南
部署日期: 2026-08-26 目标: 在 K3s 单节点集群上部署 KubeVirt,实现 libvirt VM 向容器化虚拟机的迁移。 服务器:
yi(192.168.1.111, SSH 端口 6022, 用户 zai)
目录
1. 集群架构概览
┌─────────────────────────────────────────────────────────┐
│ 宿主机 (yi / 192.168.1.111) │
│ │
│ ┌───────────────────────────────────────────────────┐ │
│ │ K3s 集群 │ │
│ │ │ │
│ │ ┌─────────┐ ┌─────┐ ┌────────┐ ┌─────────────┐ │ │
│ │ │KubeVirt │ │ CDI │ │Multus │ │ HAMi GPU │ │ │
│ │ │ v1.9.0 │ │v1.66│ │(flannel│ │ (GTX1060) │ │ │
│ │ │ 9 pods │ │4 pod│ │ +bridge│ │ 2 pods │ │ │
│ │ └─────────┘ └─────┘ └────────┘ └─────────────┘ │ │
│ │ │ │
│ │ ┌─────────┐ ┌──────────┐ ┌──────────────────┐ │ │
│ │ │ Ollama │ │Open-WebUI│ │ Traefik Ingress │ │ │
│ │ │(GPU AI) │ │ (Chat) │ │ │ │ │
│ │ └─────────┘ └──────────┘ └──────────────────┘ │ │
│ └───────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────┐ ┌─────────────────────────┐ │
│ │ KubeVirt Web UI │ │ 宿主机 libvirt (待迁移) │ │
│ │ (systemd, :8080) │ │ Harbor / Jumpserver │ │
│ └─────────────────────┘ └─────────────────────────┘ │
└─────────────────────────────────────────────────────────┘节点信息
- OS: Ubuntu 20.04 (K3s)
- CPU: 多核
- GPU: NVIDIA GTX 1060 6GB (HAMi 管理)
- containerd: K3s 内置
2. 核心组件部署状态
KubeVirt v1.9.0 (虚拟机管理)
安装命令:
bash
export RELEASE=$(curl -s https://api.github.com/repos/kubevirt/kubevirt/releases/latest | grep tag_name | cut -d '"' -f 4)
kubectl apply -f https://github.com/kubevirt/kubevirt/releases/download/${RELEASE}/kubevirt-operator.yaml
kubectl apply -f https://github.com/kubevirt/kubevirt/releases/download/${RELEASE}/kubevirt-cr.yaml| Pod | 状态 | 说明 |
|---|---|---|
| virt-operator (x2) | ✅ Running | 管理 KubeVirt 生命周期 |
| virt-api (x1) | ✅ Running | API Server |
| virt-controller (x2) | ✅ Running | VM 控制器 |
| virt-handler (x1) | ✅ Running | 节点级 VM 管理 |
| virt-template-* | ✅ Running | 模板服务 |
| virt-exportproxy (x2) | ✅ Running | 数据导出代理 |
CDI v1.66.0 (容器磁盘导入)
安装命令:
bash
export TAG=$(curl -s https://api.github.com/repos/kubevirt/containerized-data-importer/releases/latest | grep '"tag_name"' | cut -d '"' -f 4)
kubectl apply -f https://github.com/kubevirt/containerized-data-importer/releases/download/$TAG/cdi-operator.yaml
kubectl apply -f https://github.com/kubevirt/containerized-data-importer/releases/download/$TAG/cdi-cr.yaml| Pod | 状态 | 说明 |
|---|---|---|
| cdi-operator | ✅ Running | CDI 生命周期管理 |
| cdi-apiserver | ✅ Running | API 服务 |
| cdi-deployment | ✅ Running | 数据导入控制器 |
| cdi-uploadproxy | ✅ Running | 上传代理 |
Multus (多网络)
| Pod | 状态 | 说明 |
|---|---|---|
| kube-multus-ds | ✅ Running | CNI 多网络插件 |
HAMi (GPU 虚拟化)
| Pod | 状态 | 说明 |
|---|---|---|
| hami-device-plugin | ✅ Running | GPU 设备插件 |
| hami-scheduler | ✅ Running | GPU 调度器 |
应用层
| Pod | 状态 | 说明 |
|---|---|---|
| ollama | ✅ Running | AI 模型服务 (GPU 5000MB) |
| open-webui | ✅ Running | AI 聊天界面 |
3. 关键问题与解决方案
3.1 镜像拉取失败 — registries.yaml 不生效
现象: 修改 /etc/rancher/k3s/registries.yaml 后,Pod 仍然从旧源拉取镜像,超时失败。
根因: K3s 不会热重载 registries.yaml。修改配置后,containerd 的 hosts.toml 不会自动更新。
解决方案:
bash
# 1. 编辑 registries.yaml
sudo vim /etc/rancher/k3s/registries.yaml
# 2. 重启 K3s 使配置生效
sudo systemctl restart k3s
# 3. 验证 hosts.toml 已更新
sudo cat /var/lib/rancher/k3s/agent/etc/containerd/certs.d/*/hosts.toml推荐镜像源 (NJU 南京大学):
yaml
mirrors:
docker.io:
endpoint:
- "https://docker.nju.edu.cn"
quay.io:
endpoint:
- "https://quay.nju.edu.cn"
ghcr.io:
endpoint:
- "https://ghcr.nju.edu.cn"
registry.k8s.io:
endpoint:
- "https://k8s.nju.edu.cn"⚠️ 注意: 每次修改
registries.yaml后必须systemctl restart k3s,否则无效!
3.2 Multus CNI 崩溃 — 配置路径不匹配
现象: Multus DaemonSet Pod 报 CrashLoopBackOff,日志显示找不到 CNI 配置。
根因: K3s 将 flannel CNI 配置写入 /var/lib/rancher/k3s/agent/etc/cni/net.d/,而标准 Multus DaemonSet 挂载的是 /etc/cni/net.d/。
解决方案:
bash
# 复制 K3s flannel 配置到标准路径
sudo cp /var/lib/rancher/k3s/agent/etc/cni/net.d/10-flannel.conflist /etc/cni/net.d/
# 重启 Multus Pod
kubectl delete pod -n kube-system -l app=multus3.3 HAMi GPU 调度 Pending — 内存请求超限
现象: Ollama Pod 一直 Pending,调度器报 insufficient nvidia.com/gpumem。
根因: GTX1060 显存 6GB,HAMi 报告总显存 6000MB,请求 6000MB 时没有预留系统余量。
解决方案: 将 GPU 内存请求从 6000MB 降至 5000MB:
yaml
resources:
requests:
nvidia.com/gpu: "1"
nvidia.com/gpumem: "5000" # 预留 1000MB 给系统
limits:
nvidia.com/gpu: "1"
nvidia.com/gpumem: "5000"3.4 virtctl 安装
bash
export VERSION=$(curl -s https://api.github.com/repos/kubevirt/kubevirt/releases/latest | grep tag_name | cut -d '"' -f 4)
curl -L -o virtctl https://github.com/kubevirt/kubevirt/releases/download/${VERSION}/virtctl-${VERSION}-linux-amd64
chmod +x virtctl
sudo mv virtctl /usr/local/bin/
# 配置 KUBECONFIG
echo 'export KUBECONFIG=/etc/rancher/k3s/k3s.yaml' >> ~/.bashrc
source ~/.bashrc3.5 镜像拉取速度监控
实时监控 containerd 下载进度的脚本:
bash
sudo bash -c 'while true; do
for d in /var/lib/rancher/k3s/agent/containerd/io.containerd.content.v1.content/ingest/*/; do
[ -d "$d" ] || continue
ref=$(cat ${d}ref 2>/dev/null | sed "s|k8s.io/1/layer-sha256:||;s|.*:||")
dl=$(stat -c%s ${d}data 2>/dev/null)
total=$(cat ${d}total 2>/dev/null)
[ -n "$total" ] && [ "$total" -gt 0 ] && pct=$(( dl * 100 / total )) || pct=0
echo "${ref:0:12}: ${pct}% ($(( dl / 1048576 ))/$(( total / 1048576 ))MB)"
done
sleep 3; echo "---"
done'4. KubeVirt Web UI
部署方式: systemd + Python 后端
由于 docker.io 镜像拉取极慢(~50KB/s),选择在宿主机直接运行 Python HTTP 服务, 通过 kubectl/virtctl 调用 K8s API,避免拉取额外容器镜像。
文件结构
/opt/kubevirt-ui/
├── server.py # Python API 后端 (调用 kubectl/virtctl)
└── index.html # 前端 SPA 页面 (纯 HTML/JS/CSS)
/etc/systemd/system/
└── kubevirt-ui.service # systemd 服务单元systemd 服务配置
ini
[Unit]
Description=KubeVirt Web UI
After=network.target
[Service]
Type=simple
User=root
Environment=KUBECONFIG=/etc/rancher/k3s/k3s.yaml
Environment=PORT=8080
ExecStart=/usr/bin/python3 /opt/kubevirt-ui/server.py
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.targetAPI 端点
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | / | 前端页面 |
| GET | /api/health | 健康检查 |
| GET | /api/vms | 列出所有 VM |
| GET | /api/vmis | 列出所有 VMI |
| GET | /api/nodes | 列出节点 |
| GET | /api/pvcs | 列出 PVC |
| GET | /api/dvs | 列出 DataVolume |
| GET | /api/vm/{ns}/{name} | 查看单个 VM |
| POST | /api/vm/{ns}/{name}/start | 启动 VM |
| POST | /api/vm/{ns}/{name}/stop | 停止 VM |
| POST | /api/vm/{ns}/{name}/restart | 重启 VM |
| POST | /api/vm/{ns}/{name}/migrate | 迁移 VM |
| POST | /api/vm/{ns}/{name}/pause | 暂停 VM |
| POST | /api/vm/{ns}/{name}/unpause | 恢复 VM |
| POST | /api/vm/{ns}/{name}/delete | 删除 VM |
| POST | /api/vm/create | 创建 VM |
管理命令
bash
sudo systemctl status kubevirt-ui # 查看状态
sudo systemctl restart kubevirt-ui # 重启
sudo systemctl stop kubevirt-ui # 停止
sudo systemctl start kubevirt-ui # 启动
sudo journalctl -u kubevirt-ui -f # 实时日志前端功能
- 📊 集群概览:VM 总数、运行中、已停止
- 📦 节点信息:系统版本、CPU、内存
- 🖥️ VM 卡片:状态、CPU、内存、节点、IP、磁盘
- ⚡ 操作按钮:Start / Stop / Restart / Migrate / Delete / Console
- ➕ 创建 VM:表单式创建(名称、CPU、内存、镜像、Cloud-Init)
- 🔄 自动刷新:每 10 秒
5. VM 管理命令速查
基础操作
bash
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
# 列出所有 VM
kubectl get vm -A
# 列出所有 VMI (运行中的实例)
kubectl get vmi -A
# 查看 VM 详情
kubectl describe vm <name> -n <namespace>
# 查看 VMI 详情 (含 IP、节点等)
kubectl describe vmi <name> -n <namespace>virtctl 操作
bash
# 启动/停止/重启
virtctl start <vm-name> -n <namespace>
virtctl stop <vm-name> -n <namespace>
virtctl restart <vm-name> -n <namespace>
# 暂停/恢复
virtctl pause vmi <vmi-name> -n <namespace>
virtctl unpause vmi <vmi-name> -n <namespace>
# 迁移
virtctl migrate <vm-name> -n <namespace>
# 控制台
virtctl console <vmi-name> -n <namespace>
# VNC (需要 virt-viewer)
virtctl vnc <vmi-name> -n <namespace>
# 端口转发
virtctl expose virtualmachine <vm-name> --port=22 --target-port=22 --type=NodePort -n <namespace>创建 VM 示例
yaml
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
name: my-vm
namespace: default
spec:
running: true
template:
metadata:
labels:
kubevirt.io/vm: my-vm
spec:
domain:
cpu:
cores: 2
memory:
guest: 4Gi
devices:
disks:
- name: rootdisk
disk:
bus: virtio
- name: cloudinit
disk:
bus: virtio
interfaces:
- name: default
masquerade: {}
networks:
- name: default
pod: {}
volumes:
- name: rootdisk
containerDisk:
image: quay.io/containerdisks/ubuntu:22.04
- name: cloudinit
cloudInitNoCloud:
userData: |
#cloud-config
password: xxx
chpasswd: { expire: False }
ssh_pwauth: true使用 CDI DataVolume 导入磁盘
yaml
apiVersion: cdi.kubevirt.io/v1beta1
kind: DataVolume
metadata:
name: harbor-disk
namespace: default
spec:
source:
http:
url: "http://<file-server>/harbor.qcow2"
pvc:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 50Gi
storageClassName: local-path6. VM 迁移计划
待迁移 VM
| VM 名称 | 磁盘文件 | 大小 | 用途 |
|---|---|---|---|
| Harbor | /var/lib/libvirt/images/harbor.qcow2 | ~50GB | 容器镜像仓库 |
| Jumpserver | /var/lib/libvirt/images/jumpserver.qcow2 | ~30GB | 堡垒机 |
迁移步骤
方案 A: HTTP 上传 (推荐)
- 启动临时 HTTP 服务器提供 qcow2 文件:bash
cd /var/lib/libvirt/images/ python3 -m http.server 8888 - 创建 DataVolume 指向该 HTTP 源
- 创建 VirtualMachine 引用该 PVC
- 验证数据完整性
方案 B: 直接复制到 local-path
- 创建 PVC (local-path storage class)
- 找到 PVC 对应的 PV 目录
- 使用
qemu-img convert复制磁盘 - 创建 VirtualMachine 引用该 PVC
迁移后验证
bash
kubectl get vm -A
kubectl get vmi -A
virtctl console <vmi-name> -n default
kubectl get vmi <name> -o jsonpath='{.status.interfaces[0].ipAddress}'7. 服务访问地址
| 服务 | 地址 | 说明 |
|---|---|---|
| KubeVirt Web UI | http://192.168.1.111:8080 | VM 管理界面 |
| Open-WebUI | http://192.168.1.111:30391 | AI 聊天界面 |
| Ollama API | http://192.168.1.111:11434 | AI 模型 API |
| K3s API | https://192.168.1.111:6443 | K8s API Server |
8. 附录:配置文件与路径
K3s 配置
| 文件 | 用途 |
|---|---|
/etc/rancher/k3s/k3s.yaml | K3s kubeconfig |
/etc/rancher/k3s/registries.yaml | 容器镜像仓库配置 |
/var/lib/rancher/k3s/agent/etc/containerd/certs.d/*/hosts.toml | containerd 实际使用的镜像源 |
/var/lib/rancher/k3s/agent/etc/cni/net.d/10-flannel.conflist | K3s flannel CNI 配置 |
CNI 网络
| 文件 | 用途 |
|---|---|
/etc/cni/net.d/10-flannel.conflist | Multus 使用的 flannel 配置 (从 K3s 复制) |
/var/lib/rancher/k3s/agent/etc/cni/net.d/10-flannel.conflist | K3s 原始 flannel 配置 |
存储
| 路径 | 用途 |
|---|---|
/var/lib/libvirt/images/ | 宿主机 libvirt VM 磁盘 (待迁移) |
/var/lib/rancher/k3s/storage/ | local-path provisioner 存储目录 |
KubeVirt Web UI
| 文件 | 用途 |
|---|---|
/opt/kubevirt-ui/server.py | Python API 后端 |
/opt/kubevirt-ui/index.html | 前端 SPA 页面 |
/etc/systemd/system/kubevirt-ui.service | systemd 服务单元 |
变更记录
| 日期 | 操作 | 说明 |
|---|---|---|
| 2026-08-26 | 部署 KubeVirt v1.9.0 | 9/9 Pod Running |
| 2026-08-26 | 部署 CDI v1.66.0 | 4/4 Pod Running |
| 2026-08-26 | 修复 registries.yaml | 添加 NJU 镜像源,重启 K3s 生效 |
| 2026-08-26 | 修复 Multus CNI | 复制 flannel 配置到 /etc/cni/net.d/ |
| 2026-08-26 | 修复 Ollama GPU 调度 | 降低 gpumem 请求至 5000MB |
| 2026-08-26 | 安装 virtctl v1.9.0 | 添加到 PATH |
| 2026-08-26 | 部署 KubeVirt Web UI | systemd 服务,端口 8080 |
| 2026-08-26 | 创建测试 VM | demo-vm (已清理) |