MoreRSS

site iconWuSiYu修改

主题涉猎:高性能计算、AI工具链、智能硬件&DIY、Linux、家庭服务器等
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

WuSiYu的 RSS 预览

虚拟机检测绕过和nvme性能优化的libvirt配置

2026-01-24 03:36:41

免责声明:我只是为了在自建的云游戏串流虚拟机上进行远程游戏,用虚拟机是因为All-in-boom宿主机还要跑别的东西。请勿用于非法用途,当然对于非法用途的人也早就知道这些了。

参考自:https://github.com/zhaodice/qemu-anti-detection ,但这个配置会极大影响直通nvme硬盘的4k多线程性能,会从700MB/s跌倒20MB/s,导致游戏加载非常慢。因此研究了一些不影响通过检测的优化,使其恢复到200~300MB/s左右,不再成为瓶颈。

以下是xml配置文件关键段落:

<domain type='kvm' id='62' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>
  ... 略 ...
  <features>
    <acpi/>
    <apic/>
    <kvm>
      <hidden state='on'/>
    </kvm>
    <vmport state='off'/>
    <smm state='on'/>
    <ioapic driver='kvm'/>
  </features>
  <cpu mode='host-passthrough' check='none' migratable='off'/>
  <clock offset='localtime'>
    <timer name='rtc' tickpolicy='catchup'/>
    <timer name='pit' tickpolicy='delay'/>
    <timer name='hpet' present='no'/>
    <timer name='kvmclock' present='no'/>
  </clock>
  
  <devices>
    ... 略 ...
    <memballoon model='none'/>
  </devices>
  
  <qemu:commandline>
    <qemu:arg value='-smbios'/>
    ... 略(请参考最上面的链接) ...

    <qemu:arg value='-cpu'/>
    <qemu:arg value='host,family=6,model=167,stepping=1,l3-cache=on,model_id=Intel(R) Xeon(R) E-2378 CPU @ 2.60GHz,vmware-cpuid-freq=false,enforce=false,host-phys-bits=true,hypervisor=off,+x2apic,hv-time,hv-relaxed,hv-vapic,hv-spinlocks=0x1fff,hv-vendor-id=GenuineIntel,tsc-frequency=2600000000'/>
  </qemu:commandline>
</domain>

NVME硬盘4k多线程性能差主要是因为时钟源问题,为通过检测我们无法使用常用的虚拟机高性能时钟源,导致中断性能问题。(是的,pcie直通并非一定没有性能损耗)

其中最为关键的是qemu:arg中的-cpu段落,family=6,model=167,stepping=1,l3-cache=on,model_id=Intel(R) Xeon(R) E-2378 CPU @ 2.60GHz根据实际修改。经过反复测试,必须使用这种方式(libvirt原生配置不完全),同时不能加入migratable=no以打开+invtsc,哪怕加入更多参数修正也会有cpu feature细微差异,导致检测不过。但我们可以设置tsc-frequency=2600000000,强行使用tsc,数值为你的CPU基频(我这里是2.6GHz),睿频不影响。另外这里依然保留了一些hv特性,这对于开启了 Hyper-V 功能(如 VBS/WSL2)的物理机也是存在的,对于Win11机器很常见。

虚拟机检测绕过和nvme性能优化的libvirt配置最先出现在WuSiYu Blog

UCG Fiber 主路由器 + OpenWrt 透明代理 + 高可用 方案和评测(对比ROS)

2026-01-15 00:44:47

背景和需求

直连流量尽量通过主路由直连,同时内网所有设备透明代理。为了代理性能和方便管理,OpenWrt 可以跑在再服务器的虚拟机中,因此有必要在其离线时实现自动 failover,将内网降级为正常全直连网络。

过去用 RouterOS (ROS) 主路由(CCR2004) + OpenWrt 网关 + VRRP 实现,不过 ROS 的问题是设备管理不直观,不像 Unifi 的系统一样有着很好的大屏,以及 DPI、IDS/IPS 等功能。同时 ROS 没有 PPPoE 硬件 offload 支持,在这一点上甚至不如几百块的家用无线路由器。

