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 APIREST 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 连接 URIqemu:///system
API_PREFIXAPI 路径前缀/api
SECRET_KEYJWT 签名密钥默认值见源码,生产环境必须改掉
ACCESS_TOKEN_EXPIRE_MINUTESToken 有效期(分钟)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,就能直接当生产面板用了。

发表回复

后才能评论