feat: 가상화 문서들 추가
This commit is contained in:
+161
@@ -0,0 +1,161 @@
|
||||
---
|
||||
id: 3b05c1f0-13e9-414a-97a6-2078fb4bab26
|
||||
kind: CONCEPT
|
||||
slug: guest-block-io-path-to-virtqueue
|
||||
title: Guest 의 write() 가 virtqueue 에 실리기까지 — VFS · Filesystem · Page Cache · Block Layer · virtio-blk
|
||||
topic: storage-virtualization
|
||||
topicName: 스토리지 가상화
|
||||
project: virtualization
|
||||
status: 초안
|
||||
basisVersion: QEMU/KVM 의 virtio-blk frontend 와 virtqueue · 게스트가 ext4 나 XFS 에 buffered I/O 로 쓰는 경우 · SSOT 가 커널과 QEMU 버전을 적지 않았다
|
||||
studio: "https://hyeonworks.com/studio/documents/3b05c1f0-13e9-414a-97a6-2078fb4bab26/edit"
|
||||
assets:
|
||||
- key: guest-block-io-to-virtqueue
|
||||
file: ../../../final/assets/diagrams/guest-block-io-to-virtqueue/guest-block-io-to-virtqueue.svg
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#128-전체-구조
|
||||
- final/document.md#129-guest-application-read-write-에서-시작
|
||||
- final/document.md#130-vfs-공통-파일-인터페이스-계층
|
||||
- final/document.md#131-filesystem-ext4-xfs-파일-세계를-block-공간에-배치
|
||||
- final/document.md#132-inode
|
||||
- final/document.md#133-page-cache-write-가-바로-ssd-write는-아니다
|
||||
- final/document.md#134-guest-block-i-o-layer
|
||||
- final/document.md#135-dev-vda-guest가-보는-가상-block-device
|
||||
- final/document.md#136-dev-vda-와-filesystem-관계
|
||||
- final/document.md#137-virtio-blk-guest의-가상-block-device-driver
|
||||
- final/document.md#138-virtio-blk와-virtqueue
|
||||
- final/document.md#139-virtqueue의-실제-의미
|
||||
- final/document.md#166-storage-i-o-completion
|
||||
- final/document.md#127-문서-목적
|
||||
- final/document.md#172-storage-virtualization-canonical-flow
|
||||
- final/document.md#173-network-virtualization과-비교
|
||||
- final/document.md#177-최종-요약
|
||||
---
|
||||
|
||||
# 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 멀티 노드 실험을 가상 머신 두 대 위에서 돌리고 있다 보니, 이 경로를 알아 두면 게스트에서 본 저장소 지연을 애플리케이션 문제와 가상 머신 경계 아래의 문제로 갈라 볼 수 있다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **/dev/vda 뒤에 있는 것 — qcow2 · RAW · Host block device**
|
||||
이 글은 virtqueue 까지만 따라간다. 그 요청을 받은 QEMU 가 무엇에 연결돼 있는지는 그 글이 이어받는다.
|
||||
- **완료라는 말의 네 가지 뜻 — write · writeback · flush · 전원 장애 생존**
|
||||
여기서는 write() 성공이 SSD 영속화가 아니라는 데까지만 적었다. 그 응답이 어디까지 보장하는지는 그 글이 나눈다.
|
||||
- **VM 아래에 한 번 더 있는 Host block stack — blk-mq · I/O Scheduler · NVMe**
|
||||
게스트 블록 계층과 같은 이름의 계층이 호스트에도 한 번 더 있고, 그쪽에서는 다른 가상 머신의 요청과 섞인다.
|
||||
- **Guest 안에서 본 disk 로 backend 를 단정하지 않는다**
|
||||
게스트에 /dev/vda 가 보인다는 관측이 그 규칙의 근거가 된다.
|
||||
- **이 호스트의 /dev/vda 는 어떤 backend 에 붙어 있는가**
|
||||
이 글은 게스트가 보는 이름까지만 말한다. 이 장비에서 그 이름이 무엇에 붙어 있는지는 아직 확인하지 않았다.
|
||||
- **Guest 의 fsync() 지연과 Host storage 지연은 같이 오르는가**
|
||||
경로는 적었지만 각 구간의 지연을 이 호스트에서 재지 않았다.
|
||||
- **Guest 의 packet 이 Host Physical NIC 에 닿기까지 — virtio-net · virtqueue · vhost-net · TAP · Bridge**
|
||||
virtqueue 라는 같은 구조를 패킷 쪽에서 설명한 글이다.
|
||||
|
||||
## 본문
|
||||
|
||||
<!-- body:start -->
|
||||
|
||||
## 애플리케이션은 저장장치를 직접 다루지 않는다
|
||||
|
||||
가상 머신 안의 PostgreSQL 이나 Keycloak 같은 프로세스는 SSD 나 `virtio-blk` 를 직접 다루지 않는다. 파일에 데이터를 기록할 때 개념적으로 부르는 것은 시스템 콜 하나다.
|
||||
|
||||
```c label="게스트 애플리케이션이 파일에 기록할 때 부르는 system call"
|
||||
write(fd, buffer, size);
|
||||
```
|
||||
|
||||
파일과 관련된 시스템 콜은 이것 말고도 여럿이다.
|
||||
|
||||
```text label="대표적인 파일 관련 system call"
|
||||
open()
|
||||
read()
|
||||
write()
|
||||
close()
|
||||
fsync()
|
||||
```
|
||||
|
||||
이 시점에는 QEMU 도 `qcow2` 도 호스트의 NVMe 도 등장하지 않는다. 애플리케이션은 저장장치를 조작하는 것이 아니라 게스트 Linux 커널에 파일 연산을 요청하고, 그 뒤의 계층들이 그 요청을 하나씩 옮긴다.
|
||||
|
||||
## VFS 가 어느 파일시스템으로 보낼지 정한다
|
||||
|
||||
VFS(Virtual File System, 가상 파일 시스템)는 Linux 커널 안에서 여러 파일시스템을 같은 방식으로 쓸 수 있게 묶어 주는 공통 계층이다. 게스트가 `ext4` 를 쓰든 `XFS` 를 쓰든 애플리케이션이 부르는 `write()` 는 같고, 그 뒤를 VFS 가 갈라 준다. VFS 는 이 `fd` 가 어떤 파일인지 확인하고, 그 파일이 어떤 파일시스템에 속하는지 확인한 뒤 해당 파일시스템 구현으로 연산을 넘긴다.
|
||||
|
||||
## ext4 와 XFS 가 파일을 블록 공간에 배치한다
|
||||
|
||||
SSD 는 `/var/lib/postgresql/data` 같은 디렉터리 구조를 모른다. 저장장치가 보는 것은 번호가 붙은 블록 단위 공간인데 사람과 프로그램이 보는 것은 파일과 디렉터리 계층이다. 그 둘을 잇는 계층이 `ext4` 나 `XFS` 같은 파일시스템이다. 파일시스템이 관리하는 것은 파일 이름과 디렉터리 구조, 파일 크기, 소유자와 권한, 시각 정보, `inode` 와 메타데이터, 파일 데이터가 저장될 블록, 남은 공간, 파일시스템 일관성이다.
|
||||
|
||||
`inode` 는 파일 메타데이터와 저장 위치 정보를 관리하는 자료구조다. 디렉터리 항목이 파일 이름을 `inode` 번호로 옮기고, 그 `inode` 가 소유자와 권한, 크기, 시각 정보, 파일 데이터가 저장된 블록 정보를 들고 있다. 그래서 파일 이름과 `inode` 는 같은 것이 아니다. 어디까지 들어갈지는 SSOT 가 직접 그어 두었다.
|
||||
|
||||
> Storage 가상화를 이해하기 위해 inode 내부 구현까지 파고들 필요는 없지만, filesystem이 파일과 block을 연결한다는 점은 알아야 한다.
|
||||
|
||||
이 글도 거기서 멈춘다.
|
||||
|
||||
## write() 가 끝나도 SSD 에는 아직 없다
|
||||
|
||||
일반적인 buffered I/O 에서는 `write()` 가 호출될 때마다 물리 SSD 까지 즉시 내려갈 필요가 없다. 애플리케이션이 넘긴 데이터는 메모리의 페이지 캐시에 먼저 닿는다. 저장장치에 `ABC` 가 있는데 애플리케이션이 `DEF` 를 추가하면 페이지 캐시에는 `ABCDEF` 가 들어가고 SSD 에는 아직 `ABC` 만 있다. 이렇게 저장장치보다 최신인 페이지를 dirty page 라고 한다. 이후 커널 writeback 이 그 내용을 파일시스템과 블록 계층을 거쳐 저장장치 쪽으로 내려보낸다.
|
||||
|
||||
데이터가 페이지 캐시에 머무는 동안에도 호출이 성공으로 돌아오기 때문에, `write()` 성공과 물리 SSD 영속화 완료는 같은 사건이 아니다. 게스트 애플리케이션이 성공을 돌려받은 시점에 데이터가 어디에 있는지는 게스트 메모리까지만 확정된다.
|
||||
|
||||
## 블록 I/O 계층이 파일 세계를 블록 장치 세계로 옮긴다
|
||||
|
||||
파일시스템이 파일과 블록 할당을 관리하면, 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 같은 내부 구현을 별도 문서로 미뤘고 이 글도 이름까지만 적는다.
|
||||
|
||||
## /dev/vda 는 게스트가 보는 이름이다
|
||||
|
||||
물리 머신에서는 `/dev/sda` 나 `/dev/nvme0n1` 같은 블록 장치가 보인다. `virtio-blk` 를 쓰는 가상 머신에서는 흔히 `/dev/vda` 와 `/dev/vdb` 처럼 보이고, 게스트 Linux 는 그것을 하나의 블록 장치로 인식한다. 게스트 안에서 `lsblk` 를 실행하면 그 장치와 파티션이 나온다.
|
||||
|
||||
```bash label="게스트가 보는 block device"
|
||||
lsblk
|
||||
```
|
||||
|
||||
```text label="SSOT 가 든 출력 예시. 이 호스트에서 실행한 결과가 아니다"
|
||||
NAME SIZE TYPE MOUNTPOINT
|
||||
vda 100G disk
|
||||
├─vda1 1G part /boot
|
||||
└─vda2 99G part /
|
||||
```
|
||||
|
||||
게스트에 이렇게 보여도 그 장치가 호스트의 실제 SSD 인지는 게스트 안에서 알 수 없다. 이 장비의 `/dev/vda` 가 무엇에 붙어 있는지도 아직 확인하지 않았다. 게스트가 보는 것은 `/dev/vda` 에서 파티션으로, 파티션에서 파일시스템으로, 파일시스템에서 마운트 지점으로 이어지는 연결까지다. `cd /var/lib/postgresql` 로 디렉터리를 옮기는 동작은 파일시스템 세계를 지나고, `lsblk` 의 `vda` 는 블록 장치 세계를 보여 준다.
|
||||
|
||||
이름 둘이 가리키는 대상도 다르다. `/dev/vda` 가 게스트 Linux 에 보이는 블록 장치를 가리키고, `virtio-blk` 는 그 가상 블록 장치를 제어하는 게스트 커널 드라이버를 가리킨다. 네트워크 쪽의 `ens3` 와 `virtio-net` 이 같은 짝이다.
|
||||
|
||||
## virtio-blk 가 요청을 virtqueue 에 게시한다
|
||||
|
||||
게스트 블록 계층에서 `/dev/vda` 의 특정 위치에 데이터를 `WRITE` 하라는 요청이 내려오면, `virtio-blk` 드라이버가 그것을 Virtio 블록 요청으로 구성해 `virtqueue` 에 게시한다. 네트워크에서 TCP/IP 스택이 `virtio-net` 을 거쳐 `virtqueue` 로 내려가던 구조가 저장소 쪽에서도 반복된다.
|
||||
|
||||
게스트 메모리 안에 I/O 버퍼가 있고 `virtqueue` 의 디스크립터가 그 버퍼를 가리키기 때문에, `virtqueue` 를 데이터가 흘러가는 관으로 보면 부정확하다. 게시되는 요청에는 `Operation` 이 `WRITE` 인지, `Sector` 가 어디인지, `Data Buffer` 가 게스트 메모리의 어디인지가 들어간다. 뜻을 풀면 `/dev/vda` 의 이 위치에 게스트 메모리의 이 버퍼를 기록하라는 요청이다.
|
||||
|
||||

|
||||
|
||||
## 완료는 같은 virtqueue 로 돌아온다
|
||||
|
||||
`WRITE` 요청은 아래로 내려가고 완료는 반대 방향으로 올라온다. NVMe 가 완료를 올리면 NVMe 드라이버와 호스트 블록 계층, QEMU 와 백엔드를 지나 `virtqueue` 완료가 된다. `virtio-blk` 가 그것을 받아 게스트 블록 계층으로 올린다. 그래서 `virtqueue` 는 요청을 내려보내는 통로이면서 완료를 올려보내는 통로이기도 하다.
|
||||
|
||||
## 네트워크 가상화와 짝이 되는 이름들
|
||||
|
||||
제3부의 네트워크 경로를 먼저 읽었다면 이름이 하나씩 대응된다.
|
||||
|
||||
| Network | Storage |
|
||||
|---|---|
|
||||
| `virtio-net` | `virtio-blk` |
|
||||
| packet | block I/O request |
|
||||
| TX/RX virtqueue | I/O virtqueue |
|
||||
| TAP / network backend | QEMU block backend |
|
||||
| Linux Bridge/Route | Host filesystem/block stack |
|
||||
| Physical NIC | Physical SSD/NVMe |
|
||||
| Guest TCP/IP Stack | Guest VFS/Filesystem/Block Layer |
|
||||
| send/recv | read/write/fsync |
|
||||
|
||||
SSOT 는 이 표를 학습용 대응으로 두었고, 두 열의 각 요소가 `1:1` 로 같은 종류라는 데까지는 말하지 않는다.
|
||||
|
||||
## 확인하지 못한 것과 범위 밖으로 둔 것
|
||||
|
||||
SSOT 는 이 부에서 다룰 것과 미룰 것을 먼저 갈라 두었다. `qcow2` 내부의 L1/L2 테이블과 `blk-mq` 의 tag allocator, NVMe 의 submission/completion 큐는 필요할 때 별도 문서에서 다루기로 했다.
|
||||
|
||||
이 호스트에서 잰 값은 하나도 없다. 게스트의 `/dev/vda` 가 호스트에서 무엇에 붙어 있는지는 SSOT 가 적어 둔 열린 질문 가운데 첫 번째인데, 아직 확인하지 않았다. 게스트의 파일시스템이 `ext4` 인지 `XFS` 인지도 SSOT 어디에도 없어서, 이 글은 두 파일시스템이 같은 계층을 지난다는 데까지만 말한다. 요청 수나 지연을 재려면 게스트의 `fsync()` 지연과 호스트 저장소 지연을 같은 시간축에서 함께 보는 관찰이 먼저 돌아야 한다.
|
||||
|
||||
<!-- body:end -->
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
---
|
||||
id: ecaaa37d-7b33-4cbf-9bc5-4c6caad14ee5
|
||||
kind: CONCEPT
|
||||
slug: host-block-stack-under-the-vm
|
||||
title: VM 아래에 한 번 더 있는 Host block stack — blk-mq · I/O Scheduler · NVMe
|
||||
topic: storage-virtualization
|
||||
topicName: 스토리지 가상화
|
||||
project: virtualization
|
||||
status: 초안
|
||||
basisVersion: 현대 Linux 의 blk-mq 기반 block layer 와 NVMe driver · scheduler 는 none · mq-deadline · bfq 기준
|
||||
studio: "https://hyeonworks.com/studio/documents/ecaaa37d-7b33-4cbf-9bc5-4c6caad14ee5/edit"
|
||||
assets:
|
||||
- key: vms-sharing-one-nvme
|
||||
file: ../../../final/assets/diagrams/vms-sharing-one-nvme/vms-sharing-one-nvme.svg
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#158-host-block-layer
|
||||
- final/document.md#159-여러-vm이-하나의-nvme를-공유하면
|
||||
- final/document.md#160-blk-mq-multi-queue-block-layer
|
||||
- final/document.md#161-i-o-scheduler
|
||||
- final/document.md#162-none
|
||||
- final/document.md#163-실제-i-o-scheduler-확인
|
||||
- final/document.md#164-nvme-driver와-physical-device
|
||||
- final/document.md#165-nvme와-ssd-구분
|
||||
- final/document.md#167-storage-contention
|
||||
- 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 계층과 갈라 두면 어느 쪽을 재야 하는지가 먼저 정해진다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **Guest 의 write() 가 virtqueue 에 실리기까지 — VFS · Filesystem · Page Cache · Block Layer · virtio-blk**
|
||||
virtqueue 까지가 게스트 쪽 경로이고, 이 글은 그 요청이 가상 머신 경계를 넘어온 뒤를 잇는다.
|
||||
- **/dev/vda 뒤에 있는 것 — qcow2 · RAW · Host block device**
|
||||
백엔드가 파일인지 블록 장치인지에 따라 이 글이 설명하는 계층 위에 호스트 파일시스템이 한 겹 더 놓인다.
|
||||
- **완료라는 말의 네 가지 뜻 — write · writeback · flush · 전원 장애 생존**
|
||||
같은 계층을 영속성 쪽에서 본 글이다. 이 글은 요청이 어떻게 줄을 서고 누구와 섞이는지를 본다.
|
||||
- **지연 원인을 CPU 로 읽기 전에 storage 계층을 따로 잰다**
|
||||
이 글이 가른 두 경쟁을 진단 순서로 편 규칙이다.
|
||||
- **disk image 는 최종적으로 어느 Host block device 위에 있는가**
|
||||
이 호스트의 디스크 이미지가 어느 블록 장치 위에 있는지 SSOT 에 적혀 있지 않다.
|
||||
- **이 호스트의 I/O Scheduler 는 무엇인가**
|
||||
스케줄러 값을 읽는 방법은 여기 적었고, 이 호스트의 값은 그 질문이 확인한다.
|
||||
- **VM1 의 storage 부하가 VM2 의 지연을 실제로 밀어 올리는가**
|
||||
스토리지 경쟁은 이 글에서 구조로만 설명했다. 이 환경에서 실제로 겹치는지는 그 질문이 잰다.
|
||||
|
||||
## 본문
|
||||
|
||||
<!-- body:start -->
|
||||
|
||||
## 게스트 디스크 파일이 호스트 블록 요청이 되는 곳
|
||||
|
||||
백엔드가 `qcow2` 파일이면 QEMU 가 물리 SSD 를 직접 제어하지 않는다. QEMU 는 `pread` 나 `pwrite` 같은 호출로 호스트 커널에 파일 I/O 를 요청하고, 그 요청이 호스트 파일시스템과 호스트 블록 계층, NVMe 드라이버를 지나 물리 NVMe 에 닿는다. 게스트 스토리지 계층 아래에 호스트 스토리지 계층이 한 번 더 존재할 수 있다.
|
||||
|
||||
호스트 블록 계층은 그 I/O 가 가상 머신 안의 PostgreSQL 에서 시작했는지 호스트 프로세스에서 시작했는지를 본질적으로 구분해서 처리하는 계층이 아니다. 내려온 것은 모두 호스트 블록 요청이다.
|
||||
|
||||
| 호스트 쪽 어느 계층인가 | 여기서 무엇이 일어나나 |
|
||||
|---|---|
|
||||
| 호스트 파일시스템 | `qcow2` 나 RAW 파일을 읽고 쓰는 일이 파일 I/O 로 처리된다 |
|
||||
| 호스트 블록 계층 | 어디서 왔는지 구분하지 않고 모두 블록 요청으로 다룬다 |
|
||||
| `blk-mq` | CPU 마다 큐를 두어 여러 CPU 가 병렬로 블록 I/O 를 처리한다 |
|
||||
| I/O 스케줄러 | 들어온 순서 그대로가 아니라 정책에 따라 장치로 내보낸다 |
|
||||
| NVMe 드라이버 | 호스트 커널의 장치 드라이버로서 NVMe 컨트롤러에 요청을 보낸다 |
|
||||
| NVMe 컨트롤러 | PCIe 를 통해 요청을 받아 플래시에 반영한다 |
|
||||
|
||||
## 한 NVMe 로 여러 곳의 요청이 들어온다
|
||||
|
||||
가상 머신 둘의 QEMU 프로세스와 호스트의 Nginx, 그리고 호스트의 다른 프로세스에서 동시에 I/O 가 들어올 수 있다. VM1 이 X 를 쓰고 Y 를 읽고 Z 를 쓰는 동안 VM2 는 A 를 읽고 B 를 쓰고, 호스트 프로세스는 C 를 읽는 식이다. 이 요청들은 호스트 블록 계층의 큐에서 관리되다가 장치로 나간다.
|
||||
|
||||