因此我购入了一台 Unifi Cloud Gateway Fiber(UCG Fiber),三个10G口(2光1电)+ 4个2.5G电口,本文将探讨将其代替 ROS 作为高可用魔法(代理)家用网络环境的主路由器的方案和局限性。

TLDR:Unifi与ROS优缺点对比

(这里的优缺点仅针对本场景)

Unifi UCG Fiber
优点:

  • 界面美观,对客户端识别更智能(比如常见手机型号),DPI 功能还能分析流量种类和记录客户端的网络活动历史
  • PPPoE hardware offload 支持,千兆宽带满速下载时 CPU 占用没什么变化,据其他人测试能完全跑满 10G PPPoE
  • 小体积、高颜值,非常静音的主动散热,还有个小屏幕能看实时网速

缺点:

  • 灵活性很低,不支持 VRRP(就算是Unifi的机架式设备也只能自己跟自己组HA),高可用代理方案配置麻烦,目前没找到可用的方法实现高可用的IPv6代理,只能强制让代理都走 IPv4
  • 自带功能有些很不完善:
    • IPv6 支持烂,任何情况下 IPv6 只会走主要 WAN,哪怕 IPv6 地址下发的都是备用 WAN 的 v6,且主要 WAN 都没启用 v6
    • 策略路由(PBR)不支持 IPv6
    • 策略路由只能指定出口 WAN,没法指定下一跳到内网机器。而且 Fallback 功能残废,只有在 WAN 接口 Down 了时才会生效,哪怕系统都检测到该 WAN 不可用了也不会切换
    • Wireguard Server + 双 WAN 支持差,无法在备用 WAN 上接受连接(回程路由有 bug),除非主要 WAN 不可用
  • 其他一些问题:端口转发的 NAT Loopback(在内网通过映射到公网的地址访问内部服务时)无法硬件加速,只能跑到1~3GBps,跑不满内网带宽
  • 开启 Smart Queue QoS 后 PPPoE offload 失效,只能跑到 600Mbps 不到,且无法只在一个方向上开启(比如只在上行开启 QoS)
  • 客户端管理功能很强,但没有一个地方能看当前所有的网络连接(Conntrack)和实时速度(类似ROS的IP -> Firewall -> Connections)

RouterOS

  • 优点:灵活性很高,可以直接跟 OpenWrt 组VRRP,且 VRRP 支持脚本,ROS 的脚本可以便利的修改所有网络设置,可以实现各种自动化功能
  • 缺点1:对于管理客户端流量不直观,Connections 里虽然能看到一个内网 IP 连了一个外网 IP,但还得去 DHCP 分配里查这个内网 IP 的主机名或MAC地址,可能还得上网查一下这个 MAC 地址的 vendor,才能确定这个客户端是谁;对于外网 IP 也没法跟 DNS 查询记录里的域名做关联,很不直观。
  • 缺点2:MikroTik 家的硬路由性能有限,在我的 CCR2004 上 PPPoE 硬跑倒是能跑满千兆宽带下行,但一开双向的 Cake QoS 就不行了(哪怕开了fasttrack),不过可以只开上行的 QoS,下行带宽够大也不需要 QoS 了

UCG Fiber 硬件分析

通过dmesg和拆解图分析得到内部拓扑:

SoC 为高通 IPQ9574,拥有 NSS / PPE 硬件加速功能,CPU 为4x A73 @ 2.2GHz,不算太强,还是比较依赖硬件加速的。

拓扑方面,两个 10G 光口直连 SoC,所有电口(4×2.5G + 10G)连接到内部的 RTL8372 交换芯片,共享一个 10G 链路到 SoC。由于有硬件加速,光口和电口的交换不会占用CPU,但电口侧确实存在带宽瓶颈(虽然一般遇不到)。

硬件方面,内存物理大小共4G,OS中可用3G(包括Unifi宣传的也是3G),大概是有1G被SoC其他组件占用,也可能是ECC。

另外UCG Fiber还有一个加速度传感器用来检测摆放姿态,可以根据姿态调整小屏幕的显示方向。(有时间搞这些能不能把 IPv6 搞完善点啊喂)

方案概述

