KVM 管理平台教程:FastAPI + Vue 3 多主机虚拟机管理
手里几台 KVM 宿主机,虚拟机一多,靠 virsh 命令行一台一台管就管不过来了。kvm-manager 就是为这个场景做的:一个基于 FastAPI + Vue 3 的 Web 管理平台,通过 libvirt API 统一管理虚拟机,支持多主机纳管、Web 控制台、快照和资源监控,浏览器里就能完成从创建虚拟机到进控制台的全部操作。这篇教程把它的部署、多主机接入、Guest Agent 配置和 API 用法完整讲一遍。
一、项目简介
kvm-manager 是 git.cnbugs.com 上 AI-Agent 组织的一个开源项目(仓库地址 https://git.cnbugs.com/AI-Agent/kvm-manager),定位是轻量级 KVM 虚拟化管理平台。它不像 oVirt、Proxmox VE 那样自带一整套集群和存储方案,而是专注做一件事:把 libvirt 的能力用 Web 界面包装起来,让多台 KVM 宿主机上的虚拟机能在同一个面板里管理。
后端是 FastAPI + libvirt Python API,前端是 Vue 3 + Element Plus + Vite,控制台用 noVNC(WebSocket VNC 代理)实现,认证走 JWT Token。整个项目没有外部数据库依赖,主机和用户数据存在本地文件里,部署成本很低,一台宿主机上就能跑起来。
二、功能特性
- 虚拟机管理:创建、启动、停止、重启、暂停、恢复、强制关机、删除、克隆、迁移。
- 资源监控:CPU 使用率、内存使用量、磁盘读写、网络收发,列表页直接看得到实时数据。
- 存储池管理:支持多种存储类型,创建虚拟机时指定存储池,磁盘默认创建为 qcow2 格式。
- 网络管理:桥接、NAT、独立网络三种模式,创建虚拟机时选择网络。
- 控制台访问:WebSocket VNC 代理,浏览器里直接打开虚拟机桌面,不用再装 VNC 客户端;远程主机 VNC 只监听 localhost 时,后端会自动建 SSH 隧道打通。
- 快照管理:虚拟机快照的创建与恢复。
- IP 地址自动获取:QEMU Guest Agent → DHCP 租约 → ARP 表,三级回退,尽量拿到虚拟机的真实 IP。
- 多主机纳管:本地、SSH、TCP 三种方式接入远程 KVM 宿主机。
- 用户认证:JWT Token 登录,管理员可以注册新用户、改密码、删用户。
三、技术架构
| 层 | 技术选型 | 说明 |
|---|---|---|
| 后端 | FastAPI + libvirt Python API | REST API + WebSocket VNC 代理,入口 backend/app/main.py |
| 前端 | Vue 3 + Element Plus + Vite + noVNC | 管理界面与 Web 控制台,默认端口 8005 |
| 虚拟化 | QEMU/KVM + libvirt | 通过 libvirt 连接池(libvirt_conn.py)操作宿主机 |
| 认证 | JWT Token(HS256,有效期 24 小时) | 登录、改密码、用户管理都在 /api/auth 下 |
| 数据存储 | 本地文件(无外部数据库) | 主机数据默认目录 /var/lib/kvm-manager |
后端 API 按模块拆成 7 个路由:/api/auth(认证)、/api/hosts(主机管理)、/api/vm(虚拟机)、/api/storage(存储)、/api/network(网络)、/api/snapshot(快照)、/api/monitor(资源监控),外加 GET /health 健康检查和 /ws/vnc/{vm_name} 的 WebSocket 控制台代理。API 前缀默认是 /api,可在配置里改。
四、适用场景
- 中小团队的虚拟化管理:几台到十几台 KVM 宿主机,不想上 oVirt、Proxmox 这种重型方案,要一个轻量 Web 面板。
- 测试/开发环境:频繁创建、克隆、销毁虚拟机,Web 界面比命令行快得多,还能直接开控制台装系统。
- 多机房/多宿主机统一入口:通过 SSH 或 TCP 把分布在不同地方的 KVM 主机纳管进来,一个面板看所有虚拟机。
- 教学与演示:给学员一个浏览器地址就能操作虚拟机,不用教 virsh 命令。
不适用场景也要说清楚:它没有 oVirt 那种集群高可用、分布式存储和细粒度权限体系,超大规模(上百台宿主机)或强合规场景建议选更重的方案。
五、环境准备
部署用的机器本身就是一台 KVM 宿主机(或者至少能连上 libvirt 的管理机)。先确认宿主机已经装好 KVM 和 libvirt,并且当前用户能连上 qemu:///system。
# 确认 CPU 支持虚拟化(输出 vmx 或 svm)
egrep -c '(vmx|svm)' /proc/cpuinfo
# 确认 libvirtd 在运行
systemctl status libvirtd --no-pager
# 确认能连上本地 libvirt
virsh -c qemu:///system list --all
virsh -c qemu:///system pool-list --all
virsh -c qemu:///system net-list --all
# 依赖:Python 3.10+、Node.js 18+、Docker(仅 Docker 部署方式需要)
python3 --version
node --version
docker --version
还需要准备一个 ISO 安装镜像(创建虚拟机时用),放到宿主机能访问的路径,比如 /var/lib/libvirt/images/iso/。存储池默认用 default,对应 /var/lib/libvirt/images。
六、部署方式一:启动脚本(推荐)
项目自带 scripts/start.sh,一键起后端和前端,是最省事的部署方式。
git clone https://git.cnbugs.com/AI-Agent/kvm-manager.git
cd kvm-manager/scripts
# 启动所有服务
./start.sh
# 查看服务状态
./start.sh status
# 停止服务
./start.sh -s
启动成功后访问:前端界面 http://<服务器IP>:8005,后端 API http://<服务器IP>:8004,API 在线文档 http://<服务器IP>:8004/docs。先用 /docs 确认后端正常,再进前端界面登录。
七、部署方式二:手动部署
想自己控制每个环节,或者要改端口、改配置,用手动方式。后端和前端各开一个终端。
# 后端
cd kvm-manager/backend
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
uvicorn app.main:app --host 0.0.0.0 --port 8004
# 前端(另开一个终端)
cd kvm-manager/frontend
npm install
npm run dev
后端配置文件在 backend/app/config.py,也可以用同目录下的 .env 文件覆盖。关键配置项如下:
| 配置项 | 说明 | 默认值 |
|---|---|---|
LIBVIRT_URI | 本地 libvirt 连接 URI | qemu:///system |
API_PREFIX | API 路径前缀 | /api |
SECRET_KEY | JWT 签名密钥 | 默认值见源码,生产环境必须改掉 |
ACCESS_TOKEN_EXPIRE_MINUTES | Token 有效期(分钟) | 1440(24 小时) |
KVM_DATA_DIR | 主机数据存储目录 | /var/lib/kvm-manager |
注意:SECRET_KEY 在源码里的默认值是公开的(kvm-manager-secret-key-change-in-production),生产部署第一件事就是把它换掉,否则任何人都能伪造 Token。做法是在 backend/.env 里写 SECRET_KEY=自己生成的随机串,生成方法:openssl rand -hex 32。
八、部署方式三:Docker 部署
cd kvm-manager
docker-compose up -d
# 查看容器状态
docker-compose ps
# 看日志
docker-compose logs -f backend
docker-compose logs -f frontend
Docker 部署要注意:后端容器必须能访问宿主机的 libvirt socket(通常要挂载 /var/run/libvirt 或用 host 网络,见项目里的 docker-compose.yml 和 backend/Dockerfile),否则后端起得来、但连不上 KVM。部署完先调 GET /health 验证:
curl -s http://localhost:8004/health
# 正常返回 {"status":"ok","libvirt":true}
# 如果 libvirt 是 false,说明后端没连上 libvirtd,先查 libvirt 再查容器挂载
九、多主机纳管
在前端「主机管理」页面添加远程 KVM 主机,三种连接方式的区别如下:
| 连接方式 | URI 格式 | 适用场景 | 前置条件 |
|---|---|---|---|
| 本地连接 | qemu:///system | 平台就部署在 KVM 宿主机本机 | 后端进程能访问本地 libvirt socket |
| SSH 连接 | qemu+ssh://user@host/system | 远程宿主机,推荐方式 | 配置无密码 SSH 登录(密钥),VNC 只监听 localhost 时后端自动建 SSH 隧道 |
| TCP 连接 | qemu+tcp://host/system | 内网可信环境 | 远程 libvirtd 监听 TCP(需配 SASL/TLS,裸 TCP 不安全) |
# SSH 模式前置:生成密钥并分发到远程宿主机(无密码登录)
ssh-keygen -t ed25519 -N "" -f ~/.ssh/kvm-manager
ssh-copy-id -i ~/.ssh/kvm-manager user@kvm-host02
# 验证免密能通
ssh -i ~/.ssh/kvm-manager user@kvm-host02 "virsh -c qemu:///system list --all"
纳管之后,虚拟机列表页可以用 host_id 过滤单台主机,也可以用聚合接口一次看所有主机的虚拟机。远程主机的 VNC 如果只监听 127.0.0.1,后端开控制台时会自动建一条 SSH 本地端口转发隧道,不用手动搭;如果 VNC 监听的是 0.0.0.0,则直接连远程主机 IP。反过来,TCP 模式下 VNC 只听 localhost 是连不上的,必须改用 SSH 模式或者把 VNC 改成监听 0.0.0.0。
十、QEMU Guest Agent 配置
平台取虚拟机 IP 有三级回退:先问 QEMU Guest Agent,再查 DHCP 租约,最后查 ARP 表。Guest Agent 是最准的一种,没装它就只能靠后两种,经常拿不到 IP。给虚拟机装 Guest Agent分两步:虚拟机 XML 里加 virtio-serial 通道,系统里装 agent 服务。
# 项目自带脚本:单个虚拟机 / 全部虚拟机
./scripts/install-guest-agent.sh VM_NAME
./scripts/install-guest-agent.sh --all
手动配置的话,先关机虚拟机,virsh edit VM_NAME,在 <devices> 里加上:
<controller type='virtio-serial' index='0'/>
<channel type='unix'>
<source mode='bind'/>
<target type='virtio' name='org.qemu.guest_agent.0'/>
</channel>
# 开机后在虚拟机内部安装 Agent
# CentOS/RHEL
yum install -y qemu-guest-agent
systemctl enable --now qemu-guest-agent
# Ubuntu/Debian
apt install -y qemu-guest-agent
systemctl enable --now qemu-guest-agent
# 验证 Agent 通不通
virsh qemu-agent-command VM_NAME '{"execute":"guest-info"}'
十一、使用示例:Web 界面
日常操作都在前端界面完成,典型流程是:
- 创建虚拟机:虚拟机列表 → 新建,填名称、内存、CPU、磁盘、存储池、网络,选 ISO 镜像,提交后自动创建 qcow2 磁盘并开机。默认配置是 2 核 CPU、2048 MB 内存、20 GB 磁盘。
- 开关机:列表里点启动、关机、重启、暂停、恢复;关不掉的用强制关机。
- 开控制台:点虚拟机的控制台,浏览器里直接进 noVNC 桌面,装系统、排障都在里面做。
- 克隆虚拟机:基于现有虚拟机快速复制一台同配置的出来,改个名字就能用,适合批量做测试机。
- 快照:做危险操作前先打个快照,出问题一键恢复。
- 看监控:列表页直接显示每台虚拟机的 CPU 使用率和内存占用,详情页还能看到磁盘读写和网络收发统计。
- 查 IP:装了 Guest Agent 的虚拟机,详情页直接显示 IP;没装的会依次尝试 DHCP 租约和 ARP 表。
十二、使用示例:API 调用
所有 Web 界面的操作背后都是 REST API,也可以直接用 curl 调用,适合写自动化脚本。API 前缀默认 /api,完整路径见 http://<服务器>:8004/docs。先登录拿 Token:
# 登录(表单提交,返回 JWT)
curl -s -X POST http://localhost:8004/api/auth/login \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=admin&password=你的密码"
# 返回示例
# {"access_token":"eyJhbGciOi...","token_type":"bearer","username":"admin","role":"admin"}
# 把 Token 存下来,后面都带着它
export TOKEN="eyJhbGciOi..."
# 列出本地主机全部虚拟机
curl -s "http://localhost:8004/api/vm/list?host_id=local" \
-H "Authorization: Bearer $TOKEN" | python3 -m json.tool
# 聚合所有已纳管主机的虚拟机
curl -s "http://localhost:8004/api/vm/list-all" \
-H "Authorization: Bearer $TOKEN" | python3 -m json.tool
# 看一台虚拟机的详情(含磁盘、网卡、运行时统计)
curl -s "http://localhost:8004/api/vm/detail/web01?host_id=local" \
-H "Authorization: Bearer $TOKEN" | python3 -m json.tool
# 查虚拟机 IP
curl -s "http://localhost:8004/api/vm/ip/web01?host_id=local" \
-H "Authorization: Bearer $TOKEN"
# 创建虚拟机:2 核、4G 内存、40G 磁盘,挂载 ISO
curl -s -X POST "http://localhost:8004/api/vm/create?host_id=local" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "web01",
"memory_mb": 4096,
"vcpus": 2,
"disk_gb": 40,
"pool_name": "default",
"network": "default",
"iso_path": "/var/lib/libvirt/images/iso/rhel-8.10-x86_64-dvd.iso"
}'
# 开机 / 关机 / 重启 / 暂停 / 恢复 / 强制关机
curl -s -X POST http://localhost:8004/api/vm/action/web01 \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"action": "start"}'
# action 可选值:start / stop / restart / pause / resume / force_stop
用户管理也是 API:管理员用 POST /api/auth/register 建新用户,GET /api/auth/users 列用户,DELETE /api/auth/users/{username} 删用户(不能删自己),POST /api/auth/change-password 改自己的密码。
十三、WebSocket VNC 控制台
控制台的链路是:浏览器 noVNC → WebSocket /ws/vnc/{vm_name} → 后端代理 → 虚拟机 VNC 端口。后端从虚拟机 XML 里读 VNC 的监听地址和端口,再决定怎么连:本地主机直接连;远程 SSH 主机如果 VNC 只听 localhost,后端自动起一条 ssh -L 隧道转发到本地端口再连;远程 VNC 监听 0.0.0.0 则直连远程 IP。
所以如果控制台打不开,按这个顺序查:虚拟机在运行吗(VNC 端口只在运行时才分配)→ 虚拟机 XML 里有没有 <graphics type='vnc'> → VNC 监听地址是不是 127.0.0.1(远程 TCP 模式下这就是连不上的原因,改 SSH 模式或改监听地址)→ 后端日志有没有 SSH 隧道失败的报错。
十四、常见问题
| 现象 | 原因 | 处理方法 |
|---|---|---|
| 虚拟机 IP 一直拿不到 | 没装 Guest Agent,DHCP 租约和 ARP 也没命中 | 按第十节装 Guest Agent;或确认虚拟机用的网络有 DHCP |
| 控制台打不开,提示没有 VNC 配置 | 虚拟机 XML 里没有 graphics 设备,或虚拟机没运行 | 启动虚拟机;检查 virsh dumpxml VM_NAME | grep graphics |
| 远程主机控制台连不上 | VNC 只监听 localhost 但用的是 TCP 模式 | 改用 SSH 模式纳管,或把 VNC 改成监听 0.0.0.0 |
| SSH 模式纳管失败 | 没配无密码 SSH 登录 | 按第九节做密钥分发,确认 ssh user@host virsh list 能通 |
/health 返回 libvirt false | 后端连不上 libvirtd | 查 systemctl status libvirtd;Docker 部署查 socket 挂载 |
| 登录后很快又要重新登录 | Token 过期(默认 24 小时) | 重新登录;或调大 ACCESS_TOKEN_EXPIRE_MINUTES |
十五、运维与安全建议
- 先换 SECRET_KEY:源码里的默认密钥是公开的,生产部署必须在
.env里换掉,否则 Token 可以被伪造。 - 别把 8004/8005 直接暴露公网:放内网或反向代理后面加 HTTPS;CORS 默认是
allow_origins=["*"],生产环境建议收紧。 - 定期备份主机数据:
/var/lib/kvm-manager下是主机注册信息和用户库,丢了就得重新纳管,纳入日常备份。 - 宿主机资源留余量:创建虚拟机时注意宿主机剩余 CPU、内存和存储池空间,超分太多会导致虚拟机抢资源。
- 快照不是备份:快照依赖原磁盘链,磁盘坏了快照也没用,重要虚拟机还是要做真正的备份。
- 升级流程:先停服务,
git pull拉新代码,后端重装依赖(pip install -r requirements.txt),前端重新npm install && npm run build(或重启 dev),再启动验证/health。
总结一下:kvm-manager 用很小的代价解决了多台 KVM 宿主机的统一管理问题——一个 FastAPI 后端 + 一个 Vue 前端,虚拟机创建、开关机、控制台、快照、监控全在浏览器里完成。部署先用 scripts/start.sh 跑起来,远程主机走 SSH 纳管,虚拟机装上 Guest Agent 拿 IP,最后记得换掉默认 SECRET_KEY,就能直接当生产面板用了。






