feat(pipeline): keycloak-session-store 25편·virtualization 59편을 S3→S5→S6 으로 돌린다

기록 84편을 계약 에이전트로 다시 썼다. 기존 71편(kss 25 · virt 46)과, 계약에만
있고 안 쓰여 있던 새 글감 13편이다. 원장 84개를 열어 단계마다 스킬 영수증과 관문
종료 코드를 적었고 verify-pipeline-run.py 가 error 0 으로 닫는다.

SSOT 결함 둘을 고쳤다.

- kss 의 `약 58일` 이 반입 중 `약 59일` 로 바뀌어 있었다. 원 증거 파일이
  「남은 일수: 88일 … 실제 갱신까지 약 58일」로 산수를 직접 적는다. D-4a 쪽
  `약 59일` 은 강제 갱신 뒤(`VALID: 89 days`)라 맞는 값이라 그대로 뒀다.
- virt §198 의 `11.6GB` 는 §178 의 원 측정 `Mem: 11648`(MiB)과 어긋나는데
  원 가이드의 표기 그대로라 고치지 않고 쓰이는 자리에 대조를 적었다.

기록의 수치 오류 셋을 고쳤다 — CASE 요약의 「게스트 셋에 8240MB」(5120+3120 은
둘이다), k3s 편이 같은 것을 여섯·일곱·여덟로 세던 것, no-docker 편의 「셋을 더
든다」(§281 의 표는 네 행이고 디스크 행이 빠져 있었다).

계약을 셋 고쳤다.

