主题
jumpserver 虚拟机启动失败修复:宿主机重启后 default 网络未自启
故障对象:
jumpserver虚拟机(192.168.122.250,磁盘/mnt/xfs/jumpserver/jumpserver.qcow2) 发生时间:2026-09-08 13:00(宿主机重启后) 修复时间:2026-09-08 13:07 修复结果:虚拟机恢复运行,JumpServer v4.10.19 全部 8 个容器 healthy,Web/API 返回 200
一、故障现象
宿主机重启后,jumpserver 虚拟机没有起来,virsh list 里看不到它, virsh domstate 显示的是 failed 而不是普通的 shut off:
bash
$ virsh list --all
Id Name State
-------------------------------
1 harvester-01 running
3 harvester-02 running
6 harvester-03 running
- jumpserver shut off # <- 明明 autostart=enable 却没起来
- rocky-repo shut off
- sza122020 shut off
...
$ virsh domstate jumpserver --reason
shut off (failed) # 关键:reason 是 failed,说明是启动过程报错
$ virsh dominfo jumpserver | grep -E 'Autostart|Persistent'
Persistent: yes
Autostart: enable # 自启是开着的,所以不是配置漏了二、根因定位
2.1 直接原因:libvirt default 网络没起来
手动启动一次,报错直接点出问题:
bash
$ virsh start jumpserver
error: Failed to start domain 'jumpserver'
error: Requested operation is not valid: network 'default' is not active对照虚拟机 XML,它的网卡挂在 default 网络上:
xml
<interface type='network'>
<mac address='52:54:00:00:8d:d9'/>
<source network='default'/> <!-- 依赖 default 网络 -->
<model type='virtio'/>
</interface>而 default 网络当时的状态是 inactive + Autostart: no:
bash
$ virsh net-list --all
Name State Autostart Persistent
------------------------------------------------
default inactive no yes # <- 根因
harvester active yes yes
$ ls /etc/libvirt/qemu/networks/autostart/
harvester.xml # 只有 harvester,没有 default.xml2.2 为什么 harvester-01/02/03 没受影响
三台 harvester 节点用的是 harvester 网络,而 harvester 网络 Autostart=yes,宿主机重启后自动拉起,所以这三台正常自启。
这也解释了故障的范围:所有网卡挂在 default 网络、且 autostart=enable 的虚拟机全部启动失败,不止 jumpserver 一台:
| 虚拟机 | autostart | 所用网络 | 重启后状态 |
|---|---|---|---|
| harvester-01/02/03 | enable | harvester | ✅ running(网络自启正常) |
| jumpserver | enable | default | ❌ shut off (failed) |
| rocky-repo | enable | default | ❌ shut off (failed) |
| sza122020/21/22 | enable | default | ❌ shut off (failed) |
| sza122100/101/102 | enable | default | ❌ shut off (failed) |
| sza122023–31 | 未设自启 | default | 关机(符合预期) |
2.3 排查过程中确认「不是问题」的几项
启动失败时容易怀疑磁盘,这次逐项排除掉了,记录如下避免重复踩坑:
bash
# 1) 磁盘空间充足,不是写满
$ df -h /mnt/xfs /
/dev/sda 2.8T 1.2T 1.7T 41% /mnt/xfs
/dev/nvme1n1p2 916G 658G 212G 76% /
# 2) qcow2 backing file 存在且完好(重建时用了 Rocky 9 云镜像做 backing)
$ qemu-img info --backing-chain /mnt/xfs/jumpserver/jumpserver.qcow2
backing file: /u02/repo/osiso/Rocky-9-GenericCloud-Base.latest.x86_64.qcow2
corrupt: false # 镜像没有损坏
$ ls -la /u02/repo/osiso/Rocky-9-GenericCloud-Base.latest.x86_64.qcow2
-rw-r--r-- 1 libvirt-qemu kvm 645988352 # backing 文件在,权限也对
# 3) cloud-init seed 镜像在位
$ ls -la /mnt/xfs/jumpserver/
-rw-r--r-- 1 xxx xxx 6693519360 jumpserver.qcow2
-rw-r--r-- 1 libvirt-qemu kvm 374784 jumpserver-seed.iso提示:
virsh net-dhcp-leases default在网络 inactive 时仍会返回历史租约, 看起来像「虚拟机有 IP、网络是通的」,实际是 dnsmasq 租约文件的残留记录, 不能作为网络正常的依据。判断依据要用virsh net-list --all的 State 列。
三、修复步骤
bash
# 1) 立即恢复:手动启动 default 网络
virsh net-start default
# Network default started
# 2) 永久修复:设置 default 网络开机自启(关键,否则下次重启同样故障)
virsh net-autostart default
# Network default marked as autostarted
# 3) 启动 jumpserver
virsh start jumpserver
# Domain 'jumpserver' started
# 4) 顺带拉起其余同样因该根因失败的自启虚拟机
for vm in rocky-repo sza122020 sza122021 sza122022 sza122100 sza122101 sza122102; do
virsh start $vm
done修复后确认自启软链已生成:
bash
$ ls -la /etc/libvirt/qemu/networks/autostart/
default.xml -> /etc/libvirt/qemu/networks/default.xml # 新增
harvester.xml -> /etc/libvirt/qemu/networks/harvester.xml
$ virsh net-list --all
Name State Autostart Persistent
----------------------------------------------
default active yes yes # 已变为 yes
harvester active yes yes权限说明:执行用户
xxx属于libvirt组,virsh直连qemu:///system即可完成网络的 start/autostart 与虚拟机的 start,本次修复全程无需 sudo。
四、验证结果
4.1 宿主机侧
bash
$ virsh domstate jumpserver
running
$ virsh domifaddr jumpserver
Name MAC address Protocol Address
--------------------------------------------------------------------------
vnet7 52:54:00:00:8d:d9 ipv4 192.168.122.250/24 # DHCP 拿到固定 IP
$ ping -c 2 192.168.122.250
2 packets transmitted, 2 received, 0% packet loss
$ virsh list --all
Id Name State
-------------------------------
1 harvester-01 running
3 harvester-02 running
6 harvester-03 running
14 jumpserver running # 已恢复
15 rocky-repo running
16 sza122020 running
17 sza122021 running
18 sza122022 running
19 sza122100 running
20 sza122101 running
21 sza122102 running4.2 虚拟机内部(SSH 密钥登录)
bash
$ ssh root@192.168.122.250 'uptime; systemctl is-active docker'
05:08:05 up 0 min, 0 users, load average: 2.90, 0.78, 0.26
active
$ ssh root@192.168.122.250 'docker ps --format "{{.Names}} {{.Status}}"'
jms_core Up 2 minutes (healthy)
jms_chen Up 2 minutes (healthy)
jms_lion Up 2 minutes (healthy)
jms_web Up 2 minutes (healthy)
jms_koko Up 2 minutes (healthy)
jms_celery Up 2 minutes (healthy)
jms_redis Up 2 minutes (healthy)
jms_postgresql Up 2 minutes (healthy)启动节奏:虚拟机开机后约 1 分钟内 8 个容器全部 Up,但
jms_core(Django) 需要额外 1–2 分钟做数据库连接、collectstatic、migrate、初始化内置小程序, 这期间jms_chen / jms_lion / jms_koko / jms_celery会短暂显示 unhealthy, 属于依赖未就绪的正常现象,不要在这个窗口期去重启容器。 判断依据看docker logs jms_core,出现JumpServer version v4.10.19即接近就绪。
4.3 服务层
bash
$ curl -sS -o /dev/null -w '%{http_code}\n' http://192.168.122.250/
200
$ curl -sS http://192.168.122.250/core/auth/login/ | tr -d '\n' | grep -o '<title>[^<]*</title>'
<title> JumpServer 开源堡垒机 </title>
$ curl -sS -o /dev/null -w '%{http_code}\n' http://192.168.122.250/api/health/
200
# koko SSH 代理端口在监听
$ ssh root@192.168.122.250 'ss -lntp | grep -E ":(80|2222)"'
LISTEN 0 4096 0.0.0.0:2222 users:(("docker-proxy",...))
LISTEN 0 4096 0.0.0.0:80 users:(("docker-proxy",...))4.4 插曲:sza122100/101/102 首次启动后自动关机
按第三节步骤 4 批量启动后,约 5 分钟复查发现这三台又变回 shut off, 而其余 8 台正常。排查过程记录如下:
bash
$ for vm in sza122100 sza122101 sza122102; do virsh domstate $vm --reason; done
shut off (shutdown) # reason=shutdown:客户机自己发起的正常关机
shut off (shutdown) # 不是 crashed,也不是被宿主机 destroy
shut off (shutdown)
# 排除宿主机内存不足 / OOM
$ free -h
total used free available
Mem: 251Gi 72Gi 131Gi 177Gi # 余量充足
$ dmesg | grep -i 'oom\|killed process' # 无输出
# 排除磁盘缺失(这三台磁盘在 /kvm/lib/libvirt/sz/,非独立挂载,位于根分区)
$ ls -la /kvm/lib/libvirt/sz/sza12210*.qcow2
-rw-r--r-- 1 xxx xxx 17909415936 9月 8 13:09 sza122100.qcow2 # 13:09 有写入
-rw-r--r-- 1 xxx xxx 18572705792 9月 8 13:09 sza122101.qcow2 # 说明确实启动过
-rw-r--r-- 1 xxx xxx 19411304448 9月 8 13:09 sza122102.qcow2再次单独启动即恢复正常,并稳定运行未再关机:
bash
$ virsh start sza122100 && sleep 20 && virsh domstate sza122100 --reason
running (booted)
$ virsh domifaddr sza122100 | tail -1
vnet15 52:54:00:7a:64:64 ipv4 192.168.122.100/24 # DHCP 正常
$ virsh start sza122101; virsh start sza122102
$ virsh list --state-running --name | grep -vc '^$'
11 # 11 台自启虚拟机全部 running,持续观察未再掉线推断原因:宿主机 13:00 是非正常重启(last -x reboot 显示上一次会话从 8-15 一直持续到 12:57 才中断),这三台客户机内部可能残留了未执行完的关机流程, 首次被拉起后把它执行掉了;第二次启动即为干净状态。
结论:批量
virsh start之后不要只看一眼就收工,间隔几分钟复查一次virsh list --all,并配合virsh domstate --reason区分shutdown(客户机主动)/crashed(客户机崩溃)/failed(libvirt 启动报错)/destroyed(被强制关闭),四种 reason 的排查方向完全不同。
五、遗留问题:36000 HTTPS 入口未恢复(待决策)
本次修复解决了「虚拟机起不来」,但发现 2026-07-18 加固时配置的 https://ai-ear.cn:36000 入口已经不存在:虚拟机在 2026-09-05 被重建, 配置回到官方默认值,宿主机 frpc 也在 9-06 换过配置。
对照 security/incidents/2026-07-18-jumpserver-hardening.md 逐项核查:
| 加固项 | 文档要求 | 当前实际 | 状态 |
|---|---|---|---|
HTTPS_PORT | 36000 | 配置中无此项 | ❌ 丢失 |
DOMAINS | ai-ear.cn:36000 | 空值 | ❌ 丢失 |
SERVER_NAME | ai-ear.cn | 配置中无此项 | ❌ 丢失 |
| SSL 证书 | DigiCert ai-ear.cn 正式证书 | config/nginx/cert/server.{crt,key},9-05 生成的默认自签证书 | ❌ 被覆盖 |
lb_http_server.conf 的 server_name | 取消注释设为 ai-ear.cn | 仍是注释状态的 demo.jumpserver.org | ❌ 丢失 |
宿主机 frpc jumpserver-web-36000 | localPort/remotePort 36000 | 当前 /etc/frp/frpc.toml 无此条目,仅存于备份 | ❌ 丢失 |
验证:
bash
$ curl -k https://192.168.122.250:36000/
curl: (7) Failed to connect to 192.168.122.250 port 36000: Connection refused
$ grep -E '^(HTTP|HTTPS|DOMAINS|SERVER_NAME)' /opt/jumpserver/config/config.txt
HTTP_PORT=80
DOMAINS= # 空
$ grep -rn '36000' /etc/frp/ # 只剩备份文件里有
/etc/frp/frpc.toml.jumpserver:14:name = "jumpserver-web-36000"
/etc/frp/frpc.toml.0820:88:name = "jumpserver-web-36000"
/etc/frp/frpc.toml.bak.20260819083258:88:name = "jumpserver-web-36000"当前 JumpServer 只能通过内网 http://192.168.122.250/(明文 80)访问, 公网 https://ai-ear.cn:36000 不通。
恢复步骤(4 步,未擅自执行):
- 从公网机
8.153.84.140重新拷回ai-ear.cn证书到/opt/jumpserver/config/nginx/cert/server.{crt,key}; ⚠️ 原证书有效期 2026-07-16 ~ 2026-10-15,距今仅一个多月,需先确认是否已续期; - 按 2.2 节表格改
/opt/jumpserver/config/config.txt(补HTTPS_PORT=36000、SERVER_NAME、SSL_CERTIFICATE、SSL_CERTIFICATE_KEY、DOMAINS); - 取消
/opt/jumpserver/config/nginx/lb_http_server.conf里server_name的注释 并设为ai-ear.cn,然后cd /opt/jumpserver-installer-v4.10.19 && ./jmsctl.sh restart; - 宿主机
/etc/frp/frpc.toml加回jumpserver-web-36000(localIP=192.168.122.250、localPort=36000、remotePort=36000)并systemctl restart frpc。
因为这会重新开放一个公网入口、且涉及证书有效期,属于安全相关变更, 本次未执行,等待确认后再做。
六、经验总结
virsh domstate --reason比virsh list有用:shut off (failed)说明是启动过程报错,而不是正常关机,排查方向完全不同。- autostart 的虚拟机依赖 autostart 的网络:只给虚拟机设
virsh autostart不够,它引用的 libvirt 网络也必须virsh net-autostart, 否则宿主机重启后虚拟机会静默启动失败。建议把「网络自启」纳入建机流程的固定步骤。 - 批量核对法:一台虚拟机自启失败时,先用
ls /etc/libvirt/qemu/autostart/对比virsh list,可一次性找出所有受同一根因影响的虚拟机,避免漏修。 - 重建虚拟机 = 丢配置:jumpserver 在 9-05 重建后,7-18 的加固成果 (HTTPS、证书、server_name)全部丢失。重建前应导出
/opt/jumpserver/config/整个目录,重建后按加固文档逐项复验,而不是只确认「容器起来了」。