--- id: 892b6087-8961-486c-b4d9-d3dd46bc2605 kind: CONCEPT slug: qemu-block-backend-forms title: /dev/vda 뒤에 있는 것 — qcow2 · RAW · Host block device topic: storage-virtualization topicName: 스토리지 가상화 project: virtualization status: 초안 basisVersion: QEMU block backend 세 형태 — qcow2 파일 · RAW 파일 · Host block device. libvirt 로 정의한 VM 기준이고 SSOT 가 QEMU 와 libvirt 버전을 적지 않았다 studio: "https://hyeonworks.com/studio/documents/892b6087-8961-486c-b4d9-d3dd46bc2605/edit" sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다 source: - final/document.md#140-vm-boundary를-넘으면-qemu가-등장 - final/document.md#141-qemu가-물리-ssd를-직접-제어하는-것은-아니다 - final/document.md#142-qcow2-host에서는-파일-guest에서는-디스크 - final/document.md#143-qcow2-virtual-size와-실제-host-사용량 - final/document.md#144-raw-image - final/document.md#145-host-block-device를-직접-backend로-사용-가능 - final/document.md#146-실제-연결-확인 - final/document.md#128-전체-구조 - final/document.md#172-storage-virtualization-canonical-flow - final/document.md#174-핵심-claim --- # /dev/vda 뒤에 있는 것 — qcow2 · RAW · Host block device 게스트 안에서 /dev/vda 를 보는 것만으로는 호스트에 무엇이 있는지 알 수 없다. 가상 머신 경계를 넘으면 QEMU 의 virtio-blk 장치 모델과 블록 백엔드가 그 요청을 받는데, 그 백엔드는 qcow2 파일일 수도, RAW 파일일 수도, 호스트의 실제 블록 장치일 수도 있다. 갈래마다 바뀌는 것이 다르다. 파일 백엔드면 게스트 스토리지 계층 아래에 호스트 파일시스템과 블록 계층이 한 겹 더 놓이고, qcow2 면 게스트가 보는 크기와 호스트가 실제로 쓰는 크기가 갈린다. 무엇에 붙어 있는지는 게스트가 아니라 호스트에서 virsh domblklist 로 확인한다. 가상 머신에 디스크를 붙여 본 사람을 독자로 둔다. ## 관계 - **Guest 의 write() 가 virtqueue 에 실리기까지 — VFS · Filesystem · Page Cache · Block Layer · virtio-blk** 게스트 안에서 virtqueue 까지 내려온 요청을 이 글이 이어받는다. - **완료라는 말의 네 가지 뜻 — write · writeback · flush · 전원 장애 생존** 파일 백엔드면 호스트 페이지 캐시가 한 겹 더 생기고, 그 위에서 완료가 무엇을 보장하는지가 갈린다. - **VM 아래에 한 번 더 있는 Host block stack — blk-mq · I/O Scheduler · NVMe** 이 글은 백엔드 형태까지만 본다. 그 아래에서 요청이 어떻게 줄을 서는지는 그 글이 본다. - **Guest 안에서 본 disk 로 backend 를 단정하지 않는다** 이 글이 적은 세 갈래가 그 규칙의 근거다. - **이 호스트의 /dev/vda 는 어떤 backend 에 붙어 있는가** 세 갈래 가운데 어느 것인지 이 장비에서 확인하지 않았다. - **이 disk image 는 qcow2 인가 RAW 인가, 그리고 Guest 가 보는 크기와 Host 가 실제로 쓰는 크기는 얼마나 다른가** 이 글이 든 크기 차이는 SSOT 의 예시 값이고, 이 장비의 값은 그 질문이 잰다. - **disk image 는 최종적으로 어느 Host block device 위에 있는가** 파일 백엔드라면 그 파일이 놓인 호스트 장치까지 따라가야 경로가 끝난다. ## 본문 ## 가상 머신 경계를 넘으면 QEMU 가 받는다 게스트의 `virtio-blk` 가 `virtqueue` 에 게시한 요청은 가상 머신 경계를 넘어 호스트 사용자 공간의 QEMU 로 간다. QEMU 안에서는 `virtio-blk` 장치 모델과 블록 백엔드가 그 요청을 받는다. QEMU 는 게스트에게 가상 블록 장치를 노출하고, 게스트의 가상 I/O 를 호스트 백엔드에 연결한다. SSOT 는 제4부의 전체 구조를 그린 뒤 이 갈림을 핵심 문장 하나로 따로 뽑아 두었다. > Guest는 `/dev/vda`를 실제 block device처럼 보지만, Host에서는 그 disk가 qcow2 파일, RAW 파일, 또는 실제 block device에 연결되어 있을 수 있다. 이 글이 다루는 것이 그 문장이 남긴 세 갈래다. ## QEMU 가 물리 SSD 를 직접 제어하지는 않는다 백엔드가 `qcow2` 파일이면 QEMU 는 결국 호스트 Linux 에 파일 I/O 를 요청한다. `pread` 나 `pwrite` 같은 호출이 호스트 커널로 들어가고, 그 뒤로 호스트 파일시스템과 호스트 블록 계층, NVMe 드라이버를 지나 물리 NVMe 에 닿는다. 그 경로가 호스트 커널을 한 번 더 지나기 때문에, 게스트 스토리지 계층 아래에 호스트 스토리지 계층이 한 번 더 존재할 수 있다. ## 같은 대상을 호스트는 파일로, 게스트는 디스크로 본다 호스트에 `/var/lib/libvirt/images/keycloak-node1.qcow2` 라는 파일이 있다고 하자. 호스트 관점에서 그것은 파일 하나인데, 같은 대상을 게스트는 `/dev/vda` 라는 디스크로 보고 그 안을 `/dev/vda1` 과 `/dev/vda2` 로 나눠 쓴다. 두 관점 모두 맞다. ## qcow2 는 게스트가 보는 크기와 호스트가 쓰는 크기가 다르다 `qcow2` 는 가상 디스크 크기와 실제 호스트 할당량이 다를 수 있다. SSOT 가 든 예시에서는 게스트가 `/dev/vda` 를 100 GB 로 보는 동안 호스트의 `vm1.qcow2` 가 3GB 를 쓴다. 게스트가 데이터를 기록하면서 가상 크기 100GB 는 그대로인 채 실제 할당량이 1GB 에서 10GB 로, 다시 40GB 로 늘 수 있다. 이 숫자들은 SSOT 가 설명하려고 든 값이고 이 호스트에서 잰 값이 아니다. ```bash label="qcow2 의 virtual size 와 실제 할당량" qemu-img info vm1.qcow2 ``` 출력의 `virtual size` 와 실제 할당량은 서로 다른 값이다. ## RAW 와 qcow2 를 성능으로 먼저 가르지 않는다 RAW 는 `qcow2` 보다 구조가 단순하다. 게스트의 블록 요청이 `qcow2` 에서는 QEMU 의 매핑과 메타데이터 처리를 지나 `qcow2` 파일 I/O 가 되고, RAW 에서는 상대적으로 직접적인 오프셋 대응을 지나 RAW 파일 I/O 가 된다. `qcow2` 는 Copy-on-Write 와 sparse allocation, 스냅샷에 유리하지만 그 대신 매핑과 메타데이터 처리가 붙는다. 그렇다고 RAW 가 무조건 빠르고 `qcow2` 가 무조건 느리다고 일반화하면 안 된다. SSOT 는 실제 성능이 캐시 모드와 스토리지 백엔드, 작업 부하 패턴, 큐 깊이, 스냅샷 체인, 그 아래 파일시스템, 물리 장치에 영향을 받는다고 적었다. 형식 이름만으로는 어느 쪽이 빠른지 정해지지 않는다. ## 백엔드가 파일이 아니라 호스트 블록 장치일 수도 있다 백엔드는 파일이 아니어도 된다. 게스트의 `/dev/vda` 가 `virtio-blk` 와 QEMU 를 지나 호스트의 `/dev/nvme0n1p3` 같은 블록 장치에 바로 연결될 수 있고, SSOT 가 그린 이 경로에는 호스트 파일시스템이 없다. 같은 이름 아래에서 경로가 세 갈래로 갈리기 때문에, 게스트에 `/dev/vda` 가 있다는 정보만으로는 백엔드 구조를 알 수 없다. ## 세 갈래가 각각 바꾸는 것 | 호스트에서는 무엇인가 | 그래서 무엇이 달라지나 | |---|---| | `qcow2` 파일 | 게스트 파일시스템 아래에 호스트 파일시스템과 블록 계층이 한 겹 더 놓인다. Copy-on-Write 와 sparse allocation, 스냅샷을 쓸 수 있고 매핑과 메타데이터 처리가 붙는다. 게스트가 보는 가상 크기와 호스트 실제 할당량이 갈린다 | | RAW 파일 | 게스트 파일시스템 아래에 호스트 파일시스템과 블록 계층이 한 겹 더 놓인다. 블록 위치가 파일 오프셋에 상대적으로 직접 대응한다 | | 호스트 블록 장치 | SSOT 가 그린 경로에 호스트 파일시스템이 없다 | ## 무엇에 붙어 있는지는 호스트에서 확인한다 게스트에서 `lsblk` 로 장치를 보고, 호스트에서 `virsh domblklist` 로 그 장치의 `Source` 를 본다. ```bash label="게스트가 보는 block device" lsblk ``` ```bash label="호스트에서 본 VM 의 disk 연결" virsh domblklist ``` ```text label="SSOT 가 든 출력 예시. 이 호스트에서 실행한 결과가 아니다" Target Source ----------------------------------------------- vda /var/lib/libvirt/images/vm1.qcow2 ``` 이 출력이 나오면 게스트의 `/dev/vda` 가 `virtio-blk` 와 QEMU 를 지나 `/var/lib/libvirt/images/vm1.qcow2` 에 닿는 연결이 확인된다. ## 확인하지 못한 것 이 호스트의 백엔드가 셋 중 무엇인지는 SSOT 에 없다. SSOT 가 열린 질문 넷을 적어 두었는데, 그 확인을 아직 돌리지 않았다. `/dev/vda` 가 어떤 백엔드에 붙어 있는지, 백엔드가 `qcow2` 인지 RAW 인지, `qcow2` 의 가상 크기와 실제 호스트 사용량이 얼마나 다른지, 그 이미지가 최종적으로 어느 호스트 블록 장치 위에 있는지 넷이다. 앞에서 든 100 GB 와 3GB, 1GB 에서 40GB 까지의 증가는 SSOT 가 설명하려고 든 예시 값이다. `qcow2` 와 RAW 의 성능 비교도 이 저장소에 없다. SSOT 는 실제 성능을 가르는 조건만 열거했고 어느 쪽이 이 환경에서 빠른지는 재지 않았다.