Files
pve-vm-ops-guide/JOURNEY.md
thug0bin bf11a3aeb4 Initial: PVE VM 원격 제어 가이드 + VirtIO 마이그레이션 함정 분석
- GUIDE.ko.md: VM 원격 제어 (화면 캡처, QGA, qm sendkey, vhd 임포트, DR 이전, 트러블슈팅)
- JOURNEY.md: SATA→VirtIO-SCSI 마이그레이션 실패 연대기 15단계
- 핵심 발견: vioscsi START_TYPE=BOOT_START(0) 승격 필수 (pnputil만으론 부족)
- screenshots: 부팅 검증 화면

검증 환경: PVE 8.1, Win10 Pro 19045, virtio-win 0.1.285
2026-05-19 18:11:26 +09:00

13 KiB

실패의 연대기 (Failure Journal)

Windows VM의 디스크 컨트롤러를 SATA → VirtIO-SCSI로 마이그레이션하는 과정에서 만난 모든 함정의 기록. 진짜 원인 — vioscsi START_TYPE=0 (BOOT_START) 누락 — 을 찾기까지 수 시간을 소비했다. 같은 길을 가는 사람이 이 글을 읽고 30분에 끝낼 수 있길.

작성일: 2026-05-19
작업 환경: Proxmox VE 8.1.3 / Windows 10 Pro 19045 / virtio-win-0.1.285


출발점

목표:

  • SMB에 보관된 vm100.vhd (120GiB virt, 106GiB sparse) Windows 디스크 이미지를 Proxmox VM에 활용
  • 가능하면 SATA 에뮬레이션 대신 VirtIO-SCSI 컨트롤러로 운영해서 성능/효율 확보 (멀티큐, iothread, discard/TRIM)

도구:

  • qm importdisk (vhd → raw zvol 변환)
  • ZFS pool pve-zfs (vol storage)
  • pnputil (Windows 드라이버 등록)
  • qm sendkey, QEMU monitor screendump (원격 시각 확인)

Step 1 — vhd → raw zvol 변환

qm importdisk <VMID> /mnt/smb/vm100.vhd pve-zfs --format raw

결과: pve-zfs:vm-<VMID>-disk-1 생성 (REFER 73.4GB sparse). config에 unused0:로 자동 등록.

관찰: 변환 후 zvol 크기가 120GiB + ~1MB 패딩. fdisk가 GPT PMBR size mismatch 경고. 이때는 대수롭지 않게 넘어감.


Step 2 — sata1로 attach + 부팅 시도

qm set <VMID> --sata1 pve-zfs:vm-<VMID>-disk-1
qm set <VMID> --boot 'order=sata1;sata0;ide0'
qm start <VMID>

결과: Windows 10 데스크톱 정상 진입! 사용자도 다 보임. 데이터 모두 살아있음.

우리가 잘못 알았던 것: "vhd 변환이 잘 됐구나" — 사실은 sata0 (기존에 갖고 있던 vm-101-disk-0 zvol)에서 부팅된 것이었음. Get-Disk로 확인 안 했으니 모름.


Step 3 — 안정화: efidisk 추가, VirtIO 드라이버 등록

부팅이 잘 되니 마스터화 준비. 두 가지 작업 진행.

3-1. efidisk0 추가 (OVMF NVRAM 영구화)

qm set <VMID> --efidisk0 pve-zfs:1,efitype=4m,pre-enrolled-keys=0

이전엔 WARN: no efidisk configured 떠서 매 부팅마다 OVMF가 NVRAM 새로 만들고 fallback path로 부팅했음. 영구화하면 부팅 엔트리 저장됨.

3-2. VirtIO 드라이버 사전 설치

virtio-win.iso를 attach하고 임시 SCSI 디스크도 같이 띄워 게스트가 새 PCI 디바이스 인식하게 만든 다음:

pnputil /add-driver F:\vioscsi\w10\amd64\vioscsi.inf /install
pnputil /add-driver F:\viostor\w10\amd64\viostor.inf /install
pnputil /add-driver F:\NetKVM\w10\amd64\netkvm.inf /install

pnputil이 INF 파일을 driver store에 등록 + /install 옵션이 매칭되는 PnP 디바이스에 즉시 설치. Get-PnpDevice에서 "Red Hat VirtIO SCSI pass-through controller" Status=OK 보임. Get-Service vioscsi → RUNNING.

