Обновление до 3.2, Center старой линейки: Windows на NVMe
Отдельная работа после обновления кластера до 3.2 (страница «Шаги» пройдена целиком), с согласия владельца ВМ. Скрипты — из архива шага 2, команды sh tools/vapi.sh … — из того же рабочего каталога с теми же VAPI_URL и VAPI_CRED; обозначения и правила ([раб], [узел], csh на узлах) — там же, в начале страницы.
Зачем и почему не просто флаг
После Center 3.2 Windows-ВМ остаются на virtio-blk (vm.machine.nvme = false). Они работают как раньше, но запускаются только на узле своего пула (-460), фабрика им недоступна.
Просто поставить nvme = true нельзя. Пока Windows работает на virtio-blk, она держит запрет загрузки драйвера NVMe: HKLM\SYSTEM\ControlSet00N\Services\stornvme\StartOverride\0 = 3. Такая ВМ с диском NVMe дважды падает с INACCESSIBLE_BOOT_DEVICE и уходит в среду восстановления, перезагрузки не помогают.
Поэтому сначала win-nvme-switch.py снимает запрет прямо на томе выключенной ВМ: 4 байта на значение, перед правкой — ZFS-снимок тома. Потом win-nvme-flag.sql ставит nvme = true.
Что проверено
21–22.10.2026 на тестовом кластере переведены 59 Windows-ВМ на всех четырёх пулах, у каждой один диск: Server 2016, 2019, 2022 и 2025, из них 44 — из старых образов, снятых с действующих кластеров. Каждая после правки загрузилась на NVMe на своём узле, затем по NVMe-oF на другом узле и вернулась на свой. На 22.10, через 2–24 часа после перевода, все 59 работают на NVMe и отвечают по WinRM. У 15 ВМ первого пула дополнительно проверено: запуск на каждом из трёх других узлов, контрольный файл совпал, внутри диск bhyve-NVMe Healthy Online, ошибок дисков в журнале System нет.
У 8 ВМ из 59 до успешной проверки понадобились повторные запуски; итог у них тот же. Одну ВМ перевести не удалось: она не закончила первичную установку Windows (шаг 1, «ВМ в первичной установке»).
Не проверено — это и есть список для пилота на кластере: Server 2012 R2, десктопные Windows, ВМ с несколькими дисками, BitLocker, быстрый запуск и гибернация на живой ВМ.
Что нужно
- Скрипты
tools/win-nvme/из архива («Обзор», «Файлы»):win-nvme-switch.py— на узле пула (версия с поддержкой Windows Server 2025);win-nvme-flag.sql— на mgmtVM;postcheck.ps1— внутри Windows.
- Root на узле, где импортирован пул ВМ. Запуск —
python3.11 -I win-nvme-switch.py(Python 3.11 есть на узле). - Доступ на mgmtVM: флаг меняется только SQL-запросом.
- Разложить файлы, [раб] в
tools/win-nvme:scp win-nvme-switch.py root@<узел пула>:/root/на каждый узел, держащий пул с Windows-ВМ, иscp win-nvme-flag.sql root@<MGMT>:/root/. Команды на узле — из/root.
Первую ВМ кластера проводят через все шаги одну. Остальные — пачками по 3 ВМ одного пула: на тестовом кластере пачка с проверкой на одном чужом узле занимала 5–6 минут, пул из 15 ВМ — около 50 минут, разные пулы шли параллельно. <POOLNODE> — узел, где сейчас импортирован пул ВМ (не путать с <OWN> на странице «Шаги»), <OTHER> — любой другой узел.
Порядок
1. Выключить ВМ штатно
vms-stop Center Windows почти никогда не выключает: агент шлёт ACPI и примерно через 30 секунд убивает bhyve. С hard_timeout: 600 после долгого аптайма Windows игнорирует ACPI все 600 секунд. Убитая ВМ даст BLOCKED, на диск скрипт ничего не запишет. Работают два способа:
-
изнутри Windows: «Завершение работы» или
Stop-Computer -Force(на тестовом кластере — 36 раз из 37); -
без доступа в гостя (6 из 6, ВМ выключилась примерно за 11 секунд):
sh tools/vapi.sh vms-stop '{"id":<id>}'; sh tools/vapi.sh wait <id> 1 # ВМ будет убита — так и надоsh tools/vapi.sh vms-start '{"id":<id>,"node_id":<POOLNODE>}'; sh tools/vapi.sh wait <id> 3# подождать 3 минуты после старта, не дольшеsh tools/vapi.sh vms-stop '{"id":<id>,"hard_timeout":600}'; sh tools/vapi.sh wait <id> 1 900Ушла в Offline ровно через 600 секунд — её убили, повторить.
ВМ в статусе Suspended сначала возобновить, потом выключить.
ВМ в первичной установке. Сначала посмотреть консоль: Windows должна дойти до экрана входа. Пока идёт первичная установка, Windows на ACPI не реагирует, а убитая посреди установки ВМ уже не ставится: на консоли окно «Install Windows: The computer restarted unexpectedly or encountered an unexpected error. Windows installation cannot proceed». Такую ВМ не переводить — пересоздать. create_completed в Center этого не показывает: на тестовом кластере у такой ВМ он был true.
2. Проверить
[<POOLNODE>]: python3.11 -I win-nvme-switch.py check <id> [<id>…]. Без номеров — все Windows-ВМ пулов узла; работающие ВМ дают BLOCKED, так что это безопасно.
| Вердикт | Значит |
|---|---|
READY | можно править |
OK | запрета нет, флаг ставить можно |
BLOCKED | не трогать, причина в отчёте — таблица ниже |
3. Править
[<POOLNODE>]: python3.11 -I win-nvme-switch.py patch <id> [<id>…] --yes. Для каждой READY — снимок <том>@pre-nvme-<дата>, запись, перепроверка. В конце строка ids=…: ВМ, которые можно переводить. Код выхода: 0 — всё прошло; 2 — есть BLOCKED; 1 — ошибка правки (ERROR, разбирать руками, снимок назван в отчёте).
4. Сменить флаг — сразу после шага 3
[mgmt]:
cd /tmp; su -m postgres -c "psql -X -d vstack -v ids=<строка ids= из шага 3>" < /root/win-nvme-flag.sql
Файл подаётся на вход, а не через -f: каталог /root на mgmtVM пользователю postgres не читается (Permission denied). Пример в шапке самого win-nvme-flag.sql — с -f из каталога, доступного postgres; так тоже можно. Ожидается UPDATE N и COMMIT. Транзакция откатится целиком, если хоть одна ВМ не найдена, не Windows или не Offline в Center.
Между шагами 3 и 4 ВМ не запускать. Запуск на virtio-blk вернёт запрет, и после смены флага будет синий экран. Клиент может запустить ВМ из портала, поэтому шаг 4 идёт сразу за шагом 3. SQL ответил «не Offline в Center» — ВМ запускали: выключить по шагу 1 и начать с шага 2.
5. Запуск на своём узле
vapi.sh vms-start '{"id":<id>,"node_id":<POOLNODE>}', vapi.sh wait <id> 3. Экран входа в консоли — примерно через минуту; чёрный экран — Windows погасила монитор, нажать клавишу.
Внутри Windows (PowerShell от администратора) — postcheck.ps1 или:
Get-Disk | Format-Table Number, FriendlyName, HealthStatus, OperationalStatus, IsOffline
Get-WinEvent -FilterHashtable @{LogName='System'; Level=1,2,3; StartTime=(Get-Date).Date} -ErrorAction SilentlyContinue |
Where-Object ProviderName -match 'disk|stornvme|Ntfs|volmgr|BugCheck'
Ожидается: диски bhyve-NVMe, Healthy, Online, ошибок дисков нет. Диск данных Offline — Set-Disk -Number <N> -IsOffline $false. Проверить буквы дисков и службы.
6. Запуск на чужом узле
Выключить по шагу 1, затем vapi.sh vms-start '{"id":<id>,"node_id":<OTHER>}', vapi.sh wait <id> 3; в vm-list — rstart: 1; гость — как в шаге 5. -440 — бюджет RAM Center на <OTHER>: запускать по 2–3 ВМ на свободный узел. Потом вернуть ВМ на <POOLNODE> или оставить. Теперь vms-start без node_id сам выбирает для неё наименее загруженный узел.
7. Снимки
Через несколько дней работы на NVMe — удалить на <POOLNODE>, по одному: zfs list -H -t snapshot -o name | grep @pre-nvme-, затем zfs destroy <том>@pre-nvme-<дата>.
Если BLOCKED
| Причина в отчёте | Что делать |
|---|---|
vstack-vm: состояние …, «ВМ работает», «есть /dev/vmm» | ВМ не выключена — шаг 1 |
| «Том открыт (… spdk_tgt …)» | ВМ работает на другом узле по NVMe-oF (на узле пула vstack-vm при этом показывает Offline). Выключить — шаг 1 |
| «Том открыт» с другим процессом или без процесса | том держит процесс из отчёта fstat или ядро (ctl) — разобраться |
журнал NTFS не закрыт, том NTFS грязный, куст SYSTEM не чистый | ВМ убили, а не выключили — шаг 1 |
| «ВМ приостановлена (Suspended)» | возобновить, выключить — шаг 1 |
в hiberfil.sys сохранено ядро | внутри Windows powercfg /h off, выключить, повторить check |
| «Раздел зашифрован BitLocker», «системный раздел Windows не найден» | снять запрет изнутри Windows (ниже), выключить и сразу шаг 4, вписав ВМ в ids вручную |
| «NTFS не разбирается (… нет $INDEX_ROOT)» на Server 2025 | запущена старая версия скрипта — взять win-nvme-switch.py из архива этого раздела |
нет драйвера stornvme | Windows старше 8.1 / Server 2012 R2 без NVMe — оставить на virtio-blk |
| «тома … на узле нет», «vm.json на этом узле нет» | пул импортирован на другом узле — запускать там |
Снять запрет изнутри Windows (PowerShell от администратора), после этого ВМ выключить и до смены флага не запускать:
Get-ChildItem HKLM:\SYSTEM | Where-Object PSChildName -match '^ControlSet\d{3}$' | ForEach-Object {
$svc = Join-Path $_.PSPath 'Services\stornvme'
Set-ItemProperty -Path $svc -Name Start -Value 0 -Type DWord
Remove-Item -Path (Join-Path $svc 'StartOverride') -Recurse -Force -ErrorAction SilentlyContinue
}
Синий экран после смены флага
Бывает, если флаг поставили без patch или ВМ запускали между шагами 3 и 4. Признаки: два перезапуска bhyve, затем INACCESSIBLE_BOOT_DEVICE и экран «Choose an option». Данные целы.
- В консоли Center нажать «Turn off your PC» — это штатное выключение, журнал NTFS останется чистым.
- Шаги 2–4. Флаг уже
true, поэтому SQL шага 4 ответитUPDATE 0. - Шаг 5.
Откат
- ВМ уже работала на NVMe, а нужен virtio-blk. После загрузки на NVMe Windows ставит такой же запрет драйверу virtio-blk
viostor(на тестовом кластере — у всех 15 ВМ). Выключить по шагу 1, на<POOLNODE>—python3.11 -I win-nvme-switch.py patch <id> --driver viostor --yes(строкиids=в этом режиме нет), на [mgmt] —UPDATE vm.machine SET nvme = false WHERE id = <id>;, затем шаг 5. - Синий экран на NVMe после правки. Выключить,
nvme = false, запустить на virtio-blk. Обратная загрузка не проверялась. zfs rollback <том>@pre-nvme-<дата>— только если ВМ после правки не работала, иначе потеряются её данные.