为实现 Unifi 主路由 + OpenWrt 全局透明代理 + 高可用,首先 Unifi 作为主路由器,连接光猫进行 PPPoE 拨号,OpenWrt 连接到 Unifi 的 LAN 上作为客户端(类似二级路由,但不同的是终端设备不去连接 OpenWrt),然后利用 Unifi 的双 WAN 主备切换的功能,让 OpenWrt 再作为 Unifi 的一个 WAN,即OpenWrt WAN Loopback,具体如下:

  • 将 Unifi 的 WAN1 (使用DHCP,IPv6关闭)连接 OpenWrt 的 LAN ,OpenWrt 的 WAN 再连接到 Unifi 的 LAN
  • 将 Unifi 的 WAN2 接光猫拨号,开启IPv6,并把内网的 IPv6 前缀来源也设置为 WAN2
  • Unifi 上设置 WAN1(OpenWrt)为 Primary,WAN2(PPPoE)为 Backup,模式为Failover Only
  • Unifi 上设置策略路由:
    • 1. 目标 Region 为 China 的都走 WAN2(不用勾 Kill Switch,没啥用且会跟下面的DNS hijack有奇妙的冲突);
    • 2. 来自 OpenWrt 的流量都走 WAN2(避免循环,勾上Kill Switch)
  • OpenWrt 上和标准的路由+代理设置基本相同,WAN 为 DHCP,连接 Unifi 内网。然后要将 WAN 和 WAN6 接口高级设置中的“自动获取 DNS 服务器”禁用,并填入运营商的 DNS 地址,避免循环
  • 效果:正常情况下,客户端的 DNS 请求先到 Unifi 的 DNS 服务器上(对于 DPI 功能有帮助),然后走 WAN1 -> OpenWrt 的无污染 DNS,解析得到国外地址继续走 OpenWrt 做代理(OpenWrt 再通过 Unifi LAN -> PPPoE 连接代理服务器),国内地址直接走 PPPoE 发出。一旦 OpenWrt 挂掉,Unifi 检测到 WAN1 不通,会自动把全部路由和 DNS 都切换到 PPPoE,实现自动降级直连
  • (可选)此时 Unifi 上可以再设置一个 DNS hijack,让不遵守 DHCP 下发的 DNS 服务器的设备的所有 UDP 53 端口流量都强制发到 Unifi DNS 服务器:增加 DNAT 规则,匹配来源 IP 非 OpenWrt,目标 IP 非 Unifi,目标 UDP 53,将目标 IP 改写为 Unifi 的 IP

IPv6 与 WireGuard Server 问题

按上述方案配置后,你会发现两个问题:

  1. IPv6 不通:哪怕客户端已经具有了来自 PPPoE 的 v6 地址,但 v6 网络就是不通。这里来到了 Unifi 最逆天的地方了:除非主 WAN 断开,不然在任何情况下都试图让 IPv6 走主 WAN,哪怕主 WAN 都没配 IPv6,导致网络不通。
  2. Unifi 上运行的 WireGuard Server 不通:哪怕 WireGuard Server 上设置了监听的是 WAN2 (PPPoE),只要主 WAN 还在,所有回包都会走主 WAN,导致外界的客户端连不上。

这两个问题都是 Unifi 的双 WAN 功能不完善导致的,必须 SSH 进去并设置自启脚本来修正:

编辑 /data/internet_as_backup_wan_fix.sh ,内容为(按需调整IFACE和WG_PORT):

#!/bin/sh

# ================= Configuration =================
# backup WAN Interface (real Internet, e.g. PPPoE to ISP)
IFACE="ppp1"
# Routing Table ID for backup WAN (Usually 202 for WAN2)
TABLE="202"
# WireGuard Server Listen Port
WG_PORT="16384"
# Log Tag for syslog
LOG_TAG="UniFi_Network_Fixer"
# =================================================

# Function to clean up rules on exit or restart
cleanup() {
    # Remove IPv6 default route
    ip -6 route del default dev "$IFACE" metric 1 2>/dev/null

    # Remove Policy Routing rule based on Source Port
    ip rule del sport "$WG_PORT" lookup "$TABLE" 2>/dev/null

    # Remove Force-SNAT rule from NAT table
    iptables -t nat -D POSTROUTING -o "$IFACE" -p udp --sport "$WG_PORT" -j MASQUERADE 2>/dev/null
}