우리가 잘못 알았던 것: "드라이버 등록 완료, 이제 부팅 디스크 인터페이스 바꿔도 BSOD 안 나겠지" — 틀렸다.


Step 4 — 인터페이스 교체: sata1 → scsi0

깨끗한 마스터를 위해 sata1 detach, scsi0로 같은 zvol attach:

qm set <VMID> --scsi0 pve-zfs:vm-<VMID>-disk-1,iothread=1,discard=on,ssd=1
qm set <VMID> --delete sata1
qm set <VMID> --boot 'order=scsi0'
qm start <VMID>

결과: BSOD 0xc0000225 (Recovery / "필요한 장치가 연결되어 있지 않거나 장치에 액세스할 수 없습니다") — \Windows\system32\config\system 위치 못 찾음.

이때 처음으로 뭔가 잘못됐다고 느낌. 그러나 원인을 GPT signature 충돌로 오해.


Step 5 — 오해의 시작: "GPT signature 충돌이 원인이다"

Get-Disk로 보니 두 디스크 GUID 완전 동일:

Disk 0: SATA, GUID {4bca652d-2f35-4bd7-8175-8951593eb8c1}, 120GB
Disk 1: SAS,  GUID {4bca652d-2f35-4bd7-8175-8951593eb8c1}, 120GB (1MB padding 더 큼)

호스트에서 fdisk -l /dev/zvol/...로 검증 — 두 zvol의 Disk identifier 동일, 모든 partition UUID 동일.

가설 (틀린 것): Windows가 동일 GPT signature 두 디스크 보면 한쪽을 자동 Offline 처리 → sata0 detach 후 scsi0 단독으로는 BCD device path 매칭 실패.

이 가설로 BCD 수정 시도들이 시작됨.


Step 6 — BCD 수정 시도 1: device/osdevice → "boot" 키워드

bcdedit /export C:\bcd-backup-20260519.bcd
bcdedit /set {bootmgr} device boot       # 거부됨 (정상)
bcdedit /set {default} device boot       # 성공
bcdedit /set {default} osdevice boot     # 성공

boot 키워드 의미: "이 BCD가 로드된 partition을 동적으로 추적해서 사용". 매혹적이지만 잘못된 적용.

검증: shutdown → sata0 detach → 부팅 → 여전히 BSOD 0xc0000225.

왜 안 먹혔나: osdevice = boot는 사실 잘못된 의미. winload는 "boot device = system partition (EFI 100MB FAT32)"으로 해석해서 거기서 \Windows\system32\winload.efi 찾으려고 함. EFI partition에 \Windows 없으니 fail.


Step 7 — BCD 수정 시도 2: bcdboot으로 새 BCD 생성

scsi0의 EFI partition을 마운트해서 BCD 재구성:

diskpart 스크립트로 Disk 1 Partition 2에 S: 할당
bcdboot C:\Windows /s S: /f UEFI /v

bcdboot이 정상 동작:

BFSVC: Creating Recovery directory.
BFSVC: Creating OsLoader object.
BFSVC: Setting {default} to {bfc003a5-...}
부팅 파일을 만들었습니다.

새 BCD enum:

{default}.device   = partition=\Device\Harddisk0\Partition4
{default}.osdevice = partition=\Device\Harddisk0\Partition4

검증: shutdown → sata0 detach → 부팅 → 다른 BSOD 0xc0000017 (system registry file missing).

이제 한 단계 더 진행됨: OVMF→boot manager→winload까지 OK, OS partition 도달, 거기서 SYSTEM hive 못 읽음.


Step 8 — NTFS 무결성 검사: ntfsfix

호스트에서 NTFS 마운트:

ntfs-3g /dev/zvol/pve-zfs/vm-<VMID>-disk-1-part4 /mnt/winc -o ro
ls /mnt/winc/Windows/System32/config/SYSTEM   # 24.5MB 존재
hexdump -C -n 16 /mnt/winc/Windows/System32/config/SYSTEM
# 00000000  72 65 67 66 ...   ← "regf" 시그니처 OK
umount /mnt/winc

ntfsfix /dev/zvol/pve-zfs/vm-<VMID>-disk-1-part4
# Processing of $MFT and $MFTMirr completed successfully.
# Checking the alternate boot sector... OK
# NTFS partition was processed successfully.