- kss 의 sourceRepository 리비전이 cdac9b8 이었는데 그 커밋에는 docs/guides/**
  28개가 아예 없다. 9465582b 로 바꾸고, 반입한 바이트가 어느 커밋과도 같지 않다는
  것을 측정값과 함께 적었다 — 반입은 커밋이 아니라 그 시점의 작업 트리에서 떠 온
  것이다(kss 297/306 · virt 12/14 가 작업 트리와 같고, 200 커밋을 거슬러 전수
  대조했을 때 가장 가까운 커밋도 28개가 어긋났다).
- virt 계약이 「2026-09-11 재배분」이라고 적는데 SSOT 는 재배분 날짜를 적지 않고
  재배분 뒤 값은 이미 2026-09-10 측정에 찍혀 있다.
- kss 후보 대장이 지나친 절 아홉에 처분을 적었다(warn 9 → 0). 새 글감은 0건이고
  넷은 앵커가 h3 슬러그의 접두가 아니라 중간 토막이라 검사기가 못 본 것이었다.

style_profile.mjs 의 결함 둘을 고쳤다 — frontmatter 가 문장으로 세어져
(실측 398자짜리 「문장」 하나) 평균 길이를 기준 안으로 밀어 올리고 있었고,
engPerSent 의 분자는 목록을 포함한 글에서, 분모는 목록을 걷어낸 글에서 세고
있었다(Question 기록에서 11.94 → 3.86).

verify-pipeline.py 전 항목 PASS · error 0 · unittest 334건 OK.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
DongHyeonka
2026-09-17 11:01:55 +09:00
co-authored by Claude Opus 5
parent d473609e0a
commit 2109f726fe
574 changed files with 159654 additions and 1551 deletions
@@ -7,7 +7,7 @@ topic: storage-virtualization
topicName: 스토리지 가상화
project: virtualization
status: 초안
basisVersion: QEMU/KVM 의 virtio-blk frontend 와 virtqueue · 게스트가 ext4 나 XFS 에 buffered I/O 로 쓰는 경우 · SSOT 가 커널과 QEMU 버전을 적지 않았다
basisVersion: QEMU/KVM 의 virtio-blk frontend 와 virtqueue · 게스트가 ext4 나 XFS 에 buffered I/O 로 쓰는 경우 · 제4부가 커널과 QEMU 판을 고정하지 않았다
studio: "https://hyeonworks.com/studio/documents/3b05c1f0-13e9-414a-97a6-2078fb4bab26/edit"
assets:
- key: guest-block-io-to-virtqueue
@@ -35,7 +35,7 @@ source:
# Guest 의 write() 가 virtqueue 에 실리기까지 — VFS · Filesystem · Page Cache · Block Layer · virtio-blk
가상 머신 안의 PostgreSQL 이 파일에 데이터를 기록하면, 그 요청은 게스트 Linux 안에서만 여러 계층을 지난 뒤에야 가상 머신 경계에 닿는다. VFS 가 열린 파일을 어느 파일시스템 구현으로 보낼지 정하고, ext4 나 XFS 가 파일을 블록 공간에 배치하고, 페이지 캐시가 dirty page 로 받아 두었다가 나중에 writeback 한다. 이어서 블록 I/O 계층이 그 내용을 READ 와 WRITE, FLUSH 요청으로 바꾸고, 마지막에 virtio-blk 드라이버가 Virtio 블록 요청을 만들어 virtqueue 에 게시한다. 계층 이름과 게스트·호스트 구분을 아는 사람을 독자로 둔다. Keycloak 멀티 노드 실험을 가상 머신 두 대 위에서 돌리고 있다 보니, 이 경로를 알아 두면 게스트에서 본 저장소 지연을 애플리케이션 문제와 가상 머신 경계 아래의 문제로 갈라 볼 수 있다.
가상 머신 안의 PostgreSQL 이 파일에 데이터를 기록하면, 그 요청은 게스트 Linux 안에서만 VFS 와 ext4·XFS, 페이지 캐시, 블록 I/O 계층, virtio-blk 를 지난 뒤에야 virtqueue 에 실려 가상 머신 경계에 닿는다. 계층마다 무엇을 하고 무엇이 바뀌는지 차례로 살펴본다. 계층 이름과 게스트·호스트 구분을 아는 사람을 독자로 둔다. Keycloak 멀티 노드 실험을 가상 머신 두 대 위에서 돌리고 있다 보니, 이 경로를 알아 두면 게스트에서 본 저장소 지연을 애플리케이션 문제와 가상 머신 경계 아래의 문제로 갈라 볼 수 있다.
## 관계
@@ -48,7 +48,7 @@ source:
- **Guest 안에서 본 disk 로 backend 를 단정하지 않는다**
게스트에 /dev/vda 가 보인다는 관측이 그 규칙의 근거가 된다.
- **이 호스트의 /dev/vda 는 어떤 backend 에 붙어 있는가**
이 글은 게스트가 보는 이름까지만 말한다. 이 장비에서 그 이름 무엇어 있는지는 아직 확인하지 않았다.
이 글은 게스트가 보는 이름까지만 말한다. 이 장비에서 그 이름 무엇 붙는지는 그 물음이 출력으로 확정한다.
- **Guest 의 fsync() 지연과 Host storage 지연은 같이 오르는가**
경로는 적었지만 각 구간의 지연을 이 호스트에서 재지 않았다.
- **Guest 의 packet 이 Host Physical NIC 에 닿기까지 — virtio-net · virtqueue · vhost-net · TAP · Bridge**
@@ -100,7 +100,7 @@ SSD 는 `/var/lib/postgresql/data` 같은 디렉터리 구조를 모른다. 저
## 블록 I/O 계층이 파일 세계를 블록 장치 세계로 옮긴다
파일시스템이 파일과 블록 할당을 관리하면, Linux 의 블록 I/O 서브시스템이 그 요청을 아래의 블록 장치 드라이버가 처리할 수 있는 I/O 요청으로 바꿔 전달한다. 파일시스템 세계에서 `/users/data.db` 의 오프셋 8192 에 4KB 를 쓰라고 내려온 것이, 블록 장치 세계에서는 `/dev/vda` 의 특정 위치에 `READ` 나 `WRITE`, `FLUSH` 를 거는 요청이 된다. 대표 요청은 넷이다 — `READ`, `WRITE`, `FLUSH`, `DISCARD`.
파일시스템이 파일과 블록 할당을 관리하면, Linux 의 블록 I/O 서브시스템이 그 요청을 아래의 블록 장치 드라이버가 처리할 수 있는 I/O 요청으로 바꿔 전달한다. 파일시스템 세계에서 내려오는 것은 `/users/data.db` 의 오프셋 8192 에 4KB 를 쓰라는 요청이다. 블록 장치 세계에서는 그것이 `/dev/vda` 의 특정 위치에 `READ` 나 `WRITE`, `FLUSH` 를 거는 요청이 된다. 대표 요청은 넷이다 — `READ`, `WRITE`, `FLUSH`, `DISCARD`.
이 계층 안에는 `bio` 와 `request`, `queue`, `blk-mq` 가 실제로 있다. SSOT 는 `blk-mq` 의 tag allocator 같은 내부 구현을 별도 문서로 미뤘고 이 글도 이름까지만 적는다.
@@ -119,7 +119,9 @@ vda 100G disk
└─vda2 99G part /
```
게스트에 이렇게 보여도 그 장치가 호스트의 실제 SSD 인지는 게스트 안에서 알 수 없다. 이 장비의 `/dev/vda` 무엇 붙어 있는지도 아직 확인하지 않았다. 게스트가 보는 것은 `/dev/vda` 에서 파티션으로, 파티션에서 파일시스템으로, 파일시스템에서 마운트 지점으로 이어지는 연결까지다. `cd /var/lib/postgresql` 로 디렉터리를 옮기는 동작은 파일시스템 세계를 지나고, `lsblk` 의 `vda` 는 블록 장치 세계를 보여 준다.
게스트에 이렇게 보여도 그 장치가 호스트의 실제 SSD 인지는 게스트 안에서 알 수 없는데, 이 장비의 `/dev/vda` 무엇 붙어 있는지를 `virsh domblklist` 로 읽은 출력도 아직 없다. 게스트가 보는 것은 `/dev/vda` 에서 파티션으로, 파티션에서 파일시스템으로, 파일시스템에서 마운트 지점으로 이어지는 연결까지다. `cd /var/lib/postgresql` 로 디렉터리를 옮기는 동작은 파일시스템 세계를 지나고, `lsblk` 의 `vda` 는 블록 장치 세계를 보여 준다.
SSOT 의 실험대 배치 절이 2026-09-03 에 그려 둔 이 실험대의 게스트는 위 예시와 다르게 생겼다. `vda` 는 20G 이고 루트를 `ext4` 로 담아 거기서 부팅하는데, 그 옆의 `vdb` 는 370K 짜리 `iso9660` 이고 레이블이 `CIDATA` 라 마운트되지 않는다. `/dev/vdb` 가 붙어 있다고 게스트가 그것을 파일시스템으로 쓰는 것은 아니다.
이름 둘이 가리키는 대상도 다르다. `/dev/vda` 가 게스트 Linux 에 보이는 블록 장치를 가리키고, `virtio-blk` 는 그 가상 블록 장치를 제어하는 게스트 커널 드라이버를 가리킨다. 네트워크 쪽의 `ens3` 와 `virtio-net` 이 같은 짝이다.
@@ -156,6 +158,6 @@ SSOT 는 이 표를 학습용 대응으로 두었고, 두 열의 각 요소가 `
SSOT 는 이 부에서 다룰 것과 미룰 것을 먼저 갈라 두었다. `qcow2` 내부의 L1/L2 테이블과 `blk-mq` 의 tag allocator, NVMe 의 submission/completion 큐는 필요할 때 별도 문서에서 다루기로 했다.
호스트에서 잰 값은 하나도 없다. 게스트의 `/dev/vda` 가 호스트에서 무엇에 붙어 있는지는 SSOT 가 적어 둔 열린 질문 가운데 첫 번째인데, 아직 확인하지 않았다. 게스트 파일시스템이 `ext4` 인지 `XFS` 인지도 SSOT 어디에도 없어서, 이 글은 두 파일시스템이 같은 계층을 지난다는 데까지만 말한다. 요청 수나 지연을 재려면 게스트의 `fsync()` 지연과 호스트 저장소 지연을 같은 시간축에서 함께 보는 관찰이 먼저 돌아야 한다.
경로를 이 호스트에서 재 본 값은 없다. 게스트의 `/dev/vda` 가 호스트에서 무엇에 붙어 있는지는 SSOT 가 적어 둔 열린 질문 가운데 첫 번째인데, `virsh domblklist` 로 그 대응을 읽은 출력이 아직 없다. 게스트 파일시스템으로 남아 있는 것은 SSOT 의 실험대 배치 절이 `vda` 의 루트를 `ext4` 로 적어 둔 2026-09-03 스냅샷 하나뿐이고, 이 글은 `ext4` 와 `XFS` 가 같은 계층을 지난다는 데까지만 말한다. 요청 수나 지연을 재려면 게스트의 `fsync()` 지연과 호스트 저장소 지연을 같은 시간축에서 함께 보는 관찰이 먼저 돌아야 한다.
<!-- body:end -->
@@ -26,9 +26,10 @@ source:
- final/document.md#141-qemu가-물리-ssd를-직접-제어하는-것은-아니다
- final/document.md#172-storage-virtualization-canonical-flow
---
# VM 아래에 한 번 더 있는 Host block stack — blk-mq · I/O Scheduler · NVMe
게스트가 디스크라고 부르는 것이 호스트에서는 qcow2 나 RAW 파일일 수 있고, 그러면 QEMU 의 쓰기는 호스트 파일시스템을 거쳐 호스트 블록 I/O 가 된다. 호스트 블록 계층은 그 요청이 가상 머신 안의 PostgreSQL 에서 시작했는지 호스트 프로세스에서 시작했는지를 본질적으로 구분해 처리하는 계층이 아니다. 들어온 것은 모두 호스트 블록 요청이기 때문에 가상 머신 두 대와 호스트의 Nginx 와 나머지 프로세스 같은 큐로 들어온다. blk-mq 가 CPU 마다 큐를 두어 병렬로 처리하고, I/O 스케줄러가 들어온 순서 그대로 장치에 전달하지 않을 수 있다. 여기서 생기는 경쟁이 스토리지 경쟁(Storage Contention)이고, 호스트 논리 CPU 실행 시간을 두고 벌어지는 CPU 경쟁과는 다투는 자원이 다르다. 블록 계층과 스케줄러 이름을 아는 사람을 독자로 둔다. 가상 머신 위에서 Keycloak 을 돌리며 지연을 볼 때 이 계층을 CPU 계층과 갈라 두면 어느 쪽을 재야 하는지가 먼저 정해진다.
게스트가 디스크라고 부르는 것이 호스트에서는 qcow2 나 RAW 파일일 수 있고, 그러면 QEMU 의 쓰기는 호스트 파일시스템을 거쳐 호스트 블록 I/O 가 된다. 호스트 블록 계층은 그 요청이 가상 머신에서 왔는지 호스트 프로세스에서 왔는지 구분해 처리하는 계층이 아니라, 가상 머신 두 대와 호스트의 Nginx 와 나머지 프로세스 같은 큐로 받는다. 그 큐에서 blk-mq 와 I/O 스케줄러를 지나 NVMe 로 나가기까지를 따라가고, 거기서 생기는 스토리지 경쟁(Storage Contention)을 다투는 자원이 다른 CPU 경쟁과 갈라 놓는다. 블록 계층과 스케줄러 이름을 아는 사람을 독자로 둔다. 가상 머신 위에서 Keycloak 지연을 볼 때 이 계층을 CPU 계층과 갈라 두면 어느 쪽을 재야 하는지가 먼저 정해진다.
## 관계
@@ -41,7 +42,7 @@ source:
- **지연 원인을 CPU 로 읽기 전에 storage 계층을 따로 잰다**
이 글이 가른 두 경쟁을 진단 순서로 편 규칙이다.
- **disk image 는 최종적으로 어느 Host block device 위에 있는가**
이 호스트의 디스크 이미지가 어느 블록 장치 위에 있는지 SSOT 에 적혀 있지 않다.
이 호스트의 디스크 이미지 아래를 마운트와 파티션과 장치까지 잇는 출력을 그 물음이 낸다.
- **이 호스트의 I/O Scheduler 는 무엇인가**
스케줄러 값을 읽는 방법은 여기 적었고, 이 호스트의 값은 그 질문이 확인한다.
- **VM1 의 storage 부하가 VM2 의 지연을 실제로 밀어 올리는가**
@@ -80,7 +81,7 @@ source:
여러 I/O 요청이 있어도 항상 들어온 순서 그대로 장치에 전달되지는 않는다. 요청은 I/O 스케줄러를 지나 장치 드라이버로 나간다. 스케줄러마다 목적과 내보내는 정책이 다르다. 대표적으로 볼 수 있는 값은 `none``mq-deadline``bfq` 다.
`none` 을 고르면 복잡한 스케줄링 정책을 최소화해서 비교적 직접 장치 쪽으로 내보낸다. NVMe 처럼 장치 자체가 강한 병렬성과 큐 기능을 가진 경우에는 이런 단순한 정책이 적합할 수 있다. 그렇게 골랐다고 블록 계층이 아무 일도 하지 않않는다.
`none` 을 고르면 복잡한 스케줄링 정책을 최소화해서 비교적 직접 장치 쪽으로 내보낸다. NVMe 처럼 장치 자체가 강한 병렬성과 큐 기능을 가진 경우에는 이런 단순한 정책이 적합할 수 있다. `none` 골랐다고 블록 계층이 아무 일도 하지 않는 것은 아니다.
지금 선택된 스케줄러는 `sysfs` 에서 읽는다.
@@ -90,17 +91,19 @@ cat /sys/block/nvme0n1/queue/scheduler
출력이 `[none] mq-deadline` 이면 대괄호 안의 `none` 이 지금 선택된 스케줄러다. SATA 나 SCSI 장치라면 `cat /sys/block/sda/queue/scheduler` 처럼 장치 이름을 바꿔 읽는다.
여기 든 `[none] mq-deadline` 은 SSOT 가 읽는 법을 보이려고 든 출력이다. 이 호스트에서 그 명령을 아직 돌리지 않아서 이 장비의 스케줄러가 무엇인지는 이 글이 말하지 않는다.
위에 적은 `[none] mq-deadline` 은 SSOT 가 읽는 법을 보이려고 든 출력이다. 이 호스트에서 그 명령을 아직 돌리지 않아서 이 장비의 스케줄러가 무엇인지는 이 글이 말하지 않는다.
넣을 이름도 아직 정해져 있지 않다. SSOT 의 측정 환경 절이 2026-09-10 에 `df -h /` 로 받은 이 호스트의 루트는 `/dev/nvme0n1p3` 인데, 위 경로에 든 예시 이름은 `nvme0n1` 과 `sda` 처럼 파티션이 아니라 장치 쪽이다. 어느 쪽을 넣어야 하는지는 이미지 아래를 `lsblk` 로 따라 내려가 상위 장치를 본 뒤에 갈린다.
## NVMe 드라이버는 호스트 커널의 장치 드라이버다
호스트 블록 계층에서 I/O 스케줄러를 지난 요청은 NVMe 드라이버로 가고, NVMe 컨트롤러를 거쳐 물리 저장장치에 닿는다. NVMe 드라이버는 호스트 Linux 커널의 장치 드라이버다. 네트워크에서 물리 NIC 드라이버가 하드웨어를 제어하는 것과 같은 계층에 놓인다.
이름이 비슷해 섞이는 두 낱말도 여기서 갈린다. SSD 는 저장장치의 넓은 종류를 가리키고, NVMe 는 PCIe 기반 비휘발성 저장장치를 위한 프로토콜이자 인터페이스다. SSD 아래에서 SATA SSD 는 SATA 와 AHCI 를 쓴다. NVMe SSD 는 PCIe 와 NVMe 를 쓴다. NVMe SSD 에서는 Linux NVMe 드라이버가 PCIe 를 통해 NVMe 컨트롤러에 요청을 보낸다. 컨트롤러는 플래시를 다룬다.
이름이 비슷해 섞이는 두 낱말도 여기서 갈린다. SSD 는 저장장치의 넓은 종류를 가리키고, NVMe 는 PCIe 기반 비휘발성 저장장치를 위한 프로토콜이자 인터페이스다. SSD 아래에서 SATA SSD 는 SATA 와 AHCI 를 쓰고, NVMe SSD 는 PCIe 와 NVMe 를 쓴다. NVMe SSD 에서는 Linux NVMe 드라이버가 PCIe 를 통해 NVMe 컨트롤러에 요청을 보낸다. 컨트롤러는 플래시를 다룬다.
## 스토리지 경쟁과 CPU 경쟁은 다투는 자원이 다르다
여러 가상 머신이 같은 물리 NVMe 를 쓰면 스토리지 자원 경쟁이 생길 수 있다. VM1 의 PostgreSQL 과 VM2 의 Keycloak 이 요청한 I/O 같은 호스트 블록 계층과 같은 I/O 큐를 지나 하나의 NVMe 로 가기 때문에, VM1 에서 대량 I/O 가 발생하면 VM2 의 스토리지 지연이 올라갈 수 있다. 이렇게 벌어지는 경쟁을 스토리지 경쟁(Storage Contention)이라고 한다.
여러 가상 머신이 같은 물리 NVMe 를 쓰면 스토리지 자원 경쟁이 생길 수 있다. VM1 의 PostgreSQL 과 VM2 의 Keycloak 이 요청한 I/O 같은 호스트 블록 계층과 같은 I/O 큐를 지나 하나의 NVMe 로 간다. 그래서 VM1 에서 대량 I/O 가 발생하면 VM2 의 스토리지 지연이 올라갈 수 있다. 이렇게 벌어지는 경쟁을 스토리지 경쟁(Storage Contention)이라고 한다.
```text label="두 경쟁이 다투는 자원"
CPU Contention
@@ -114,6 +117,6 @@ CPU 경쟁은 호스트 논리 CPU 의 실행 시간을 두고 벌어지고, 스
## 이 호스트에서 확인하지 않은 것
이 글에도 이 테스트 호스트에서 잰 값은 없다. 이 호스트의 I/O 스케줄러가 무엇으로 설정돼 있는지 SSOT 에 없고, 가상 머신들의 디스크 이미지가 최종적으로 어느 호스트 블록 장치 위에 있는지도 없다. 가상 머신 두 대와 호스트 프로세스의 I/O 가 이 환경에서 실제로 같은 시간에 겹치는지도 재지 않았다. VM1 의 대량 I/O 가 VM2 의 스토리지 지연을 올린다는 것은 이 계층 구성에서 나오는 가능성이고 이 서버에서 관측한 값이 아니다. 이 글은 요청이 호스트에서 어떤 순서로 어느 계층을 지나는지까지 설명하고, 이 서버가 그 계층에서 실제로 막히는지는 재지 않았다.
이 글에도 이 테스트 호스트에서 잰 값은 없다. 이 호스트의 I/O 스케줄러는 `/sys/block` 아래를 읽은 출력이 없어 무엇으로 설정돼 있는지 알 수 없고, 디스크 이미지 아래를 마운트에서 파티션과 장치까지 이은 출력도 없다. 가상 머신 두 대와 호스트 프로세스의 I/O 가 이 환경에서 실제로 같은 시간에 겹치는지도 재지 않았다. VM1 의 대량 I/O 가 VM2 의 스토리지 지연을 올린다는 것은 이 계층 구성에서 나오는 가능성이고 이 서버에서 관측한 값이 아니다. 이 글은 요청이 호스트에서 어떤 순서로 어느 계층을 지나는지까지 설명하고, 이 서버가 그 계층에서 실제로 막히는지는 재지 않았다.
<!-- body:end -->
@@ -7,7 +7,7 @@ topic: storage-virtualization
topicName: 스토리지 가상화
project: virtualization
status: 초안
basisVersion: QEMU block backend 세 형태 — qcow2 파일 · RAW 파일 · Host block device. libvirt 로 정의한 VM 기준이고 SSOT 가 QEMU 와 libvirt 버전을 적지 않았다
basisVersion: QEMU block backend 세 형태 — qcow2 파일 · RAW 파일 · Host block device. libvirt 로 정의한 VM 기준이고 제4부가 QEMU 와 libvirt 판을 고정하지 않았다
studio: "https://hyeonworks.com/studio/documents/892b6087-8961-486c-b4d9-d3dd46bc2605/edit"
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
source:
@@ -25,7 +25,7 @@ source:
# /dev/vda 뒤에 있는 것 — qcow2 · RAW · Host block device
게스트 안에서 /dev/vda 를 보는 것만으로는 호스트에 무엇이 있는지 알 수 없다. 가상 머신 경계를 넘으면 QEMU 의 virtio-blk 장치 모델과 블록 백엔드가 그 요청을 받는데, 그 백엔드는 qcow2 파일일 수도, RAW 파일일 수도, 호스트의 실제 블록 장치일 수도 있다. 갈래마다 바뀌는 것이 다르다. 파일 백엔드면 게스트 스토리지 계층 아래에 호스트 파일시스템과 블록 계층이 한 겹 더 놓이고, qcow2 면 게스트가 보는 크기와 호스트가 실제로 쓰는 크기가 갈린다. 무엇에 붙어 있는지는 게스트가 아니라 호스트에서 virsh domblklist 로 확인한다. 가상 머신에 디스크를 붙여 본 사람을 독자로 둔다.
게스트 안에서 /dev/vda 를 보는 것만으로는 호스트에 무엇이 있는지 알 수 없다. 가상 머신 경계를 넘으면 QEMU 의 virtio-blk 장치 모델과 블록 백엔드가 그 요청을 받는데, 그 백엔드는 qcow2 파일일 수도, RAW 파일일 수도, 호스트의 실제 블록 장치일 수도 있다. 파일 백엔드면 게스트 스토리지 계층 아래에 호스트 파일시스템과 블록 계층이 한 겹 더 놓이고, qcow2 면 게스트가 보는 크기와 호스트가 실제로 쓰는 크기가 갈린다. 무엇에 붙어 있는지는 게스트가 아니라 호스트에서 virsh domblklist 로 확인한다. 가상 머신에 디스크를 붙여 본 사람을 독자로 둔다.
## 관계
@@ -38,7 +38,7 @@ source:
- **Guest 안에서 본 disk 로 backend 를 단정하지 않는다**
이 글이 적은 세 갈래가 그 규칙의 근거다.
- **이 호스트의 /dev/vda 는 어떤 backend 에 붙어 있는가**
세 갈래 가운데 어느 것인지 이 장비에서 확인하지 않았다.
세 갈래 가운데 어느 것인지를 그 물음이 이 장비에서 출력으로 확정한다.
- **이 disk image 는 qcow2 인가 RAW 인가, 그리고 Guest 가 보는 크기와 Host 가 실제로 쓰는 크기는 얼마나 다른가**
이 글이 든 크기 차이는 SSOT 의 예시 값이고, 이 장비의 값은 그 질문이 잰다.
- **disk image 는 최종적으로 어느 Host block device 위에 있는가**
@@ -76,12 +76,18 @@ qemu-img info vm1.qcow2
출력의 `virtual size` 와 실제 할당량은 서로 다른 값이다.
SSOT 의 실측 절이 2026-09-10 에 이 호스트의 바닥 이미지에 그 명령을 돌린 출력을 남겼다. `file format: qcow2` 에 `virtual size: 3 GiB (3221225472 bytes)` 이고 `disk size: 335 MiB` 다. 같은 절의 목록에서 20GB 로 선언한 게스트 오버레이 둘은 `1521025024` 와 `697499648` 바이트, 곧 1.4 GiB 와 665 MiB 였다.
바닥 쪽 차이가 안 쓴 공간만으로 생기지는 않는다. SSOT 의 qcow2 내부 절이 같은 파일에 `qemu-img map --output=json` 을 돌려 `compressed: True` 인 구간을 606개, 전체를 1236개로 셌다. 3 GiB 가운데 2.01 GiB 가 구멍이고 남은 1010 MiB 를 zlib 로 압축해 324 MiB 가 된다. 배포본 이미지가 배포 시점에 이미 압축된 채로 오기 때문이고, 게스트가 그 클러스터에 쓰면 압축하지 않은 형태로 오버레이에 새로 할당된다. 바닥은 작은데 오버레이가 상대적으로 커 보이는 이유 가운데 하나가 그것이라고 SSOT 는 적었다.
## RAW 와 qcow2 를 성능으로 먼저 가르지 않는다
RAW 는 `qcow2` 보다 구조가 단순하다. 게스트의 블록 요청이 `qcow2` 에서는 QEMU 의 매핑과 메타데이터 처리를 지나 `qcow2` 파일 I/O 가 되고, RAW 에서는 상대적으로 직접적인 오프셋 대응을 지나 RAW 파일 I/O 가 된다. `qcow2` 는 Copy-on-Write 와 sparse allocation, 스냅샷에 유리하지만 그 대신 매핑과 메타데이터 처리가 붙는다.
그렇다고 RAW 가 무조건 빠르고 `qcow2` 가 무조건 느리다고 일반화하면 안 된다. SSOT 는 실제 성능이 캐시 모드와 스토리지 백엔드, 작업 부하 패턴, 큐 깊이, 스냅샷 체인, 그 아래 파일시스템, 물리 장치에 영향을 받는다고 적었다. 형식 이름만으로는 어느 쪽이 빠른지 정해지지 않는다.
그 목록 가운데 스냅샷 체인이 왜 걸리는지는 SSOT 의 qcow2 내부 절이 따로 풀었다. 매핑 항목이 0 인 클러스터를 만날 때마다 한 층 아래로 내려가야 해서 체인이 깊으면 읽기가 느려지고, 그래서 이 실험대는 층을 두세 겹 넘게 쌓지 않는다. 굳힐 때는 `qemu-img commit` 으로 아래층에 병합하거나 `qemu-img convert` 로 단일 파일로 평탄화한다.
## 백엔드가 파일이 아니라 호스트 블록 장치일 수도 있다
백엔드는 파일이 아니어도 된다. 게스트의 `/dev/vda` 가 `virtio-blk` 와 QEMU 를 지나 호스트의 `/dev/nvme0n1p3` 같은 블록 장치에 바로 연결될 수 있고, SSOT 가 그린 이 경로에는 호스트 파일시스템이 없다. 같은 이름 아래에서 경로가 세 갈래로 갈리기 때문에, 게스트에 `/dev/vda` 가 있다는 정보만으로는 백엔드 구조를 알 수 없다.
@@ -116,7 +122,9 @@ vda /var/lib/libvirt/images/vm1.qcow2
## 확인하지 못한 것
호스트의 백엔드가 셋 중 무엇인지는 SSOT 에 없다. SSOT 가 열린 질문 넷을 적어 두었는데, 그 확인을 아직 돌리지 않았다. `/dev/vda` 가 어떤 백엔드에 붙어 있는지, 백엔드가 `qcow2` 인지 RAW 인지, `qcow2` 의 가상 크기와 실제 호스트 사용량이 얼마나 다른지, 그 이미지가 최종적으로 어느 호스트 블록 장치 위에 있는지 넷이다. 앞에서 든 100 GB 와 3GB, 1GB 에서 40GB 까지의 증가는 SSOT 가 설명하려고 든 예시 값이다.
글은 백엔드가 셋으로 갈린다는 데까지 말하고, 이 호스트가 그중 무엇인지는 여기서 정하지 않는다. SSOT 의 실험대 구축 절과 실측 절이 게스트마다 `base.qcow2` 위의 오버레이 파일을 붙였다고 적었으므로 파일 쪽 갈래이지만, 그 대응을 `virsh domblklist` 로 읽은 출력은 아직 없다. 형식이 무엇인지, 가상 크기와 실제 사용량이 얼마나 다른지, 그 이미지가 어느 호스트 블록 장치 위에 있는지는 관계로 걸어 둔 질문 셋이 출력으로 확정한다.
앞에서 든 100 GB 와 3GB, 1GB 에서 40GB 까지의 증가는 SSOT 가 설명하려고 든 예시 값이고 이 호스트에서 잰 값이 아니다. 이 호스트에서 `qemu-img info` 가 돌아간 것은 바닥 `base.qcow2` 하나여서, 오버레이 둘은 `ls -l` 의 파일 크기로만 남아 있고 그 안의 `virtual size` 는 읽히지 않았다.
`qcow2` 와 RAW 의 성능 비교도 이 저장소에 없다. SSOT 는 실제 성능을 가르는 조건만 열거했고 어느 쪽이 이 환경에서 빠른지는 재지 않았다.
@@ -29,9 +29,10 @@ source:
- final/document.md#133-page-cache-write-가-바로-ssd-write는-아니다
- final/document.md#174-핵심-claim
---
# 완료라는 말의 네 가지 뜻 — write · writeback · flush · 전원 장애 생존
buffered I/O 에서 write() 는 페이지 캐시까지만 내려간다. 가상 머신에서는 그 페이지 캐시가 게스트와 호스트 양쪽에 두 번 나타날 수 있다. 게스트가 성공을 돌려받은 데이터가 호스트 메모리에 머무는 동안 호스트 전원이 나가면 물리 SSD 에는 이전 상태가 남는다. 그래서 완료라는 말이 네 단계로 갈린다 — write() 완료, writeback 완료, fsync/flush 완료, 전원 장애에도 안전한 영속성. Direct I/O 와 QEMU 캐시 모드 이 가운데 어디까지 는지를 바꾼다. 게스트에게 FLUSH 완료라고 응답했는데 데이터가 실제로는 호스트 메모리에만 있으면, 그것은 느린 구성이 아니라 영속성 계약이 깨진 상태다. 페이지 캐시와 fsync 를 아는 사람을 독자로 둔다. Keycloak 과 PostgreSQL 을 가상 머신 위에서 돌리는 실험에서 이 구분을 먼저 세워 두면, 지연을 재는 것과 데이터가 남는지를 재는 것 한 실험에 섞지 않게 된다.
buffered I/O 에서 write() 는 페이지 캐시까지만 내려가고, 가상 머신에서는 그 페이지 캐시가 게스트와 호스트 양쪽에 두 번 나타날 수 있다. 게스트가 성공을 돌려받은 데이터가 호스트 메모리에 머무는 동안 호스트 전원이 나가면 물리 SSD 에는 이전 상태가 남는다. 그래서 완료라는 말이 네 단계로 갈린다 — write() 완료, writeback 완료, fsync/flush 완료, 전원 장애에도 안전한 영속성. Direct I/O 와 QEMU 캐시 모드 이 가운데 어디까지 는지를 바꾼다. 페이지 캐시와 fsync 를 아는 사람을 독자로 둔다. Keycloak 과 PostgreSQL 을 가상 머신 위에서 돌리는 실험에서 이 구분을 먼저 세워 지연을 재는 것과 데이터가 남는지를 재는 것 한 실험에 섞지 않다.
## 관계
@@ -124,9 +125,9 @@ QEMU 와 libvirt 로 정의한 디스크에서 대표적으로 볼 수 있는
> writeback caching에서는 volatile cache가 존재할 수 있으므로, Guest의 flush/fsync semantics가 전체 backend/storage stack에서 올바르게 보존되는지가 중요하다.
그래서 보는 것은 게스트가 요구한 영속성이 게스트 파일시스템에서 게스트 블록 계층과 virtio, QEMU 와 백엔드, 호스트 스토리지를 지나 장치까지 가는 동안 그 의미가 깨지지 않는지다.
그래서 확인할 것은 게스트가 요구한 영속성이 게스트 파일시스템에서 게스트 블록 계층과 virtio, QEMU 와 백엔드, 호스트 스토리지를 지나 장치까지 가는 동안 그 의미가 깨지지 않는지다.
두 설정 가운데 어느 쪽을 쓸지 이 프로젝트는 정하지 않았다. SSOT 는 두 설정의 뜻과 각각의 오해를 갈라 놓기만 했고, 어느 쪽을 쓰기로 했다고도 그렇게 해서 무엇을 감수했다고도 적지 않았다. 이 호스트의 지금 값이 무엇인지부터 확인되지 않아서, 관계로 걸어 둔 질문이 그것 먼저 답한다.
두 설정 가운데 어느 쪽을 쓸지 이 프로젝트는 정하지 않았다. SSOT 는 두 설정의 뜻과 각각의 오해를 갈라 놓기만 했고, 어느 쪽을 쓰기로 했다고도 그렇게 해서 무엇을 감수했다고도 적지 않았다. 게스트를 만든 `virt-install` 세 줄을 SSOT 의 실험대 구축 절이 그대로 옮겨 적었는데 거기에도 캐시 옵션이 없다. 디스크는 `--disk size=20,backing_store=/var/lib/libvirt/images/base.qcow2` 와 시드 볼륨 둘로만 지정돼 있어서, 이 호스트의 디스크 정의에 값이 적혀 있는지부터 확인되지 않았다. 관계로 걸어 둔 질문이 그것 먼저 답한다.
## 호스트 페이지 캐시를 우회해도 장치 안에 캐시가 있다
@@ -20,7 +20,7 @@ source:
# 이 disk image 는 qcow2 인가 RAW 인가, 그리고 Guest 가 보는 크기와 Host 가 실제로 쓰는 크기는 얼마나 다른가
게스트가 100 GB 짜리 디스크를 보고 있어도 호스트에서 그 파일이 차지하는 공간은 훨씬 작을 수 있다. 개념 문서는 그 차이를 예시 숫자로만 들어 두고, 세 명령이 각각 다른 의미의 크기를 보여 준다고 적었다. 이 물음은 이 호스트의 이미지마다 형식과 세 크기를 한 표로 적는 데서 끝난다. 형식과 세 값이 채워지면 저장 공간 계획의 근거가 생기고, 그 뒤의 성능 판단은 여기서 하지 않는다.
§199 가 2026-09-10 에 이 호스트에서 읽어 둔 값이 있다. 바닥 이미지 `base.qcow2` 의 형식은 qcow2 이고, 20GB 로 선언한 게스트 오버레이 둘의 실제 파일은 1.4 GiB 와 665 MiB 였다. 오버레이마다 세 명령을 돌려 형식과 세 크기를 나란히 읽은 값은 아직 없다. 이 물음은 이미지마다 그것을 한 표로 적는 데서 끝나고, 성능 판단은 여기서 하지 않는다.
## 관계
@@ -40,30 +40,40 @@ source:
- 게스트가 데이터를 기록하면서 실제 사용량이 늘 수 있다. 개념 문서는 Virtual 100GB 를 유지한 채 Actual 이 1GB 에서 10GB 로, 다시 40GB 로 가는 예를 보였다.
- 개념 문서는 확인 명령으로 qemu-img info vm1.qcow2 를 들고 virtual size 와 실제 allocation 을 구분해서 봐야 한다고 적었다.
- RAW 는 qcow2 보다 구조가 단순하다. qcow2 는 Copy-on-Write 와 sparse allocation 과 스냅숏 등에 유리하지만 매핑과 메타데이터 처리가 있고, RAW 는 상대적으로 직접적인 offset 대응으로 간다.
- 형식만으로 성능을 가르지 말라고 개념 문서가 못박았다. RAW 는 무조건 빠르고 qcow2 는 무조건 느리다는 일반화를 막고, 실제 성능 캐시 모드 스토리지 백엔드 부하 패턴 큐 깊이 스냅숏 체인 그 아래 파일시스템 물리 장치에 영향을 받는다고 었다.
- 형식만으로 성능을 가르지 말라고 개념 문서가 못박았다. RAW 는 무조건 빠르고 qcow2 는 무조건 느리다는 일반화를 막고, 실제 성능 캐시 모드, 스토리지 백엔드, 부하 패턴, 큐 깊이, 스냅숏 체인, 그 아래 파일시스템, 물리 장치에 영향을 받는다고 었다.
- 개념 문서가 이 확인에 적어 둔 명령은 셋이다. 세 명령이 보여주는 의미가 서로 다를 수 있으므로 비교하라고 했다.
형식과 크기 : qemu-img info
실제 점유량 : du -h
파일 크기 : ls -lh
- 이 호스트의 이미지 형식도 크기도 적힌 기록이 없다.
- 백엔드가 파일이라는 것은 §187 의 `virt-install` 세 줄과 §199 의 `ls -l` 출력이 보여 준다.
- 형식과 바닥 이미지의 크기는 §199 가 2026-09-10 에 `test-server` 에서 재 두었다. `qemu-img info /var/lib/libvirt/images/base.qcow2` 가 낸 값은 `file format: qcow2`, `virtual size: 3 GiB (3221225472 bytes)`, `disk size: 335 MiB` 다.
- 같은 절의 `ls -l /var/lib/libvirt/images/` 가 선언한 크기와 파일 크기를 나란히 보인다.
base.qcow2 : 351404032 (335 MiB)
kc-lab-1.qcow2 : 1521025024 (1.4 GiB) — 선언 20GB
kc-lab-2.qcow2 : 697499648 (665 MiB) — 선언 20GB
시드 ISO 두 장 : 각 378880 (370 KiB)
- §199 는 그 결과를 한 줄로 정리했다. 20GB 씩 둘, 합쳐 40GB 를 선언했지만 실제로는 2.1GB 를 썼다.
- §199 는 두 오버레이가 두 배 넘게 갈린 이유도 함께 적었다. k3s server 가 agent 보다 두 배 이상 쓰는 것은 컨트롤 플레인 바이너리와 SQLite 때문이다. 그래서 오버레이 크기는 이미지 형식이 아니라 그 게스트가 무엇을 도는지를 따라간다.
- §231 은 같은 바닥 이미지에 `qemu-img map --output=json` 을 돌려 `compressed: True` 인 구간이 606개이고 전체가 1236개라고 적었다. 3 GiB 가운데 2.01 GiB 가 구멍이고 남은 1010 MiB 를 압축해 324 MiB 가 된다. 배포본 qcow2 는 배포 시점에 이미 zlib 로 압축돼 있어서, 바닥 쪽 차이에는 희소 할당과 압축이 겹쳐 있다. 오버레이에 쌓이는 클러스터는 압축되지 않는다.
## 가정
- 이미지 경로가 앞선 물음에서 확정된다고 본다. 경로가 없으면 세 명령을 어디에 돌릴지 정해지지 않는다.
- 백엔드가 파일이라고 전제한다. 호스트 블록 장치로 나오면 이 물음 자체가 이 호스트에 적용되지 않는다.
- §199 가 목록으로 보인 경로가 지금도 그 가상 머신들이 쓰는 이미지라고 본다. 도메인 정의와 대조한 출력은 없다.
- 오버레이도 바닥과 같은 qcow2 라고 본다. §187 이 `backing_store` 로 만들었다고 적었지만 오버레이에 `qemu-img info` 를 돌린 출력은 없다. §232 는 그 출력의 `backing file:` 줄이 없으면 바닥 이미지이고 있으면 오버레이라고 적었으므로, 오버레이에 한 번 돌리면 형식과 이 가정이 같은 출력에서 함께 갈린다.
- 세 명령을 같은 시점에 돌린다고 본다. 실제 사용량은 게스트가 기록하면서 늘 수 있어서, 시점이 벌어지면 한 표에 서로 다른 시각의 값이 들어간다.
## 미지수
- 가상 머신마다 디스크 이미지가 qcow2 인지 RAW 인지.
- qemu-img info 가 보여 주는 virtual size 와 disk size 가 각각 얼마인지.
- du -h 의 실제 점유량과 ls -lh 의 파일 크기가 그 값들과 얼마나 벌어져 있는지.
- 오버레이 `kc-lab-1.qcow2``kc-lab-2.qcow2``qemu-img info` 값. 형식과 virtual size, disk size 를 이미지마다 읽은 기록이 없고, §199 가 읽은 것은 바닥 `base.qcow2` 하나다.
- `du -h` 의 실제 점유량이 §199 의 `ls -l` 바이트 수와 얼마나 벌어지는지. 두 명령은 서로 다른 것을 센다.
- 엣지 게스트의 이미지. §199 는 엣지를 만들기 전 시점이라 목록에 `kc-lab-edge.qcow2` 가 없다.
- 지금 시점의 값. §199 는 2026-09-10 값이고 그 뒤로 오버레이가 얼마나 자랐는지는 재지 않았다. 증가를 볼 수 있는 구간은 그 앞 한 주뿐이다 — §215 가 2026-09-03 에 그린 배치에서 `kc-lab-1.qcow2` 는 264M 이고 일주일 뒤 §199 의 같은 파일이 1.4 GiB 다. 두 값은 어긋난 것이 아니라 다른 날의 것이라고 §211 이 스냅샷 날짜로 못박았다.
## 제약
- 성능 결론을 여기서 내지 않는다. 형식만으로 일반화하지 말라고 개념 문서가 적었고, 성능은 부하와 지연을 재는 다른 물음들이 받는다.
- 한 시점의 세 값만 적는다. 그 차이가 시간에 따라 어떻게 자라는지는 별도 측정으로 넘긴다.
- 앞선 물음이 Source 를 확정하기 전에는 이 확인을 시작할 수 없다.
- qcow2 파일 안이 어떻게 생겼는지는 여기서 다루지 않는다. 매핑표와 클러스터와 refcount 는 §231 이 맡고, 이 물음은 명령이 내놓는 값만 받는다.
- 개념 문서는 형식을 묻는 물음과 세 크기를 묻는 물음을 따로 적었는데 여기서는 하나로 받았다. 형식은 qemu-img info 출력의 첫 줄이고 세 값을 견주는 일의 전제라 같은 출력으로 함께 닫힌다. 같은 측정으로 닫히는 물음을 두 편으로 두지 않는다.
## 선택지
@@ -21,7 +21,7 @@ source:
# Guest 의 fsync() 지연과 Host storage 지연은 같이 오르는가
게스트 안의 fsync() 는 게스트 파일시스템 블록 계층 virtio-blk QEMU 를 지나 호스트 스토리지까지 의미가 전달되어야 하는 요청이다. 그 전달이 이 호스트에서 실제로 이어지는지는 두 계열을 같은 시간축에 놓고 봐야 갈린다. 이 물음은 게스트 쪽 애플리케이션 지연과 호스트 쪽 스토리지 지연을 같은 타임스탬프로 남겨, 둘이 함께 움직이는지를 확인한다.
게스트 안의 fsync() 는 게스트 파일시스템, 블록 계층, virtio-blk, QEMU 를 지나 호스트 스토리지까지 의미가 전달되어야 하는 요청이다. 그 전달이 이 호스트에서 실제로 이어지는지는 두 계열을 같은 시간축에 놓고 봐야 갈린다. 이 물음은 게스트 쪽 애플리케이션 지연과 호스트 쪽 스토리지 지연을 같은 타임스탬프로 남겨, 둘이 함께 움직이는지를 확인한다.
## 관계
@@ -45,7 +45,7 @@ source:
- §150 은 write(fd, data, size) 의 성공만으로는 정전 이후 생존을 보장하지 않고, 필요한 시점에 fsync(fd) 로 변경 내용을 필요한 영속성 경계까지 반영하도록 요청한다고 적었다.
- §150 은 가상 머신에서 그 요청이 지나야 하는 경로를 이렇게 그렸다.
PostgreSQL, fsync(), Guest Filesystem, Guest Block Layer, FLUSH 등, virtio-blk, QEMU/Backend, Host Storage Stack, Physical Storage
- §148 은 write() 완료 writeback 완료 fsync/flush 완료 전원 장애에도 안전한 durability 가 서로 같지 않다고 못박았다.
- §148 은 write() 완료, writeback 완료, fsync/flush 완료, 전원 장애에도 안전한 durability 가 서로 같지 않다고 못박았다.
- §170 은 PostgreSQL 이 WAL 등의 durability protocol 을 사용하며 필요한 시점에 스토리지 동기화를 수행한다고 적었다. 가상 머신의 스토리지 계층이 flush 와 fsync 의 의미를 제대로 보존하지 않으면 PostgreSQL 이 전제한 영속성과 실제 스토리지 동작이 어긋날 수 있다.
- §171 은 모든 쓰기에서 스토리지 동기화를 기다리면 지연이 커질 수 있고, 특히 DB 부하에서는 fsync() 지연이 트랜잭션 지연과 연결될 수 있다고 적었다.
- §171 은 더 적극적인 캐싱으로 쓰기 지연을 개선할 수 있지만 durability semantics 는 반드시 보존해야 한다고 덧붙였다. fsync() 를 없애서 빨라졌다면 그것이 최적화가 아니라 durability contract 를 제거한 것일 수 있다.
@@ -67,7 +67,7 @@ source:
- 겹친다면 두 값이 같은 크기로 움직이는지, 게스트 쪽이 더 크게 벌어지는지.
- 게스트 지연은 오르는데 호스트 스토리지 지연이 따라 오르지 않는 구간이 있는지. 있다면 그 차이가 게스트 쪽 대기에서 생기는지 QEMU 와 백엔드 쪽에서 생기는지.
- 이때 걸려 있던 캐시 모드가 무엇인지. 그 값은 별도 물음이 읽는다.
- 게스트 쪽 지연을 어떤 단위로 기록할지. 개념 문서는 게스트에서 지연을 재는 명령을 적지 않았고 호스트 쪽 iostat 만 적었다.
- 게스트 쪽 지연을 무엇으로 어떤 단위로 기록할지. §169 가 게스트 쪽에 적은 명령은 장치 입출력과 마운트를 보는 것이라, 애플리케이션이나 트랜잭션 지연은 거기서 나오지 않는다.
## 제약
@@ -76,7 +76,7 @@ source:
- 측정 조건에 캐시 모드와 백엔드 형식과 장치 이름을 함께 적는다. 이 셋이 없으면 같은 값을 다른 환경과 견줄 수 없다.
- 측정하는 동안 다른 스토리지 실험을 같은 장치 위에서 겹쳐 돌리지 않는다.
- 호스트 지연이 오른 이유까지 이 물음이 가르지 않는다. 다른 가상 머신의 부하인지 호스트 메모리 압박인지는 그것을 재는 두 물음이 받는다.
- 그 가운데 메모리 압박 쪽 물음과는 호스트 스토리지 지표를 같이 본다. 그래도 한 물음으로 합치지 않았다. 그쪽은 호스트에서 major fault 를 유도해 그것이 스토리지를 미는지를 보고 이쪽은 게스트의 fsync() 가 호스트까지 전달되는지를 봐서, 유도 방법도 견주는 계열도 달라 한 실행으로 닫히지 않는다.
- 그 가운데 메모리 압박 쪽 물음과는 호스트 스토리지 지표를 같이 본다. 그래도 한 물음으로 합치지 않았다. 그쪽은 호스트에서 major fault 를 유도해 그것이 스토리지를 미는지를 보고, 이쪽은 게스트의 fsync() 가 호스트까지 전달되는지를 본다. 유도 방법도 견주는 계열도 달라 한 실행으로 닫히지 않는다.
## 선택지
@@ -20,7 +20,7 @@ source:
# disk image 는 최종적으로 어느 Host block device 위에 있는가
스트의 디스크가 호스트에서는 파일 하나라는 것까지는 백엔드를 묻는 앞 물음이 확정한다. 그 파일은 다시 호스트 파일시스템 위에 있고 그 파일시스템은 어느 파티션과 물리 장치 위에 있어서, 이미지 경로에서 시작해 그 아래를 마운트와 파티션과 장치 이름까지 따라 내려가야 한다. 두 가상 머신의 이미지가 같은 장치를 쓰는지도 여기서 갈다.
이 호스트의 루트가 `/dev/nvme0n1p3` 이고 이미지가 `/var/lib/libvirt/images/` 에 모여 있다는 것까지는 §197 과 §199 가 적었다. 남은 것은 그 디렉터리가 그 파티션 위에 있다는 것을 `findmnt``lsblk` 출력으로 잇는 일이다. 두 가상 머신의 이미지가 같은 장치를 쓰는지가 거기서 갈리고, 그 출력에 나온 장치 이름이 스케줄러를 묻는 물음의 입력이 된다.
## 관계
@@ -48,22 +48,25 @@ source:
- §175 OQ-5 는 확인 명령으로 lsblk 와 findmnt 를 들었다.
- §169 의 호스트 관측 명령 목록에도 lsblk 가 들어 있다.
- §158 의 vm1.qcow2 와 /dev/nvme0n1 은 경로를 설명하려고 든 예시 이름이고 이 호스트에서 읽은 값이 아니다.
- 이 호스트의 마운트 배치와 물리 장치 이름은 개념 문서다.
- §197 은 2026-09-10 에 `df -h /` 를 돌려 `/dev/nvme0n1p3 226G 9.9G 204G 5% /` 를 받았다. 이 호스트의 루트 파일시스템이 그 파티션 위다.
- §199 의 `ls -l` 은 이미지가 `/var/lib/libvirt/images/` 아래에 모여 있는 것을 보였고, 같은 절의 `virsh pool-info default` 는 Capacity 225.31 GiB, Allocation 7.84 GiB, Available 217.46 GiB 를 냈다. §199 는 그 Allocation 이 풀 전체, 곧 호스트 루트 파일시스템의 사용량이라고 적었다.
- 그래서 `default` 풀이 루트 파일시스템 위에 있고 게스트 두 대의 오버레이가 같은 디렉터리에 있다.
- `findmnt``lsblk` 출력은 SSOT 에 없다. 이미지 경로가 어느 마운트에 속하고 그 마운트의 장치가 어느 파티션과 상위 장치에 걸려 있는지를 명령으로 확인한 기록이 없다.
## 가정
- 이미지 경로가 앞 물음에서 이미 확정되어 있다고 보고 그 경로에서 시작한다. 백엔드가 파일이 아니라 호스트 블록 장치면 이 확인은 장치 이름에서 시작한다.
- 확인하는 동안 마운트 구성과 이미지 위치가 바뀌지 않는다고 전제한다.
- findmnt 가 보여 주는 source 이름이 물리 장치 이름과 다를 수 있다고 보고 lsblk 로 상하 관계까지 이어서 읽는다.
- findmnt 가 보여 주는 source 이름이 물리 장치 이름과 다를 수 있다고 보고, 어느 상위 장치에 속하는지까지 lsblk 로 이어서 읽는다.
- 호스트에 붙어 두 명령을 같은 시각에 돌릴 수 있다고 본다.
## 미지수
- 이미지가 놓인 디렉터리가 어느 마운트 지점에 속하는지.
- 그 마운트가 어느 파티션 위에 있고 그 파티션이 어느 블록 장치에 속하는지.
- 가상 머신들의 이미지가 같은 장치를 공유하는지 서로 다른 장치에 있는지.
- `/var/lib/libvirt/images/``/` 마운트에 속한다는 것을 `findmnt` 출력으로 확인한 기록. §199 는 풀을 설명하며 그렇게 적었을 뿐 명령 출력을 남기지 않았다.
- `nvme0n1p3` 위에 파티션이 어떻게 놓여 있고 그이 어느 상위 장치에 속하는지. `lsblk` 출력이 없다.
- 이미지를 담은 파일시스템이 무엇인지. §158 은 ext4 와 XFS 를 예로 들었을 뿐 이 호스트의 값을 적지 않았다.
- 그 장치가 NVMe 인지 다른 종류인지. §163 이 SATA/SCSI device 를 따로 언급했으므로 확인 명령의 장치 이름도 그에 따라 달라진다.
- 엣지 게스트의 이미지도 같은 장치에 있는지. §199 의 목록은 엣지를 만들기 전 시점이라 그 파일이 없다.
- `base.qcow2` 가 오버레이와 같은 장치에 있는지. §231 은 오버레이의 매핑 항목이 0 인 클러스터를 읽으면 바닥 파일의 같은 위치를 읽는다고 적었고 §215 는 게스트 둘이 그 바닥 하나를 공유한다고 그렸다. 그래서 따라 내려갈 경로가 오버레이 하나로 끝나지 않는다.
## 제약
@@ -96,6 +99,6 @@ source:
1. 백엔드를 묻는 앞 물음이 확정한 Source 경로를 그대로 가져온다.
2. 경로마다 findmnt 로 그 경로가 속한 마운트와 source device 를 적는다 (§175 OQ-5).
3. 같은 경로에 lsblk 를 돌려 그 장치에 파티션이 어떻게 나뉘어 있고 어느 상위 장치에 속하는지 적는다.
4. 이미지 경로, 마운트, 파티션, 장치를 가상 머신마다 한 행으로 남긴다.
4. 이미지 경로, 마운트, 파티션, 장치를 가상 머신마다 한 행으로 남긴다. 게스트 둘이 공유하는 `base.qcow2` 도 한 행으로 함께 적는다.
닫는 조건 : 이미지마다 최종 블록 장치 이름이 확정되면 닫는다. 가상 머신들이 같은 장치를 공유하면 §159 가 그린 상황이 이 환경이라고 적고, 그것이 지연으로 이어지는지는 VM1 부하와 VM2 지연을 재는 물음이 받는다. 서로 다른 장치면 그 사실을 적고 그 물음의 전제가 이 환경에 없다고 함께 적는다. 어느 쪽이든 여기서 확정한 장치 이름을 I/O Scheduler 를 묻는 물음으로 넘긴다.
@@ -55,7 +55,9 @@ bfq
§175 OQ-6 은 같은 파일을 장치 이름을 넣어 읽으라고 했다.
이 호스트의 값은 개념 문서에 없다. §163 의 nvme0n1 과 sda 는 명령의 모양을 보이려고 든 예시 이름이다.
§197 은 2026-09-10 에 `df -h /` 를 돌려 이 호스트의 루트가 `/dev/nvme0n1p3` 위에 있는 것을 받았고, §199 는 이미지가 `/var/lib/libvirt/images/` 아래에 있다고 적었다. 명령에 넣을 장치 이름의 후보가 거기서 나온다. 다만 §197 이 낸 이름은 파티션이고 §163 이 경로에 넣어 보인 것은 nvme0n1 과 sda 처럼 장치 쪽 이름이다. 둘 가운데 무엇이 들어가는지는 앞 물음의 lsblk 가 상위 장치를 보인 뒤에 갈린다.
이 호스트의 스케줄러 값은 SSOT 에 없다. `/sys/block` 아래의 파일을 읽은 출력이 어디에도 없고, §163 의 nvme0n1 과 sda 는 명령의 모양을 보이려고 든 예시 이름이다.
## 가정
@@ -40,7 +40,7 @@ source:
§154 는 cache=none 을 개념적으로 Host Page Cache 를 우회하는 방향의 I/O 구성으로 놓았다. 이중 caching 은 줄일 수 있지만, Host Page Cache 우회가 무조건 즉시 durable media 반영을 뜻하지는 않는다.
§155 는 cache=writeback 을 Host Page Cache 를 사용할 수 있는 구성으로 놓았다. 일반 write 는 Host RAM 에서 빠르게 completion 될 수 있고, 나중에 Host RAM 에서 Storage 로 내려간다. 이 설정이어도 Guest 의 fsync()/FLUSH 가 무시되지는 않는다. 정상적인 stack 이라면 durability 요구가 Guest fsync/FLUSH 에서 virtio FLUSH, QEMU/backend, Host sync/flush, Storage 를 지난다. 거기서 필요한 완료 확인을 받아 Guest completion 까지 전달되어야 한다고 적었다.
§155 는 cache=writeback 을 Host Page Cache 를 사용할 수 있는 구성으로 놓았다. 일반 write 는 Host RAM 에서 빠르게 완료될 수 있고, 나중에 Host RAM 에서 Storage 로 내려간다. 이 설정이어도 Guest 의 fsync()/FLUSH 가 무시되지는 않는다. 정상적인 stack 이라면 durability 요구가 Guest fsync/FLUSH 에서 virtio FLUSH, QEMU/backend, Host sync/flush, Storage 를 지난다. 거기서 필요한 완료 확인을 받아 Guest completion 까지 전달되어야 한다고 적었다.
§156 은 그래서 정확한 표현을 이렇게 적었다. writeback caching 에서는 volatile cache 가 존재할 수 있으므로, Guest 의 flush/fsync semantics 가 전체 backend/storage stack 에서 올바르게 보존되는지가 중요하다.
@@ -48,7 +48,11 @@ source:
§175 OQ-4 는 확인 방법으로 virsh dumpxml <VM_NAME> 을 들고, disk driver 설정의 cache 관련 값을 확인하라고 했다.
이 호스트에 어느 값이 걸려 있는지도, 어느 값을 쓰기로 정했다는 기록도 개념 문서에 없다.
§187 은 게스트를 만든 `virt-install` 세 줄을 그대로 적었는데 거기에 cache 옵션이 없다. 디스크는 `--disk size=20,backing_store=/var/lib/libvirt/images/base.qcow2``--disk vol=default/seed-kc-lab-1.iso,device=disk,bus=virtio,readonly=on` 둘로만 지정했다.
§178 과 §197 이 이 호스트의 판을 적었다. QEMU 11.1.1 과 libvirt 12.7.0 이고 커널은 `7.2.2-arch1-1` 이다. 값이 적혀 있지 않을 때 무엇이 적용되는지는 그 판들이 정한다.
이 호스트의 disk 요소에 어느 값이 걸려 있는지도, 어느 값을 쓰기로 정했다는 기록도 SSOT 에 없다. `virsh dumpxml` 출력이 없다.
## 가정
@@ -39,39 +39,53 @@ source:
§135 는 virtio-blk 를 쓰는 가상 머신에서 /dev/vda 나 /dev/vdb 같은 이름이 흔히 보인다고 적었다. Guest Linux 는 그 이름을 하나의 block device 로 인식하지만, 그것이 호스트의 실제 SSD 를 뜻하지는 않는다.
backend 가 반드시 파일일 필요도 없다. §145 는 그 아래에 qcow2 파일 RAW 파일 Host block device 셋이 올 수 있다고 갈라 적고, 게스트에 그 이름이 있다는 정보만으로는 backend 구조를 알 수 없다고 못박았다.
백엔드가 반드시 파일일 필요도 없다. §145 는 그 아래에 qcow2 파일, RAW 파일, Host block device 셋이 올 수 있다고 갈라 적고, 게스트에 그 이름이 있다는 정보만으로는 백엔드 구조를 알 수 없다고 못박았다.
§140 은 가상 머신 경계를 넘으면 QEMU 가 등장한다고 그렸다. QEMU 의 virtio-blk Device Model 과 Block Backend 가 게스트의 virtual I/O 를 호스트 backend 에 연결한다.
§140 은 가상 머신 경계를 넘으면 QEMU 가 등장한다고 그렸다. QEMU 의 `virtio-blk` 장치 모델과 블록 백엔드가 게스트의 가상 I/O 를 호스트 백엔드에 연결한다.
확인 방법으로는 §146 과 §175 OQ-1 이 같은 둘을 든다. 게스트에서는 lsblk 를 돌리고, 호스트에서는 virsh domblklist <VM_NAME> 을 돌린다.
§146 의 예시 출력은 Target vda 에 Source /var/lib/libvirt/images/vm1.qcow2 가 붙은 한 행이었다. 그 대응이 나오면 게스트의 /dev/vda 에서 virtio-blk 와 QEMU 를 지나 그 파일까지 이어진다. 그 경로는 대응이 어떻게 읽히는지 보이려고 든 예시이지 이 호스트에서 읽은 값이 아니다.
이 호스트의 가상 머신이 어떤 Source 에 붙어 있는지를 적은 기록은 개념 문서에 없다.
이 호스트의 가상 머신은 libvirt 로 정의되어 있다. §187 이 게스트를 만든 `virt-install` 세 줄을 그대로 적었고 §199 가 `virsh pool-info default` 출력을 남겼다.
`virt-install` 세 줄은 게스트마다 디스크를 둘씩 붙인다. 하나는 `--disk size=20,backing_store=/var/lib/libvirt/images/base.qcow2` 로 만든 오버레이이고, 다른 하나는 `--disk vol=default/seed-kc-lab-1.iso,device=disk,bus=virtio,readonly=on` 으로 붙인 시드 볼륨이다. 엣지만 `--disk size=10` 이다.
§215 는 그 결과를 게스트 쪽 이름으로 그렸다. `vda` 는 20G 이고 ext4 로 마운트돼 거기서 부팅한다. `vdb` 는 370K 이고 레이블이 CIDATA 인 iso9660 이라 마운트되지 않는다. `vda` 뒤에 `kc-lab-1.qcow2` 가 있고 그 아래 backing 으로 `base.qcow2` 가 있어서, 게스트 두 대가 그 바닥 하나를 공유한다.
§199 는 2026-09-10 에 `ls -l /var/lib/libvirt/images/` 를 돌린 출력을 남겼고 거기에 `base.qcow2`, `kc-lab-1.qcow2`, `kc-lab-2.qcow2`, 시드 ISO 둘이 파일로 보이므로 이 호스트의 백엔드는 §145 가 가른 셋 가운데 파일 쪽이다.
§234 는 그 파일들이 게스트에 어떻게 붙는지를 한 줄로 적었다. 이 실험대의 게스트는 `vda`(qcow2 오버레이)와 `vdb`(raw 시드 ISO) 두 디스크다. 그래서 한 게스트 안에서도 Target 둘의 형식이 갈리고, 한 행만 읽고 백엔드를 정하면 나머지 한 행이 빠진다.
시드에 `bus=virtio` 가 붙은 이유는 §237 이 확정된 함정으로 적어 두었다. `virt-install --cloud-init` 은 시드 ISO 를 SATA CD-ROM 으로 붙이는데 Debian `genericcloud` 변종에는 물리 하드웨어 드라이버가 빠져 있어 게스트가 그 장치를 보지 못하고, 오류 메시지는 어디에도 남지 않은 채 hostname 이 `localhost` 로 남는 것으로만 드러난다. 그래서 §237 은 이 확인의 성공 판정을 시드 ISO 가 `sda` 가 아니라 `vdb` 로 보이는 것이라고 적었다. Target 이름 자체가 판정 대상이다.
`virsh domblklist` 출력은 SSOT 에 없다. Target 과 Source 를 한 행으로 이어 보인 출력이 없고, §215 의 배치는 게스트가 두 대이던 2026-09-03 스냅샷이라 지금 도메인 정의와 대조되지 않았다.
## 가정
호스트와 각 게스트에 붙어 명령을 돌릴 수 있다고 본다.
이 환경의 가상 머신이 libvirt 로 정의되어 있어서 virsh 가 그 가상 머신을 안다고 전제한다. §146 과 §175 OQ-1 이 확인 방법으로 virsh 명령을 든 것이 근거이고, 이 호스트에서 확인하지는 않았다.
확인하는 동안 디스크 구성이 바뀌지 않는다고 본다.
§215 가 그린 배치가 지금도 그대로라고 보고 대조할 대상으로 삼는다. §211 은 그 그림이 2026-09-03 값이고 그 뒤에 엣지 게스트가 늘었다고 적었다.
## 미지수
이 호스트의 각 가상 머신에 디스크가 몇 개 붙어 있는지.
지금 돌고 있는 도메인에 붙은 디스크가 §187 이 만들 때 준 둘과 같은지.
각 Target 의 Source 가 무엇인지.
각 Target 의 Source 경로가 `virsh domblklist` 출력에 무엇으로 나오는지.
그 Source 가 파일인지 Host block device 인지.
게스트 안에서 `lsblk` 로 본 장치와 파티션이 §215 가 그린 `vda``vdb` 에 그대로 대응하는지.
엣지를 더한 뒤의 배치. §215 는 게스트 두 대였을 때의 그림이고 §178 은 지금 게스트가 셋이라고 적었다.
## 제약
이 물음은 backend 가 무엇인지까지 답한다. 형식과 크기는 다음 물음이 받고, 성능은 여기서 판정하지 않는다.
이 물음은 백엔드가 무엇인지까지 답한다. 형식과 크기는 다음 물음이 받고, 성능은 여기서 판정하지 않는다.
게스트 안에서 본 이름은 답이 되지 않는다. §145 가 그 추론을 명시적으로 막았기 때문이다.
이 호스트에서 얻은 출력이 없으므로 다른 장비의 디스크 구성을 근거로 삼지 않는다.
다른 장비의 디스크 구성을 근거로 삼지 않는다. 이 호스트에서 얻은 출력은 §199 의 `ls -l``virsh pool-info default` 뿐이고, Target 과 Source 의 대응은 그 안에 없다.
## 선택지
@@ -83,13 +97,13 @@ backend 가 반드시 파일일 필요도 없다. §145 는 그 아래에 qcow2
### 2. 호스트 쪽 출력만 먼저 받는다
virsh domblklist 는 호스트에서만 돌아가고 Target 과 Source 를 한 번에 주기 때문에, 가상 머신에 로그인하지 않아도 backend 경로가 확정된다. 이어지는 두 물음이 요구하는 이미지 경로도 바로 얻는다.
virsh domblklist 는 호스트에서만 돌아가고 Target 과 Source 를 한 번에 주기 때문에, 가상 머신에 로그인하지 않아도 백엔드 경로가 확정된다. 이어지는 두 물음이 요구하는 이미지 경로도 바로 얻는다.
대신 게스트가 그 디스크를 어떤 이름과 파티션으로 보고 있는지가 빠진다. 나중에 게스트 안에서 잰 스토리지 수치를 이 표에 붙이려면 그때 대응을 다시 확인해야 한다.
### 3. libvirt 정의 전문을 받아 disk 요소를 읽는다 — 제외
virsh dumpxml 은 disk 요소에 source 와 driver 를 함께 담고 있어서 backend 경로와 cache 설정을 한 번에 볼 수 있다.
virsh dumpxml 은 disk 요소에 source 와 driver 를 함께 담고 있어서 백엔드 경로와 cache 설정을 한 번에 볼 수 있다.
§175 는 dumpxml 을 cache mode 를 확인하는 항목에 두었고, 연결 확인에는 §146 과 같은 domblklist 를 들었다. 여기서 dumpxml 을 쓰면 한 출력으로 두 물음이 닫히게 되어 어느 확인이 무엇을 근거로 끝났는지가 흐려진다. cache 값은 그 물음이 받는다.
@@ -51,11 +51,13 @@ Storage Contention : IOPS / bandwidth / queue / device 처리시간 경쟁
§175 OQ-7 이 적은 실험은 한 문장이다. VM1 에서 별도의 테스트 파일이나 디스크로 controlled I/O load 를 발생시키고 VM2 의 애플리케이션 지연과 Host storage 지표를 동시에 본다. 부하를 무엇으로 만들지, 얼마나 크게 얼마나 오래 걸지는 그 한 문장에 없어서 재는 쪽이 정한다.
이 호스트에서 그렇게 재 본 결과는 개념 문서다. §168 의 30% 와 2초 도 CPU 와 지연이 어긋날 수 있다는 것을 보이려고 든 예시 값이다.
이 호스트의 배치는 §197 과 §199 가 적었다. 루트가 `/dev/nvme0n1p3` 위에 있고 게스트 이미지가 전부 `/var/lib/libvirt/images/` 아래다. `virsh pool-info default` 가 낸 Capacity 225.31 GiB 는 §199 가 호스트 루트 파일시스템이라고 적은 그 풀의 값이다. 그 디렉터리가 그 파티션 위에 있다는 것을 `findmnt` 로 확인한 출력은 없다.
이 호스트에서 부하를 걸고 두 값을 나란히 재 본 결과는 SSOT 에 없다. §168 의 30% 와 2초도 CPU 와 지연이 어긋날 수 있다는 것을 보이려고 든 예시 값이다.
## 가정
두 가상 머신의 이미지가 같은 block device 위에 있다고 보고 실험을 짠다. 앞 물음이 서로 다른 장치라고 확정하면 이 전제가 없어진다.
두 가상 머신의 이미지가 같은 block device 위에 있다고 보고 실험을 짠다. 같은 디렉터리에 있다는 것까지는 §199 가 보였고, 그 디렉터리 아래를 장치까지 이은 출력은 앞 물음이 낸다.
VM1 에 controlled I/O load 를 걸었다가 걷을 수 있고, 걷은 뒤 상태가 부하 이전의 기준 구간으로 돌아온다고 본다. 돌아오는지는 세 번째 구간에서 확인한다.
@@ -43,7 +43,7 @@ durability 는 전원이 나가도 데이터가 남아 있는 영속성을 말
이 기준은 빨라진 것을 되돌리라고 하지 않는다. 무엇이 빨라졌는지 옆에 어느 보장이 사라졌는지를 같이 적게 한다.
이것을 완료의 네 단계를 설명하는 개념 기록 안 각주로 두지 않고 따로 뺀 것은 이이 필요해지는 때가 다르기 때문이다. 개념을 읽을 때가 아니라 설정을 고르고 성능 개선을 평가할 때 걸리는데, 각주로 두면 다음에 같은 판단을 하는 사람이 그때 이것을 찾지 못한다.
완료의 네 단계를 설명하는 개념 기록 안 각주로 두지 않고 따로 뺀 것은 이 기준이 필요해지는 때가 다르기 때문이다. 개념을 읽을 때가 아니라 설정을 고르고 성능 개선을 평가할 때 걸리는데, 각주로 두면 다음에 같은 판단을 하는 사람이 그때 이것을 찾지 못한다.
## 규칙
@@ -63,7 +63,7 @@ FLUSH 완료 응답을 받은 쪽은 그 데이터가 살아남는다고 가정
개념 문서는 none 을 캐시 자체가 없음으로, writeback 을 무조건 위험으로 읽는 것이 부정확하다고 적었다. cache=writeback 을 골라도 정상적인 스택이라면 게스트의 fsync 와 FLUSH 는 virtio FLUSH 와 QEMU 와 백엔드를 지나 호스트의 sync 와 flush 로 전달된다. 필요한 완료 확인 뒤에 게스트로 완료가 돌아간다. 개념 문서는 정확한 표현을 이렇게 적었다 — writeback caching 에서는 volatile cache 가 존재할 수 있으므로 Guest 의 flush/fsync semantics 가 전체 backend/storage stack 에서 올바르게 보존되는지가 중요하다.
그래서 이 기준은 어느 캐시 모드를 쓰라고 말하지 않는다. 개념 문서는 두 설정의 뜻과 각각의 오해를 갈라 놓기만 했고 어느 쪽으로 정했다고 적지 않았으며 감수한 비용도 적지 않았다. 이 호스트가 지금 무엇을 쓰고 있는지도 아직 읽지 않았다.
그래서 이 기준은 어느 캐시 모드를 쓰라고 말하지 않는다. 개념 문서는 두 설정의 뜻과 각각의 오해를 갈라 놓기만 했고 어느 쪽으로 정했다고 적지 않았으며 감수한 비용도 적지 않았다. 이 호스트가 지금 무엇을 쓰고 있는지도 아직 읽지 않았다. 이 저장소에 남은 것은 게스트를 만든 virt-install 세 줄에 캐시 옵션이 없다는 것까지다.
### 5. DB 부하에서는 애플리케이션이 쓰는 영속성 규약을 함께 적는다
@@ -81,7 +81,7 @@ PostgreSQL 은 WAL 등의 durability protocol 을 사용하며 필요한 시점
- 영속성을 실제로 포기해도 되는 데이터가 있다. 다시 만들 수 있는 캐시나 파생 파일이 그렇고, 그때는 빨라진 것이 정당한 선택이다. 이 기준이 요구하는 것은 포기를 막는 것이 아니라 무엇을 포기했는지 적게 하는 것이다.
- cache=none 이나 Direct I/O 를 골랐다고 영속성이 확보되지도 않는다. 개념 문서는 Direct I/O 의 핵심이 페이지 캐시 우회이며 호스트 페이지 캐시를 우회하는 것이 즉시 durable media 반영을 뜻하지 않는다고 적었고, 그 뒤에 스토리지 컨트롤러나 장치가 휘발성 쓰기 캐시를 가질 수 있다고 덧붙였다.
- 개념 문서는 실제 ordering 과 durability semantics 가 더 복잡하다고 밝혔다. 그래서 이 기준으로 특정 설정이 안전하다는 결론을 내지 않는다. 실제 운영에서는 장치의 flush 와 FUA semantics, power-loss protection 여부도 함께 본다.
- 개념 문서는 실제 순서 보장과 영속성의 의미가 더 복잡하다고 밝혔다. 그래서 이 기준으로 특정 설정이 안전하다는 결론을 내지 않는다. 실제 운영에서는 장치의 flush 와 FUA semantics, power-loss protection 여부도 함께 본다.
- 이 저장소에는 영속성을 실제로 재 본 실험이 없다. 여기 적은 것은 개념 문서가 서술한 구분이고 이 호스트에서 확인된 동작이 아니다.
## 예시
@@ -92,4 +92,4 @@ PostgreSQL 은 WAL 등의 durability protocol 을 사용하며 필요한 시점
- 전원 장애에도 안전한 상태 : 위 셋과 다른 단계로 센다
- 빨라졌다 : 어느 단계를 건너뛰었는지 옆에 적는다
- 포기해도 되는 데이터 : 다시 만들 수 있는 캐시 · 파생 파일
- 이 호스트에서 잰 영속성 측정 : x
- 이 호스트에서 잰 영속성 : x
@@ -54,20 +54,24 @@ source:
### 2. Target 과 Source 를 이어 붙인 뒤에 판독을 시작한다
게스트에서 lsblk 로 보이는 블록 장치와 파티션을 적고, 호스트에서 virsh domblklist <VM_NAME> 로 Target 과 Source 를 적는다. 개념 문서가 든 예시에서는 Target vda 에 Source /var/lib/libvirt/images/vm1.qcow2 가 붙어 있었다. 이 대응이 있어야 게스트의 /dev/vda 에서 virtio-blk 와 QEMU 를 지나 그 파일까지 이어진다. 그 경로는 개념 문서가 예시로 붙여 둔 이름이고 이 호스트에서 읽은 값이 아니다 — 두 명령을 이 호스트에서 돌린 출력은 아직 없다.
게스트에서 lsblk 로 보이는 블록 장치와 파티션을 적고, 호스트에서 virsh domblklist <VM_NAME> 로 Target 과 Source 를 적는다. 개념 문서가 든 예시에서는 Target vda 에 Source /var/lib/libvirt/images/vm1.qcow2 가 붙어 있었다. 이 대응이 있어야 게스트의 /dev/vda 에서 virtio-blk 와 QEMU 를 지나 그 파일까지 이어진다. 그 경로는 개념 문서가 예시로 붙여 둔 이름이다. 두 명령을 이 호스트에서 돌린 출력은 아직 없다.
### 3. Source 가 파일인지 호스트 블록 장치인지 확정한다
개념 문서는 백엔드가 반드시 파일일 필요는 없다고 적고 게스트의 /dev/vda 아래에 호스트의 /dev/nvme0n1p3 이 오는 구성을 들었다. 파일이면 qemu-img info 에 그 경로를 주어 형식을 읽고, 호스트 블록 장치면 qcow2 쪽 확인 항목은 애초에 걸리지 않는다.
두 명령이 필요로 하는 것도 다르다. qemu-img info 는 이미지 파일만 만지는 도구라 가상 머신이 꺼져 있어도 돌고 도메인이 아예 없어도 돈다. Target 과 Source 를 잇는 virsh domblklist 는 도메인이 있어야 하므로, 이미지 파일만 손에 있는 상태에서 확정되는 것은 형식까지다.
### 4. 용량은 virtual size 와 실제 할당을 갈라 읽는다
qcow2 는 가상 디스크 크기와 실제 호스트 할당량이 다를 수 있다. 개념 문서가 든 그림은 게스트가 보는 공간 100 GB 에 호스트 실제 할당 3GB 였다. 게스트가 데이터를 기록하면서 Actual 이 1GB 에서 10GB 로, 다시 40GB 로 늘 수 있다는 예도 함께 보였다. qemu-img info 를 볼 때 virtual size 와 실제 allocation 을 구분해서 봐야 한다.
qcow2 는 가상 디스크 크기와 실제 호스트 할당량이 다를 수 있다. 개념 문서가 든 그림은 게스트가 보는 공간 100 GB 에 호스트 실제 할당 3GB 였다. 게스트가 데이터를 기록하면서 실제 할당량이 1GB 에서 10GB 로, 다시 40GB 로 늘 수 있다는 예도 함께 보였다. qemu-img info 를 볼 때 virtual size 와 실제 allocation 을 구분해서 봐야 한다.
### 5. 파일이면 그 파일이 놓인 호스트 파일시스템과 블록 장치까지 적는다
백엔드가 qcow2 파일이면 QEMU 는 결국 호스트 리눅스에 파일 입출력을 요청한다. 그 요청은 Host Filesystem 과 Host Block Layer 와 NVMe Driver 를 지나 Physical NVMe 로 내려간다. 게스트의 스토리지 스택 아래에 호스트의 스토리지 스택이 한 번 더 있으므로, 어느 파일시스템과 어느 블록 장치 위에 있는지를 lsblk 로 함께 적는다.
적을 파일이 하나가 아닐 수 있다. qemu-img info 출력에 backing file 줄이 있으면 그 파일은 오버레이이고, 자기가 들고 있지 않은 클러스터를 읽을 때마다 바닥 파일을 읽는다. 바닥까지 같은 항목으로 적어야 그 게스트가 실제로 읽는 파일이 모두 덮인다.
## 적용 조건
- 가상 머신의 디스크 성능이나 용량이나 영속성을 판단하기 전
@@ -78,8 +82,8 @@ qcow2 는 가상 디스크 크기와 실제 호스트 할당량이 다를 수
## 예외
- 백엔드가 호스트 블록 장치면 qcow2 쪽 확인은 걸리지 않는다. 가상 크기와 실제 할당량의 차이도, 매핑과 메타데이터 처리도 그 구성에는 없다.
- 백엔드를 확정해도 성능은 예측되지 않는다. 개념 문서는 RAW 는 무조건 빠르고 qcow2 는 무조건 느리다는 식으로 일반화하지 말라고 못박고, 실제 성능 캐시 모드 스토리지 백엔드 부하 패턴 큐 깊이 스냅숏 체인 그 아래 파일시스템 물리 장치에 영향을 받는다고 었다. 이 기준은 무엇 위에서 재고 있는지까지만 확정한다.
-저장소에는 이 절차를 실제로 돌린 출력이 없다. 여기 적은 것은 개념 문서가 서술한 확인 순서이고 이 호스트에서 나온 값이 아니다.
- 백엔드를 확정해도 성능은 예측되지 않는다. 개념 문서는 RAW 는 무조건 빠르고 qcow2 는 무조건 느리다는 식으로 일반화하지 말라고 못박고, 실제 성능 캐시 모드, 스토리지 백엔드, 부하 패턴, 큐 깊이, 스냅숏 체인, 그 아래 파일시스템, 물리 장치에 영향을 받는다고 었다. 이 기준은 무엇 위에서 재고 있는지까지만 확정한다.
-절차를 처음부터 끝까지 돌린 출력은 이 저장소에 없다. 네 번째 규칙의 qemu-img info 는 바닥 이미지 하나에만 돌렸고, virsh domblklist 와 게스트 lsblk 의 출력은 아직 없다. 나머지는 개념 문서가 서술한 확인 순서이고 이 호스트에서 나온 값이 아니다.
## 예시
@@ -88,4 +92,5 @@ qcow2 는 가상 디스크 크기와 실제 호스트 할당량이 다를 수
- Source 의 세 갈래 : qcow2 파일 · RAW 파일 · 호스트 블록 장치
- 용량 : virtual size 와 실제 allocation 을 따로 적는다
- 파일이면 더 볼 것 : 그 파일이 올라간 파일시스템과 블록 장치
- 이 호스트에서 돌린 출력 : x
- 이 호스트에서 돌린 qemu-img info : o — 바닥 이미지 하나뿐이다
- 이 호스트에서 돌린 virsh domblklist : x
@@ -73,7 +73,7 @@ NVMe
### 5. 무엇을 볼지 정한 뒤에 명령을 고른다
명령 목록 자체는 규칙이 아니다. 무엇을 함께 볼지는 앞의 규칙들이 정하고 명령은 그 도구를 댈 뿐이라, 목록만 옮겨 적으면 무엇을 가르려고 그것을 보는지가 남지 않는다.
명령 목록 자체는 규칙이 아니다. 무엇을 함께 볼지는 앞의 규칙들이 정하고 명령은 그 도구 뿐이라, 목록만 옮겨 적으면 무엇을 가르려고 그것을 보는지가 남지 않는다.
장치 입출력 관측은 iostat -xz 1 로 한다. 읽기와 쓰기의 처리량과 IOPS 와 요청 지연과 큐 상태와 device utilization 성격의 지표가 여기서 나오고, 어떤 프로세스가 입출력을 발생시키는지는 iotop 으로 본다. 개념 문서가 양쪽에 나눠 적은 명령은 이렇다.
@@ -90,7 +90,7 @@ Host : virsh domblklist <VM_NAME> · qemu-img info 에 disk image 경로 · lsbl
## 예외
- 이 기준은 어느 자원인지를 좁힐 뿐 원인을 확정하지 않는다. 스토리지 지표가 깨끗하게 나와도 CPU 쪽을 배제하려면 vCPU 경쟁과 steal time 을 따로 본다. 그것은 CPU 가상화 쪽 기준이 받는다.
- iostat -xz 1 이 보여 주는 device utilization 성격의 지표는 NVMe 처럼 병렬성이 큰 장치에서 포화도로 그대로 읽히지 않는다. 개념 문서가 NVMe 는 높은 병렬성과 큐 깊이를 지원한다고 적었다.
- iostat -xz 1 이 보여 주는 device utilization 성격의 지표는 NVMe 처럼 병렬성이 큰 장치에서 포화도로 그대로 읽히지 않는다. 개념 문서가 NVMe 는 높은 병렬성과 큐 깊이를 지원한다고 적었다. 이 저장소의 실측 기록은 이 호스트의 루트를 /dev/nvme0n1p3 로 적었고 게스트 이미지가 전부 그 아래에 모여 있다고 적었으므로, 이 예외는 이 환경에 그대로 걸린다.
- 호스트에서 잰 값은 어느 가상 머신의 입출력인지 갈라 주지 않는다. 개념 문서는 Host Block Layer 가 그 입출력을 가상 머신 안의 프로세스가 시작했는지 호스트 프로세스가 시작했는지 본질적으로 구분해서 처리하는 계층이 아니라고 밝혔다. 가상 머신별로 가르려면 iotop 이나 게스트 쪽 관측을 같은 시각에 함께 찍는다.
- 이 저장소에는 이 기준으로 원인을 실제로 가른 측정이 없다. 여기 적은 것은 개념 문서가 서술한 판독 순서이고 이 호스트에서 확인된 값이 아니다.