logo NodeSeekbeta

飞牛 OS 虚拟机开机自启动通用教程

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

QqeIg2Ja99uS6mbLaLF4RYp6F7Y3xdC0.webp

适用于使用 systemd 和 libvirt/virsh 管理虚拟机的飞牛 OS。
OpenWrt、Linux、Windows 等虚拟机均可使用,同一台飞牛设备也可以配置多台虚拟机。

最简使用顺序:执行第三部分安装一次通用组件,然后针对每台虚拟机执行第四部分。

一、为什么开启自启动后仍然没有启动

飞牛 OS 开机时,虚拟机服务 libvirt 有时会早于存储卷挂载完成。

如果虚拟机磁盘位于 /vol1、/vol2 等存储卷中,libvirt 尝试自启动时可能暂时找不到 .qcow2 或其他虚拟磁盘,于是启动失败。存储卷稍后虽然已经挂载,但系统通常不会自动再尝试一次。

本教程增加一个通用的延迟启动机制:

  1. 开机后延迟触发。
  2. 等待 libvirt 和虚拟磁盘可用,默认最多等待 10 分钟。
  3. 虚拟机已经运行时不做任何操作,不会重复启动或重启。
  4. 虚拟机未运行时才执行启动。

二、准备工作

使用 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缩短一下,太多了~ yct022

  • @AI米玩 #1

    我也搞不懂怎么要那么复杂吗 为啥飞牛OS的虚拟机不能开机自启

    有问题和图上一样就直接把SSH交给AI去弄了 一般也不会手动的

你好啊,陌生人!

我的朋友,看起来你是新来的,如果想参与到讨论中,点击下面的按钮!

📈用户数目📈

目前论坛共有72267位seeker

🎉欢迎新用户🎉