검증: 부팅 시도 → BSOD 0xc0000017 그대로.

SYSTEM hive 파일은 존재 + 헤더 정상 + NTFS metadata clean. 그러나 부팅 실패.


Step 9 — NVRAM 초기화 시도

가설: OVMF NVRAM에 옛 부트 엔트리(detach된 디스크 가리키는 것)가 캐시되어 fallback 실패.

qm set <VMID> --delete efidisk0
qm set <VMID> --efidisk0 pve-zfs:1,efitype=4m,pre-enrolled-keys=0
qm start <VMID>

결과: 새 NVRAM 만들고 부팅 → BSOD 0xc0000017 그대로.


Step 10 — WinRE Startup Repair 시도

BSOD 화면에 "F1 to enter Recovery Environment" 안내가 있음. qm sendkey <VMID> f1로 시도.

결과: WinRE 진입 안 됨. vhd 백업본에 WinRE WIM이 비활성/누락 상태. 막다른 길.


Step 11 — SSD 마이그레이션식 클론 시도

사용자 제안: "보통은 디스크 두 개 만들고 1:1 복사하는 도구로 옮기잖아, SSD 마이그레이션처럼"

ZFS send/recv로 vhd 변환본을 새 zvol에 클론:

zfs snapshot pve-zfs/vm-<VMID>-disk-1@clone-master
time (zfs send pve-zfs/vm-<VMID>-disk-1@clone-master | zfs recv pve-zfs/vm-<VMID>-disk-4)
# real    10m50.003s

config 교체: 기존 vhd 변환본 detach + 클론을 scsi0로 attach → boot order=scsi0 단독.

결과: BSOD 0xc0000017 — 클론도 동일 증상.

이로써 가설이 확정적으로 깨짐: GPT 충돌이 단독 원인이라면 단일 디스크 상태에서 해결됐어야 함. 그런데 안 됨.


Step 12 — 결정적 진단: sata0 단독 부팅 검증

qm set <VMID> --delete scsi0
qm set <VMID> --sata0 pve-zfs:vm-<VMID>-disk-0,size=120G
qm set <VMID> --boot 'order=sata0'
qm start <VMID>

결과: 정상 부팅 (Windows 10 잠금화면, 0bin 사용자).

이게 결정적. sata0 단독은 부팅 가능 + vhd 변환본 단독은 부팅 불가. 두 zvol 데이터가 GPT signature는 같지만 어딘가 다르다 — 또는 컨트롤러 차이 자체가 원인.

게스트 상태 확인:

Get-Disk         → Disk 0 SATA Online
Get-Partition    → DiskNumber=0 IsBoot=True
Get-Service vioscsi → RUNNING
sc qc vioscsi    → START_TYPE: 3 DEMAND_START   ← 🔥 발견!

진짜 원인 발견: vioscsi 서비스가 등록은 되어 있지만 boot critical(=0)이 아니라 수동 시작(=3). winload가 부팅 시점에 vioscsi.sys를 로드하지 않음 → VirtIO-SCSI 디스크에서 부팅 시 디스크 접근 못 함 → BSOD.

pnputil /add-driver vioscsi.inf /install은 드라이버 파일과 PnP 매칭만 등록. 서비스의 Start 값을 자동으로 boot critical로 승격하지 않는다.


Step 13 — 진짜 해결: vioscsi boot critical 승격

sc config vioscsi start= boot

검증:

sc qc vioscsi   → START_TYPE: 0 BOOT_START
reg query HKLM\SYSTEM\CurrentControlSet\Services\vioscsi /v Start
                → Start REG_DWORD 0x0

Step 14 — Windows 정상 종료 + ZFS 클론 + 마스터화

# Windows ACPI shutdown
qm shutdown <VMID> --timeout 180

# 검증된 sata0 디스크를 새 zvol로 클론
zfs snapshot pve-zfs/vm-<VMID>-disk-0@master-final
time (zfs send pve-zfs/vm-<VMID>-disk-0@master-final | zfs recv pve-zfs/vm-<VMID>-disk-5)
# real    28m43.568s

# config 교체
qm set <VMID> --delete sata0
qm set <VMID> --scsi0 pve-zfs:vm-<VMID>-disk-5,iothread=1,discard=on,ssd=1
qm set <VMID> --scsihw virtio-scsi-single
qm set <VMID> --boot 'order=scsi0'
qm start <VMID>