|
||||
|
||||
## `blk-mq` 는 CPU 마다 큐를 둔다
|
||||
|
||||
현대 Linux 의 블록 계층은 `blk-mq` 로 되어 있다. CPU0 부터 CPU3 까지 각 CPU 가 자기 큐를 가지고 그 큐들이 NVMe 로 이어진다. NVMe 가 높은 병렬성과 큐 깊이를 지원하기 때문에 여러 CPU 가 병렬로 블록 I/O 를 처리할 수 있는 구조가 중요해진다. 그래서 스토리지 처리 성능은 CPU 스케줄링과 완전히 분리되지 않는다.
|
||||
|
||||
## I/O 스케줄러는 들어온 순서 그대로 내보내지 않는다
|
||||
|
||||
여러 I/O 요청이 있어도 항상 들어온 순서 그대로 장치에 전달되지는 않는다. 요청은 I/O 스케줄러를 지나 장치 드라이버로 나간다. 스케줄러마다 목적과 내보내는 정책이 다르다. 대표적으로 볼 수 있는 값은 `none` 과 `mq-deadline` 과 `bfq` 다.
|
||||
|
||||
`none` 을 고르면 복잡한 스케줄링 정책을 최소화해서 비교적 직접 장치 쪽으로 내보낸다. NVMe 처럼 장치 자체가 강한 병렬성과 큐 기능을 가진 경우에는 이런 단순한 정책이 적합할 수 있다. 그렇게 골랐다고 블록 계층이 아무 일도 하지 않지는 않는다.
|
||||
|
||||
지금 선택된 스케줄러는 `sysfs` 에서 읽는다.
|
||||
|
||||
```bash label="호스트의 현재 I/O Scheduler"
|
||||
cat /sys/block/nvme0n1/queue/scheduler
|
||||
```
|
||||
|
||||
출력이 `[none] mq-deadline` 이면 대괄호 안의 `none` 이 지금 선택된 스케줄러다. SATA 나 SCSI 장치라면 `cat /sys/block/sda/queue/scheduler` 처럼 장치 이름을 바꿔 읽는다.
|
||||
|
||||
여기 든 `[none] mq-deadline` 은 SSOT 가 읽는 법을 보이려고 든 출력이다. 이 호스트에서 그 명령을 아직 돌리지 않아서 이 장비의 스케줄러가 무엇인지는 이 글이 말하지 않는다.
|
||||
|
||||
## 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 컨트롤러에 요청을 보낸다. 컨트롤러는 플래시를 다룬다.
|
||||
|
||||
## 스토리지 경쟁과 CPU 경쟁은 다투는 자원이 다르다
|
||||
|
||||
여러 가상 머신이 같은 물리 NVMe 를 쓰면 스토리지 자원 경쟁이 생길 수 있다. VM1 의 PostgreSQL 과 VM2 의 Keycloak 이 요청한 I/O 가 같은 호스트 블록 계층과 같은 I/O 큐를 지나 하나의 NVMe 로 가기 때문에, VM1 에서 대량 I/O 가 발생하면 VM2 의 스토리지 지연이 올라갈 수 있다. 이렇게 벌어지는 경쟁을 스토리지 경쟁(Storage Contention)이라고 한다.
|
||||
|
||||
```text label="두 경쟁이 다투는 자원"
|
||||
CPU Contention
|
||||
→ Host logical CPU 실행 시간 경쟁
|
||||
|
||||
Storage Contention
|
||||
→ IOPS / bandwidth / queue / device 처리시간 경쟁
|
||||
```
|
||||
|
||||
CPU 경쟁은 호스트 논리 CPU 의 실행 시간을 두고 벌어지고, 스토리지 경쟁은 IOPS 와 대역폭, 큐, 장치 처리시간을 두고 벌어진다. 애플리케이션에서는 둘 다 「느리다」로 관찰되지만 다투는 자원이 달라서 따로 잰다.
|
||||
|
||||
## 이 호스트에서 확인하지 않은 것
|
||||
|
||||
이 글에도 이 테스트 호스트에서 잰 값은 없다. 이 호스트의 I/O 스케줄러가 무엇으로 설정돼 있는지 SSOT 에 없고, 가상 머신들의 디스크 이미지가 최종적으로 어느 호스트 블록 장치 위에 있는지도 없다. 가상 머신 두 대와 호스트 프로세스의 I/O 가 이 환경에서 실제로 같은 시간에 겹치는지도 재지 않았다. VM1 의 대량 I/O 가 VM2 의 스토리지 지연을 올린다는 것은 이 계층 구성에서 나오는 가능성이고 이 서버에서 관측한 값이 아니다. 이 글은 요청이 호스트에서 어떤 순서로 어느 계층을 지나는지까지 설명하고, 이 서버가 그 계층에서 실제로 막히는지는 재지 않았다.
|
||||
|
||||
<!-- body:end -->
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
---
|
||||
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 위에 있는가**
|
||||
파일 백엔드라면 그 파일이 놓인 호스트 장치까지 따라가야 경로가 끝난다.
|
||||
|
||||
## 본문
|
||||
|
||||
<!-- body:start -->
|
||||
|
||||
## 가상 머신 경계를 넘으면 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 <VM_NAME>
|
||||
```
|
||||
|
||||
```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 는 실제 성능을 가르는 조건만 열거했고 어느 쪽이 이 환경에서 빠른지는 재지 않았다.
|
||||
|
||||
<!-- body:end -->
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
---
|
||||
id: 8fafaeea-8746-4537-87a4-679a3a91aa4c
|
||||
kind: CONCEPT
|
||||
slug: write-completion-is-not-durability
|
||||
title: 완료라는 말의 네 가지 뜻 — write · writeback · flush · 전원 장애 생존
|
||||
topic: storage-virtualization
|
||||
topicName: 스토리지 가상화
|
||||
project: virtualization
|
||||
status: 초안
|
||||
basisVersion: Linux O_DIRECT 와 QEMU/libvirt disk 의 cache=none · cache=writeback 두 설정 기준
|
||||
studio: "https://hyeonworks.com/studio/documents/8fafaeea-8746-4537-87a4-679a3a91aa4c/edit"
|
||||
assets:
|
||||
- key: write-completion-boundaries
|
||||
file: ../../../final/assets/diagrams/write-completion-boundaries/write-completion-boundaries.svg
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#147-vm에서는-page-cache가-두-번-나타날-수-있다
|
||||
- final/document.md#148-write-완료와-영속화는-다르다
|
||||
- final/document.md#149-direct-i-o
|
||||
- final/document.md#150-fsync-가-필요한-이유
|
||||
- final/document.md#151-flush
|
||||
- final/document.md#152-가장-위험한-상황-거짓-완료
|
||||
- final/document.md#153-qemu-cache-mode
|
||||
- final/document.md#154-cache=none
|
||||
- final/document.md#155-cache=writeback
|
||||
- final/document.md#156-writeback-=-위험-이라고-단정하면-안-되는-이유
|
||||
- final/document.md#157-device-side-cache
|
||||
- final/document.md#170-postgresql-예시-wal과-durability
|
||||
- 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 을 가상 머신 위에서 돌리는 실험에서 이 구분을 먼저 세워 두면, 지연을 재는 것과 데이터가 남는지를 재는 것을 한 실험에 섞지 않게 된다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **Guest 의 write() 가 virtqueue 에 실리기까지 — VFS · Filesystem · Page Cache · Block Layer · virtio-blk**
|
||||
게스트 쪽 경로를 VFS 부터 virtqueue 까지 이어 그린 글이다. 이 글은 그 경로 위에서 완료 응답이 무엇을 보장하는지만 본다.
|
||||
- **/dev/vda 뒤에 있는 것 — qcow2 · RAW · Host block device**
|
||||
백엔드가 파일이면 게스트 파일시스템 아래에 호스트 파일시스템과 호스트 페이지 캐시가 한 겹 더 놓인다. 두 번째 페이지 캐시가 왜 생기는지를 그 글이 설명한다.
|
||||
- **VM 아래에 한 번 더 있는 Host block stack — blk-mq · I/O Scheduler · NVMe**
|
||||
fsync 요구가 호스트로 내려간 뒤 어느 계층을 지나는지는 그 글이 맡는다.
|
||||
- **빨라진 구성이 durability contract 를 지운 것은 아닌지 먼저 가른다**
|
||||
이 개념이 세운 네 경계를 설정 선택과 성능 개선의 판정에 적용하는 규칙이다.
|
||||
- **이 VM 들의 QEMU disk cache mode 는 무엇인가**
|
||||
이 호스트가 cache=none 구성인지 cache=writeback 구성인지 SSOT 에 적혀 있지 않다.
|
||||
- **Guest 의 fsync() 지연과 Host storage 지연은 같이 오르는가**
|
||||
fsync() 지연 값은 그 질문이 게스트와 호스트를 같은 시간축으로 잰 뒤에 이 글에 들어온다.
|
||||
|
||||
## 본문
|
||||
|
||||
<!-- body:start -->
|
||||
|
||||
## 게스트와 호스트에 페이지 캐시가 두 번 생긴다
|
||||
|
||||
일반적인 buffered I/O 에서는 `write()` 가 호출될 때마다 물리 SSD 까지 즉시 내려갈 필요가 없다. 커널은 그 내용을 메모리의 페이지 캐시에 받아 두고, 나중에 writeback 으로 파일시스템과 블록 계층을 지나 저장장치 쪽에 내려보낸다. 저장장치보다 최신인 페이지를 dirty page 라고 한다. 저장장치에 `ABC` 가 있는 상태에서 애플리케이션이 `DEF` 를 추가하면 페이지 캐시는 `ABCDEF` 를 dirty 상태로 들고 있고 SSD 에는 아직 `ABC` 만 있다.
|
||||
|
||||
가상 머신에서는 이 페이지 캐시가 한 번 더 나타날 수 있다. 게스트가 buffered I/O 를 쓰고 디스크 백엔드가 호스트의 파일이며 호스트도 페이지 캐시를 쓰는 구성이라고 하자. 게스트 PostgreSQL 의 쓰기는 게스트 `ext4` 와 게스트 페이지 캐시를 지나 게스트 블록 계층과 `virtio-blk` 를 거쳐 `virtqueue` 로 나간다. 가상 머신 경계를 넘은 다음에는 QEMU 와 디스크 이미지 파일을 지나 호스트 페이지 캐시에 다시 담긴다. 같은 데이터가 게스트 메모리와 호스트 메모리 양쪽에 캐시될 수 있다.
|
||||
|
||||
## write() 성공이 어디까지를 뜻하는가
|
||||
|
||||
`write()` 가 성공을 돌려준 시점에 데이터는 게스트 페이지 캐시에 있고, virtio 를 지나 호스트 페이지 캐시까지 갔을 수도 있다. 그 상태에서 호스트 전원이 나가면 물리 SSD 에는 그 데이터가 없기 때문에, 완료라는 말이 네 단계로 갈린다.
|
||||
|
||||
```text label="완료의 네 단계"
|
||||
write() 완료
|
||||
≠
|
||||
writeback 완료
|
||||
≠
|
||||
fsync/flush 완료
|
||||
≠
|
||||
전원 장애에도 안전한 durability
|
||||
```
|
||||
|
||||
## fsync() 는 요구를 계층 전체로 내려보낸다
|
||||
|
||||
애플리케이션이 부르는 쓰기 호출은 이렇게 생겼다.
|
||||
|
||||
```c label="이 호출이 성공해도 정전 이후 생존은 보장되지 않는다"
|
||||
write(fd, data, size);
|
||||
```
|
||||
|
||||
필요한 시점에 `fsync()` 를 불러 변경 내용을 필요한 영속성 경계까지 반영하도록 요청한다.
|
||||
|
||||
```c label="영속성 경계까지 반영을 요청한다"
|
||||
fsync(fd);
|
||||
```
|
||||
|
||||
가상 머신에서는 이 요청이 한 계층에서 끝나지 않는다. 게스트 PostgreSQL 의 `fsync()` 가 게스트 파일시스템으로 가고, 게스트 블록 계층에서 `FLUSH` 등으로 바뀐다. 그다음 `virtio-blk` 를 통해 QEMU 와 백엔드로 내려가고, 호스트 스토리지 계층을 지나 물리 저장장치까지 그 의미가 전달되어야 한다.
|
||||
|
||||

