Skip to content

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.xml

2.2 为什么 harvester-01/02/03 没受影响

三台 harvester 节点用的是 harvester 网络,而 harvester 网络 Autostart=yes,宿主机重启后自动拉起,所以这三台正常自启。

这也解释了故障的范围:所有网卡挂在 default 网络、且 autostart=enable 的虚拟机全部启动失败,不止 jumpserver 一台:

虚拟机autostart所用网络重启后状态
harvester-01/02/03enableharvester✅ running(网络自启正常)
jumpserverenabledefault❌ shut off (failed)
rocky-repoenabledefault❌ shut off (failed)
sza122020/21/22enabledefault❌ shut off (failed)
sza122100/101/102enabledefault❌ 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      running

4.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_PORT36000配置中无此项❌ 丢失
DOMAINSai-ear.cn:36000空值❌ 丢失
SERVER_NAMEai-ear.cn配置中无此项❌ 丢失
SSL 证书DigiCert ai-ear.cn 正式证书config/nginx/cert/server.{crt,key},9-05 生成的默认自签证书❌ 被覆盖
lb_http_server.confserver_name取消注释设为 ai-ear.cn仍是注释状态的 demo.jumpserver.org❌ 丢失
宿主机 frpc jumpserver-web-36000localPort/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 步,未擅自执行):

  1. 从公网机 8.153.84.140 重新拷回 ai-ear.cn 证书到 /opt/jumpserver/config/nginx/cert/server.{crt,key}; ⚠️ 原证书有效期 2026-07-16 ~ 2026-10-15,距今仅一个多月,需先确认是否已续期;
  2. 按 2.2 节表格改 /opt/jumpserver/config/config.txt(补 HTTPS_PORT=36000SERVER_NAMESSL_CERTIFICATESSL_CERTIFICATE_KEYDOMAINS);
  3. 取消 /opt/jumpserver/config/nginx/lb_http_server.confserver_name 的注释 并设为 ai-ear.cn,然后 cd /opt/jumpserver-installer-v4.10.19 && ./jmsctl.sh restart
  4. 宿主机 /etc/frp/frpc.toml 加回 jumpserver-web-36000localIP=192.168.122.250localPort=36000remotePort=36000)并 systemctl restart frpc

因为这会重新开放一个公网入口、且涉及证书有效期,属于安全相关变更, 本次未执行,等待确认后再做

六、经验总结

  1. virsh domstate --reasonvirsh list 有用shut off (failed) 说明是启动过程报错,而不是正常关机,排查方向完全不同。
  2. autostart 的虚拟机依赖 autostart 的网络:只给虚拟机设 virsh autostart 不够,它引用的 libvirt 网络也必须 virsh net-autostart, 否则宿主机重启后虚拟机会静默启动失败。建议把「网络自启」纳入建机流程的固定步骤。
  3. 批量核对法:一台虚拟机自启失败时,先用 ls /etc/libvirt/qemu/autostart/ 对比 virsh list,可一次性找出所有受同一根因影响的虚拟机,避免漏修。
  4. 重建虚拟机 = 丢配置:jumpserver 在 9-05 重建后,7-18 的加固成果 (HTTPS、证书、server_name)全部丢失。重建前应导出 /opt/jumpserver/config/ 整个目录,重建后按加固文档逐项复验,而不是只确认「容器起来了」。