今天第一次折腾飞牛OS上虚拟机 关机后自启不了 叫AI弄了 但也记录一下万一需要手动

适用于使用
systemd和libvirt/virsh管理虚拟机的飞牛 OS。
OpenWrt、Linux、Windows 等虚拟机均可使用,同一台飞牛设备也可以配置多台虚拟机。
最简使用顺序:执行第三部分安装一次通用组件,然后针对每台虚拟机执行第四部分。
一、为什么开启自启动后仍然没有启动
飞牛 OS 开机时,虚拟机服务 libvirt 有时会早于存储卷挂载完成。
如果虚拟机磁盘位于 /vol1、/vol2 等存储卷中,libvirt 尝试自启动时可能暂时找不到 .qcow2 或其他虚拟磁盘,于是启动失败。存储卷稍后虽然已经挂载,但系统通常不会自动再尝试一次。
本教程增加一个通用的延迟启动机制:
- 开机后延迟触发。
- 等待
libvirt和虚拟磁盘可用,默认最多等待 10 分钟。 - 虚拟机已经运行时不做任何操作,不会重复启动或重启。
- 虚拟机未运行时才执行启动。
二、准备工作
使用 FinalShell、SSH 或其他终端连接飞牛 OS,然后确认当前账号可以使用 sudo。
列出所有虚拟机:
sudo virsh list --all
示例输出:
Id Name State
------------------------------
- abc123xyz shut off
记下 Name 列中的名称。飞牛网页中显示的备注名称,可能与这里的 libvirt 内部名称不同,后续命令必须使用 virsh list --all 显示的名称。
三、安装通用延迟启动组件
下面整段命令在每台飞牛 OS 上只需执行一次。复制全部内容,粘贴到终端执行:
sudo -v
sudo tee /usr/local/sbin/fnos-vm-autostart >/dev/null <<'SCRIPT'
#!/bin/bash
set -u
DOMAIN="${1:-}"
MAX_WAIT="${2:-600}"
INTERVAL=5
log_message() {
local message="$*"
printf '%s\n' "$message"
logger -t fnos-vm-autostart "$message" 2>/dev/null || true
}
if [ -z "$DOMAIN" ]; then
log_message "错误:没有提供虚拟机名称"
exit 2
fi
deadline=$((SECONDS + MAX_WAIT))
# 等待 libvirt 就绪并能够读取虚拟机定义。
while ! virsh dominfo "$DOMAIN" >/dev/null 2>&1; do
if (( SECONDS >= deadline )); then
log_message "超时:等待 libvirt 或虚拟机 $DOMAIN 可用"
exit 1
fi
sleep "$INTERVAL"
done
# 从虚拟机定义中读取本地磁盘,并等待所在存储卷完成挂载。
while true; do
mapfile -t disks < <(
virsh domblklist "$DOMAIN" --details 2>/dev/null |
awk '$2 == "disk" && $4 ~ /^\// {print $4}'
)
missing=()
for disk in "${disks[@]}"; do
[ -e "$disk" ] || missing+=("$disk")
done
if (( ${#missing[@]} == 0 )); then
break
fi
if (( SECONDS >= deadline )); then
log_message "超时:虚拟机 $DOMAIN 的磁盘仍不可用:${missing[*]}"
exit 1
fi
sleep "$INTERVAL"
done
# virsh list --name 会列出所有处于活动状态的虚拟机,包括暂停状态。
if virsh list --name 2>/dev/null | grep -Fqx -- "$DOMAIN"; then
log_message "虚拟机 $DOMAIN 已经在运行,无需启动"
exit 0
fi
log_message "正在启动虚拟机 $DOMAIN"
virsh start "$DOMAIN"
SCRIPT
sudo chmod 755 /usr/local/sbin/fnos-vm-autostart
sudo tee /etc/systemd/system/[email protected] >/dev/null <<'SERVICE'
[Unit]
Description=Delayed autostart for fnOS VM %I
After=libvirtd.service
Wants=libvirtd.service
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/fnos-vm-autostart "%I" 600
TimeoutStartSec=660
SERVICE
sudo tee /etc/systemd/system/[email protected] >/dev/null <<'TIMER'
[Unit]
Description=Delayed autostart timer for fnOS VM %I
[Timer]
OnBootSec=20s
AccuracySec=1s
Unit=fnos-vm-autostart@%i.service
[Install]
WantedBy=timers.target
TIMER
sudo systemctl daemon-reload
echo "通用延迟启动组件安装完成"
四、给一台虚拟机启用开机自启动
先把下面第一行的 虚拟机内部名称 替换成 sudo virsh list --all 中显示的名称,然后执行整段命令:
VM_NAME='虚拟机内部名称'
INSTANCE="$(systemd-escape "$VM_NAME")"
sudo virsh dominfo "$VM_NAME" >/dev/null &&
sudo virsh autostart "$VM_NAME" &&
sudo systemctl enable "fnos-vm-autostart@${INSTANCE}.timer" &&
sudo systemctl start "fnos-vm-autostart@${INSTANCE}.timer"
例如,虚拟机内部名称为 abc123xyz:
VM_NAME='abc123xyz'
INSTANCE="$(systemd-escape "$VM_NAME")"
sudo virsh dominfo "$VM_NAME" >/dev/null &&
sudo virsh autostart "$VM_NAME" &&
sudo systemctl enable "fnos-vm-autostart@${INSTANCE}.timer" &&
sudo systemctl start "fnos-vm-autostart@${INSTANCE}.timer"
说明:
virsh autostart保留 libvirt 原生自启动。- systemd 定时器会在开机后再次检查,解决存储卷挂载较晚导致的首次启动失败。
- 如果当前虚拟机已经运行,脚本只会记录“已经在运行”,不会重启虚拟机。
五、立即测试
执行以下命令不会重启正在运行的虚拟机:
VM_NAME='虚拟机内部名称'
INSTANCE="$(systemd-escape "$VM_NAME")"
sudo systemctl start "fnos-vm-autostart@${INSTANCE}.service"
sudo systemctl status "fnos-vm-autostart@${INSTANCE}.service" --no-pager -l
sudo virsh domstate "$VM_NAME"
正常情况下,服务状态会显示成功,日志中会出现以下两种信息之一:
虚拟机 xxx 已经在运行,无需启动
或:
正在启动虚拟机 xxx
Domain 'xxx' started
查看本次开机的详细日志:
VM_NAME='虚拟机内部名称'
INSTANCE="$(systemd-escape "$VM_NAME")"
sudo journalctl -u "fnos-vm-autostart@${INSTANCE}.service" -b --no-pager
检查定时器是否已启用:
sudo systemctl list-timers --all 'fnos-vm-autostart@*'
最终验证方法是在方便的时候正常重启飞牛 OS,然后执行:
sudo virsh list --all
目标虚拟机的状态应为 running。
六、配置多台虚拟机
通用组件只安装一次。每增加一台需要自启动的虚拟机,重新执行第四部分即可:
VM_NAME='另一台虚拟机的内部名称'
INSTANCE="$(systemd-escape "$VM_NAME")"
sudo virsh autostart "$VM_NAME"
sudo systemctl enable --now "fnos-vm-autostart@${INSTANCE}.timer"
每台虚拟机会拥有独立的定时器和日志,彼此不会互相影响。
七、取消某台虚拟机的自启动
VM_NAME='虚拟机内部名称'
INSTANCE="$(systemd-escape "$VM_NAME")"
sudo systemctl disable --now "fnos-vm-autostart@${INSTANCE}.timer"
sudo virsh autostart --disable "$VM_NAME"
这只取消指定虚拟机,不影响其他虚拟机。
八、完全删除通用组件
请先按上一节取消所有虚拟机的定时器,再执行:
sudo rm -f /usr/local/sbin/fnos-vm-autostart
sudo rm -f /etc/systemd/system/[email protected]
sudo rm -f /etc/systemd/system/[email protected]
sudo systemctl daemon-reload
sudo systemctl reset-failed
九、常见问题
1. 提示找不到虚拟机
再次执行:
sudo virsh list --all
必须使用 Name 列中的内部名称,不要直接填写飞牛网页里的备注名。
2. 日志提示磁盘超时
先查看磁盘路径:
sudo virsh domblklist '虚拟机内部名称' --details
再确认对应的 /vol1、/vol2 等存储卷是否正常挂载:
df -h
默认最长等待 600 秒。如设备挂载存储需要更长时间,可编辑:
sudo nano /etc/systemd/system/[email protected]
将下面两处时间适当增大,例如改为 900 秒:
ExecStart=/usr/local/sbin/fnos-vm-autostart "%I" 900
TimeoutStartSec=960
保存后执行:
sudo systemctl daemon-reload
3. 飞牛系统升级后失效
先检查文件和定时器:
ls -l /usr/local/sbin/fnos-vm-autostart
ls -l /etc/systemd/system/fnos-vm-autostart@.*
sudo systemctl list-unit-files 'fnos-vm-autostart@*'
如果文件被系统升级删除,重新执行第三部分和第四部分即可。
4. 删除并重建了虚拟机
重建后内部名称通常会变化。先取消旧名称对应的定时器,再使用新名称重新执行第四部分。
十、使用注意
- 不要把网页显示名称当成
virsh内部名称。 - 脚本不会关闭、重启或强制停止正在运行的虚拟机。
- 修改配置后先使用“立即测试”,确认无误后再择机重启 NAS。
- 本方案依赖
systemd、libvirt、virsh和 Bash;如果系统版本不包含这些组件,需要按实际虚拟化环境调整。 - 飞牛 OS 大版本升级后,建议检查一次定时器和虚拟机状态。
让codex缩短一下,太多了~
@AI米玩 #1
我也搞不懂怎么要那么复杂吗 为啥飞牛OS的虚拟机不能开机自启
有问题和图上一样就直接把SSH交给AI去弄了 一般也不会手动的