|
||||
|
||||
`WRITE` 와 `FLUSH` 가 요구하는 것은 서로 다르다.
|
||||
|
||||
```text label="WRITE 와 FLUSH 가 요구하는 것"
|
||||
WRITE
|
||||
↓
|
||||
"이 데이터를 써라"
|
||||
|
||||
FLUSH
|
||||
↓
|
||||
"앞서 쓴 데이터를 필요한 영속성 경계까지
|
||||
반영하고 완료 상태를 보장해라"
|
||||
```
|
||||
|
||||
실제 순서 보장과 영속성의 의미는 이 두 줄보다 복잡하다. SSOT 도 그렇게 밝혀 두었고, 이 글은 두 요청의 구분까지만 말한다.
|
||||
|
||||
## Direct I/O 가 우회하는 대상은 페이지 캐시다
|
||||
|
||||
buffered I/O 에서는 QEMU 의 쓰기가 호스트 페이지 캐시를 거쳐 호스트 파일시스템과 블록 계층을 지나 SSD 로 간다. Direct I/O 에서는 호스트 페이지 캐시를 건너뛰고 호스트 파일시스템과 블록 I/O 경로로 바로 들어간다. Linux 의 `O_DIRECT` 가 여기에 쓰인다. 우회하는 대상은 페이지 캐시이고, Direct I/O 로 썼다고 영속성이 자동으로 보장되지는 않는다.
|
||||
|
||||
## cache=none 과 cache=writeback 이 정하는 것
|
||||
|
||||
QEMU 와 libvirt 로 정의한 디스크에서 대표적으로 볼 수 있는 설정은 `cache=none` 과 `cache=writeback` 이다. 두 값이 정하는 것은 QEMU 가 호스트 페이지 캐시와 쓰기 완료·플러시의 의미를 어떤 방식으로 쓰는가다. 이름만으로는 캐시가 아예 없는지, 무조건 위험한지가 정해지지 않는다.
|
||||
|
||||
| 설정 | 호스트 페이지 캐시를 어떻게 쓰나 |
|
||||
|---|---|
|
||||
| `cache=none` | 우회하는 방향으로 구성한다. 이중 캐시를 줄일 수 있다 |
|
||||
| `cache=writeback` | 쓸 수 있게 구성한다. 일반 쓰기가 호스트 메모리에서 빠르게 완료될 수 있다 |
|
||||
|
||||
호스트 페이지 캐시를 우회한다고 해서 쓰기가 곧바로 영속 매체에 반영되지는 않는다. `cache=writeback` 으로 두어도 게스트의 `fsync()` 와 `FLUSH` 가 무시되지 않는다. 정상적인 계층이라면 게스트의 `fsync` 와 `FLUSH` 가 virtio `FLUSH` 로 이어진다. QEMU 와 백엔드에서 호스트 동기화와 플러시로 이어지고, 저장장치에서 필요한 완료를 확인한 뒤 게스트 완료로 돌아온다.
|
||||
|
||||
`cache=writeback` 을 위험하다는 한 낱말로 줄이지 않으려고 SSOT 는 정확한 표현을 따로 적어 두었다.
|
||||
|
||||
> writeback caching에서는 volatile cache가 존재할 수 있으므로, Guest의 flush/fsync semantics가 전체 backend/storage stack에서 올바르게 보존되는지가 중요하다.
|
||||
|
||||
그래서 보는 것은 게스트가 요구한 영속성이 게스트 파일시스템에서 게스트 블록 계층과 virtio, QEMU 와 백엔드, 호스트 스토리지를 지나 장치까지 가는 동안 그 의미가 깨지지 않는지다.
|
||||
|
||||
두 설정 가운데 어느 쪽을 쓸지 이 프로젝트는 정하지 않았다. SSOT 는 두 설정의 뜻과 각각의 오해를 갈라 놓기만 했고, 어느 쪽을 쓰기로 했다고도 그렇게 해서 무엇을 감수했다고도 적지 않았다. 이 호스트의 지금 값이 무엇인지부터 확인되지 않아서, 관계로 걸어 둔 질문이 그것을 먼저 답한다.
|
||||
|
||||
## 호스트 페이지 캐시를 우회해도 장치 안에 캐시가 있다
|
||||
|
||||
Direct I/O 로 호스트 페이지 캐시를 건너뛰어도 요청은 호스트 블록 계층과 NVMe 드라이버, NVMe 컨트롤러를 지나 장치 쪽 캐시를 거쳐 플래시에 닿는다. 스토리지 컨트롤러나 장치가 휘발성 쓰기 캐시를 가질 수 있다.
|
||||
|
||||
```text label="RAM 을 떠난 뒤에도 세 상태가 다르다"
|
||||
RAM에서 나갔다
|
||||
≠
|
||||
Device에 command가 전달됐다
|
||||
≠
|
||||
전원이 끊겨도 살아남는 상태가 됐다
|
||||
```
|
||||
|
||||
운영에서는 장치의 플러시와 FUA(Force Unit Access) 의미, 전원 손실 보호(power-loss protection)를 갖췄는지도 확인 대상이 된다. SSOT 는 그것이 중요할 수 있다고만 적었고, 이 호스트의 장치가 무엇을 갖췄는지는 적지 않았다.
|
||||
|
||||
## 거짓 완료는 성능 문제가 아니다
|
||||
|
||||
게스트가 `WRITE` 에 이어 `FLUSH` 를 요청했는데 데이터는 호스트 메모리에만 있고 물리 저장장치에는 이전 데이터가 들어 있다고 하자. 이때 게스트에게 `FLUSH 완료` 라고 응답하면 PostgreSQL 은 영속성이 확보됐다고 판단한다. 직후에 호스트 전원이 나가면 메모리의 데이터가 사라진다. 이것은 영속성 계약이 깨지는 정확성(correctness) 문제다.
|
||||
|
||||
PostgreSQL 로 보면 무엇이 어긋나는지 드러난다. 잔액을 바꾸고 커밋하는 트랜잭션을 생각해 본다.
|
||||
|
||||
```sql label="durability 가 걸리는 트랜잭션"
|
||||
BEGIN;
|
||||
|
||||
UPDATE users
|
||||
SET balance = 1000
|
||||
WHERE id = 1;
|
||||
|
||||
COMMIT;
|
||||
```
|
||||
|
||||
PostgreSQL 은 WAL(Write-Ahead Log) 등의 영속성 프로토콜을 쓰며 필요한 시점에 스토리지 동기화를 수행한다. WAL 쓰기는 게스트 페이지 캐시와 `fsync` 를 지나 게스트 파일시스템과 게스트 블록 계층, `virtio-blk`, QEMU, 호스트 스토리지를 거쳐 물리 저장장치까지 간다. 그 완료가 되돌아와야 필요한 영속성 조건이 충족됐다고 보고 `COMMIT` 을 성공으로 처리한다. 가상 머신 스토리지 계층이 플러시와 `fsync` 의 의미를 제대로 보존하지 않으면, PostgreSQL 의 영속성 가정과 실제 저장장치 동작이 어긋날 수 있다.
|
||||
|
||||
## 이 호스트에서 확인하지 않은 것
|
||||
|
||||
이 글에 이 테스트 호스트에서 잰 값은 하나도 없다. 두 가상 머신의 디스크 캐시 모드가 무엇으로 설정돼 있는지 SSOT 에 없어서, 이 호스트가 `cache=none` 구성인지 `cache=writeback` 구성인지 여기서 말하지 않는다. 거짓 완료가 이 환경에서 실제로 일어나는지도 재현하지 않았다. 장치의 플러시와 FUA 의미가 어떻게 동작하는지, 전원 손실 보호를 갖췄는지도 확인하지 않았다. `fsync()` 지연 값은 관계로 걸어 둔 질문이 게스트 지연과 호스트 스토리지 지연을 같은 시간축으로 잰 뒤에 이 글에 들어온다. 이 글은 이 구조에서 완료 응답이 무엇을 보장하는지까지 설명하고, 이 서버가 그 보장을 지키는지는 재지 않았다.
|
||||
|
||||
<!-- body:end -->
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
---
|
||||
id: ae69e2e1-1a3b-4ae3-ae2a-572af07b95d7
|
||||
kind: QUESTION
|
||||
slug: disk-image-format-and-actual-host-usage
|
||||
title: 이 disk image 는 qcow2 인가 RAW 인가, 그리고 Guest 가 보는 크기와 Host 가 실제로 쓰는 크기는 얼마나 다른가
|
||||
topic: storage-virtualization
|
||||
topicName: 스토리지 가상화
|
||||
project: virtualization
|
||||
status: 초안
|
||||
questionStatus: OPEN
|
||||
studio: "https://hyeonworks.com/studio/documents/ae69e2e1-1a3b-4ae3-ae2a-572af07b95d7/edit"
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#175-실제-테스트-서버에서-확인할-open-questions-oq-3
|
||||
- final/document.md#175-실제-테스트-서버에서-확인할-open-questions-oq-2
|
||||
- final/document.md#143-qcow2-virtual-size와-실제-host-사용량
|
||||
- final/document.md#144-raw-image
|
||||
- final/document.md#142-qcow2-host에서는-파일-guest에서는-디스크
|
||||
---
|
||||
|
||||
# 이 disk image 는 qcow2 인가 RAW 인가, 그리고 Guest 가 보는 크기와 Host 가 실제로 쓰는 크기는 얼마나 다른가
|
||||
|
||||
게스트가 100 GB 짜리 디스크를 보고 있어도 호스트에서 그 파일이 차지하는 공간은 훨씬 작을 수 있다. 개념 문서는 그 차이를 예시 숫자로만 들어 두고, 세 명령이 각각 다른 의미의 크기를 보여 준다고 적었다. 이 물음은 이 호스트의 이미지마다 형식과 세 크기를 한 표로 적는 데서 끝난다. 형식과 세 값이 채워지면 저장 공간 계획의 근거가 생기고, 그 뒤의 성능 판단은 여기서 하지 않는다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **/dev/vda 뒤에 있는 것 — qcow2 · RAW · Host block device**
|
||||
형식이 어디에서 갈리고 각 형식이 무엇을 더 하는지를 그 글이 설명한다.
|
||||
- **Guest 안에서 본 disk 로 backend 를 단정하지 않는다**
|
||||
이 물음이 그 기준의 형식 확인과 용량 확인 항목을 이 호스트에서 채운다.
|
||||
- **이 호스트의 /dev/vda 는 어떤 backend 에 붙어 있는가**
|
||||
명령을 돌릴 경로를 그 물음이 확정한다.
|
||||
- **disk image 는 최종적으로 어느 Host block device 위에 있는가**
|
||||
같은 경로를 받아 그 파일 아래를 잇는 물음이다.
|
||||
|
||||
## 사실
|
||||
|
||||
- 개념 문서는 같은 대상이 호스트에서는 파일 하나이고 게스트에서는 /dev/vda 라는 디스크이며 둘 다 맞다고 놓았다.
|
||||
- qcow2 는 가상 디스크 크기와 실제 호스트 할당량이 다를 수 있다. 개념 문서가 든 그림은 게스트가 보는 공간 100 GB 에 호스트 실제 할당 3GB 였다.
|
||||
- 게스트가 데이터를 기록하면서 실제 사용량이 늘 수 있다. 개념 문서는 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 는 무조건 느리다는 일반화를 막고, 실제 성능이 캐시 모드와 스토리지 백엔드와 부하 패턴과 큐 깊이와 스냅숏 체인과 그 아래 파일시스템과 물리 장치에 영향을 받는다고 들었다.
|
||||
- 개념 문서가 이 확인에 적어 둔 명령은 셋이다. 세 명령이 보여주는 의미가 서로 다를 수 있으므로 비교하라고 했다.
|
||||
형식과 크기 : qemu-img info
|
||||
실제 점유량 : du -h
|
||||
파일 크기 : ls -lh
|
||||
- 이 호스트의 이미지 형식도 크기도 적힌 기록이 없다.
|
||||
|
||||
## 가정
|
||||
|
||||
- 이미지 경로가 앞선 물음에서 확정된다고 본다. 경로가 없으면 세 명령을 어디에 돌릴지 정해지지 않는다.
|
||||
- 백엔드가 파일이라고 전제한다. 호스트 블록 장치로 나오면 이 물음 자체가 이 호스트에 적용되지 않는다.
|
||||
- 세 명령을 같은 시점에 돌린다고 본다. 실제 사용량은 게스트가 기록하면서 늘 수 있어서, 시점이 벌어지면 한 표에 서로 다른 시각의 값이 들어간다.
|
||||
|
||||
## 미지수
|
||||
|
||||
- 가상 머신마다 디스크 이미지가 qcow2 인지 RAW 인지.
|
||||
- qemu-img info 가 보여 주는 virtual size 와 disk size 가 각각 얼마인지.
|
||||
- du -h 의 실제 점유량과 ls -lh 의 파일 크기가 그 값들과 얼마나 벌어져 있는지.
|
||||
|
||||
## 제약
|
||||
|
||||
- 성능 결론을 여기서 내지 않는다. 형식만으로 일반화하지 말라고 개념 문서가 적었고, 성능은 부하와 지연을 재는 다른 물음들이 받는다.
|
||||
- 한 시점의 세 값만 적는다. 그 차이가 시간에 따라 어떻게 자라는지는 별도 측정으로 넘긴다.
|
||||
- 앞선 물음이 Source 를 확정하기 전에는 이 확인을 시작할 수 없다.
|
||||
- 개념 문서는 형식을 묻는 물음과 세 크기를 묻는 물음을 따로 적었는데 여기서는 하나로 받았다. 형식은 qemu-img info 출력의 첫 줄이고 세 값을 견주는 일의 전제라 같은 출력으로 함께 닫힌다. 같은 측정으로 닫히는 물음을 두 편으로 두지 않는다.
|
||||
|
||||
## 선택지
|
||||
|
||||
### 1. 이미지마다 세 명령을 이어서 돌리고 한 표로 남긴다
|
||||
|
||||
같은 경로에 qemu-img info 와 du -h 와 ls -lh 를 이어서 돌린다. 형식과 virtual size 와 disk size 는 첫 출력에서 읽고 그 옆에 나머지 두 값을 붙이면, 세 값의 차이가 한 행에서 보인다. 세 명령이 붙어 돌아가므로 시점 차이도 거의 없다.
|
||||
|
||||
이미지가 많으면 출력이 길어지므로 가상 머신 이름과 경로를 행 머리에 둔다.
|
||||
|
||||
### 2. 형식만 먼저 읽고 크기 비교는 뒤로 미룬다
|
||||
|
||||
qemu-img info 한 번이면 file format 이 나오므로 qcow2 인지 RAW 인지가 먼저 갈리고, RAW 로 나오면 확인할 항목이 줄어든다. 개념 문서가 두 물음으로 나눠 적은 모양이 이 순서다.
|
||||
|
||||
대신 같은 경로에 두 번 접근하게 되고, 두 시점 사이에 게스트가 기록하면 크기 값이 형식을 읽은 시점의 상태와 어긋난다.
|
||||
|
||||
### 3. 파일시스템 사용량 합계로 대신한다 — 제외
|
||||
|
||||
이미지가 놓인 파일시스템을 df -h 로 보면 파일을 하나씩 재지 않아도 전체 점유가 나온다.
|
||||
|
||||
그 값에는 이미지가 아닌 파일도 함께 들어가 있어서 이미지 하나가 얼마를 쓰는지 갈라 주지 않는다. 개념 문서가 비교하라고 한 것은 같은 이미지에 대한 세 값이고, 합계로는 virtual size 와의 차이도 나오지 않는다.
|
||||
|
||||
## 다음 검증
|
||||
|
||||
1. 앞선 물음이 확정한 Source 경로마다 qemu-img info 를 돌려 file format 과 virtual size 와 disk size 를 적는다.
|
||||
2. 같은 경로에 du -h 와 ls -lh 를 돌려 두 값을 나란히 적는다.
|
||||
3. 가상 머신이 여럿이면 이미지마다 한 행으로 한 표에 모으고 세 명령의 출력을 그대로 증거로 둔다.
|
||||
|
||||
닫는 조건 : 이미지마다 형식과 세 크기가 한 표로 적히면 닫는다. qcow2 이고 세 값이 벌어져 있으면 개념 문서가 예시로만 든 차이가 이 호스트에서 얼마인지 확정한 것이고, 그 차이가 자라는 모습은 별도 측정으로 넘긴다. RAW 이면 그 갈래가 이 환경이라고 적고 sparse 인지 아닌지만 세 값의 차이로 판정한다.
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
---
|
||||
id: be3c2c58-1ed4-4eae-876a-3114faa61c94
|
||||
kind: QUESTION
|
||||
slug: guest-fsync-latency-vs-host-storage-latency
|
||||
title: Guest 의 fsync() 지연과 Host storage 지연은 같이 오르는가
|
||||
topic: storage-virtualization
|
||||
topicName: 스토리지 가상화
|
||||
project: virtualization
|
||||
status: 초안
|
||||
questionStatus: OPEN
|
||||
studio: "https://hyeonworks.com/studio/documents/be3c2c58-1ed4-4eae-876a-3114faa61c94/edit"
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#175-실제-테스트-서버에서-확인할-open-questions-oq-8
|
||||
- final/document.md#150-fsync-가-필요한-이유
|
||||
- final/document.md#169-storage-관측-명령어
|
||||
- final/document.md#170-postgresql-예시-wal과-durability
|
||||
- final/document.md#171-성능과-durability의-trade-off
|
||||
- final/document.md#148-write-완료와-영속화는-다르다
|
||||
---
|
||||
|
||||
# Guest 의 fsync() 지연과 Host storage 지연은 같이 오르는가
|
||||
|
||||
게스트 안의 fsync() 는 게스트 파일시스템과 블록 계층과 virtio-blk 와 QEMU 를 지나 호스트 스토리지까지 의미가 전달되어야 하는 요청이다. 그 전달이 이 호스트에서 실제로 이어지는지는 두 계열을 같은 시간축에 놓고 봐야 갈린다. 이 물음은 게스트 쪽 애플리케이션 지연과 호스트 쪽 스토리지 지연을 같은 타임스탬프로 남겨, 둘이 함께 움직이는지를 확인한다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **완료라는 말의 네 가지 뜻 — write · writeback · flush · 전원 장애 생존**
|
||||
게스트가 받은 완료 응답이 어느 뜻인지를 그 개념이 가른다.
|
||||
- **Guest 의 write() 가 virtqueue 에 실리기까지 — VFS · Filesystem · Page Cache · Block Layer · virtio-blk**
|
||||
fsync() 가 전달되어야 하는 앞쪽 경로를 그 개념이 단계로 설명한다.
|
||||
- **빨라진 구성이 durability contract 를 지운 것은 아닌지 먼저 가른다**
|
||||
두 계열이 갈렸을 때 지연을 줄이는 방향으로 가려면 그 기준을 지나야 한다.
|
||||
- **지연 원인을 CPU 로 읽기 전에 storage 계층을 따로 잰다**
|
||||
게스트 지연이 올랐을 때 CPU 가 아니라 스토리지를 먼저 보는 순서를 그 기준이 요구한다.
|
||||
- **이 VM 들의 QEMU disk cache mode 는 무엇인가**
|
||||
그 값이 이 측정의 조건이므로 같은 기록에 함께 적는다.
|
||||
- **VM1 의 storage 부하가 VM2 의 지연을 실제로 밀어 올리는가**
|
||||
같은 호스트 지표를 보지만 그쪽은 다른 가상 머신의 부하가 넘어오는지를 묻는다.
|
||||
- **호스트 major fault 와 스토리지 지연은 같은 시간축에서 함께 움직이는가**
|
||||
호스트 쪽에서 스토리지를 미는 다른 원인을 그 물음이 따로 본다.
|
||||
|
||||
## 사실
|
||||
|
||||
- §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 가 서로 같지 않다고 못박았다.
|
||||
- §170 은 PostgreSQL 이 WAL 등의 durability protocol 을 사용하며 필요한 시점에 스토리지 동기화를 수행한다고 적었다. 가상 머신의 스토리지 계층이 flush 와 fsync 의 의미를 제대로 보존하지 않으면 PostgreSQL 이 전제한 영속성과 실제 스토리지 동작이 어긋날 수 있다.
|
||||
- §171 은 모든 쓰기에서 스토리지 동기화를 기다리면 지연이 커질 수 있고, 특히 DB 부하에서는 fsync() 지연이 트랜잭션 지연과 연결될 수 있다고 적었다.
|
||||
- §171 은 더 적극적인 캐싱으로 쓰기 지연을 개선할 수 있지만 durability semantics 는 반드시 보존해야 한다고 덧붙였다. fsync() 를 없애서 빨라졌다면 그것이 최적화가 아니라 durability contract 를 제거한 것일 수 있다.
|
||||
- §169 는 호스트 관측으로 iostat -xz 1 과 iotop 을 들고, 게스트 쪽 확인 명령으로 lsblk · mount · df -h · cat /proc/mounts · iostat -xz 1 을 적었다.
|
||||
- §175 OQ-8 은 게스트의 애플리케이션 지연과 호스트 iostat 를 시간축으로 함께 관찰하라고 했다.
|
||||
- 이 호스트에서 두 계열을 재 본 값은 개념 문서에 없다.
|
||||
|
||||
## 가정
|
||||
|
||||
- 게스트에서 애플리케이션이나 데이터베이스의 지연을 시간축으로 기록할 수 있다고 본다. 그 기록에 fsync() 자체의 지연이 따로 나오는지, 아니면 트랜잭션 지연으로만 보이는지는 해 보기 전에 알 수 없다.
|
||||
- 호스트와 게스트의 시계가 맞아 두 계열을 같은 타임스탬프에 놓을 수 있다고 전제한다. 어긋나 있으면 함께 움직였다는 판정 자체가 성립하지 않는다.
|
||||
- 부하가 없는 구간과 있는 구간을 만들 수 있고, 두 구간에서 같은 방법으로 잴 수 있다고 본다.
|
||||
- 측정 중에 캐시 모드를 비롯한 디스크 설정이 바뀌지 않는다고 두고 두 구간을 견준다.
|
||||
- 같은 장치를 쓰는 다른 가상 머신이나 호스트 프로세스의 입출력이 섞일 수 있다고 보고, 어느 프로세스가 입출력을 내는지 iotop 으로 함께 남긴다.
|
||||
|
||||
## 미지수
|
||||
|
||||
- 게스트의 fsync() 나 트랜잭션 지연이 커지는 구간과 호스트 스토리지 지연이 커지는 구간이 시간축에서 겹치는지.
|
||||
- 겹친다면 두 값이 같은 크기로 움직이는지, 게스트 쪽이 더 크게 벌어지는지.
|
||||
- 게스트 지연은 오르는데 호스트 스토리지 지연이 따라 오르지 않는 구간이 있는지. 있다면 그 차이가 게스트 쪽 대기에서 생기는지 QEMU 와 백엔드 쪽에서 생기는지.
|
||||
- 이때 걸려 있던 캐시 모드가 무엇인지. 그 값은 별도 물음이 읽는다.
|
||||
- 게스트 쪽 지연을 어떤 단위로 기록할지. 개념 문서는 게스트에서 지연을 재는 명령을 적지 않았고 호스트 쪽 iostat 만 적었다.
|
||||
|
||||
## 제약
|
||||
|
||||
- 이 물음은 영속성을 줄여 지연을 낮추는 방향을 검토하지 않는다. §171 이 fsync() 를 없애 빨라진 것을 최적화로 부르지 말라고 적었고, 그런 방향은 별도 기준을 지나야 한다.
|
||||
- 두 계열을 같은 시각에 남기지 못하면 이 물음은 닫히지 않는다. 나중에 따로 잰 값을 겹쳐 놓고 함께 올랐다고 적지 않는다.
|
||||
- 측정 조건에 캐시 모드와 백엔드 형식과 장치 이름을 함께 적는다. 이 셋이 없으면 같은 값을 다른 환경과 견줄 수 없다.
|
||||
- 측정하는 동안 다른 스토리지 실험을 같은 장치 위에서 겹쳐 돌리지 않는다.
|
||||
- 호스트 지연이 오른 이유까지 이 물음이 가르지 않는다. 다른 가상 머신의 부하인지 호스트 메모리 압박인지는 그것을 재는 두 물음이 받는다.
|
||||
- 그 가운데 메모리 압박 쪽 물음과는 호스트 스토리지 지표를 같이 본다. 그래도 한 물음으로 합치지 않았다. 그쪽은 호스트에서 major fault 를 유도해 그것이 스토리지를 미는지를 보고 이쪽은 게스트의 fsync() 가 호스트까지 전달되는지를 봐서, 유도 방법도 견주는 계열도 달라 한 실행으로 닫히지 않는다.
|
||||
|
||||
## 선택지
|
||||
|
||||
### 1. 부하 없는 구간과 있는 구간을 각각 찍어 두 계열을 겹친다
|
||||
|
||||
게스트 쪽 애플리케이션과 데이터베이스의 지연을 기록하면서 같은 시각에 호스트에서 iostat -xz 1 을 돌린다. 두 구간을 같은 방법으로 남기면 부하가 들어왔을 때 두 계열이 함께 움직였는지 그 자료에서 읽힌다. §175 OQ-8 이 적은 방법 그대로다.
|
||||
|
||||
게스트 쪽 지연을 무엇으로 기록할지 먼저 정해야 한다.
|
||||
|
||||
### 2. 데이터베이스 트랜잭션 부하만 걸고 fsync 쪽만 본다
|
||||
|
||||
§170 이 그린 WAL 경로를 따라 쓰기 위주의 트랜잭션을 돌리면 fsync() 가 지연에 직접 걸리는 구간을 만들기 쉽다. 두 계열이 어긋나는 구간을 찾기에도 이 부하가 낫다.
|
||||
|
||||
읽기 위주 구간에서는 어떻게 움직이는지 못 본다. §171 은 DB 부하를 예로 들었을 뿐 다른 부하를 배제하지 않았다.
|
||||
|
||||
### 3. 제외 — 캐시 모드를 바꿔 가며 두 계열을 비교한다
|
||||
|
||||
cache=none 과 cache=writeback 에서 두 계열이 어떻게 달라지는지 보면 전달 경로의 모양이 더 뚜렷해진다. 그러나 지금 필요한 것은 현재 구성에서 경로가 이어지는지 하나이고, 설정을 바꾸면 두 구간이 서로 다른 구성에서 나온 값이 된다. 현재 값이 무엇인지도 아직 읽지 않았다.
|
||||
|
||||
## 다음 검증
|
||||
|
||||
1. 캐시 모드를 묻는 물음의 결과를 측정 조건으로 먼저 적는다. 함께 적을 것은 백엔드 형식과 이미지가 놓인 장치 이름이다.
|
||||
2. 게스트에서 애플리케이션이나 데이터베이스의 지연을 시간축으로 기록한다. 무엇으로 어떻게 기록했는지 함께 남긴다.
|
||||
3. 같은 시각에 호스트에서 iostat -xz 1 을 돌려 두 계열을 같은 타임스탬프로 남긴다 (§175 OQ-8).
|
||||
4. 부하가 없는 구간과 있는 구간을 모두 찍고, 어느 프로세스가 입출력을 내는지 iotop 으로 함께 적는다 (§169).
|
||||
|
||||
닫는 조건 : 두 계열이 같은 구간에서 함께 오르면 §150 이 그린 전달 경로가 이 환경에서 이어진다는 것을 확인한 것이고, 그 시계열이 Case 가 된다. 게스트의 fsync() 지연은 오르는데 호스트 스토리지 지연이 따라 오르지 않으면 그 차이가 어느 계층에서 생기는지 — 게스트 쪽 대기인지 QEMU 와 백엔드 쪽인지 — 를 다음 물음으로 넘기고 닫는다. 어느 쪽이든 성능을 위해 영속성을 줄이는 방향은 「빨라진 구성이 durability contract 를 지운 것은 아닌지 먼저 가른다」를 지나야 한다.
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
---
|
||||
id: eead2379-94fe-463a-9e5e-06a01bf2105d
|
||||
kind: QUESTION
|
||||
slug: host-block-device-under-the-disk-image
|
||||
title: disk image 는 최종적으로 어느 Host block device 위에 있는가
|
||||
topic: storage-virtualization
|
||||
topicName: 스토리지 가상화
|
||||
project: virtualization
|
||||
status: 초안
|
||||
questionStatus: OPEN
|
||||
studio: "https://hyeonworks.com/studio/documents/eead2379-94fe-463a-9e5e-06a01bf2105d/edit"
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#175-실제-테스트-서버에서-확인할-open-questions-oq-5
|
||||
- final/document.md#158-host-block-layer
|
||||
- final/document.md#159-여러-vm이-하나의-nvme를-공유하면
|
||||
- final/document.md#141-qemu가-물리-ssd를-직접-제어하는-것은-아니다
|
||||
- final/document.md#163-실제-i-o-scheduler-확인
|
||||
---
|
||||
|
||||
# disk image 는 최종적으로 어느 Host block device 위에 있는가
|
||||
|
||||
게스트의 디스크가 호스트에서는 파일 하나라는 것까지는 백엔드를 묻는 앞 물음이 확정한다. 그 파일은 다시 호스트 파일시스템 위에 있고 그 파일시스템은 어느 파티션과 물리 장치 위에 있어서, 이미지 경로에서 시작해 그 아래를 마운트와 파티션과 장치 이름까지 따라 내려가야 한다. 두 가상 머신의 이미지가 같은 장치를 쓰는지도 여기서 갈린다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **VM 아래에 한 번 더 있는 Host block stack — blk-mq · I/O Scheduler · NVMe**
|
||||
이미지 파일 아래에서 무엇이 그 입출력을 받는지를 그 개념이 설명한다.
|
||||
- **/dev/vda 뒤에 있는 것 — qcow2 · RAW · Host block device**
|
||||
백엔드가 파일인지 장치인지에 따라 여기서 따라 내려갈 경로의 시작이 달라진다.
|
||||
- **이 호스트의 /dev/vda 는 어떤 backend 에 붙어 있는가**
|
||||
그 물음이 확정한 Source 경로가 이 확인의 입력이다.
|
||||
- **이 호스트의 I/O Scheduler 는 무엇인가**
|
||||
여기서 확정한 장치 이름을 넣어야 그 물음의 명령이 완성된다.
|
||||
- **VM1 의 storage 부하가 VM2 의 지연을 실제로 밀어 올리는가**
|
||||
두 가상 머신이 같은 장치를 쓰는지가 그 실험의 전제다.
|
||||
- **Guest 안에서 본 disk 로 backend 를 단정하지 않는다**
|
||||
게스트 쪽 이름에서 물리 장치를 추정하지 않고 호스트에서 따라 내려가는 순서를 그 기준이 요구한다.
|
||||
|
||||
## 사실
|
||||
|
||||
- §141 은 백엔드가 qcow2 파일이면 QEMU 가 결국 호스트 리눅스에 파일 입출력을 요청한다고 적었다.
|
||||
그 요청은 Host Kernel, Host Filesystem, Host Block Layer, NVMe Driver 를 지나 Physical NVMe 로 내려간다. 게스트의 스토리지 스택 아래에 호스트의 스토리지 스택이 한 번 더 존재할 수 있다.
|
||||
- §158 은 qcow2 와 RAW 의 파일 입출력이 호스트 파일시스템을 거쳐 실제 호스트 블록 입출력이 된다고 적고 경로를 이렇게 그렸다.
|
||||
QEMU, vm1.qcow2, Host ext4/XFS, Host Block Layer, /dev/nvme0n1
|
||||
- §158 은 Host Block Layer 가 그 입출력을 가상 머신 안의 PostgreSQL 이 냈는지 호스트 프로세스가 냈는지 본질적으로 구분해서 처리하는 계층이 아니라고 밝혔다. 모두 호스트의 블록 요청이다.
|
||||
- §159 는 VM1 의 QEMU 와 VM2 의 QEMU 와 Nginx 와 호스트의 다른 프로세스가 같은 Host Block Layer 를 지나 하나의 NVMe 로 간다고 그렸다.
|
||||
- §175 OQ-5 는 확인 명령으로 lsblk 와 findmnt 를 들었다.
|
||||
- §169 의 호스트 관측 명령 목록에도 lsblk 가 들어 있다.
|
||||
- §158 의 vm1.qcow2 와 /dev/nvme0n1 은 경로를 설명하려고 든 예시 이름이고 이 호스트에서 읽은 값이 아니다.
|
||||
- 이 호스트의 마운트 배치와 물리 장치 이름은 개념 문서에 없다.
|
||||
|
||||
## 가정
|
||||
|
||||
- 이미지 경로가 앞 물음에서 이미 확정되어 있다고 보고 그 경로에서 시작한다. 백엔드가 파일이 아니라 호스트 블록 장치면 이 확인은 장치 이름에서 시작한다.
|
||||
- 확인하는 동안 마운트 구성과 이미지 위치가 바뀌지 않는다고 전제한다.
|
||||
- findmnt 가 보여 주는 source 이름이 물리 장치 이름과 다를 수 있다고 보고 lsblk 로 상하 관계까지 이어서 읽는다.
|
||||
- 호스트에 붙어 두 명령을 같은 시각에 돌릴 수 있다고 본다.
|
||||
|
||||
## 미지수
|
||||
|
||||
- 이미지가 놓인 디렉터리가 어느 마운트 지점에 속하는지.
|
||||
- 그 마운트가 어느 파티션 위에 있고 그 파티션이 어느 블록 장치에 속하는지.
|
||||
- 가상 머신들의 이미지가 같은 장치를 공유하는지 서로 다른 장치에 있는지.
|
||||
- 이미지를 담은 파일시스템이 무엇인지. §158 은 ext4 와 XFS 를 예로 들었을 뿐 이 호스트의 값을 적지 않았다.
|
||||
- 그 장치가 NVMe 인지 다른 종류인지. §163 이 SATA/SCSI device 를 따로 언급했으므로 확인 명령의 장치 이름도 그에 따라 달라진다.
|
||||
|
||||
## 제약
|
||||
|
||||
- 확인만 한다. 마운트를 바꾸거나 이미지를 다른 장치로 옮기지 않는다. 배치를 바꾸면 지금 구성에서 무엇을 공유하는지 못 본다.
|
||||
- §158 이 예로 든 이름을 이 호스트의 값으로 옮겨 적지 않는다. 출력에 나온 이름만 적는다.
|
||||
- 여기서 나온 장치 이름이 다음 두 물음의 입력이므로, 가상 머신마다 한 행으로 남기고 어느 이미지에서 나온 이름인지 함께 적는다.
|
||||
- 장치를 나눌지 말지는 이 물음이 정하지 않는다. 공유하고 있다는 사실과 그것이 지연을 실제로 밀어 올리는지는 다른 물음이 받는다.
|
||||
- 백엔드를 묻는 앞 물음과 이어서 돌리게 되지만 한 물음으로 합치지 않았다. 그쪽은 게스트의 장치가 어느 Source 에 붙어 있는지까지 확정하고 이 물음은 그 경로가 놓인 물리 장치까지 내려간다. 닫는 명령이 다르고 그 출력이 여는 다음 물음도 다르다.
|
||||
|
||||
## 선택지
|
||||
|
||||
### 1. 이미지 경로마다 findmnt 로 마운트를 읽고 lsblk 로 그 아래를 잇는다
|
||||
|
||||
경로 하나에서 시작해 마운트, source device, 파티션, 상위 장치까지 한 줄기로 내려간다. §175 OQ-5 가 든 두 명령 그대로이고, 이미지가 여럿이면 경로마다 같은 순서를 되풀이한다.
|
||||
|
||||
가상 머신이 늘어난 만큼 실행 횟수도 늘어난다.
|
||||
|
||||
### 2. 호스트 전체의 lsblk 를 먼저 한 장 받고 그 위에 이미지 경로를 얹는다
|
||||
|
||||
장치와 파티션의 전체 모양을 먼저 확보한 뒤 findmnt 로 각 이미지가 그 가운데 어디에 붙는지 표시해서, 두 가상 머신이 같은 장치를 쓰는지가 한 장에서 바로 보이게 한다.
|
||||
|
||||
이미지 경로와 마운트의 대응은 결국 findmnt 로 한 번 더 확인해야 한다.
|
||||
|
||||
### 3. 제외 — 가상 머신 한 대만 확인하고 나머지는 뒤로 미룬다
|
||||
|
||||
한 대만 따라 내려가도 경로의 모양은 확인된다. 그러나 이 물음이 답해야 하는 것에는 가상 머신들이 같은 장치를 공유하는지가 들어 있는데, 그것은 한 대의 출력만으로는 갈리지 않는다.
|
||||
|
||||
## 다음 검증
|
||||
|
||||
1. 백엔드를 묻는 앞 물음이 확정한 Source 경로를 그대로 가져온다.
|
||||
2. 경로마다 findmnt 로 그 경로가 속한 마운트와 source device 를 적는다 (§175 OQ-5).
|
||||
3. 같은 경로에 lsblk 를 돌려 그 장치에 파티션이 어떻게 나뉘어 있고 어느 상위 장치에 속하는지 적는다.
|
||||
4. 이미지 경로, 마운트, 파티션, 장치를 가상 머신마다 한 행으로 남긴다.
|
||||
|
||||
닫는 조건 : 이미지마다 최종 블록 장치 이름이 확정되면 닫는다. 가상 머신들이 같은 장치를 공유하면 §159 가 그린 상황이 이 환경이라고 적고, 그것이 지연으로 이어지는지는 VM1 부하와 VM2 지연을 재는 물음이 받는다. 서로 다른 장치면 그 사실을 적고 그 물음의 전제가 이 환경에 없다고 함께 적는다. 어느 쪽이든 여기서 확정한 장치 이름을 I/O Scheduler 를 묻는 물음으로 넘긴다.
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
---
|
||||
id: 77aa0188-5c2b-4f47-bda7-7c66b084968d
|
||||
kind: QUESTION
|
||||
slug: host-io-scheduler
|
||||
title: 이 호스트의 I/O Scheduler 는 무엇인가
|
||||
topic: storage-virtualization
|
||||
topicName: 스토리지 가상화
|
||||
project: virtualization
|
||||
status: 초안
|
||||
questionStatus: OPEN
|
||||
studio: "https://hyeonworks.com/studio/documents/77aa0188-5c2b-4f47-bda7-7c66b084968d/edit"
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#175-실제-테스트-서버에서-확인할-open-questions-oq-6
|
||||
- final/document.md#161-i-o-scheduler
|
||||
- final/document.md#162-none
|
||||
- final/document.md#163-실제-i-o-scheduler-확인
|
||||
- final/document.md#160-blk-mq-multi-queue-block-layer
|
||||
---
|
||||
|
||||
# 이 호스트의 I/O Scheduler 는 무엇인가
|
||||
|
||||
여러 가상 머신과 호스트 프로세스가 낸 입출력은 같은 호스트 블록 계층으로 들어와 장치로 dispatch 되는데, 그 순서를 정하는 정책이 스케줄러다. 정책이 무엇인지에 따라 부하 구간의 지연을 읽는 조건이 달라진다. 값을 읽는 데는 파일 하나를 보면 되고, 그 파일 이름에 넣을 장치는 이미지 아래를 따라 내려간 물음이 확정한다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **VM 아래에 한 번 더 있는 Host block stack — blk-mq · I/O Scheduler · NVMe**
|
||||
스케줄러가 그 스택의 어디에서 무엇을 정하는지를 그 개념이 설명한다.
|
||||
- **disk image 는 최종적으로 어느 Host block device 위에 있는가**
|
||||
그 물음이 확정한 장치 이름을 넣어야 이 확인 명령이 완성된다.
|
||||
- **VM1 의 storage 부하가 VM2 의 지연을 실제로 밀어 올리는가**
|
||||
여기서 읽은 정책이 그 실험의 결과를 읽는 조건이 된다.
|
||||
- **지연 원인을 CPU 로 읽기 전에 storage 계층을 따로 잰다**
|
||||
그 기준이 요구하는 스토리지 쪽 조건 가운데 하나를 이 값이 채운다.
|
||||
|
||||
## 사실
|
||||
|
||||
§161 은 여러 입출력 요청이 있다고 해서 항상 들어온 순서 그대로 장치에 전달되지는 않고 I/O Scheduler 가 dispatch 정책을 갖는다고 적었다. 대표적으로 볼 수 있는 것으로 셋을 들었다.
|
||||
|
||||
none
|
||||
mq-deadline
|
||||
bfq
|
||||
|
||||
스케줄러마다 목적과 정책이 다르다.
|
||||
|
||||
§162 는 none 을 복잡한 스케줄링 정책을 최소화해 비교적 직접 장치 쪽으로 dispatch 하는 방향으로 설명했다. NVMe 처럼 장치 자체가 강한 병렬성과 큐 기능을 가진 경우 이런 단순한 정책이 적합할 수 있다. 다만 none 이 블록 계층이 아무 일도 하지 않는다는 상태를 뜻하지는 않는다.
|
||||
|
||||
§160 은 blk-mq 를 CPU 마다 큐를 두어 여러 CPU 가 병렬로 블록 입출력을 처리하는 구조로 그렸고, 스토리지 처리도 CPU 스케줄링과 완전히 독립된 세계는 아니라고 덧붙였다.
|
||||
|
||||
§163 은 호스트 확인 명령으로 cat /sys/block/nvme0n1/queue/scheduler 를 들고 예시 출력을 이렇게 보였다.
|
||||
|
||||
[none] mq-deadline
|
||||
|
||||
대괄호 안이 현재 선택된 스케줄러다. SATA/SCSI device 라면 cat /sys/block/sda/queue/scheduler 처럼 확인한다.
|
||||
|
||||
§175 OQ-6 은 같은 파일을 장치 이름을 넣어 읽으라고 했다.
|
||||
|
||||
이 호스트의 값은 개념 문서에 없다. §163 의 nvme0n1 과 sda 는 명령의 모양을 보이려고 든 예시 이름이다.
|
||||
|
||||
## 가정
|
||||
|
||||
호스트에 붙어 sysfs 아래의 파일을 읽을 수 있다고 본다.
|
||||
|
||||
이미지를 담은 장치 이름이 앞 물음에서 확정되어 있다고 보고 그 이름을 명령에 넣는다.
|
||||
|
||||
읽는 시점의 값이 부하를 걸 때도 같다고 전제하는데, 측정 사이에 누가 값을 바꾸면 이 전제가 깨진다.
|
||||
|
||||
## 미지수
|
||||
|
||||
이미지를 담고 있는 장치의 현재 스케줄러가 무엇인지.
|
||||
|
||||
그 장치에서 선택할 수 있는 목록에 무엇이 들어 있는지. §161 이 든 셋과 같은지도 확인해야 안다.
|
||||
|
||||
이미지가 놓인 장치가 여럿일 때 장치마다 값이 다른지.
|
||||
|
||||
## 제약
|
||||
|
||||
스케줄러를 바꿀지 말지는 이 물음이 정하지 않고, 바꾼 전후를 비교한 측정이 나온 뒤에 결정으로 넘긴다.
|
||||
|
||||
읽기만 한다. 값을 바꾸면 지금 구성에서 부하가 어떻게 처리되는지 못 본다.
|
||||
|
||||
§163 의 예시 이름을 이 호스트의 장치 이름으로 옮겨 적지 않고, 앞 물음이 확정한 이름을 넣는다.
|
||||
|
||||
개념 문서가 명령과 출력 읽는 법을 다 적어 두어서 비어 있는 것은 이 호스트의 값 하나다. 그래서 이 물음이 정할 것은 어느 장치에 그 명령을 돌릴지뿐이고, 그 이름은 이 물음이 스스로 내지 않는다.
|
||||
|
||||
## 선택지
|
||||
|
||||
### 1. 이미지를 담은 장치만 읽는다
|
||||
|
||||
이 물음이 필요한 것은 가상 머신의 입출력이 지나는 장치의 정책 하나여서, 파일 하나를 읽고 대괄호 안의 값을 적으면 끝난다.
|
||||
|
||||
호스트의 다른 장치가 다른 정책으로 돌고 있어도 그 사실은 안 보인다.
|
||||
|
||||
### 2. 호스트의 블록 장치를 전부 훑어 값을 함께 적는다
|
||||
|
||||
장치마다 같은 파일을 읽어 한 표로 만들면 이미지가 놓인 장치의 값이 이 호스트에서 예외인지 아닌지도 함께 보인다. 이미지가 여러 장치에 나뉘어 있는 구성이면 어차피 여러 장치를 읽어야 한다.
|
||||
|
||||
지금 판단에 쓰이지 않는 장치의 값까지 남게 된다.
|
||||
|
||||
## 다음 검증
|
||||
|
||||
1. 이미지 아래를 따라 내려간 물음이 확정한 장치 이름을 가져온다.
|
||||
2. 그 이름을 넣어 /sys/block 아래의 queue/scheduler 파일을 읽고 출력을 그대로 적는다 (§175 OQ-6). NVMe 면 cat /sys/block/nvme0n1/queue/scheduler 와 같은 모양이고, SATA/SCSI device 면 cat /sys/block/sda/queue/scheduler 와 같은 모양이다.
|
||||
3. 대괄호가 붙은 값을 현재 선택된 스케줄러로 적고, 대괄호 밖의 이름은 선택지로 함께 적는다.
|
||||
4. 이미지가 놓인 장치가 여럿이면 장치마다 되풀이한다.
|
||||
|
||||
닫는 조건 : 장치마다 현재 스케줄러와 선택지가 출력으로 적히면 닫는다. none 이면 §162 의 서술이 이 호스트에 적용된다고 적는다. mq-deadline 이나 bfq 면 dispatch 정책이 개입한다는 사실을 VM1 부하와 VM2 지연을 재는 물음의 해석 조건으로 넘긴다.
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
---
|
||||
id: 480f86ac-86fb-4290-9bf5-584763aa1363
|
||||
kind: QUESTION
|
||||
slug: qemu-disk-cache-mode
|
||||
title: 이 VM 들의 QEMU disk cache mode 는 무엇인가
|
||||
topic: storage-virtualization
|
||||
topicName: 스토리지 가상화
|
||||
project: virtualization
|
||||
status: 초안
|
||||
questionStatus: OPEN
|
||||
studio: "https://hyeonworks.com/studio/documents/480f86ac-86fb-4290-9bf5-584763aa1363/edit"
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#175-실제-테스트-서버에서-확인할-open-questions-oq-4
|
||||
- final/document.md#153-qemu-cache-mode
|
||||
- final/document.md#154-cache=none
|
||||
- final/document.md#155-cache=writeback
|
||||
- final/document.md#156-writeback-=-위험-이라고-단정하면-안-되는-이유
|
||||
- final/document.md#147-vm에서는-page-cache가-두-번-나타날-수-있다
|
||||
---
|
||||
|
||||
# 이 VM 들의 QEMU disk cache mode 는 무엇인가
|
||||
|
||||
이름만 보고 none 을 캐시가 아예 없는 구성으로, writeback 을 무조건 위험한 구성으로 읽으면 부정확하다고 개념 문서가 못박았다. 다만 그 문서는 두 설정이 Host Page Cache 와 완료·flush 의 뜻을 어떻게 바꾸는지까지만 갈라 놓았고, 이 호스트의 가상 머신들이 지금 어느 값으로 돌고 있는지는 적지 않았다. 그래서 이 물음은 값을 고르지 않는다. 지금 걸려 있는 값을 읽고, 그 값이 이 환경에서 무엇을 뜻하는지까지 적는다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **완료라는 말의 네 가지 뜻 — write · writeback · flush · 전원 장애 생존**
|
||||
cache 값이 바꾸는 것이 그 네 가지 가운데 어디까지인지를 그 개념이 가른다.
|
||||
- **빨라진 구성이 durability contract 를 지운 것은 아닌지 먼저 가른다**
|
||||
읽은 값을 근거로 설정을 바꾸려 할 때 그 기준을 지나야 한다.
|
||||
- **Guest 의 fsync() 지연과 Host storage 지연은 같이 오르는가**
|
||||
그 측정의 조건으로 여기서 읽은 값을 함께 기록한다.
|
||||
- **이 호스트의 /dev/vda 는 어떤 backend 에 붙어 있는가**
|
||||
그 물음이 확정한 Source 를 같은 행에 두어야 이 값이 어떤 backend 위의 값인지 읽힌다.
|
||||
|
||||
## 사실
|
||||
|
||||
§153 은 QEMU/libvirt 디스크에서 대표적으로 볼 수 있는 설정으로 cache=none 과 cache=writeback 둘을 들었다. 이름만 보고 none = cache 자체가 없음 · writeback = 무조건 위험으로 해석하면 부정확하다. 핵심은 QEMU 가 Host Page Cache 와 write completion/flush semantics 를 어떤 방식으로 사용할 것인가라고 적었다.
|
||||
|
||||
§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 까지 전달되어야 한다고 적었다.
|
||||
|
||||
§156 은 그래서 정확한 표현을 이렇게 적었다. writeback caching 에서는 volatile cache 가 존재할 수 있으므로, Guest 의 flush/fsync semantics 가 전체 backend/storage stack 에서 올바르게 보존되는지가 중요하다.
|
||||
|
||||
§147 은 Guest buffered I/O 와 Host file-backed disk 와 Host Page Cache 를 함께 쓰는 구성을 들었다. 그러면 같은 데이터가 Guest RAM 과 Host RAM 양쪽에 cache 될 수 있다고 밝혔다.
|
||||
|
||||
§175 OQ-4 는 확인 방법으로 virsh dumpxml <VM_NAME> 을 들고, disk driver 설정의 cache 관련 값을 확인하라고 했다.
|
||||
|
||||
이 호스트에 어느 값이 걸려 있는지도, 어느 값을 쓰기로 정했다는 기록도 개념 문서에 없다.
|
||||
|
||||
## 가정
|
||||
|
||||
호스트에 붙어 가상 머신마다 virsh dumpxml <VM_NAME> 을 돌릴 수 있다고 본다.
|
||||
|
||||
받아 낸 XML 이 지금 돌고 있는 가상 머신에 실제로 적용된 설정과 같다고 전제한다. 정의만 고치고 다시 시작하지 않은 가상 머신이 있으면 이 전제가 깨진다.
|
||||
|
||||
가상 머신에 디스크가 여럿이면 디스크마다 값이 다를 수 있어서 요소 단위로 읽는다.
|
||||
|
||||
값이 적혀 있지 않은 디스크에도 실제로 적용되는 동작이 있다고 전제한다. 개념 문서는 값이 없을 때 무엇이 적용되는지를 다루지 않았다.
|
||||
|
||||
## 미지수
|
||||
|
||||
각 가상 머신의 disk driver 에 cache 값이 명시되어 있는지.
|
||||
|
||||
명시되어 있다면 그 값이 none 인지 writeback 인지, 아니면 §153 이 들지 않은 다른 값인지.
|
||||
|
||||
명시가 없을 때 이 환경에서 실제로 적용되는 값이 무엇이고 그것을 무엇으로 읽는지. 개념 문서가 확인 방법을 적지 않아서 실행하는 쪽이 정한다.
|
||||
|
||||
디스크가 여럿인 가상 머신에서 값이 디스크마다 갈리는지.
|
||||
|
||||
읽은 값이 §147 이 그린 이중 caching 이 이 호스트에 있는지를 가르는지. 게스트 쪽 I/O 가 buffered 인지까지 함께 봐야 답이 나온다.
|
||||
|
||||
## 제약
|
||||
|
||||
이 물음은 cache 값을 정하지 않는다. §156 은 이름만 보고 단정하지 말라고 적었을 뿐 어느 쪽을 쓰라고 하지 않았고, 감수할 비용도 개념 문서에 없다. 무엇으로 둘지는 값을 읽고 fsync 쪽 측정이 나온 뒤에 정한다.
|
||||
|
||||
이 프로젝트는 어느 쪽으로 둘지를 아직 결정으로 올리지 않고 미결로 남겨 두었다. 권고가 없는 서술을 결정으로 올리면 그런 설정이 있다는 것을 그것을 고른 이유로 바꾸는 셈이기 때문이다. 게다가 지금 걸려 있는 값조차 확인되지 않았고, 그 확인이 이 물음이다.
|
||||
|
||||
읽은 값 하나로는 §156 이 요구한 것 — Guest 의 flush/fsync semantics 가 전체 chain 에서 보존되는가 — 에 답하지 못한다.
|
||||
|
||||
읽는 동안 디스크 설정을 바꾸지 않는다. 바꾸고 받은 XML 은 지금 구성의 값이 아니기 때문이다.
|
||||
|
||||
이 호스트에서 확인한 값이 없으므로 다른 장비의 기본값을 이 호스트의 값으로 옮겨 적지 않는다.
|
||||
|
||||
## 선택지
|
||||
|
||||
### 1. 가상 머신마다 dumpxml 을 받아 disk 요소를 출력 그대로 인용한다
|
||||
|
||||
받은 XML 에서 disk 요소를 잘라 그대로 남기면 그 값뿐 아니라 driver 이름과 Source 도 한 번에 확정된다. 값이 적혀 있지 않은 디스크는 적혀 있지 않다는 상태로 보인다.
|
||||
|
||||
가상 머신이 늘면 출력도 그만큼 늘어난다.
|
||||
|
||||
### 2. cache 값만 뽑아 가상 머신·디스크 단위 한 행으로 정리한다
|
||||
|
||||
가상 머신 이름, 디스크의 Target, Source, 형식, cache 값을 한 행에 두면 어느 backend 위의 어떤 값인지가 표 하나로 읽힌다. Source 와 형식은 backend 를 묻는 두 물음이 이미 확정한다.
|
||||
|
||||
뽑아 적는 동안 원본 XML 을 남기지 않으면 나중에 다시 대조할 수 없으므로, 원본도 함께 남긴다.
|
||||
|
||||
### 3. 제외 — 두 값을 바꿔 가며 성능 차이를 재 본다
|
||||
|
||||
none 과 writeback 을 번갈아 걸고 지연을 재면 이 환경에서 무엇이 걸려 있는지 바로 보인다. 그러나 지금 필요한 답은 현재 값 하나이고, 설정을 바꾸는 순간 그 뒤에 읽은 값은 지금 구성의 값이 아니다. 값을 바꿔 비교하는 실험은 durability 쪽 기준을 지난 뒤에 따로 잡는다.
|
||||
|
||||
## 다음 검증
|
||||
|
||||
1. 가상 머신마다 virsh dumpxml <VM_NAME> 을 돌리고 disk 요소의 driver 설정을 출력 그대로 남긴다 (§175 OQ-4).
|
||||
2. cache 관련 값을 가상 머신·디스크 단위로 한 행씩 적는다. 값이 적혀 있지 않으면 없다고 적는다.
|
||||
3. 같은 행에 그 디스크의 Source 와 형식을 둔다. 두 값은 앞의 두 물음이 확정한다.
|
||||
|
||||
닫는 조건 : 가상 머신마다 cache 값이 출력으로 확정되면 닫는다. cache=none 이면 §154 의 서술이, cache=writeback 이면 §155 의 서술이 이 호스트에 적용된다고 적는다. 어느 쪽이든 §156 이 요구한 flush/fsync semantics 보존 여부는 이 값만으로 답하지 않는다고 함께 적는다. 값이 명시되어 있지 않으면 그 사실을 적고, 실제 적용 값을 확인할 방법은 다음 검증으로 넘긴다.
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
---
|
||||
id: 1f76d54b-bb17-4174-8128-0c2d71d4256f
|
||||
kind: QUESTION
|
||||
slug: vm-disk-backend-mapping
|
||||
title: 이 호스트의 /dev/vda 는 어떤 backend 에 붙어 있는가
|
||||
topic: storage-virtualization
|
||||
topicName: 스토리지 가상화
|
||||
project: virtualization
|
||||
status: 초안
|
||||
questionStatus: OPEN
|
||||
studio: "https://hyeonworks.com/studio/documents/1f76d54b-bb17-4174-8128-0c2d71d4256f/edit"
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#175-실제-테스트-서버에서-확인할-open-questions-oq-1
|
||||
- final/document.md#135-dev-vda-guest가-보는-가상-block-device
|
||||
- final/document.md#145-host-block-device를-직접-backend로-사용-가능
|
||||
- final/document.md#146-실제-연결-확인
|
||||
- final/document.md#140-vm-boundary를-넘으면-qemu가-등장
|
||||
---
|
||||
|
||||
# 이 호스트의 /dev/vda 는 어떤 backend 에 붙어 있는가
|
||||
|
||||
게스트 안에서 보이는 디스크 이름 하나는 서로 다른 세 가지 호스트 구성 위에서 똑같이 나온다. qcow2 파일일 수도 있고, RAW 파일일 수도 있고, Host block device 를 그대로 붙인 것일 수도 있다. 그래서 이 물음은 성능이나 용량을 재기 전에 이 호스트의 가상 머신이 그중 어느 구성인지부터 출력으로 확정한다. 여기서 갈린 결과가 이 주제의 다음 물음 둘이 이 환경에 적용되는지를 정한다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **/dev/vda 뒤에 있는 것 — qcow2 · RAW · Host block device**
|
||||
이 물음이 가르려는 세 갈래를 그 글이 설명한다.
|
||||
- **Guest 의 write() 가 virtqueue 에 실리기까지 — VFS · Filesystem · Page Cache · Block Layer · virtio-blk**
|
||||
게스트가 보는 그 이름이 어디에서 나왔는지를 그 글이 설명한다.
|
||||
- **Guest 안에서 본 disk 로 backend 를 단정하지 않는다**
|
||||
이 확인을 반복 적용할 기준으로 편 글이고, 이 물음이 그 기준의 첫 항목을 이 호스트에서 채운다.
|
||||
- **이 disk image 는 qcow2 인가 RAW 인가, 그리고 Guest 가 보는 크기와 Host 가 실제로 쓰는 크기는 얼마나 다른가**
|
||||
Source 가 파일로 나왔을 때 이어지는 물음이다.
|
||||
- **disk image 는 최종적으로 어느 Host block device 위에 있는가**
|
||||
같은 조건에서 그 파일 아래를 잇는 물음이다.
|
||||
|
||||
## 사실
|
||||
|
||||
§135 는 virtio-blk 를 쓰는 가상 머신에서 /dev/vda 나 /dev/vdb 같은 이름이 흔히 보인다고 적었다. Guest Linux 는 그 이름을 하나의 block device 로 인식하지만, 그것이 호스트의 실제 SSD 를 뜻하지는 않는다.
|
||||
|
||||
backend 가 반드시 파일일 필요도 없다. §145 는 그 아래에 qcow2 파일과 RAW 파일과 Host block device 셋이 올 수 있다고 갈라 적고, 게스트에 그 이름이 있다는 정보만으로는 backend 구조를 알 수 없다고 못박았다.
|
||||
|
||||
§140 은 가상 머신 경계를 넘으면 QEMU 가 등장한다고 그렸다. QEMU 의 virtio-blk Device Model 과 Block Backend 가 게스트의 virtual I/O 를 호스트 backend 에 연결한다.
|
||||
|
||||
확인 방법으로는 §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 로 정의되어 있어서 virsh 가 그 가상 머신을 안다고 전제한다. §146 과 §175 OQ-1 이 확인 방법으로 virsh 명령을 든 것이 근거이고, 이 호스트에서 확인하지는 않았다.
|
||||
|
||||
확인하는 동안 디스크 구성이 바뀌지 않는다고 본다.
|
||||
|
||||
## 미지수
|
||||
|
||||
이 호스트의 각 가상 머신에 디스크가 몇 개 붙어 있는지.
|
||||
|
||||
각 Target 의 Source 가 무엇인지.
|
||||
|
||||
그 Source 가 파일인지 Host block device 인지.
|
||||
|
||||
## 제약
|
||||
|
||||
이 물음은 backend 가 무엇인지까지 답한다. 형식과 크기는 다음 물음이 받고, 성능은 여기서 판정하지 않는다.
|
||||
|
||||
게스트 안에서 본 이름은 답이 되지 않는다. §145 가 그 추론을 명시적으로 막았기 때문이다.
|
||||
|
||||
이 호스트에서 얻은 출력이 없으므로 다른 장비의 디스크 구성을 근거로 삼지 않는다.
|
||||
|
||||
## 선택지
|
||||
|
||||
### 1. 게스트와 호스트를 한 번씩 돌고 한 표로 남긴다
|
||||
|
||||
§146 이 든 두 명령을 그대로 돌린다. 각 게스트에서 lsblk 로 보이는 block device 와 파티션을 받고, 호스트에서 가상 머신마다 virsh domblklist <VM_NAME> 을 돌려 Target 과 Source 를 받는다. 두 출력이 있으면 게스트가 보는 이름과 호스트의 실제 대상이 가상 머신마다 한 행으로 이어진다.
|
||||
|
||||
로그인할 수 없는 가상 머신이 있으면 그쪽은 호스트 출력만 얻고, 게스트가 그 디스크를 어떤 파티션으로 나눠 쓰는지는 비게 된다.
|
||||
|
||||
### 2. 호스트 쪽 출력만 먼저 받는다
|
||||
|
||||
virsh domblklist 는 호스트에서만 돌아가고 Target 과 Source 를 한 번에 주기 때문에, 가상 머신에 로그인하지 않아도 backend 경로가 확정된다. 이어지는 두 물음이 요구하는 이미지 경로도 바로 얻는다.
|
||||
|
||||
대신 게스트가 그 디스크를 어떤 이름과 파티션으로 보고 있는지가 빠진다. 나중에 게스트 안에서 잰 스토리지 수치를 이 표에 붙이려면 그때 대응을 다시 확인해야 한다.
|
||||
|
||||
### 3. libvirt 정의 전문을 받아 disk 요소를 읽는다 — 제외
|
||||
|
||||
virsh dumpxml 은 disk 요소에 source 와 driver 를 함께 담고 있어서 backend 경로와 cache 설정을 한 번에 볼 수 있다.
|
||||
|
||||
§175 는 dumpxml 을 cache mode 를 확인하는 항목에 두었고, 연결 확인에는 §146 과 같은 domblklist 를 들었다. 여기서 dumpxml 을 쓰면 한 출력으로 두 물음이 닫히게 되어 어느 확인이 무엇을 근거로 끝났는지가 흐려진다. cache 값은 그 물음이 받는다.
|
||||
|
||||
## 다음 검증
|
||||
|
||||
1. 각 게스트에서 lsblk 를 돌려 보이는 block device 와 파티션을 적는다.
|
||||
2. 호스트에서 가상 머신마다 virsh domblklist <VM_NAME> 을 돌려 Target 과 Source 를 적는다.
|
||||
3. 가상 머신마다 한 행으로 정리하고 두 명령의 출력을 그대로 증거로 둔다.
|
||||
|
||||
닫는 조건 : 가상 머신마다 Target 과 Source 의 대응이 출력으로 확정되면 닫는다. Source 가 파일이면 형식과 실제 사용량을 묻는 물음과 그 파일이 올라간 Host block device 를 묻는 물음으로 이어진다. Host block device 면 §145 가 적은 그 갈래가 이 환경이라고 적고, qcow2 를 전제한 후속 물음 둘은 이 호스트에 적용되지 않는다고 함께 적는다.
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
---
|
||||
id: ed64d4ff-aa01-48cc-ae68-6a683b025cde
|
||||
kind: QUESTION
|
||||
slug: vm1-storage-load-vs-vm2-latency
|
||||
title: VM1 의 storage 부하가 VM2 의 지연을 실제로 밀어 올리는가
|
||||
topic: storage-virtualization
|
||||
topicName: 스토리지 가상화
|
||||
project: virtualization
|
||||
status: 초안
|
||||
questionStatus: OPEN
|
||||
studio: "https://hyeonworks.com/studio/documents/ed64d4ff-aa01-48cc-ae68-6a683b025cde/edit"
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#175-실제-테스트-서버에서-확인할-open-questions-oq-7
|
||||
- final/document.md#167-storage-contention
|
||||
- final/document.md#159-여러-vm이-하나의-nvme를-공유하면
|
||||
- final/document.md#168-cpu가-정상이어도-storage-때문에-느릴-수-있다
|
||||
---
|
||||
|
||||
# VM1 의 storage 부하가 VM2 의 지연을 실제로 밀어 올리는가
|
||||
|
||||
한 물리 NVMe 를 나눠 쓰는 구성에서 VM1 이 I/O 를 쏟아내면 VM2 의 지연이 오를 수 있다고 개념 문서가 적었다. 개념 문서가 적은 것은 거기까지이고, 이 호스트에서 재 본 값은 없다. 그래서 이 물음은 VM1 에 controlled I/O load 를 걸고 VM2 의 애플리케이션 지연과 Host storage 지표를 같은 시간축에 놓아, 그 경쟁이 이 구성에서 실제로 관측되는지를 가른다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **VM 아래에 한 번 더 있는 Host block stack — blk-mq · I/O Scheduler · NVMe**
|
||||
두 가상 머신의 I/O 가 어디서 만나는지를 그 개념이 설명한다.
|
||||
- **지연 원인을 CPU 로 읽기 전에 storage 계층을 따로 잰다**
|
||||
부하 구간에 CPU 와 스토리지 지표를 함께 찍는 이유가 그 기준이다.
|
||||
- **disk image 는 최종적으로 어느 Host block device 위에 있는가**
|
||||
두 가상 머신이 같은 장치를 쓰는지가 이 실험의 전제이고, 그 물음이 그것을 확정한다.
|
||||
- **이 호스트의 I/O Scheduler 는 무엇인가**
|
||||
dispatch 정책이 무엇인지가 부하 구간의 값을 읽는 조건이 된다.
|
||||
- **Guest 의 fsync() 지연과 Host storage 지연은 같이 오르는가**
|
||||
같은 호스트 지표를 보지만 그쪽은 Guest 가 요구한 durability 가 전달되는지를 묻는다.
|
||||
- **가상 머신 두 대에 동시에 부하를 주면 이 호스트에서 vCPU 경쟁이 실제로 생기는가**
|
||||
부하를 거는 모양이 닮았지만 재는 자원이 CPU 라 같은 측정으로 닫히지 않는다. 그래서 하나로 합치지 않고 관계로만 이었다.
|
||||
|
||||
## 사실
|
||||
|
||||
§159 는 VM1 QEMU 와 VM2 QEMU 와 Nginx 와 Host 기타 프로세스가 같은 Host Block Layer queue 로 들어와 device 로 dispatch 된다고 그렸다. VM1 의 WRITE X · READ Y · WRITE Z 와 VM2 의 READ A · WRITE B 와 Host process 의 READ C 가 그 queue 에서 함께 관리된다.
|
||||
|
||||
§167 은 여러 가상 머신이 동일한 Physical NVMe 를 사용하면 storage resource 경쟁이 발생할 수 있고, VM1 에서 대량 I/O 가 발생하면 VM2 의 storage latency 가 증가할 수 있다고 적었다. 그러면서 두 경쟁을 이렇게 갈랐다.
|
||||
|
||||
CPU Contention : Host logical CPU 실행 시간 경쟁
|
||||
Storage Contention : IOPS / bandwidth / queue / device 처리시간 경쟁
|
||||
|
||||
§168 은 PostgreSQL 이 storage completion 을 기다리는 동안 CPU usage 가 높지 않을 수 있다고 적고, CPU 30% 인데 Request latency 2초 인 상황을 들었다. 그래서 CPU 지표만으로 지연의 원인을 판단하면 안 된다고 했다.
|
||||
|
||||
§169 는 device I/O 관측으로 iostat -xz 1 을, 어떤 프로세스가 I/O 를 발생시키는지 볼 때는 iotop 을 들었다. iostat 에서 볼 대상으로 read/write throughput, IOPS, request latency, queue 상태, device utilization 성격의 지표를 적었다.
|
||||
|
||||
§175 OQ-7 이 적은 실험은 한 문장이다. VM1 에서 별도의 테스트 파일이나 디스크로 controlled I/O load 를 발생시키고 VM2 의 애플리케이션 지연과 Host storage 지표를 동시에 본다. 부하를 무엇으로 만들지, 얼마나 크게 얼마나 오래 걸지는 그 한 문장에 없어서 재는 쪽이 정한다.
|
||||
|
||||
이 호스트에서 그렇게 재 본 결과는 개념 문서에 없다. §168 의 30% 와 2초 도 CPU 와 지연이 어긋날 수 있다는 것을 보이려고 든 예시 값이다.
|
||||
|
||||
## 가정
|
||||
|
||||
두 가상 머신의 이미지가 같은 block device 위에 있다고 보고 실험을 짠다. 앞 물음이 서로 다른 장치라고 확정하면 이 전제가 없어진다.
|
||||
|
||||
VM1 에 controlled I/O load 를 걸었다가 걷을 수 있고, 걷은 뒤 상태가 부하 이전의 기준 구간으로 돌아온다고 본다. 돌아오는지는 세 번째 구간에서 확인한다.
|
||||
|
||||
VM2 의 애플리케이션 지연을 세 구간에서 같은 방법으로 잴 수 있다고 전제한다.
|
||||
|
||||
호스트와 두 게스트의 시계가 맞아서 지연과 iostat 값을 같은 시간축에 놓을 수 있다고 본다.
|
||||
|
||||
부하를 거는 동안 가상 머신 구성과 VM2 의 워크로드가 바뀌지 않는다고 두고 세 구간을 견준다.
|
||||
|
||||
호스트 쪽 Nginx 와 다른 프로세스도 같은 장치를 쓰는데, 부하 구간의 스토리지 지표를 전부 VM1 몫으로 읽으면 이 전제가 깨진다. 그래서 어느 프로세스가 I/O 를 내는지 iotop 으로 함께 찍는다.
|
||||
|
||||
## 미지수
|
||||
|
||||
VM1 에 부하를 걸었을 때 VM2 의 애플리케이션 지연이 기준 구간과 달라지는지.
|
||||
|
||||
달라진다면 같은 구간의 Host storage 지표 — IOPS, throughput, request latency, queue 상태 — 가 같은 방향으로 움직이는지.
|
||||
|
||||
그 변화가 CPU 쪽 지표와 무관한지. §168 은 CPU 가 한가해도 지연이 커질 수 있다고 적었다.
|
||||
|
||||
어느 정도의 부하에서 VM2 의 지연이 움직이기 시작하는지. 개념 문서는 부하의 크기와 지속 시간을 적지 않았다.
|
||||
|
||||
부하를 걷은 뒤 VM2 의 지연이 기준 구간으로 돌아오는지, 돌아온다면 얼마나 걸리는지.
|
||||
|
||||
## 제약
|
||||
|
||||
controlled I/O load 는 VM1 안의 별도 테스트 파일이나 디스크에 건다 (§175 OQ-7). VM1 의 운영 데이터에 걸면 부하를 되돌릴 수 없다.
|
||||
|
||||
세 구간 모두 같은 명령으로 찍는다. 구간마다 다른 도구로 재면 값이 달라진 것인지 도구가 다른 것인지 갈리지 않기 때문이다.
|
||||
|
||||
지연이 올랐다는 관측만으로 원인을 스토리지 경쟁으로 확정하지 않는다. 같은 구간의 CPU 지표를 함께 놓고 §167 이 가른 두 경쟁 가운데 어느 쪽인지 본다.
|
||||
|
||||
장치를 나눌지, 스케줄러를 바꿀지, I/O 를 제한할지는 이 물음이 정하지 않는다. 재고 나온 표가 그 결정의 근거가 된다.
|
||||
|
||||
측정하는 동안 다른 실험을 같은 장치 위에서 돌리지 않는다. 시간을 나눈다.
|
||||
|
||||
## 선택지
|
||||
|
||||
### 1. 기준 · 부하 · 회복 세 구간을 한 번에 돌린다
|
||||
|
||||
VM2 의 애플리케이션 지연과 호스트의 iostat -xz 1 과 CPU 를 기준 구간에서 한 번 찍고, VM1 에 부하를 건 구간에서 다시 찍고, 부하를 걷은 뒤 세 번째로 찍는다. 세 구간이 한 표에 들어가면 지연이 어느 지표와 함께 움직였는지 그 표에서 읽힌다. §175 OQ-7 이 적은 순서 그대로다.
|
||||
|
||||
부하를 만드는 방법과 크기를 먼저 정해야 하고, VM2 에 같은 부하를 세 번 거는 준비가 필요하다.
|
||||
|
||||
### 2. 부하를 단계로 올리며 지연을 따라 찍는다
|
||||
|
||||
부하를 한 단계씩 올려 가며 같은 셋을 반복해 찍으면 VM2 의 지연이 어느 지점에서 움직이기 시작하는지까지 나온다. 개념 문서가 부하 크기를 적지 않았으므로 한 번의 부하로는 그 크기가 적절했는지 알 수 없다.
|
||||
|
||||
실행 횟수가 늘고, 단계마다 같은 시간을 유지해야 값을 견줄 수 있다.
|
||||
|
||||
### 3. 제외 — 호스트에서 직접 I/O 부하를 만들어 대신한다
|
||||
|
||||
VM1 대신 호스트 프로세스로 부하를 만들면 준비가 짧다. §159 는 Host process 의 I/O 도 같은 queue 로 들어온다고 그렸으므로 장치 쪽 경쟁은 비슷하게 만들 수 있다. 그러나 이 물음이 묻는 것은 VM1 의 부하가 VM2 로 이어지는지이고, 호스트에서 만든 부하는 QEMU 를 지나지 않아서 같은 경로가 아니다.
|
||||
|
||||
## 다음 검증
|
||||
|
||||
1. 두 가상 머신의 이미지가 같은 block device 를 쓰는지 먼저 확정한다.
|
||||
2. 기준 구간에서 VM2 의 애플리케이션 지연과 호스트의 iostat -xz 1 과 CPU 를 같은 시각에 기록한다.
|
||||
3. VM1 에서 별도의 테스트 파일이나 디스크로 controlled I/O load 를 발생시킨다. 무엇으로 어느 정도의 부하를 얼마나 오래 걸었는지 함께 적는다 (§175 OQ-7).
|
||||
4. 부하 구간에서 2번의 셋을 같은 방법으로 다시 기록하고, 어느 가상 머신이 I/O 를 내는지 iotop 으로 함께 남긴다 (§169).
|
||||
5. 부하를 걷고 회복 구간을 세 번째로 찍는다.
|
||||
|
||||
닫는 조건 : 세 구간 표에서 부하 구간에만 VM2 의 지연과 Host storage 지표가 함께 오르면 §167 이 그린 경쟁이 이 환경에서 이어진다는 것을 확인한 것이고, 그 시계열이 Case 가 된다. VM1 부하 중에도 VM2 의 지연이 기준 구간과 다르지 않으면 이 구성과 이 부하 수준에서는 경쟁이 관측되지 않는다고 적고 닫는다. 장치를 나눌지 · 스케줄러를 바꿀지 · I/O 를 제한할지는 그 Case 가 나온 뒤 결정으로 넘긴다.
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
---
|
||||
id: decbab8d-9c98-45c6-b5b8-e6162e407299
|
||||
kind: REFERENCE
|
||||
slug: a-speedup-that-removed-durability-is-not-an-optimization
|
||||
title: 빨라진 구성이 durability contract 를 지운 것은 아닌지 먼저 가른다
|
||||
topic: storage-virtualization
|
||||
topicName: 스토리지 가상화
|
||||
project: virtualization
|
||||
status: 초안
|
||||
studio: "https://hyeonworks.com/studio/documents/decbab8d-9c98-45c6-b5b8-e6162e407299/edit"
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#171-성능과-durability의-trade-off
|
||||
- final/document.md#152-가장-위험한-상황-거짓-완료
|
||||
- final/document.md#156-writeback-=-위험-이라고-단정하면-안-되는-이유
|
||||
- final/document.md#148-write-완료와-영속화는-다르다
|
||||
- final/document.md#157-device-side-cache
|
||||
- final/document.md#150-fsync-가-필요한-이유
|
||||
- final/document.md#170-postgresql-예시-wal과-durability
|
||||
- final/document.md#174-핵심-claim
|
||||
---
|
||||
|
||||
# 빨라진 구성이 durability contract 를 지운 것은 아닌지 먼저 가른다
|
||||
|
||||
durability 는 전원이 나가도 데이터가 남아 있는 영속성을 말한다. 쓰기 지연이 줄면 그 변경은 성능 개선으로 기록되지만, 무엇을 더 이상 보장하지 않게 됐는지는 아무 데도 적히지 않는다. 개념 문서는 fsync() 를 없애서 빨라졌다면 그것이 최적화가 아니라 durability contract 를 제거한 것일 수 있다고 적었다. 게스트가 요청한 FLUSH 에 완료로 응답했는데 데이터는 호스트 RAM 에만 있는 상태도 성능 문제가 아니라 correctness 문제로 놓았다. 이 기준은 빨라진 구성을 평가할 때 어느 단계가 빠졌는지를 먼저 적게 한다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **완료라는 말의 네 가지 뜻 — write · writeback · flush · 전원 장애 생존**
|
||||
이 기준이 갈라 적으라고 하는 네 단계를 그 글이 설명한다.
|
||||
- **/dev/vda 뒤에 있는 것 — qcow2 · RAW · Host block device**
|
||||
영속성을 어느 백엔드 위에서 이야기하는지 그 글이 설명한다.
|
||||
- **이 VM 들의 QEMU disk cache mode 는 무엇인가**
|
||||
이 기준이 걸리는 설정 값을 이 환경에서 읽는 물음이다.
|
||||
- **Guest 의 fsync() 지연과 Host storage 지연은 같이 오르는가**
|
||||
영속성을 지킨 상태의 지연이 얼마인지 이 환경에서 재는 물음이다.
|
||||
- **지연 원인을 CPU 로 읽기 전에 storage 계층을 따로 잰다**
|
||||
같은 스토리지 판독에 쓰는 기준이고, 그쪽은 자원을 가르고 이쪽은 보장을 가른다.
|
||||
|
||||
## 목적
|
||||
|
||||
스토리지 설정을 바꿔 쓰기 지연이 줄었을 때 그것을 성능 항목으로만 적으면 어떤 실패에 노출됐는지가 기록에 남지 않는다. 개념 문서가 가장 위험한 상황으로 든 것은 거짓 완료다. 게스트가 WRITE 와 FLUSH 를 요청했는데 데이터는 호스트 RAM 에 있고 물리 스토리지에는 옛 데이터가 있는 상태인데도 게스트에게 FLUSH 완료라고 응답하는 경우다. 그러면 PostgreSQL 은 영속성이 확보되었다고 판단하고, 직후 호스트 전원이 나가면 RAM 의 데이터가 사라진다.
|
||||
|
||||
이 기준은 빨라진 것을 되돌리라고 하지 않는다. 무엇이 빨라졌는지 옆에 어느 보장이 사라졌는지를 같이 적게 한다.
|
||||
|
||||
이것을 완료의 네 단계를 설명하는 개념 기록 안의 각주로 두지 않고 따로 뺀 것은 이것이 필요해지는 때가 다르기 때문이다. 개념을 읽을 때가 아니라 설정을 고르고 성능 개선을 평가할 때 걸리는데, 각주로 두면 다음에 같은 판단을 하는 사람이 그때 이것을 찾지 못한다.
|
||||
|
||||
## 규칙
|
||||
|
||||
### 1. 완료를 네 단계로 갈라 어디까지 보장되는지 적는다
|
||||
|
||||
개념 문서는 write() 완료와 writeback 완료와 fsync/flush 완료와 전원 장애에도 안전한 durability 를 서로 다른 것으로 놓았다. write(fd, data, size) 가 성공했다는 것만으로 정전 이후 생존이 보장되지 않으므로, 필요한 시점에 fsync(fd) 를 불러 변경 내용을 필요한 영속성 경계까지 반영하도록 요청한다. 어떤 구성을 적을 때 이 네 단계 중 어디까지가 보장되는지를 함께 적는다.
|
||||
|
||||
### 2. 빨라진 구성에서 사라진 경계를 이름으로 적는다
|
||||
|
||||
무엇이 몇 밀리초 줄었는지보다 먼저, 그 변경이 위 네 단계 중 어느 것을 건너뛰게 했는지를 적는다. 개념 문서가 그린 전달 경로는 게스트가 요구한 durability 에서 시작한다. 거기서 Guest Filesystem, Guest Block Layer, virtio, QEMU 와 backend, Host Storage, Device 로 이어진다. 이 경로는 어디에서 의미가 끊겨도 결과가 같아서, 끊긴 곳을 짚지 못하면 그 구성이 무엇을 보장하는지 다음 사람이 다시 조사해야 한다.
|
||||
|
||||
### 3. 거짓 완료를 성능 항목으로 분류하지 않는다
|
||||
|
||||
FLUSH 완료 응답을 받은 쪽은 그 데이터가 살아남는다고 가정하고 그 다음 동작을 한다. PostgreSQL 이라면 COMMIT 을 성공으로 처리한다. 이 가정이 틀린 상태는 느려지는 것이 아니라 응답한 내용이 사실과 다른 것이므로, 개념 문서는 이것을 durability contract 가 깨지는 correctness 문제로 적었다.
|
||||
|
||||
### 4. 캐시 모드를 이름만 보고 안전과 위험으로 가르지 않는다
|
||||
|
||||
개념 문서는 none 을 캐시 자체가 없음으로, writeback 을 무조건 위험으로 읽는 것이 부정확하다고 적었다. cache=writeback 을 골라도 정상적인 스택이라면 게스트의 fsync 와 FLUSH 는 virtio FLUSH 와 QEMU 와 백엔드를 지나 호스트의 sync 와 flush 로 전달된다. 필요한 완료 확인 뒤에 게스트로 완료가 돌아간다. 개념 문서는 정확한 표현을 이렇게 적었다 — writeback caching 에서는 volatile cache 가 존재할 수 있으므로 Guest 의 flush/fsync semantics 가 전체 backend/storage stack 에서 올바르게 보존되는지가 중요하다.
|
||||
|
||||
그래서 이 기준은 어느 캐시 모드를 쓰라고 말하지 않는다. 개념 문서는 두 설정의 뜻과 각각의 오해를 갈라 놓기만 했고 어느 쪽으로 정했다고 적지 않았으며 감수한 비용도 적지 않았다. 이 호스트가 지금 무엇을 쓰고 있는지도 아직 읽지 않았다.
|
||||
|
||||
### 5. DB 부하에서는 애플리케이션이 쓰는 영속성 규약을 함께 적는다
|
||||
|
||||
PostgreSQL 은 WAL 등의 durability protocol 을 사용하며 필요한 시점에 스토리지 동기화를 수행한다. 가상 머신의 스토리지 계층이 flush 와 fsync 의 의미를 제대로 보존하지 않으면 애플리케이션이 전제한 영속성과 실제 스토리지 동작이 어긋난다. 그래서 설정만 적지 않고 그 위에서 도는 애플리케이션이 무엇을 언제 동기화하는지도 같이 적는다.
|
||||
|
||||
## 적용 조건
|
||||
|
||||
- 스토리지 관련 설정이나 코드를 바꿔 쓰기 지연이나 커밋 지연이 줄었을 때
|
||||
- QEMU 의 캐시 모드를 고르기 전
|
||||
- Direct I/O 를 켜거나 끄기 전
|
||||
- fsync() 호출을 더하거나 뺄 때
|
||||
- 파일시스템의 마운트 옵션을 고를 때
|
||||
|
||||
## 예외
|
||||
|
||||
- 영속성을 실제로 포기해도 되는 데이터가 있다. 다시 만들 수 있는 캐시나 파생 파일이 그렇고, 그때는 빨라진 것이 정당한 선택이다. 이 기준이 요구하는 것은 포기를 막는 것이 아니라 무엇을 포기했는지 적게 하는 것이다.
|
||||
- cache=none 이나 Direct I/O 를 골랐다고 영속성이 확보되지도 않는다. 개념 문서는 Direct I/O 의 핵심이 페이지 캐시 우회이며 호스트 페이지 캐시를 우회하는 것이 즉시 durable media 반영을 뜻하지 않는다고 적었고, 그 뒤에 스토리지 컨트롤러나 장치가 휘발성 쓰기 캐시를 가질 수 있다고 덧붙였다.
|
||||
- 개념 문서는 실제 ordering 과 durability semantics 가 더 복잡하다고 밝혔다. 그래서 이 기준으로 특정 설정이 안전하다는 결론을 내지 않는다. 실제 운영에서는 장치의 flush 와 FUA semantics, power-loss protection 여부도 함께 본다.
|
||||
- 이 저장소에는 영속성을 실제로 재 본 실험이 없다. 여기 적은 것은 개념 문서가 서술한 구분이고 이 호스트에서 확인된 동작이 아니다.
|
||||
|
||||
## 예시
|
||||
|
||||
- write() 완료 : 애플리케이션 호출이 돌아온 상태
|
||||
- writeback 완료 : 캐시에서 아래로 내려간 것까지다
|
||||
- fsync/flush 완료 : 여기서도 장치 쪽 휘발성 캐시가 뒤에 남을 수 있다
|
||||
- 전원 장애에도 안전한 상태 : 위 셋과 다른 단계로 센다
|
||||
- 빨라졌다 : 어느 단계를 건너뛰었는지 옆에 적는다
|
||||
- 포기해도 되는 데이터 : 다시 만들 수 있는 캐시 · 파생 파일
|
||||
- 이 호스트에서 잰 영속성 측정 : x
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
---
|
||||
id: 274c5636-33f6-4c19-a54b-d2a96af31289
|
||||
kind: REFERENCE
|
||||
slug: confirm-the-disk-backend-on-the-host
|
||||
title: Guest 안에서 본 disk 로 backend 를 단정하지 않는다
|
||||
topic: storage-virtualization
|
||||
topicName: 스토리지 가상화
|
||||
project: virtualization
|
||||
status: 초안
|
||||
studio: "https://hyeonworks.com/studio/documents/274c5636-33f6-4c19-a54b-d2a96af31289/edit"
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#145-host-block-device를-직접-backend로-사용-가능
|
||||
- final/document.md#146-실제-연결-확인
|
||||
- final/document.md#143-qcow2-virtual-size와-실제-host-사용량
|
||||
- final/document.md#144-raw-image
|
||||
- final/document.md#135-dev-vda-guest가-보는-가상-block-device
|
||||
- final/document.md#142-qcow2-host에서는-파일-guest에서는-디스크
|
||||
- final/document.md#169-storage-관측-명령어
|
||||
---
|
||||
|
||||
# Guest 안에서 본 disk 로 backend 를 단정하지 않는다
|
||||
|
||||
백엔드는 게스트가 디스크로 쓰는 그 장치를 호스트 쪽에서 실제로 대 주는 것을 말한다. 게스트에 로그인해 lsblk 를 돌리면 /dev/vda 와 그 파티션이 나오고, 거기까지는 물리 머신과 같은 화면이다. 개념 문서는 그 이름이 호스트의 실제 SSD 를 뜻하지 않으며 아래에 qcow2 파일과 RAW 파일과 호스트 블록 장치 셋이 올 수 있다고 적었다. 이 기준은 용량과 성능과 영속성을 판단하기 전에 호스트 쪽에서 무엇에 붙어 있는지를 먼저 확정하게 한다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **/dev/vda 뒤에 있는 것 — qcow2 · RAW · Host block device**
|
||||
이 기준이 가르라고 하는 세 갈래를 그 글이 설명한다.
|
||||
- **Guest 의 write() 가 virtqueue 에 실리기까지 — VFS · Filesystem · Page Cache · Block Layer · virtio-blk**
|
||||
게스트가 본 그 디스크 이름이 어디에서 나왔는지를 그 글이 설명한다.
|
||||
- **이 호스트의 /dev/vda 는 어떤 backend 에 붙어 있는가**
|
||||
이 기준의 첫 확인을 이 환경에서 실제로 돌리는 물음이다.
|
||||
- **이 disk image 는 qcow2 인가 RAW 인가, 그리고 Guest 가 보는 크기와 Host 가 실제로 쓰는 크기는 얼마나 다른가**
|
||||
백엔드가 파일로 확정됐을 때 형식과 크기를 읽는 물음이다.
|
||||
- **disk image 는 최종적으로 어느 Host block device 위에 있는가**
|
||||
그 파일이 놓인 파일시스템과 블록 장치까지 잇는 물음이다.
|
||||
- **지연 원인을 CPU 로 읽기 전에 storage 계층을 따로 잰다**
|
||||
그 기준의 호스트 쪽 관측은 여기서 확정한 백엔드를 전제한다.
|
||||
|
||||
## 목적
|
||||
|
||||
게스트 안의 이름을 호스트의 저장 구조로 읽으면 그 위에서 하는 판단이 모두 어긋난다. 개념 문서는 게스트 리눅스가 /dev/vda 를 하나의 블록 장치로 인식하지만 그것이 호스트의 실제 SSD 를 뜻하지는 않는다고 적었다. 같은 대상을 호스트 관점에서 보면 파일 하나이고 게스트 관점에서 보면 디스크이며, 둘 다 맞다.
|
||||
|
||||
무엇 위에서 재고 있는지가 정해지지 않으면 용량 판독과 성능 판독이 서로 다른 대상을 가리킨다. 게스트가 100 GB 를 본다는 사실과 호스트가 그만큼 쓰고 있다는 사실은 다른 이야기이고, 형식이 무엇인지에 따라 확인할 항목도 달라진다.
|
||||
|
||||
개념 문서가 실제 서버에서 확인하라고 적어 둔 물음 가운데 넷이 이 확정 위에서 돌아간다. 백엔드가 무엇인지, 형식이 무엇인지, 크기가 얼마나 벌어져 있는지, 그 아래가 어느 장치인지다. 넷마다 같은 절차를 되풀이해 적지 않고 한 편으로 두었으므로 각 물음이 여기를 가리킨다.
|
||||
|
||||
## 규칙
|
||||
|
||||
### 1. 게스트의 장치 이름을 호스트의 저장 장치로 옮겨 읽지 않는다
|
||||
|
||||
물리 머신에서는 /dev/sda 나 /dev/nvme0n1 이 보이고 virtio-blk 를 쓰는 가상 머신에서는 /dev/vda 나 /dev/vdb 가 보인다. 이름이 그렇게 갈릴 뿐 게스트 쪽 화면은 두 경우 모두 블록 장치 하나와 그 파티션으로 보인다. 게스트에서 얻은 이름은 게스트가 인식한 것까지 말해 주고 그 아래 구조는 말해 주지 않는다.
|
||||
|
||||
### 2. Target 과 Source 를 이어 붙인 뒤에 판독을 시작한다
|
||||
|
||||
게스트에서 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 쪽 확인 항목은 애초에 걸리지 않는다.
|
||||
|
||||
### 4. 용량은 virtual size 와 실제 할당을 갈라 읽는다
|
||||
|
||||
qcow2 는 가상 디스크 크기와 실제 호스트 할당량이 다를 수 있다. 개념 문서가 든 그림은 게스트가 보는 공간 100 GB 에 호스트 실제 할당 3GB 였다. 게스트가 데이터를 기록하면서 Actual 이 1GB 에서 10GB 로, 다시 40GB 로 늘 수 있다는 예도 함께 보였다. qemu-img info 를 볼 때 virtual size 와 실제 allocation 을 구분해서 봐야 한다.
|
||||
|
||||
### 5. 파일이면 그 파일이 놓인 호스트 파일시스템과 블록 장치까지 적는다
|
||||
|
||||
백엔드가 qcow2 파일이면 QEMU 는 결국 호스트 리눅스에 파일 입출력을 요청한다. 그 요청은 Host Filesystem 과 Host Block Layer 와 NVMe Driver 를 지나 Physical NVMe 로 내려간다. 게스트의 스토리지 스택 아래에 호스트의 스토리지 스택이 한 번 더 있으므로, 어느 파일시스템과 어느 블록 장치 위에 있는지를 lsblk 로 함께 적는다.
|
||||
|
||||
## 적용 조건
|
||||
|
||||
- 가상 머신의 디스크 성능이나 용량이나 영속성을 판단하기 전
|
||||
- 게스트 안에서 잰 스토리지 수치를 해석하기 전
|
||||
- 저장 공간 계획을 세우거나 이미지를 옮기기 전
|
||||
- 가상 머신 여러 대가 같은 물리 장치를 쓰는지 확인할 때
|
||||
|
||||
## 예외
|
||||
|
||||
- 백엔드가 호스트 블록 장치면 qcow2 쪽 확인은 걸리지 않는다. 가상 크기와 실제 할당량의 차이도, 매핑과 메타데이터 처리도 그 구성에는 없다.
|
||||
- 백엔드를 확정해도 성능은 예측되지 않는다. 개념 문서는 RAW 는 무조건 빠르고 qcow2 는 무조건 느리다는 식으로 일반화하지 말라고 못박고, 실제 성능이 캐시 모드와 스토리지 백엔드와 부하 패턴과 큐 깊이와 스냅숏 체인과 그 아래 파일시스템과 물리 장치에 영향을 받는다고 들었다. 이 기준은 무엇 위에서 재고 있는지까지만 확정한다.
|
||||
- 이 저장소에는 이 절차를 실제로 돌린 출력이 없다. 여기 적은 것은 개념 문서가 서술한 확인 순서이고 이 호스트에서 나온 값이 아니다.
|
||||
|
||||
## 예시
|
||||
|
||||
- 게스트에서 보이는 것 : /dev/vda 와 그 파티션
|
||||
- 호스트에서 이어 붙일 것 : Target 과 Source 의 대응
|
||||
- Source 의 세 갈래 : qcow2 파일 · RAW 파일 · 호스트 블록 장치
|
||||
- 용량 : virtual size 와 실제 allocation 을 따로 적는다
|
||||
- 파일이면 더 볼 것 : 그 파일이 올라간 파일시스템과 블록 장치
|
||||
- 이 호스트에서 돌린 출력 : x
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
---
|
||||
id: b1cda8c4-d31d-4ae4-a4d9-4103a0ab3109
|
||||
kind: REFERENCE
|
||||
slug: read-storage-before-blaming-cpu-for-latency
|
||||
title: 지연 원인을 CPU 로 읽기 전에 storage 계층을 따로 잰다
|
||||
topic: storage-virtualization
|
||||
topicName: 스토리지 가상화
|
||||
project: virtualization
|
||||
status: 초안
|
||||
studio: "https://hyeonworks.com/studio/documents/b1cda8c4-d31d-4ae4-a4d9-4103a0ab3109/edit"
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#168-cpu가-정상이어도-storage-때문에-느릴-수-있다
|
||||
- final/document.md#167-storage-contention
|
||||
- final/document.md#169-storage-관측-명령어
|
||||
- final/document.md#174-핵심-claim
|
||||
- final/document.md#177-최종-요약
|
||||
---
|
||||
|
||||
# 지연 원인을 CPU 로 읽기 전에 storage 계층을 따로 잰다
|
||||
|
||||
요청이 느린데 CPU 사용률이 낮으면 CPU 는 원인이 아니라고 읽고 다음 후보로 넘어가기 쉬운데, 그 사이에 스토리지 대기는 어느 지표에도 나타나지 않는다. 개념 문서는 애플리케이션이 스토리지 완료를 기다리는 동안 CPU 사용률이 높지 않을 수 있다고 적고 CPU 30% 인데 요청 지연 2초인 조합을 들었다. 이 기준은 자원을 CPU 와 스토리지로 가르는 순서와, 그때 같은 시간축에 무엇을 남길지를 고정한다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **VM 아래에 한 번 더 있는 Host block stack — blk-mq · I/O Scheduler · NVMe**
|
||||
이 기준이 함께 보라고 하는 호스트 쪽 지표가 어느 계층에서 나오는지를 그 글이 설명한다.
|
||||
- **Guest 의 write() 가 virtqueue 에 실리기까지 — VFS · Filesystem · Page Cache · Block Layer · virtio-blk**
|
||||
게스트 안에서 잰 입출력 지연이 어디까지의 시간인지를 그 글이 설명한다.
|
||||
- **Guest 안에서 본 disk 로 backend 를 단정하지 않는다**
|
||||
무엇 위에서 재고 있는지를 먼저 확정하는 기준이다. 이 기준의 호스트 쪽 관측은 그 확정을 전제한다.
|
||||
- **VM1 의 storage 부하가 VM2 의 지연을 실제로 밀어 올리는가**
|
||||
다른 가상 머신의 스토리지 부하를 함께 보라는 항목을 이 환경에서 재는 물음이다.
|
||||
- **Guest 의 fsync() 지연과 Host storage 지연은 같이 오르는가**
|
||||
게스트 지표와 호스트 지표를 같은 시간축에 놓으라는 항목을 이 환경에서 재는 물음이다.
|
||||
- **메모리 증상 하나로 계층을 단정하지 않는다**
|
||||
같은 형태의 판독 기준이고, 그쪽은 메모리 증상 안에서 계층을 가른다.
|
||||
|
||||
## 목적
|
||||
|
||||
CPU 사용률 하나로 지연 원인을 자원에 귀속하면 스토리지 대기가 후보에서 빠진다. 개념 문서는 그 조합을 그대로 적었다. PostgreSQL 이 스토리지 완료를 기다리고 있으면 CPU 사용률이 높지 않을 수도 있어서, CPU 30% 인데 요청 지연 2초가 가능하다.
|
||||
|
||||
CPU 경쟁과 스토리지 경쟁은 경쟁하는 자원이 다르다. 개념 문서는 앞의 것을 호스트 논리 CPU 실행 시간 경쟁으로, 뒤의 것을 IOPS 와 대역폭과 큐와 장치 처리시간 경쟁으로 갈라 두었다. 두 자원을 한 지표로 대신 읽으면 어느 쪽이 밀렸는지 갈리지 않는다.
|
||||
|
||||
## 규칙
|
||||
|
||||
### 1. CPU 사용률이 낮다는 이유로 스토리지를 후보에서 빼지 않는다
|
||||
|
||||
CPU 사용률이 낮은 것은 CPU 가 놀고 있다는 뜻일 수도 있고 무언가를 기다리고 있다는 뜻일 수도 있다. 개념 문서가 그린 경로는 HTTP Request 에서 Keycloak, PostgreSQL, fsync(), Storage 로 이어진다. 이 경로에서 PostgreSQL 이 스토리지 완료를 기다리면 CPU 는 그 시간 동안 일하지 않으므로, CPU 지표만으로 지연 원인을 판단하지 않는다.
|
||||
|
||||
### 2. CPU 경쟁과 스토리지 경쟁을 다른 자원 경쟁으로 세어 적는다
|
||||
|
||||
호스트 논리 CPU 실행 시간이 모자란 것과 IOPS 나 대역폭이나 큐나 장치 처리시간이 모자란 것은 다른 사건이다. 개념 문서는 여러 가상 머신이 같은 물리 NVMe 를 쓰면 VM1 에서 대량 입출력이 발생했을 때 VM2 의 스토리지 지연이 커질 수 있다고 적었다. 이때 VM2 의 vCPU 는 부족하지 않을 수 있어서, 두 경쟁을 한 칸에 적으면 조치할 대상이 정해지지 않는다.
|
||||
|
||||
### 3. 여덟 지표를 같은 시간축에 함께 남긴다
|
||||
|
||||
개념 문서가 스토리지 문제를 분석할 때 CPU 사용률과 함께 보라고 적은 것은 여덟이다.
|
||||
|
||||
Guest I/O latency
|
||||
Host I/O queue
|
||||
Host storage latency
|
||||
QEMU backend
|
||||
cache mode
|
||||
I/O Scheduler
|
||||
NVMe
|
||||
다른 VM 의 Storage load
|
||||
|
||||
앞의 셋은 시간에 따라 변하는 값이라 같은 시각에 함께 찍어야 짝이 맞다. 뒤의 다섯은 그때의 구성이라 한 번 적어 두면 그 측정 구간 전체에 붙는다. 이 여덟을 이 호스트에서 한 시간축에 모아 찍은 기록은 아직 없다.
|
||||
|
||||
### 4. 게스트 안에서 잰 값만으로 스토리지 성능을 결론내지 않는다
|
||||
|
||||
개념 문서의 Claim 6 은 스토리지 성능이 게스트 내부만으로 결정되지 않는다고 적었다. 영향을 주는 것으로 든 것은 QEMU 와 백엔드, 호스트 블록 큐, 입출력 스케줄러, NVMe, 캐시, 다른 가상 머신의 스토리지 부하다. 게스트 쪽 지표는 이 여섯이 모두 반영된 뒤의 값이므로, 게스트만 보면 값이 왜 그렇게 나왔는지는 알 수 없다.
|
||||
|
||||
### 5. 무엇을 볼지 정한 뒤에 명령을 고른다
|
||||
|
||||
명령 목록 자체는 규칙이 아니다. 무엇을 함께 볼지는 앞의 규칙들이 정하고 명령은 그 도구를 댈 뿐이라, 목록만 옮겨 적으면 무엇을 가르려고 그것을 보는지가 남지 않는다.
|
||||
|
||||
장치 입출력 관측은 iostat -xz 1 로 한다. 읽기와 쓰기의 처리량과 IOPS 와 요청 지연과 큐 상태와 device utilization 성격의 지표가 여기서 나오고, 어떤 프로세스가 입출력을 발생시키는지는 iotop 으로 본다. 개념 문서가 양쪽에 나눠 적은 명령은 이렇다.
|
||||
|
||||
Guest : lsblk · mount · df -h · cat /proc/mounts · iostat -xz 1
|
||||
Host : virsh domblklist <VM_NAME> · qemu-img info 에 disk image 경로 · lsblk · /sys/block 아래 그 device 의 queue/scheduler · iostat -xz 1 · iotop
|
||||
|
||||
## 적용 조건
|
||||
|
||||
- 요청 지연이나 처리량 저하의 원인을 자원에 귀속하려는 판독 전부
|
||||
- CPU 사용률이 낮은데 요청 지연이 큰 구간
|
||||
- 데이터베이스가 요청 경로에 들어 있는 서비스
|
||||
- 가상 머신 여러 대가 한 물리 장치를 함께 쓰는 환경
|
||||
|
||||
## 예외
|
||||
|
||||
- 이 기준은 어느 자원인지를 좁힐 뿐 원인을 확정하지 않는다. 스토리지 지표가 깨끗하게 나와도 CPU 쪽을 배제하려면 vCPU 경쟁과 steal time 을 따로 본다. 그것은 CPU 가상화 쪽 기준이 받는다.
|
||||
- iostat -xz 1 이 보여 주는 device utilization 성격의 지표는 NVMe 처럼 병렬성이 큰 장치에서 포화도로 그대로 읽히지 않는다. 개념 문서가 NVMe 는 높은 병렬성과 큐 깊이를 지원한다고 적었다.
|
||||
- 호스트에서 잰 값은 어느 가상 머신의 입출력인지 갈라 주지 않는다. 개념 문서는 Host Block Layer 가 그 입출력을 가상 머신 안의 프로세스가 시작했는지 호스트 프로세스가 시작했는지 본질적으로 구분해서 처리하는 계층이 아니라고 밝혔다. 가상 머신별로 가르려면 iotop 이나 게스트 쪽 관측을 같은 시각에 함께 찍는다.
|
||||
- 이 저장소에는 이 기준으로 원인을 실제로 가른 측정이 없다. 여기 적은 것은 개념 문서가 서술한 판독 순서이고 이 호스트에서 확인된 값이 아니다.
|
||||
|
||||
## 예시
|
||||
|
||||
- CPU 30% · 요청 지연 2초 : 스토리지 대기가 후보에 남는 조합
|
||||
- CPU Contention : 호스트 논리 CPU 실행 시간 경쟁
|
||||
- Storage Contention : IOPS · 대역폭 · 큐 · 장치 처리시간 경쟁
|
||||
- 같은 시각에 함께 찍는 값 : Guest I/O latency · Host I/O queue · Host storage latency
|
||||
- 측정 구간에 한 번 적어 두는 구성 : QEMU backend · cache mode · I/O Scheduler · NVMe
|
||||
- 이 호스트에서 잰 값 : x
|
||||
Reference in New Issue
Block a user