Step 15 — 검증: 진짜 마스터 완성

Get-Disk         → Disk 0  QEMU QEMU HARDDISK  BusType=SAS  120GB  Online
Get-Partition    → DiskNumber=0 IsBoot=True
sc query vioscsi → RUNNING
sc qc vioscsi    → START_TYPE: 0 BOOT_START

단일 디스크 + virtio-scsi-single + iothread + discard + boot critical = 마스터 완성. Windows 10 잠금화면 정상 진입.


교훈 (lessons learned)

진짜 원인은 vioscsi의 START_TYPE

다른 모든 가설(GPT signature 충돌, BCD 손상, NVRAM 캐시, vhd 변환 미세 손상)은 부분적으로 그럴듯했지만 단독 원인이 아니었다. 진짜 원인은:

vioscsi Service Start=3 (DEMAND_START)
            ↓
winload가 부팅 시점에 vioscsi.sys 안 로드
            ↓
virtio-scsi 디스크에서 부팅 시 디스크 IO 핸들러 부재
            ↓
INACCESSIBLE_BOOT_DEVICE → BSOD 0xc0000017 또는 0xc0000225

pnputil의 한계

  • pnputil /add-driver vioscsi.inf /install
  • 드라이버 파일과 PnP 디바이스 매칭은 잘 한다
  • 그러나 서비스의 boot priority는 건드리지 않는다
  • 그래서 매뉴얼하게 sc config vioscsi start= boot 또는 레지스트리 직접 수정 필요

"vhd → raw 변환본"은 부팅 보증 안 된다

  • qm importdisk로 vhd → raw 변환 시 사이즈에 패딩 추가, GPT backup 위치 미세 차이
  • 검증된 sata 원본을 ZFS 클론하는 게 더 안전
  • vhd는 "백업본"이지 "라이브 부팅 가능 디스크"가 아닐 수 있음

동일 GPT signature 디스크 두 개를 동시 attach하지 말 것

  • Windows가 한쪽을 자동 Offline 처리하지만 NT namespace 매핑/BCD path 종속성으로 detach 시 BSOD
  • 마이그레이션 작업은 단계별로 단독 attach 검증 권장

bcdedit osdevice = boot은 함정

  • "boot manager가 로드된 partition을 동적 추적"이라는 매혹적인 표현
  • 그러나 winload는 그걸 "system partition (EFI)"으로 해석 → \Windows 못 찾음 → BSOD
  • 잘못된 BCD 수정 패턴. 사용하지 말 것.

bcdboot은 깔끔하지만 만능 아님

  • BCD store 새로 생성 + 자기 자신 가리키도록 설정
  • 그러나 partition UUID는 정확히 같은 zvol이라야 매칭됨
  • 동일 GPT signature 디스크가 같이 있을 때는 효과 제한적

부록: 알아두면 좋은 진단 명령

:: 서비스 시작 유형
sc qc vioscsi | findstr START_TYPE
reg query HKLM\SYSTEM\CurrentControlSet\Services\vioscsi /v Start

:: VirtIO 디바이스 인식
powershell "Get-PnpDevice | Where Manufacturer -like '*Red Hat*' | Format-Table -AutoSize"

:: 부팅 디스크
powershell "Get-Disk | Format-Table -AutoSize Number,FriendlyName,BusType,Size"
powershell "Get-Partition | Where IsBoot -eq \$true | Format-List"

:: BCD 진단
bcdedit /enum {bootmgr}
bcdedit /enum {default}

:: driverstore의 OEM INF 등록 상태
pnputil /enum-drivers | findstr /i /c:vioscsi /c:viostor /c:netkvm
# 호스트 (PVE)에서 게스트 디스크 NTFS 검증
mount -t ntfs-3g -o ro /dev/zvol/<pool>/vm-<VMID>-disk-N-part4 /mnt/winc
ls /mnt/winc/Windows/System32/config/SYSTEM
hexdump -C -n 16 /mnt/winc/Windows/System32/config/SYSTEM   # "regf" 시그니처
umount /mnt/winc
ntfsfix /dev/zvol/<pool>/vm-<VMID>-disk-N-part4

# 두 zvol 비교
fdisk -l /dev/zvol/<pool>/vm-<VMID>-disk-N