# Run cleanup on start to ensure a clean slate
cleanup

logger -t "$LOG_TAG" "Starting network fix script..."

while true; do
    # Check if the interface exists
    if ip link show "$IFACE" > /dev/null 2>&1; then

        # -----------------------------------------------------
        # Task 1: Fix IPv6 Direct Access
        # Problem: Unifi doesn't add a default IPv6 route for Secondary WAN.
        # Fix: Manually add a default route to Main Table via ppp1.
        # -----------------------------------------------------
        HAS_V6_ROUTE=$(ip -6 route show default dev "$IFACE" metric 1)
        if [ -z "$HAS_V6_ROUTE" ]; then
            ip -6 route add default dev "$IFACE" metric 1
            logger -t "$LOG_TAG" "IPv6: Added default route via $IFACE"
        fi

        # -----------------------------------------------------
        # Task 2: Fix WireGuard VPN Server on Secondary WAN
        # Problem: Local UDP traffic uses Primary WAN gateway & source IP.
        # Fix A: Policy Routing based on Source Port (Directs traffic to WAN2 Table)
        # -----------------------------------------------------
        if ! ip rule show | grep -q "sport $WG_PORT lookup $TABLE"; then
            # Priority 98 ensures it runs before Unifi's default rules
            ip rule add sport "$WG_PORT" lookup "$TABLE" priority 98
            ip route flush cache
            logger -t "$LOG_TAG" "VPN: Added Policy Routing for Source Port $WG_PORT"
        fi

        # -----------------------------------------------------
        # Fix B: Force SNAT (Masquerade)
        # Problem: Source IP might be internal (e.g., 192.168.x.x) even if routed correctly.
        # Fix: Force NAT on egress to ensure Source IP matches the Public IP.
        # -----------------------------------------------------
        if ! iptables -t nat -C POSTROUTING -o "$IFACE" -p udp --sport "$WG_PORT" -j MASQUERADE 2>/dev/null; then
            # Insert at top (1) to override any conflicting rules
            iptables -t nat -I POSTROUTING 1 -o "$IFACE" -p udp --sport "$WG_PORT" -j MASQUERADE
            logger -t "$LOG_TAG" "VPN: Added Force-SNAT rule for port $WG_PORT"
        fi

    fi

    # Check every 30 seconds to handle re-dials or IP changes
    sleep 30
done

加上执行权限:chmod +x /data/internet_as_backup_wan_fix.sh

然后使用 crontab -e 并添加以下内容,设置开机自启:

@reboot /data/internet_as_backup_wan_fix.sh > /dev/null 2>&1 &

注意:每次底层 Unifi OS 升级后需要重新添加 crontab,因此建议关闭自动更新

为什么一定要用这种方案?

  1. 为什么不能 OpenWrt 直接做二级网关(LAN 侧)? Unifi 不支持 VRRP。如果把 OpenWrt 设为 DHCP 下发的默认网关,一旦 OpenWrt 挂了,Unifi 无法自动把网关切回自己,全家断网。
  2. 为什么不能 OpenWrt 做旁路网关配合 PBR? Unifi 的策略路由(Traffic Routes)只能选 WAN 接口作为出口,不能指定 LAN 侧的某个 IP(OpenWrt)作为下一跳。
  3. 为什么必须让 OpenWrt 充当 Primary WAN?
    如果反过来让 PPPoE 作为 Primary WAN,OpenWrt 作为 Backup WAN,则无法利用双 WAN 的 Failover 功能来应对 OpenWrt 下线的情况,通过策略路由则会有以下问题:
    • 策略路由的 Fallback 功能残废:需要设置策略路由让海外流量走 Backup WAN,但问题是策略路由的 Fallback 与双 WAN Fallback 机制不通,后者会用 ping 检测是否真正可用,而前者只会在相应接口 Down 掉时才会被禁用,导致就算 OpenWrt 挂掉或死机,但接口还在,海外可直连的流量依然无法 Fallback
    • DNS 配置麻烦:默认的 DNS 行为会直接走 PPPoE,造成 DNS 无法抗污染。而若指定走 OpenWrt 的话又难以 Failover,需要 SSH 进去 Unifi 来配置 dnsmasq 为 strict-order 模式,然后使用主-备 DNS 服务器,也很麻烦

附录:在或许还有一种更简单但更不优雅的方案,就是 OpenWrt 或者其他运行代理插件的主机再跑一个 WireGuard Server,然后在 Unifi 上用 VPN Client 连接并用策略路由进行分流。

另外还有网友提到,组一个 OSPF 并向 Unifi 广播需要代理的网段也是一种办法。

UCG Fiber 主路由器 + OpenWrt 透明代理 + 高可用 方案和评测(对比ROS)最先出现在WuSiYu Blog

OpenWrt上AdGuardHome前置配置的一些小坑

2025-12-03 19:46:54

为了配合一些上网插件并达到最好的过滤效果,需要将AdGuardHome前置于OpenWrt自带的dnsmasq:

[设备] -> [AdGuardHome 53端口] -> [dnsmasq 54端口] -> 一些插件 -> 多种DNS服务器

AdGuardHome在这里作为第一级DNS服务器,运行在标准53端口上,直接接受客户端连接。然后其上游为OpenWrt的自带dnsmasq的DNS服务器,这里需要将其端口改为端口54以避免冲突。之后一些上网插件会自动将dnsmasq的上游设置为它的一些程序,最终连接到大陆或外网的DNS。

这一套似乎没什么问题,但其实有一些坑:

坑1: DHCP v4 不下发DNS服务器配置

OpenWrt dnsmasq的DNS服务若运行在未53端口上时,其DHCP v4 服务就不会默认发送DNS选项。导致部分支持IPv6的设备依然可以通过IPv6连接DNS,表现正常,但仅支持IPv4的设备就会无法上网。

解法很简单,需要在OpenWrt -> 网络 -> lan -> DHCP 服务器 -> 高级设置 处,手动强制加入一个DHCP选项来下发DNS服务器配置:6,<路由器IP>

坑2: 在一些插件内,勾选DNS重定向会绕过AdGuardHome

如果你勾选了这个选择,那DNS请求会被强制直接转达到dnsmasq的地址,也就是54端口上,导致AdGuardHome被绕过了。那如果你仍然需要类似的功能呢?可以在 OpenWrt -> 网络 -> 防火墙 -> 端口转发中手动创建一条规则

具体如下:

# /etc/config/firewall
config redirect 'dns_int'
	option name 'Intercept-DNS'
	option family 'any'
	option proto 'tcp udp'
	option src 'lan'
	option src_dport '53'
	option target 'DNAT'

OpenWrt上AdGuardHome前置配置的一些小坑最先出现在WuSiYu Blog

Unraid 7.2+ WebUI美化主题和自定义CSS插件

2025-11-27 04:00:08

由于 Unraid 升级到 7.2+ 版本后,原本常用的 “Theme Engine” 插件已不再兼容,为了继续自定义 WebUI 的样式,我开发了一个轻量级的 CSS 注入插件,并在此基础上发布了一套我个人使用的 “Modernization” 主题。

1. Custom WebUI CSS 插件

这是一个专门为 Unraid 7.2+ 设计的轻量级插件。我的需求很简单:将自定义 CSS 代码注入到网页中。既然旧插件无法使用,我便编写了这个替代方案。

主要功能:

  • 全局 CSS 注入: 将 CSS 代码自动加载到每个页面的 <head> 中。
  • 深色主题附加CSS: 可输入仅在 Unraid 主题设置为 ‘black’ 时加载的额外CSS,用于方便在黑/白主题间兼容和切换。
  • 额外资源文件: 支持从 Flash 到 Web 目录同步额外的静态资源(如背景图片等),可在CSS中引用。

项目地址: https://github.com/WuSiYu/unraid-custom-css

Unraid论坛地址:https://forums.unraid.net/topic/195276-plugin-simple-custom-webui-css-plugin-for-unraid-72

注意: 本插件仅在 Unraid 7.2+ 上测试通过。如果你使用的是 7.1.4 及以下版本,建议继续使用 Theme Engine。

2. Modernization Theme

配合上述插件,我整理了个人使用的 CSS 样式,命名为 Modernization Theme。

设计特点:

  • 现代化外观: 重新设计了 Dashboard 和 Main 等核心页面的视觉体验。
  • 按钮风格: 改善了Unraid原本比较丑的圆角渐变边框设计,现在具有完美的圆角,并在支持的浏览器上还有渐变文字。
  • 黑/白主题兼容: 适配 Unraid 原生的 White 和 Black 两种基础主题。
  • 稳健性: 克制了 CSS 的修改范围,尽量避免破坏第三方插件的显示效果。

3. 安装与使用指南

第一步:安装插件

进入 Unraid 的 PLUGINS -> Install Plugin 页面,输入以下 URL 进行安装:

https://raw.githubusercontent.com/WuSiYu/unraid-custom-css/refs/heads/master/custom.css.plg

(正在等待CA应用商店审核,不出意外等之后也可以直接在上面装)

第二步:输入主题CSS

  1. 安装完成后,前往 SETTINGS -> Custom WebUI CSS
  2. 打开下方链接,复制其中的 CSS 代码: https://github.com/WuSiYu/unraid-custom-css/blob/master/example.css
  3. 将代码粘贴到插件的配置框中,点击应用即可。

本主题有意限制了自定义样式的生效范围,以最大程度避免破坏第三方插件的样式。但如果你在使用过程中依然了遇到任何第三方插件的兼容性问题,欢迎反馈。

Unraid 7.2+ WebUI美化主题和自定义CSS插件最先出现在WuSiYu Blog

杂谈:libvirt/qemu Windows游戏VM的一些优化配置

2025-10-13 01:01:15

一些libvirt配置备忘,针对Windows guest,虚拟机游戏场景

虚拟化功能选项

取自cockpit-machine生成的Windows 10 guest配置

  <features>
    <acpi/>
    <apic/>
    <hyperv mode='custom'>
      <relaxed state='on'/>
      <vapic state='on'/>
      <spinlocks state='on' retries='8191'/>
      <vpindex state='on'/>
      <runtime state='on'/>
      <synic state='on'/>
      <stimer state='on'/>
      <frequencies state='on'/>
      <tlbflush state='on'/>
      <ipi state='on'/>
      <evmcs state='on'/>
      <avic state='on'/>
    </hyperv>
    <vmport state='off'/>
  </features>

CPU feature设置

在CPU节加入一些神奇的选项可以提高CPU-Z跑分,在我的E-2378的单核从450提升到500,物理机为540左右

  <cpu mode='host-passthrough' check='none' migratable='on'>
    <topology sockets='1' dies='1' clusters='1' cores='8' threads='2'/> <!--以实际情况为准-->
    <feature policy='require' name='topoext'/>
    <feature policy='disable' name='vmx'/>
    <feature policy='disable' name='svm'/>
    <feature policy='require' name='apic'/>
    <feature policy='require' name='hypervisor'/>
    <feature policy='require' name='invtsc'/>
  </cpu>

超线程和 CPU Pinning

首先使用lscpu -e查看宿主机超线程CPU序号拓扑,一般是前半都是t0(每个物理核的0号线程),后半都是t1,然后将同一物理核心的宿主机CPU序号分配到相邻的vcpu上,似乎这样在Windows虚拟机中顺序才是正确的(有待进一步验证):

  <vcpu placement='static'>16</vcpu>
  <cputune>
    <vcpupin vcpu='0' cpuset='0'/>
    <vcpupin vcpu='1' cpuset='8'/>
    <vcpupin vcpu='2' cpuset='1'/>
    <vcpupin vcpu='3' cpuset='9'/>
    <vcpupin vcpu='4' cpuset='2'/>
    <vcpupin vcpu='5' cpuset='10'/>
    <vcpupin vcpu='6' cpuset='3'/>
    <vcpupin vcpu='7' cpuset='11'/>
    <vcpupin vcpu='8' cpuset='4'/>
    <vcpupin vcpu='9' cpuset='12'/>
    <vcpupin vcpu='10' cpuset='5'/>
    <vcpupin vcpu='11' cpuset='13'/>
    <vcpupin vcpu='12' cpuset='6'/>
    <vcpupin vcpu='13' cpuset='14'/>
    <vcpupin vcpu='14' cpuset='7'/>
    <vcpupin vcpu='15' cpuset='15'/>
  </cputune>

宿主机优化(可选)

tuned profile选择virtual-host

qemu自动用透明内存大页,也可以显示用手动内存大页,但不足时虚拟机无法启动

杂谈:libvirt/qemu Windows游戏VM的一些优化配置最先出现在WuSiYu Blog

Fedora + libvirt 下在宿主关机时安全关闭虚拟机

2025-07-29 19:36:51

很简单的一个问题,但过程比较难绷,所以特此记录

为了能方便的既跑虚拟机,又跑Docker容器,我经常使用Fedora Server来作为宿主系统,配合红帽官方的Cockpit面板进行管理,其对虚拟机(基于libvirt/qemu/kvm)和容器(官方只支持Podman)的支持尚可。

但其在关机时虚拟机会直接被强制停止,并不安全。我们希望在直接对宿主机进行关机或重启时,其能够像PVE、EXSi等虚拟化系统一样发起并等待虚拟机的安全关机。

首先这个功能libvirt是提供了的,由libvirt-guests.service实现,但不知为何在Fedora并没有被默认启用。

进一步的,我们发现其配置文件/etc/sysconfig/libvirt-guests从某个版本起被移除了(你还能搜到关于这件事的相关讨论,暴躁用户 vs 倔强maintainer),更不方便我们配置了,因此首先恢复其内容:

# URIs to check for running guests
# example: URIS='default xen:/// vbox+tcp://host/system lxc:///'
#URIS=default

# action taken on host boot
# - start   all guests which were running on shutdown are started on boot
#           regardless on their autostart settings                                 
# - ignore  libvirt-guests init script won't start any guest on boot, however,     
#           guests marked as autostart will still be automatically started by      
#           libvirtd                                                               
#ON_BOOT=start                                                                     

# Number of seconds to wait between each guest start. Set to 0 to allow            
# parallel startup.
#START_DELAY=0

# action taken on host shutdown
# - suspend   all running guests are suspended using virsh managedsave
# - shutdown  all running guests are asked to shutdown. Please be careful with
#             this settings since there is no way to distinguish between a
#             guest which is stuck or ignores shutdown requests and a guest
#             which just needs a long time to shutdown. When setting
#             ON_SHUTDOWN=shutdown, you must also set SHUTDOWN_TIMEOUT to a
#             value suitable for your guests.
ON_SHUTDOWN=shutdown

# If set to non-zero, shutdown will suspend guests concurrently. Number of
# guests on shutdown at any time will not exceed number set in this variable.
#PARALLEL_SHUTDOWN=0

# Number of seconds we're willing to wait for a guest to shut down. If parallel
# shutdown is enabled, this timeout applies as a timeout for shutting down all
# guests on a single URI defined in the variable URIS. If this is 0, then there
# is no time out (use with caution, as guests might not respond to a shutdown
# request). The default value is 300 seconds (5 minutes).
#SHUTDOWN_TIMEOUT=300

# If non-zero, try to bypass the file system cache when saving and
# restoring guests, even though this may give slower operation for
# some file systems.
#BYPASS_CACHE=0

注意我这里设置的是ON_SHUTDOWN=shutdown,因为对于有PCIe设备直通的虚拟机suspend可能无法使用。

然后启用libvirt-guests.service服务:

sudo systemctl enable --now libvirt-guests

然后在虚拟机关机时(或libvirt-guests服务关闭时)正在运行的虚拟机应当能正常关机,并且这些虚拟机会在宿主机下次启动时自动启动(恢复状态)。

这里不得不吐槽一下红帽经常搞一些breaking change,之前升级就被Network UPS Tools (NUT)的服务配置坑过一次

Fedora + libvirt 下在宿主关机时安全关闭虚拟机最先出现在WuSiYu Blog