feat: 가상화 문서들 추가

This commit is contained in:
DongHyeonka
2026-09-10 08:54:05 +09:00
parent e9f6a93327
commit 43e1aadef0
695 changed files with 153404 additions and 12754 deletions
@@ -0,0 +1,336 @@
---
id: 2f2ffafc-91ff-4971-a6a8-7c0e7d844ca2
kind: CONCEPT
slug: guest-memory-address-translation
title: Guest 메모리 주소가 물리 RAM 에 닿기까지 — GVA · GPA · HPA
topic: memory-virtualization
topicName: 메모리 가상화
project: virtualization
status: 초안
basisVersion: x86-64 Intel EPT · AMD 는 NPT 계열로 대응 · Linux KVM 의 KVM_SET_USER_MEMORY_REGION · QEMU/libvirt · 기본 page 4 KiB
studio: "https://hyeonworks.com/studio/documents/2f2ffafc-91ff-4971-a6a8-7c0e7d844ca2/edit"
assets:
- key: guest-memory-address-translation-path
file: ../../../final/assets/diagrams/guest-memory-address-translation-path/guest-memory-address-translation-path.svg
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
source:
- final/document.md#29-이-문서에서-먼저-고정할-전체-구조
- final/document.md#30-일반-linux의-virtual-memory부터-시작한다
- final/document.md#31-page와-physical-frame
- final/document.md#32-virtual-address-=-page-+-offset
- final/document.md#33-guest-page-table
- final/document.md#34-mmu-실제-주소-변환을-수행하는-cpu-하드웨어
- final/document.md#35-tlb-주소-변환-결과의-cpu-cache
- final/document.md#36-bare-metal과-vm의-차이
- final/document.md#37-ept-extended-page-tables
- final/document.md#38-왜-ept가-필요한가
- final/document.md#39-shadow-page-table과-ept의-의미
- final/document.md#40-qemu는-guest-ram을-어떻게-준비하는가
- final/document.md#41-kvm_set_user_memory_region
- final/document.md#42-configured-memory와-실제-physical-ram-사용량은-같지-않을-수-있다
- final/document.md#43-guest-page-table-자체도-메모리에-있다
- final/document.md#44-정상-memory-access는-매번-vm-exit하지-않는다
- final/document.md#79-전체-memory-virtualization-실행-경로
- final/document.md#80-전체-memory-virtualization-관리-경로
- final/document.md#82-핵심-claim-registry
- final/document.md#87-최종-기준-그림
- final/document.md#88-결론
---
# Guest 메모리 주소가 물리 RAM 에 닿기까지 — GVA · GPA · HPA
가상 머신 안의 프로세스가 메모리를 한 번 읽으면 그 주소는 이름이 두 번 바뀐다. 게스트 페이지 테이블이 GVA(Guest Virtual Address)를 GPA(Guest Physical Address)로 바꾼다. 그 GPA 를 EPT 가 다시 HPA(Host Physical Address)로 바꾼 뒤에야 접근이 실제 호스트 RAM 에 닿는다. 게스트 Linux 는 GPA 를 자기 물리 주소로 여기지만, 그 값이 서버 RAM 의 주소와 같을 필요는 없다. 메모리를 마련하는 쪽과 주소를 바꾸는 쪽도 갈린다. QEMU 가 게스트 RAM 을 자기 호스트 가상 주소 공간에 마련해 KVM 에 등록하고, 접근마다 일어나는 변환은 CPU 의 MMU 가 맡는다. 가상 머신 위에서 Keycloak 실험을 돌리는 동안 「가상 머신에 RAM 을 몇 GiB 줬다」가 호스트 RAM 에서 무엇을 뜻하는지 가르려면 이 경로가 먼저 필요하다.
## 관계
- **VM 에서 page fault 는 세 계층에서 따로 일어난다**
이 경로의 어느 단계에서 접근이 완료되지 못하는지, 그때 어느 계층이 그 사건을 받는지를 그 글이 이어 받는다.
- **Huge Page 를 VM 에서 볼 때 갈라지는 세 자리**
페이지 크기를 키우는 선택이 이 경로의 두 변환 단계 가운데 어디에 걸리는지를 다룬다.
- **Host 메모리 압박이 Guest 와 스토리지까지 내려가는 경로**
HPA 뒤의 호스트 RAM 이 모자라질 때 메모리 회수(reclaim)와 스왑이 무엇을 하는지는 그 글이 맡는다.
- **NUMA 에서는 vCPU 배치와 메모리 배치를 함께 본다**
이 경로의 마지막 단계인 호스트 물리 페이지가 어느 NUMA 노드에 있는지를 다룬다.
- **이 호스트의 가상 머신들은 설정한 메모리와 지금 잡고 있는 메모리가 얼마나 다른가**
설정한 메모리와 게스트가 쓰는 메모리, 호스트에서 실제로 상주(resident)하는 메모리를 이 글은 구분만 했고 값은 재지 않았다.
- **QEMU 프로세스가 실제로 붙잡고 있는 호스트 메모리는 얼마이고 어떻게 나뉘어 있는가**
QEMU 가 마련한 메모리가 이 서버에서 실제로 물리 메모리를 얼마나 붙잡는지를 그 질문이 받는다.
- **메모리 증상 하나로 계층을 단정하지 않는다**
이 경로가 그 기준이 쓰는 계층 구분을 만든다.
- **KVM 에서 vCPU 가 물리 CPU 에서 실행되기까지**
같은 가상 머신을 CPU 쪽에서 본 글이다. VM Exit 을 그쪽에서 읽고 오면 메모리 접근마다 Exit 이 나는지가 먼저 걸린다.
## 본문
<!-- body:start -->
## GVA · GPA · HPA — 주소 이름이 두 번 바뀐다
KVM/QEMU 가상 머신에서 게스트 애플리케이션이 메모리를 한 번 읽는 길을 가장 단순하게 그리면 이렇다.
```text label="Guest Application 의 memory access 경로"
Guest Application
│ Guest Virtual Address (GVA)
Guest Page Table
│ Guest Physical Address (GPA)
EPT (Intel) / NPT (AMD)
│ Host Physical Address (HPA)
Physical RAM
```
세 주소가 각각 누구의 주소인지부터 갈라 놓는다.
| 주소 | 누구의 주소인가 |
|---|---|
| GVA | 게스트 프로세스가 쓰는 가상 주소 |
| GPA | 게스트 OS 가 물리 메모리라고 여기는 주소 |
| HPA | 실제 호스트 서버 RAM 의 물리 주소 |
게스트 안에서 도는 Keycloak 이 변수 하나를 읽는다고 하면, 그 한 번의 접근이 세 주소를 차례로 지난다.
```text label="Guest 안의 Keycloak 이 변수를 읽을 때 (SSOT 의 예시 주소)"
Keycloak
│ GVA 0x7f001234
Guest Page Table
│ GPA 0x00101234
EPT
│ HPA 0x8a101234
Physical RAM
```
게스트 Linux 는 GPA 를 자기 실제 물리 주소라고 여긴다. 가상 머신이므로 그 GPA 가 실제 서버의 HPA 와 같을 필요는 없고, KVM 과 CPU 가상화 계층이 둘을 갈라 둔다. 위 주소 값은 원문이 설명하려고 든 예시이고 이 서버에서 읽은 값이 아니다.
## 일반 Linux 프로세스도 물리 주소를 직접 쓰지 않는다
첫 단계는 KVM 고유의 기능이 아니다. 일반적인 Linux 프로세스도 실제 RAM 주소를 직접 쓰지 않는다. 게스트 안에서 Keycloak 과 PostgreSQL 이 함께 돌면 두 프로세스에 각각 독립된 가상 주소 공간이 생긴다. 그래서 둘 다 `0x1000` 이라는 주소를 쓸 수 있는데, 같은 가상 주소라도 서로 다른 물리 프레임으로 매핑할 수 있기 때문이다. 그리고 가상 머신 안에서 이 물리 주소는 정확히는 GPA 다.
Linux 는 메모리를 주소 하나씩 매핑하지 않고 일정 크기 단위로 나누어 관리한다. x86-64 Linux 에서 흔히 쓰는 기본 페이지 크기는 4 KiB 이고, 물리 메모리도 같은 크기의 프레임 단위로 볼 수 있다. 그래서 가상 주소는 페이지 번호와 페이지 안의 오프셋으로 갈린다.
```text label="4 KiB page 에서 0x1234 를 나눈 결과"
Virtual Address
0x1234
┌──────────────┬─────────────┐
│ Virtual Page │ Offset │
│ 1 │ 0x234 │
└──────────────┴─────────────┘
```
페이지 테이블에 가상 페이지 1 이 게스트 물리 프레임 7 로 이어지는 매핑이 있으면, 변환 뒤에도 페이지 안의 오프셋 `0x234` 는 그대로다. 페이지 테이블은 어느 물리 프레임으로 갈 것인가를 정하고 오프셋은 건드리지 않는다.
## 페이지 테이블은 게스트 커널이 만들고 MMU 가 읽는다
게스트 Linux 커널은 프로세스마다 가상 페이지와 게스트 물리 프레임의 매핑을 관리한다. 프로세스를 만들 때, `mmap()` 을 부를 때, 페이지를 할당할 때, 권한을 바꿀 때, copy-on-write 가 일어날 때 페이지 테이블을 만들거나 고친다. 다만 CPU 가 메모리에 접근할 때마다 게스트 커널 코드가 직접 테이블을 하나씩 검색하지는 않는다.
주소 변환을 실제로 수행하는 주체는 CPU 의 MMU(Memory Management Unit, 메모리 관리 장치)다. MMU 는 CPU 안에 들어 있는 변환 하드웨어여서, 커널이 구성해 둔 페이지 테이블을 읽어 가상 주소를 물리 주소로 바꾼다. 만드는 쪽과 쓰는 쪽이 이렇게 갈린다.
```text label="Page Table 을 구성하는 쪽과 사용하는 쪽"
Guest Linux Kernel
│ Page Table 구성/관리
Page Table
│ 사용
MMU
│ 주소 변환
Memory Access
```
## TLB 가 페이지 테이블 탐색을 건너뛴다
메모리에 접근할 때마다 페이지 테이블 전체를 훑으면 비용이 크다. 그래서 CPU 는 최근 변환 결과를 TLB(Translation Lookaside Buffer, 주소 변환 캐시)에 담아 둔다. 가상 페이지 1 이 물리 프레임 7 로 이어진다는 변환이 TLB 에 있으면 같은 페이지의 다음 접근에서는 그 탐색을 건너뛴다.
```text label="TLB HIT 과 MISS"
Virtual Address
TLB
┌──┴──┐
│ │
HIT MISS
│ │
│ ▼
│ Page Table Walk
│ │
└──┬──┘
Physical Address
```
TLB 에 없으면 TLB Miss 가 나고, 페이지 테이블을 조회해 정상 매핑을 찾은 뒤 실행을 이어 간다. 접근 자체를 완료할 수 없는 Page Fault 와는 다른 사건이며, 그 구분과 계층별 처리는 fault 를 다루는 글이 이어 받는다.
## 베어메탈은 변환이 한 번, 가상 머신은 두 번
베어메탈 Linux 에서는 프로세스의 가상 주소가 페이지 테이블을 지나 호스트 물리 주소가 되고 거기서 끝난다. 가상 머신에서는 게스트가 얻은 물리 주소가 실제 호스트 물리 주소가 아니어서 한 단계가 더 붙는다. 이 `GPA → HPA` 두 번째 변환을 위해 Intel 에서는 EPT(Extended Page Tables)를 쓴다. EPT 는 Intel 의 second-level address translation 기술이고, AMD 에는 대응되는 NPT(Nested Page Tables) 계열 기능이 있다.
| 무엇이 | 무엇을 무엇으로 바꾸나 | 주요 관리 주체 |
|---|---|---|
| 게스트 페이지 테이블 | GVA → GPA | 게스트 OS |
| EPT | GPA → HPA | KVM/호스트 가상화 계층 |
| 실행 시점의 실제 변환 | 두 변환 계층 활용 | CPU MMU |
게스트 페이지 테이블과 EPT 는 같은 테이블이 아니다. 하나는 게스트 OS 가 자기 프로세스를 위해 관리하고, 다른 하나는 가상화 계층이 게스트 전체의 물리 주소 공간을 호스트 RAM 에 이으려고 관리한다.
두 번째 변환이 왜 필요한지는 가상 머신 두 대를 놓고 보면 드러난다. 원문이 든 예에서는 각각 8 GiB RAM 을 가진 VM1 과 VM2 가 둘 다 GPA `0x1000` 을 쓸 수 있고, EPT 가 그 둘을 서로 다른 HPA 로 잇는다. 그래서 게스트가 보는 물리 메모리 주소 공간을 실제 호스트 RAM 에서 격리해 구현할 수 있다.
하드웨어가 두 번째 변환을 맡아 주지 않던 시절에는 방식이 달랐다. 하이퍼바이저가 게스트 페이지 테이블 변경을 추적하면서, GVA 에서 실제 호스트 메모리까지 이어지는 shadow mapping 을 관리하는 방식이 쓰일 수 있었다. 게스트 페이지 테이블이 바뀔 때마다 하이퍼바이저가 관련 매핑을 유지해야 하므로 관리 비용과 복잡성이 커질 수 있다. EPT 와 NPT 는 그 두 단계 변환을 CPU 가 하드웨어로 지원하게 한다.
## QEMU 가 게스트 RAM 을 마련하고 KVM 이 그 영역을 등록한다
가상 머신에 8 GiB RAM 을 설정했다고 하자. QEMU 는 호스트의 사용자 공간 프로세스이므로 QEMU 자신도 호스트 가상 주소 공간을 갖고, 게스트 RAM 을 담을 메모리도 그 안에 마련한다. QEMU 가 직접 "물리 주소 X부터 8 GiB를 달라"고 RAM 하드웨어를 제어하는 것이 아니다. QEMU 의 메모리도 일반 호스트 프로세스의 메모리와 같은 길을 지난다.
```text label="QEMU 가 마련한 Guest RAM backing 이 Host 물리 메모리에 닿는 길"
QEMU Host Virtual Address
Host Page Table
Host Physical Address
```
그다음 QEMU 는 자신이 마련한 호스트 사용자 공간 메모리 영역이 게스트의 어느 GPA 범위에 대응하는지를 KVM 에 등록한다. 대표 ioctl 이 `KVM_SET_USER_MEMORY_REGION` 이고, 개념적으로 게스트 GPA 범위와 QEMU 호스트 가상 주소 범위가 어떻게 대응하는지를 전달한다. 등록이 끝나면 세 계층이 하는 일이 갈린다.
```text label="Guest 메모리를 두고 QEMU · KVM · CPU 가 하는 일"
QEMU
→ Guest RAM을 위한 Host userspace backing 제공
KVM
→ Guest memory region 및 virtualization mapping 관리
CPU
→ 실제 runtime address translation 수행
```
QEMU 가 메모리 접근마다 EPT 를 소프트웨어로 검색하지는 않는다. 등록은 관리 동작이고 변환은 하드웨어 동작이다.
## 설정한 메모리와 호스트가 지금 붙잡은 메모리는 다르다
가상 머신에 16 GiB 를 설정했다고 해서, 어떤 일반 구성에서든 시작하는 순간 실제 호스트 RAM 16 GiB 가 반드시 전부 곧바로 물리적으로 점유되는 것은 아니다. 값 셋을 따로 세어야 한다.
```text label="같다고 볼 수 없는 세 값"
Configured Memory
Guest가 현재 실제 사용하는 Memory
Host에서 현재 resident한 Physical Memory
```
물리 메모리가 실제로 붙는 시점과 방식은 호스트 쪽 구성에 따라 달라질 수 있다. 호스트의 demand paging, 어떤 메모리로 뒷받침하는지, HugeTLB, memory locking, preallocation, overcommit 정책 등이 거기에 걸린다. 그래서 "VM RAM 16GiB = Host RAM에서 고정된 연속 16GiB"라고 단순화하면 안 된다. 여기 쓴 16 GiB 도 원문의 예시 값이고 이 서버의 설정이 아니다. 이 호스트에서 그 셋을 견주지도 않았다. 세 값은 같은 시점에 나란히 읽어야 갈리는데, 그 관찰을 아직 돌리지 않았다.
## 게스트 페이지 테이블 자체도 게스트 메모리에 있다
두 단계 변환에 비용이 붙는 까닭이 여기 있다. 게스트 페이지 테이블은 게스트 물리 메모리에 저장된 자료구조이므로, CPU 가 게스트 페이지 테이블 엔트리를 읽는 과정에서도 그 엔트리가 저장된 GPA 를 실제 HPA 로 바꿔야 한다.
```text label="entry 를 읽는 데에도 EPT 변환이 든다"
GVA
Guest Page Table Walk
│ Page Table 자체가 Guest Memory에 존재
└────→ EPT를 이용해 실제 RAM에서 entry를 읽음
GPA 획득
EPT
HPA
```
그래서 두 단계에 걸친 페이지 테이블 탐색에는 비용이 들고 TLB 가 그만큼 중요해진다. Huge Page 가 TLB 로 덮는 범위와 페이지 테이블 탐색 부담을 함께 건드리는 까닭도 여기에서 온다.
## 정상 메모리 접근은 매번 VM Exit 하지 않는다
원문은 이 대목을 「잘못된 이해」라고 이름 붙인 그림으로 먼저 연다.
```text label="원문이 먼저 놓은 「잘못된 이해」"
잘못된 이해
Guest Memory Access
VM Exit
KVM
RAM
```
게스트의 메모리 접근이 곧장 VM Exit 을 거쳐 KVM 으로 가고 거기서 RAM 에 닿는 경로다. 제1부에서 VM Exit 을 읽고 온 사람이 메모리 접근에 그대로 옮겨 붙일 그림이라, 원문은 맞는 경로를 그리기 전에 이것부터 아니라고 적었다. 정상 매핑이 있으면 CPU 하드웨어가 직접 변환을 수행한다.
```text label="정상 mapping 이 있을 때의 실행 경로"
Guest instruction
CPU MMU / TLB
Guest Page Table + EPT
HPA
Physical RAM
```
따라서 정상적인 게스트 RAM 접근마다 QEMU/KVM 의 사용자 공간·커널 경로를 왕복하지는 않는다. 하이퍼바이저가 끼어드는 때는 지금 조건으로 접근을 완료할 수 없을 때이고, 두 번째 변환 단계에서 그렇게 되면 EPT Violation 으로 VM Exit 이 일어난다.
## 실행 경로와 관리 경로를 나누어 본다
실행 경로는 CPU 하드웨어가 접근할 때마다 지나는 길이고, 관리 경로는 가상 머신을 만들고 바꿀 때 지나는 길이다. 둘을 한 그림에 두면 `virsh` 명령 하나가 메모리 접근 경로에 끼어 있는 것처럼 읽힌다.
```text label="Memory Virtualization 관리 경로"
User
virsh
libvirt
QEMU
├─ Guest RAM backing
├─ QEMU HVA
├─ virtio-balloon device
└─ ioctl(KVM_SET_USER_MEMORY_REGION)
KVM
├─ Guest memory slots/regions 관리
└─ EPT 관련 virtualization mapping 관리
CPU
```
`virsh` 와 libvirt 는 가상 머신 구성과 관리를 맡고, QEMU 는 게스트 RAM 을 담을 호스트 사용자 공간 메모리와 장치 구성을 마련하며, KVM 은 게스트 메모리 영역을 하드웨어 가상화 기능에 잇는다. 실행 시점의 변환은 CPU 의 MMU 와 EPT 하드웨어가 한다. 실행 경로 쪽은 게스트 애플리케이션에서 시작해 TLB 와 게스트 페이지 테이블을 지나 GPA 가 된다. 그다음 VM Boundary 를 넘어 EPT 를 지나 HPA 가 되고, 호스트 물리 페이지와 그 페이지가 속한 NUMA(Non-Uniform Memory Access) 노드로 이어진다.
![Guest 구역 안의 Keycloak 과 PostgreSQL 에서 나온 GVA 가 Guest TLB 를 지나고, TLB Miss 이면 Guest Page Table 을 조회해 GPA 가 되며, 그 GPA 가 VM Boundary 를 넘어 EPT 에서 HPA 로 바뀐 뒤 Host RAM 구역의 Host Physical Memory 에 닿고 마지막에 NUMA Node 0 과 NUMA Node 1 로 갈라지는 주소 변환 경로도. 상자 안의 작은 글씨는 그 단계가 어느 계층인지와 무엇을 무엇으로 바꾸는지를 적은 것이다.](../../../final/assets/diagrams/guest-memory-address-translation-path/guest-memory-address-translation-path.svg)
위쪽 점선 상자 안에서 끝나는 것이 첫 번째 변환이고, 그 상자를 나온 뒤의 EPT 가 두 번째 변환을 맡는다. 관리 경로는 이 그림에 없다 — 위의 `virsh` 목록이 그쪽을 그린 것이다.
## 이 글이 확정하지 않는 것
이 호스트에서 잰 값은 하나도 없다. 원문 제2부는 개념 서술이고, 실제 서버에 딸린 설정과 동작은 OQ(Open Question) 번호가 붙은 확인 목록 열넷으로 미뤄져 있다. 그 목록을 아직 돌리지 않았으므로 이 글은 구조가 어떻게 동작하는지까지만 말한다.
설정한 메모리와 실제로 붙잡힌 메모리의 차이는 OQ-2 와 OQ-3 이 받는다. 가상 머신에 설정된 메모리와 지금 쓰는 메모리를 읽고, 같은 시점에 QEMU 프로세스가 호스트에서 상주하는 메모리를 읽어 견주는 확인이다.
```bash label="OQ-2 · OQ-3 이 이 호스트에서 읽을 값"
virsh dominfo <VM_NAME>
virsh dommemstat <VM_NAME>
ps -o pid,rss,vsz,cmd -p <QEMU_PID>
```
원문이 커널·QEMU·libvirt 버전을 한 번도 적지 않아 이 글도 특정 버전에 고정하지 않았다. 여기 적은 것은 그 문서가 서술한 구성이고, 버전이 달라져 서술이 낡았는지는 위 확인을 실제 호스트에서 돌릴 때 드러난다.
범위 밖으로 미룬 것도 있다. 호스트 메모리 압박과 회수, 스왑, ballooning, OOM(Out Of Memory), NUMA 배치는 이 경로 위에서 벌어지는 자원 압박 쪽이라 각각 다른 글이 다룬다. Huge Page 도 마찬가지로 이 경로의 두 변환 단계에 걸리는 선택이어서 따로 뺐다.
<!-- body:end -->
@@ -0,0 +1,179 @@
---
id: efce9a6e-5f5d-45ec-86e5-056d8cb1b8d2
kind: CONCEPT
slug: huge-pages-in-a-vm
title: Huge Page 를 VM 에서 볼 때 갈라지는 세 자리
topic: memory-virtualization
topicName: 메모리 가상화
project: virtualization
status: 초안
basisVersion: Linux THP(/sys/kernel/mm/transparent_hugepage/enabled) · HugeTLB pool · x86-64 의 4 KiB / 2 MiB / 1 GiB page
studio: "https://hyeonworks.com/studio/documents/efce9a6e-5f5d-45ec-86e5-056d8cb1b8d2/edit"
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
source:
- final/document.md#50-huge-page가-필요한-이유
- final/document.md#51-huge-page와-tlb-coverage
- final/document.md#52-vm에서-huge-page를-볼-때-주의할-점
- final/document.md#53-thp-transparent-huge-pages
- final/document.md#54-thp의-trade-off
- final/document.md#55-hugetlb
- final/document.md#56-thp와-hugetlb-비교
- final/document.md#82-핵심-claim-registry
---
# Huge Page 를 VM 에서 볼 때 갈라지는 세 자리
페이지 크기를 키우면 같은 메모리 범위를 더 적은 매핑으로 표현할 수 있고, TLB(Translation Lookaside Buffer, 주소 변환 캐시) 엔트리 하나가 덮는 범위도 넓어진다. 그런데 가상 머신에는 주소 변환 단계가 둘이라 「Huge Page 를 쓴다」는 말만으로는 어디를 말하는지 정해지지 않는다. 게스트 페이지 테이블 단계인지, QEMU 가 마련한 호스트 쪽 메모리인지, EPT(Extended Page Tables) 매핑인지를 나누어 물어야 그 말이 뜻을 갖는다. Linux 가 큰 페이지를 마련하는 방법도 THP(Transparent Huge Pages)와 HugeTLB 두 가지인데, 둘은 관리 주체도 사전 예약 여부도 다르다.
## 관계
- **Guest 메모리 주소가 물리 RAM 에 닿기까지 — GVA · GPA · HPA**
페이지 크기 선택이 걸리는 두 변환 단계를 그 글이 세운다.
- **이 호스트의 THP 정책과 huge page 상태는 무엇인가**
이 서버의 THP 정책과 huge page 관련 값을 읽는 질문이다. 이 글은 그 값이 무엇을 뜻하는지까지만 적었다.
- **가상 머신 RAM 이 HugeTLB 로 명시적으로 backing 되어 있는가**
세 곳 가운데 호스트 쪽을 이 서버에서 확인하는 질문이다.
- **메모리 실험 결과에는 Host · VM · Workload 조건을 함께 남긴다**
THP 정책과 huge page 설정은 그 기준이 요구하는 기록 항목에 들어간다.
## 본문
<!-- body:start -->
## 페이지가 커지면 매핑 수가 준다
같은 메모리 범위라도 페이지 크기에 따라 필요한 매핑 수가 달라진다. 원문은 8 GiB 를 예로 들어 셋을 나란히 계산했다.
| 페이지 크기 | 8 GiB 를 표현하는 데 필요한 페이지 수 |
|---|---|
| 4 KiB | 2,097,152 pages |
| 2 MiB | 4,096 pages |
| 1 GiB | 8 pages |
큰 페이지는 더 적은 매핑으로 넓은 메모리 범위를 표현할 수 있다. 위 8 GiB 는 계산을 보이려고 원문이 든 예시 값이고 이 서버 가상 머신의 설정이 아니다.
## TLB 엔트리 하나가 덮는 범위
TLB 엔트리 하나가 가리키는 페이지가 커지면 캐시된 변환 하나로 더 넓은 주소 범위를 덮을 수 있다.
```text label="mapping 수가 같을 때 덮이는 범위 (원문의 단순 예)"
4 KiB page × 512 mappings
= 2 MiB coverage
2 MiB page × 512 mappings
= 1 GiB coverage
```
실제 CPU 는 페이지 크기마다 TLB 구조와 엔트리 수가 다르기 때문에, 이 숫자를 특정 CPU 의 실제 TLB 용량으로 해석하면 안 된다. 여기서 남는 것은 방향 하나다. 페이지 크기가 커지면 변환 하나가 덮는 범위가 넓어지고 TLB 압박이 줄어들 가능성이 생긴다. 페이지 테이블 엔트리 수와 페이지 테이블 탐색 부담도 함께 줄어들 가능성이 있다.
## 가상 머신에서는 세 곳을 따로 물어야 한다
가상 머신에는 GVA(Guest Virtual Address)를 GPA(Guest Physical Address)로 바꾸는 단계와, 그 GPA 를 HPA(Host Physical Address)로 바꾸는 단계가 있다.
```text label="page 크기 선택이 걸릴 수 있는 두 변환 단계"
GVA
│ Guest Page Table
GPA
│ EPT
HPA
```
그래서 "Huge Page를 사용한다"는 말만으로는 부족하다. 세 가지를 갈라 물어야 한다.
- 게스트 페이지 테이블 단계에서 큰 페이지를 쓰는가?
- 호스트가 그 메모리를 Huge Page 로 받치고 있는가?
- EPT 매핑에서 큰 매핑을 활용하는가?
게스트와 호스트의 페이지 크기 선택을 같은 설정 하나로 묶어 다루면 안 된다. 게스트 안에서 THP 가 켜져 있어도 그것은 첫 질문에 대한 답이고, 그 가상 머신의 RAM 을 호스트가 무엇으로 받치는지는 두 번째 질문이 따로 받는다.
## THP — 커널이 조건이 맞을 때 알아서 쓰는 방식
THP 는 Linux 가 가능한 메모리 영역에 대해 Huge Page 를 투명하게 활용하려는 기능이다. 애플리케이션이 평소처럼 `malloc()` 이나 `mmap()` 을 부르면 커널이 조건이 맞는 영역에 Huge Page 활용을 시도한다. 애플리케이션이 큰 페이지를 달라고 따로 요청하지 않는다는 뜻에서 투명하다.
지금 어떤 정책이 걸려 있는지는 `/sys/kernel/mm/transparent_hugepage/enabled` 에서 읽는다.
```bash label="THP 정책 확인"
cat /sys/kernel/mm/transparent_hugepage/enabled
```
```text label="정책 값의 예 — 대괄호가 현재 선택된 값이다"
always [madvise] never
```
이 값은 커널과 배포판, 호스트 설정에 따라 다르므로 실제 시스템에서 확인한다. 그래서 원문은 메모리 실험마다 반드시 같이 남길 조건 목록에 THP policy 를 Host 항목으로, Memory backing 설정을 VM 항목으로 넣어 두었다. 조건을 남기지 않으면 "Memory pressure에서 느려졌다"는 결과를 다른 환경에 재사용하기 어렵다는 것이 그 목록에 붙은 이유다.
## THP 가 치르는 비용
Huge Page 에는 이어진 큰 물리 메모리 영역이 필요하다. 2 MiB 하나가 4 KiB 페이지 512 개에 해당해서, 그만큼 이어진 영역을 확보하지 못하면 커널이 compaction 같은 작업을 수행할 수 있다.
```text label="fragmentation 이 심할 때 끼어드는 작업"
Huge Page 필요
큰 contiguous memory 필요
Fragmentation
Compaction 가능
Latency 영향 가능
```
그래서 THP 는 항상 성능을 높인다고 단정할 수 없다. 특히 지연에 민감한 워크로드에서는 측정이 필요하다고 원문이 직접 적었고, 이 서버에서 그 측정을 하지 않았다.
## HugeTLB — 미리 확보해 둔 풀
HugeTLB 는 Huge Page 풀을 명시적으로 두고 쓸 수 있는 Linux 메커니즘이다. 커널이 알아서 활용하는 THP 와 달리, 관리자가 풀을 준비하고 애플리케이션이나 가상 머신이 그 풀을 명시적으로 쓴다.
```text label="Host physical RAM 안에 잡아 둔 pool"
Physical RAM
┌──────────────────────────┐
│ Normal Memory │
├──────────────────────────┤
│ HugeTLB Pool │
│ 2 MiB │
│ 2 MiB │
│ 2 MiB │
│ ... │
└──────────────────────────┘
```
미리 확보해 두면 예측 가능성을 높일 수 있지만, 풀로 떼어 둔 만큼은 일반 할당이 쓸 수 없기 때문에 일반적인 메모리 할당의 유연성은 그만큼 줄어든다.
## 둘을 가르는 여섯 항목
| 무엇을 견주나 | THP | HugeTLB |
|---|---|---|
| 관리 | 커널이 투명하게 활용 | 명시적인 풀 |
| 애플리케이션 개입 | 상대적으로 적음 | 명시적 구성 가능 |
| 유연성 | 상대적으로 높음 | 상대적으로 낮음 |
| 사전 예약 | 핵심 방식 아님 | 가능 |
| compaction 영향 | 발생 가능 | 미리 확보해 일부 상황 회피 가능 |
| 가상 머신 RAM backing | 사용 가능 | 명시적으로 사용 가능 |
호스트에서 두 값을 함께 읽으면 어느 쪽이 쓰이고 있는지 갈린다.
```bash label="Host 의 huge page 상태와 THP 정책"
grep -i huge /proc/meminfo
cat /sys/kernel/mm/transparent_hugepage/enabled
```
`AnonHugePages` 와 `HugePages_Total` 은 같은 뜻이 아니다. 앞의 값은 익명 메모리에서 THP 로 쓰이고 있는 양이고, 뒤의 값은 HugeTLB 풀로 확보해 둔 페이지 수다. 한쪽이 0 이어도 다른 쪽이 커질 수 있어서, 두 값을 한 이름으로 묶어 읽으면 어떤 방식이 돌고 있는지 알 수 없다.
## 이 글이 확정하지 않는 것
이 호스트에서 잰 값은 하나도 없다. THP 정책도 `HugePages_Total` 도 읽지 않았고 가상 머신의 메모리 backing 설정도 확인하지 않았다. 원문 제2부는 개념 서술이고 실제 서버에 딸린 값은 OQ(Open Question) 번호가 붙은 확인 목록 열넷으로 미뤄져 있다.
호스트 쪽 값은 OQ-4 가 받는다. THP 정책과 함께 `AnonHugePages`, `HugePages_Total`, `HugePages_Free`, `Hugepagesize` 를 읽어 이 서버가 어느 방식을 쓰고 있는지 확정하는 확인이다.
가상 머신 쪽 backing 은 OQ-5 가 받는다. libvirt 의 memory backing 설정을 읽고 호스트의 `/proc/meminfo`, QEMU 의 `smaps` 계열 값과 교차 검증한다.
```bash label="OQ-5 가 읽을 가상 머신 정의"
virsh dumpxml <VM_NAME>
```
두 확인이 끝나기 전에는 이 글의 세 질문 가운데 어느 것도 이 서버에 대해 답이 없다. 그리고 두 확인이 끝나도 셋째 질문은 남는다. EPT 매핑에서 큰 매핑을 쓰는가를 이 호스트에서 재라고 적은 항목은 그 열넷에 없다. 원문이 커널·QEMU·libvirt 버전을 적지 않아 이 글도 특정 버전에 고정하지 않았고, THP 정책의 기본값이 배포판마다 다르다는 점도 원문이 확인하라고만 적었다.
<!-- body:end -->
@@ -0,0 +1,118 @@
---
id: 647d6d11-5bb2-4028-a530-b10c8925aa11
kind: CONCEPT
slug: memory-pressure-reclaim-swap-oom
title: Host 메모리 압박이 Guest 와 스토리지까지 내려가는 경로
topic: memory-virtualization
topicName: 메모리 가상화
project: virtualization
status: 초안
basisVersion: Linux memory reclaim · swap · OOM Killer · cgroup memory limit · QEMU 의 Guest RAM backing
studio: "https://hyeonworks.com/studio/documents/647d6d11-5bb2-4028-a530-b10c8925aa11/edit"
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
source:
- final/document.md#57-memory-overcommit
- final/document.md#58-cpu-overcommit과-memory-overcommit의-차이
- final/document.md#59-host-memory-pressure와-reclaim
- final/document.md#60-host-swap이-vm에-미치는-영향
- final/document.md#61-guest-swap과-host-swap
- final/document.md#62-memory-pressure와-storage-contention의-연결
- final/document.md#71-oom
- final/document.md#72-guest-oom과-host-oom
- final/document.md#81-cpu-network-storage-memory-연결
- final/document.md#82-핵심-claim-registry
---
# Host 메모리 압박이 Guest 와 스토리지까지 내려가는 경로
가상 머신들에 설정한 메모리 총량이 호스트의 물리 RAM 보다 클 수 있다. 설정한 용량과 지금 실제로 쓰는 양이 같지 않다 보니 그런 구성이 성립하는데, 가상 머신들의 수요가 동시에 오르면 Linux 는 회수와 스왑으로 대응한다. CPU 가 모자랄 때는 스케줄러가 실행 시간을 나눠 주지만, RAM 이 모자랄 때는 나눌 시간이 없고 지금 존재해야 하는 페이지를 어디에 둘 것인가만 남는다. 그래서 호스트의 메모리 압박은 게스트 안에서 단순한 메모리 접근으로 보이던 동작을 스토리지 I/O 대기로 바꾸고, 여러 갈래의 I/O 가 한 물리 장치로 몰리면 DB 와 애플리케이션 지연까지 올린다. 회수로도 모자라면 OOM(Out Of Memory)으로 가는데, 그것을 게스트 커널이 처리했는지 호스트 커널이 처리했는지에 따라 멈추는 범위가 다르다.
## 관계
- **Guest 메모리 주소가 물리 RAM 에 닿기까지 — GVA · GPA · HPA**
여기서 눌리는 것은 그 경로의 마지막 단계인 호스트 물리 페이지다. 그 경로를 먼저 읽으면 무엇이 스왑으로 나가는지가 정해진다.
- **VM 에서 page fault 는 세 계층에서 따로 일어난다**
호스트 쪽 페이지가 없을 때 발생하는 Host Page Fault 가 이 압박 경로를 관측하는 지점이다. 세 계층의 fault 를 가르는 기준은 그쪽에 있다.
- **virtio-balloon 이 Guest 메모리를 회수하고 돌려주는 방식**
무작정 스왑으로 내보내는 대신 게스트 커널과 협력해 회수하는 다른 방법이다. 압박에 대응하는 수단이라 이 경로와 짝을 이룬다.
- **이 호스트의 가상 머신들은 설정한 메모리와 지금 잡고 있는 메모리가 얼마나 다른가**
이 환경이 초과 할당 상태인지를 이 질문이 확정한다. 여기서는 그것을 사실로 두지 않았다.
- **지금 게스트와 호스트에서 swap 이 실제로 오가고 있는가**
게스트 쪽과 호스트 쪽 스왑을 같은 시각에 재는 질문이다. 이 글은 두 스왑이 다른 계층이라는 것까지만 적었다.
- **호스트 메모리 압박이 게스트 애플리케이션 지연을 실제로 밀어 올리는가**
압박에서 지연까지 이어지는 경로를 이 환경에서 재현하는 질문이다. 이 글에서는 그 경로를 개념으로만 그렸다.
- **메모리 증상 하나로 계층을 단정하지 않는다**
스왑 관측이나 OOM 을 보고 원인을 정하려 할 때 쓰는 기준이다. 이 글이 그 기준의 Host Memory 갈래를 설명한다.
## 본문
<!-- body:start -->
## CPU 가 모자랄 때와 RAM 이 모자랄 때
가상 머신들에 설정한 메모리 총량이 호스트의 물리 RAM 보다 큰 구성을 메모리 초과 할당(memory overcommit)이라고 한다. 근거 문서는 이 구성을 예시 숫자로 설명한다.
```text label="설정 총량이 Host RAM 을 넘는 구성 — 근거 문서의 예시"
Host Physical RAM = 32 GiB
VM1 configured = 16 GiB
VM2 configured = 16 GiB
VM3 configured = 16 GiB
Total configured = 48 GiB
```
이 숫자는 개념을 보이려고 든 예시이고 이 테스트 서버에서 잰 값이 아니다. 설정한 용량과, 그 가상 머신이 실제로 쓰고 있는 메모리(working set)나 호스트에서 그 몫으로 붙잡고 있는 메모리(resident memory)가 같지 않을 수 있기 때문에 이런 구성이 가능할 수 있다. 같은 예시에서 세 가상 머신이 실제로 쓰는 양을 각각 약 5G, 4G, 3G 로 잡으면 합이 약 12G 라 32 GiB 안에 들어간다. 세 대의 수요가 동시에 증가하면 그때부터 문제가 발생한다. 이 호스트의 값은 근거 문서 어디에도 없다. 물리 RAM 도 스왑 설정도 가상 머신들에 설정한 메모리의 합도 적혀 있지 않아서, 아래에 적는 경로가 이 환경에서 지금 돌고 있는지는 이 글이 정하지 못한다.
CPU 가 모자랄 때와 RAM 이 모자랄 때 Linux 가 하는 일이 다르다. CPU 가 모자라면 스케줄러가 실행 시간을 나누고 실행 가능한 작업은 자기 차례를 기다리는데, 시간은 잘라서 뒤로 미룰 수 있기 때문이다. RAM 이 모자랄 때는 미룰 것이 없어서 지금 존재해야 하는 페이지를 어디에 둘 것인가만 남는다. 그래서 메모리 압박에서는 회수와 스왑, ballooning, OOM 같은 메커니즘이 추가로 개입한다. 메모리 초과 할당은 CPU 초과 할당과 동일한 성격의 자원 공유가 아니다.
## 호스트가 메모리를 회수하는 순서
호스트 RAM 수요가 실제로 쓸 수 있는 물리 메모리에 접근하면 Linux 는 메모리 회수(reclaim)를 시도한다. 회수할 수 있는 캐시와 페이지를 먼저 처리하고, 그것으로 모자라면 힙이나 스택처럼 원본 파일이 없는 익명 메모리(anonymous memory)를 스왑(swap)으로 내보내고, 그래도 부족하면 심각한 압박이나 OOM 으로 간다.
회수 비용은 페이지 종류에 따라 갈린다. 원본이 스토리지에 있는 깨끗한 파일 기반 페이지(file-backed clean page)는 RAM 에서 버렸다가 필요할 때 스토리지에서 다시 읽으면 된다. 같은 파일 기반 페이지라도 내용이 바뀐 dirty 상태라면 버리기 전에 디스크로 써 내리는 writeback 이 먼저 필요할 수 있다. 익명 메모리는 원본 파일을 그대로 다시 읽어 올 수 없으므로 스왑처럼 내용을 받아 두는 backing 이 있어야 내보낼 수 있다.
## 게스트의 RAM 접근이 스토리지 대기로 바뀔 때
QEMU 가 게스트 RAM 으로 잡아 둔 호스트 물리 페이지가 스왑으로 나갈 수 있는 구성이라고 하자. 가상 머신 안의 Keycloak 은 그냥 메모리에 접근한다고 생각하고 실행된다. 그런데 그 접근에 필요한 호스트 쪽 페이지가 RAM 에 없으면 Host Page Fault 가 발생하고, 스왑인 I/O 가 끝나 물리 RAM 으로 복원된 뒤에야 게스트 실행이 이어진다. 게스트 관점에서 RAM 접근인 동작이 호스트에서는 스토리지 I/O 를 기다리는 동작으로 바뀌게 된다.
## 게스트 스왑과 호스트 스왑은 다른 계층에서 일어난다
게스트 스왑은 게스트 애플리케이션의 메모리 압박을 게스트 커널이 받아 `/dev/vda` 로 내려보내는 경로다. 그 요청은 `virtio-blk` 와 virtqueue 를 거쳐 QEMU 로 가고 호스트 스토리지에 닿는다. 호스트 스왑은 시작하는 곳이 다르다. QEMU 가 게스트 RAM 으로 잡아 둔 호스트 메모리가 호스트 메모리 압박 아래에서 호스트 커널에 눌려 호스트 스왑으로 나간다. 시작하는 곳도 지나는 계층도 달라서 한쪽 값으로 다른 쪽을 짐작할 수 없다. 게스트가 메모리에 여유가 있어 보이는 동안에도 호스트에서는 스왑과 회수가 심할 수 있다.
## 네 갈래 I/O 가 한 장치로 몰릴 때
게스트와 호스트가 동시에 메모리 압박을 겪으면 게스트 스왑 I/O 와 호스트 스왑 I/O, DB I/O, 파일 시스템 writeback 이 하나의 물리 NVMe 로 몰릴 수 있다. 그러면 호스트 메모리 압박이 회수와 스왑을 부르고, 늘어난 스토리지 I/O 가 스토리지 경쟁이 되고, DB 지연이 오르고, 그 위의 애플리케이션 지연까지 오른다.
메모리는 CPU 와 네트워크, 스토리지 실행을 모두 받치는 계층이라 압박이 메모리 안에서 끝나지 않는다. CPU 사용률이 낮은 구간에도 이 경로가 돌고 있을 수 있다.
이 절에는 그림을 넣지 않았다. 근거 절이 「Guest와 Host가 동시에 memory pressure를 겪으면」이라고 적어 네 갈래에 순서가 없는데, 그림 도구가 이 문맥에서 허용한 구성 문법은 순서를 요구하는 `sequence` 하나뿐이었다. 그리려면 없는 순서를 지어내야 한다.
## 회수로도 모자라면 OOM
Linux 가 메모리 할당 요구를 만족시키지 못하고 회수를 비롯한 방법으로도 필요한 메모리를 확보하지 못하면 OOM 상황이 발생할 수 있다. 그러면 OOM Killer 가 프로세스를 골라 종료하고 그만큼의 메모리를 확보한다.
같은 OOM 이라도 어느 커널이 처리했는지에 따라 멈추는 범위가 달라진다. 게스트 RAM 이 부족해 게스트 커널이 OOM 을 처리하면 게스트 안의 프로세스가 종료된다. 근거 문서가 든 예가 Keycloak 프로세스 종료다. 호스트 물리 RAM 이 부족해 호스트 커널이 OOM 을 처리하면 호스트 프로세스가 종료될 수 있는데, 그때 QEMU 가 골라지면 해당 가상 머신 전체가 중단되고 그 안에서 돌던 프로세스가 모두 함께 멈춘다.
cgroup 메모리 상한이 걸린 환경에서는 호스트 전체 RAM 에 여유가 있어도 그 cgroup 경계에서 OOM 이 발생할 수 있다. 그래서 OOM 하나를 보고 호스트 RAM 이 모자랐다고 정하지 않고 OOM 이 어느 계층에서 났는지를 확인한다.
## 근거 문서가 번호를 붙여 둔 문장
근거 문서는 이 경로에서 고정한 문장에 번호를 붙여 두었다. 실험 기록에서 어느 문장을 확인했는지 가리킬 때 이 번호를 쓴다.
| 번호 | 근거 문서가 고정한 문장 |
|---|---|
| CLAIM-MEM-12 | Memory Overcommit은 CPU Overcommit과 성격이 다르다. RAM pressure에서는 reclaim/swap/ballooning/OOM이 개입할 수 있다. |
| CLAIM-MEM-13 | Guest Swap과 Host Swap은 서로 다른 계층에서 발생한다. |
| CLAIM-MEM-14 | Host memory pressure는 swap/writeback을 통해 storage contention과 application latency를 악화시킬 수 있다. |
| CLAIM-MEM-17 | Guest OOM과 Host OOM은 영향 범위가 다르다. Host OOM에서 QEMU가 종료되면 VM 전체가 중단될 수 있다. |
## 이 문서가 확정하지 않는 것
이 글이 확정하는 것은 구조가 어떻게 동작하는가까지다. 이 테스트 서버에서 잰 값은 하나도 없다.
이 호스트의 물리 RAM 도, 스왑 설정도, 가상 머신들에 설정한 메모리의 합도 근거 문서에 적혀 있지 않다. 그래서 이 환경이 초과 할당 상태인지조차 아직 사실이 아니다. 설정한 메모리와 게스트가 실제로 쓰는 메모리, 호스트에서 그 몫으로 붙잡고 있는 메모리를 가상 머신마다 적으면 그 답이 나온다.
스왑이 지금 오가고 있는지도 재지 않았다. `Swap Used` 값 하나로는 진행 중인 활동인지 과거에 내려간 뒤 남아 있는 cold page 인지 갈리지 않으니 게스트와 호스트를 같은 시각에 관측해야 한다. 호스트 압박이 게스트 애플리케이션 지연을 실제로 밀어 올리는지는 압박이 없는 구간과 있는 구간을 나눠 재야 알 수 있고, 그 구간에서 스토리지 지연과 CPU 도 같이 기록해야 원인을 메모리 쪽으로 좁힐 수 있다. 세 확인은 각각 열린 질문으로 두었고 관계에 걸어 두었다.
<!-- body:end -->
@@ -0,0 +1,123 @@
---
id: 68811272-5352-4409-aa20-7e22e601ede8
kind: CONCEPT
slug: numa-locality-for-vcpu-and-memory
title: NUMA 에서는 vCPU 배치와 메모리 배치를 함께 본다
topic: memory-virtualization
topicName: 메모리 가상화
project: virtualization
status: 초안
basisVersion: multi-socket NUMA x86-64 · Linux lscpu 와 numactl · numastat · libvirt virsh vcpupin 과 vcpuinfo
studio: "https://hyeonworks.com/studio/documents/68811272-5352-4409-aa20-7e22e601ede8/edit"
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
assets:
- key: numa-vcpu-and-memory-placement
file: ../../../final/assets/diagrams/numa-vcpu-and-memory-placement/numa-vcpu-and-memory-placement.svg
source:
- final/document.md#73-numa
- final/document.md#74-local-memory와-remote-memory
- final/document.md#75-vcpu와-numa의-연결
- final/document.md#76-vcpu-pinning만으로는-numa-최적화가-끝나지-않는다
- final/document.md#77-guest-numa
- final/document.md#78-numa는-실제-장비-topology부터-확인한다
- final/document.md#81-cpu-network-storage-memory-연결
- final/document.md#82-핵심-claim-registry
---
# NUMA 에서는 vCPU 배치와 메모리 배치를 함께 본다
RAM 을 하나의 균일한 자원처럼 다루면 어느 CPU 가 읽든 비용이 같다. CPU 소켓이 둘 이상인 NUMA 시스템에서는 그 가정이 성립하지 않는다. 소켓마다 가까운 RAM 이 따로 있고, 다른 소켓의 RAM 을 읽으면 인터커넥트를 건너므로 추가 비용이 붙을 수 있다. 가상 머신에서 이것이 걸리는 이유는 게스트 vCPU 가 호스트에서는 QEMU 의 vCPU 스레드이기 때문이다. 그 스레드가 도는 노드와 그 가상 머신의 메모리가 놓인 노드가 어긋나면, 게스트 안에서는 단순한 메모리 접근인 동작이 실제 하드웨어에서는 다른 노드까지 건너가는 접근이 된다. 그래서 vCPU 를 어느 CPU 에 묶었는지만 보고 배치를 끝내지 않고 메모리가 어느 노드에 있는지를 함께 본다. CPU 가상화 쪽 문서가 메모리 배치와 NUMA 튜닝을 여기로 넘겼다.
## 관계
- **KVM 에서 vCPU 가 물리 CPU 에서 실행되기까지**
게스트 vCPU 가 QEMU vCPU 스레드로 호스트 스케줄러 위에서 실행된다는 것이 그쪽 설명이다. 이 글은 그 스레드가 도는 노드와 메모리가 있는 노드를 잇는다.
- **Guest 메모리 주소가 물리 RAM 에 닿기까지 — GVA · GPA · HPA**
변환이 끝나 닿는 HPA 가 어느 노드의 RAM 인지가 여기서 갈린다. 변환 방식은 그쪽이 설명한다.
- **이 호스트의 NUMA 토폴로지는 가상 머신 성능을 고려해야 할 구조인가**
노드가 하나인지 둘 이상인지를 확정하는 질문이다. 그 답이 나오기 전에는 이 글의 어긋남이 이 환경에 있는지 알 수 없다.
- **QEMU 메모리는 vCPU 가 도는 NUMA node 와 같은 node 에 있는가**
이 글이 말한 어긋남을 이 호스트에서 실제로 재는 질문이다.
- **NUMA remote access 가 이 작업의 지연을 실제로 바꾸는가**
배치가 어긋나 있다는 사실과 그것이 지연을 바꾼다는 사실은 다르다. 뒤쪽을 그 질문이 받는다.
- **메모리 증상 하나로 계층을 단정하지 않는다**
그 기준이 가르는 다섯 갈래 가운데 NUMA 갈래를 이 글이 설명한다.
## 본문
<!-- body:start -->
## 어느 CPU 가 어느 RAM 을 읽느냐로 비용이 갈린다
지금까지 RAM 은 하나의 균일한 자원처럼 다룰 수 있었다. CPU 소켓이 둘 이상인 NUMA 시스템에서는 어느 CPU 가 어느 RAM 에 접근하느냐에 따라 비용이 달라질 수 있다. NUMA(Non-Uniform Memory Access)는 이름 그대로 메모리 접근 비용이 균일하지 않다는 뜻이다.
구조는 이렇다. NUMA 노드마다 CPU 소켓이 있고 그 소켓에 붙은 로컬 RAM 이 있으며, 노드들은 인터커넥트(interconnect)로 이어져 있다. CPU 가 자기 노드의 RAM 을 읽으면 로컬 접근(local access)이고, 인터커넥트를 건너 다른 노드의 RAM 을 읽으면 원격 접근(remote access)이다. 일반적으로 원격 접근이 로컬 접근과 동일한 비용이라고 가정할 수 없으며, 추가 지연과 대역폭 비용이 있을 수 있다.
## 게스트의 메모리 접근이 인터커넥트를 건널 때
게스트 vCPU 는 호스트에서 QEMU 의 vCPU 스레드다. 그 스레드는 호스트 Linux 스케줄러를 거쳐 호스트 논리 CPU 에 배치되는데, 그 논리 CPU 는 어느 NUMA 노드엔가 속해 있다.
한 가상 머신의 vCPU 스레드가 노드 0 의 CPU 에서 실행되는데 그 가상 머신을 받치는 호스트 물리 페이지가 노드 1 에 있다고 하자. 그러면 노드 0 의 CPU 가 인터커넥트를 지나 노드 1 의 RAM 을 읽게 된다. 게스트 안에서는 그냥 메모리 접근 한 번이고 게스트 OS 는 그 요청이 소켓을 건넜는지 알지 못하는데, 비용은 그 아래 하드웨어에서 붙는다.
## vCPU pinning 만으로는 끝나지 않는다
vCPU 를 특정 CPU 집합에 묶는 설정을 pinning 이라고 한다. 어떤 가상 머신의 vCPU 를 노드 0 의 CPU 에 pinning 했는데 그 가상 머신의 RAM 이 주로 노드 1 에 배치되어 있으면, pinning 이후에도 원격 접근이 많아질 수 있다. 묶은 것은 실행 위치이고 메모리가 어디에 있는지는 그 설정이 정하지 않기 때문이다.
그래서 vCPU 를 어디에서 실행할지와 메모리를 어느 노드에 두고 묶을지를 함께 봐야 NUMA locality 가 정해진다. 근거 문서가 든 이상적인 구성은 한 가상 머신의 vCPU 넷이 모두 노드 0 의 CPU 에서 실행되고 그 가상 머신의 메모리도 노드 0 의 RAM 에 있는 모양이다.
![QEMU vCPU Thread, VM1 Memory Backing, Node 0 CPU, Node 0 RAM, Node 1 RAM 다섯 참여자 사이의 순서도. 1번에서 vCPU thread 가 Node 0 CPU 에 pinning 되고 2번에서 VM1 의 memory backing 이 Node 1 에 놓이면 3번의 접근이 점선으로 Node 1 RAM 까지 간다. 4번에서 memory 를 Node 0 에 묶은 구성에서는 5번의 같은 접근이 Node 0 RAM 에서 끝난다.](../../../final/assets/diagrams/numa-vcpu-and-memory-placement/numa-vcpu-and-memory-placement.svg)
이 호스트가 노드 몇 개인지는 근거 문서에 없다. 하나로 나오면 지금 말한 어긋남은 이 환경에 성립하지 않는다.
## 큰 가상 머신에서는 게스트에게 토폴로지를 보여 준다
가상 머신이 커지면 그 안에서도 같은 문제가 생긴다. vCPU 열여섯 개와 RAM 64 GiB 를 가진 가상 머신을 하나의 균일한 메모리로 보이게 하면, 게스트 스케줄러는 어느 vCPU 가 어느 메모리에 가까운지 알 방법이 없다. 그래서 큰 가상 머신에서는 게스트에게 NUMA 토폴로지 자체를 노출할 수 있다. 게스트 NUMA 노드 0 에 `vCPU 0~7` 과 RAM 절반, 노드 1 에 `vCPU 8~15` 와 나머지 절반을 두는 식이다.
노출한 뒤에는 게스트가 인식하는 토폴로지와 실제 호스트 배치가 합리적으로 대응되도록 구성한다. 게스트 NUMA 0 이 호스트 NUMA 0 에, 게스트 NUMA 1 이 호스트 NUMA 1 에 대응하지 않으면, 게스트가 로컬이라고 판단해 고른 메모리가 호스트에서는 원격이 된다.
이 프로젝트의 가상 머신이 그런 크기인지는 근거 문서에 적혀 있지 않다. vCPU 수도 설정한 RAM 도 나오지 않아서, 이 절이 이 환경에 걸리는 이야기인지는 그 값을 적은 뒤에 정해진다.
## 이 장비의 토폴로지부터 읽는다
호스트가 NUMA 노드 한 개라면 노드를 건너는 원격 접근 문제가 주요 이슈가 아닐 수 있으므로, 실제 환경에서는 토폴로지를 먼저 측정하고 NUMA 최적화가 필요한지 판단한다.
```bash label="Host 의 NUMA topology"
lscpu
numactl --hardware
```
`lscpu` 는 노드 수와 노드별 CPU 목록을 알려 준다. 근거 문서가 든 출력 예시는 이런 모양이고, 이 테스트 서버에서 읽은 값이 아니다.
```text label="근거 문서가 든 lscpu 출력 예시"
NUMA node(s): 2
NUMA node0 CPU(s): 0-7
NUMA node1 CPU(s): 8-15
```
`numactl --hardware` 는 여기에 노드별 메모리 크기와 노드 사이 거리를 더해 준다. 노드가 둘 이상으로 나오면 그다음은 이 가상 머신들이 어디에 있는지를 본다.
```bash label="QEMU process 의 node 별 memory 분포와 vCPU 배치"
numastat -p <QEMU_PID>
virsh vcpupin <VM_NAME>
virsh vcpuinfo <VM_NAME>
```
`numastat` 은 지정한 프로세스의 메모리가 노드마다 얼마씩 있는지 보여 주므로, QEMU 프로세스를 지정하면 그 가상 머신의 메모리가 어느 노드에 얼마나 놓여 있는지 나온다. `virsh vcpupin` 과 `virsh vcpuinfo` 는 같은 가상 머신의 vCPU 가 어느 CPU 에 묶여 있고 어디서 실행 중인지 알려 준다. 두 출력을 나란히 놓아야 앞 절의 어긋남이 이 장비에 있는지 없는지 말할 수 있다.
## 근거 문서가 번호를 붙여 둔 문장
근거 문서는 이 대목에서 고정한 문장에 번호를 붙여 두었다. 실험 기록에서 어느 문장을 확인했는지 가리킬 때 이 번호를 쓴다.
| 번호 | 근거 문서가 고정한 문장 |
|---|---|
| CLAIM-MEM-18 | NUMA 시스템에서는 vCPU placement와 memory placement를 함께 봐야 한다. |
## 이 문서가 확정하지 않는 것
이 글이 확정하는 것은 배치가 어긋나면 무엇이 원격 접근이 되는가까지다. 이 테스트 서버에서 잰 값은 하나도 없다.
이 호스트가 노드 하나인지 여럿인지가 정해지지 않았다. 노드 하나로 나오면 앞 절의 어긋남이 이 환경에 성립하지 않으므로 NUMA 를 현재 실험에서 뒤로 미룬다. 여럿으로 나와야 QEMU 프로세스의 노드별 메모리 분포와 vCPU 배치를 나란히 적는 확인이 의미를 갖는다. 토폴로지 확인은 CPU 가상화 쪽 질문이 받고 있다.
배치가 어긋나 있다는 것과 그 어긋남이 이 작업의 지연을 바꾼다는 것도 다른 사실이다. 근거 문서는 토폴로지만 보고 성능 문제라고 단정하지 말라고 적었다. vCPU 와 메모리를 같은 노드로 맞춘 구성과 원격 접근이 많아지도록 바꾼 구성에서 같은 작업을 같은 조건으로 돌려 지연과 처리량을 견줘야 답이 나온다. 이 가상 머신들에 게스트 NUMA 를 노출했는지도 근거 문서에 없다. 세 확인은 각각 열린 질문으로 두었고 관계에 걸어 두었다.
<!-- body:end -->
@@ -0,0 +1,237 @@
---
id: e80853fa-98b8-4cc3-a2df-427c7147794b
kind: CONCEPT
slug: page-fault-layers-in-a-vm
title: VM 에서 page fault 는 세 계층에서 따로 일어난다
topic: memory-virtualization
topicName: 메모리 가상화
project: virtualization
status: 초안
basisVersion: x86-64 Intel EPT 기준의 second-stage translation · Linux Guest/Host kernel 의 page fault 처리 · AMD 는 NPT 계열로 대응
studio: "https://hyeonworks.com/studio/documents/e80853fa-98b8-4cc3-a2df-427c7147794b/edit"
assets:
- key: page-fault-layers-in-a-vm
file: ../../../final/assets/diagrams/page-fault-layers-in-a-vm/page-fault-layers-in-a-vm.svg
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
source:
- final/document.md#35-tlb-주소-변환-결과의-cpu-cache
- final/document.md#45-guest-page-fault
- final/document.md#46-page-fault의-대표적인-원인
- final/document.md#47-ept-violation
- final/document.md#48-guest-page-fault와-ept-violation-비교
- final/document.md#49-host-page-fault도-별도로-존재한다
- final/document.md#82-핵심-claim-registry
- final/document.md#86-문제를-진단할-때의-분류
---
# VM 에서 page fault 는 세 계층에서 따로 일어난다
가상 머신 안에서 메모리 접근이 완료되지 못하면 그 사건을 받는 곳이 셋이다. GVA(Guest Virtual Address)를 GPA(Guest Physical Address)로 바꾸는 첫 단계에서 막히면 Guest Page Fault 가 나서 게스트 커널이 처리한다. 그 GPA 를 HPA(Host Physical Address)로 바꾸는 두 번째 단계에서 막히면 EPT Violation 이 나고, VM Exit 뒤 KVM 이 받는다. QEMU 도 호스트의 일반 프로세스여서 자기가 마련한 메모리에 대해 Host Page Fault 를 따로 겪는다. 이름이 모두 fault 라 한 덩어리로 읽히지만 일어나는 단계도 처리하는 쪽도 다르고, 셋 중 어느 것이 늘었는지에 따라 볼 지표가 갈린다.
## 관계
- **Guest 메모리 주소가 물리 RAM 에 닿기까지 — GVA · GPA · HPA**
정상일 때 접근이 어떤 경로를 지나는지를 그 글이 세운다. 이 글은 그 경로가 완료되지 않을 때를 맡는다.
- **Host 메모리 압박이 Guest 와 스토리지까지 내려가는 경로**
호스트 쪽 fault 가 늘어나는 배경인 reclaim 과 swap 을 그 글이 다룬다.
- **게스트 page fault 증가는 workload 변화를 따라가는가**
게스트 쪽 fault 지표를 이 호스트에서 재는 질문이다. 이 글은 원인 분류까지만 적었다.
- **호스트 major fault 와 스토리지 지연은 같은 시간축에서 함께 움직이는가**
호스트 쪽 major fault 와 스토리지 지연을 같은 시간축에 놓는 질문이다.
- **메모리 증상 하나로 계층을 단정하지 않는다**
이 세 갈래가 그 기준이 요구하는 계층 구분을 만든다.
## 본문
<!-- body:start -->
## TLB Miss 와 Page Fault 는 다른 사건이다
주소 변환 결과는 CPU 의 TLB(Translation Lookaside Buffer, 주소 변환 캐시)에 담긴다. 거기에 찾는 변환이 없으면 TLB Miss 가 나지만, 그것으로 접근이 실패하지는 않는다.
```text label="TLB Miss 가 났을 때"
TLB에 translation cache가 없음
Page Table을 조회
정상 mapping 존재
계속 실행
```
Page Fault 는 조건이 다르다. 페이지 테이블 상태로는 지금 접근을 정상으로 끝낼 수 없어서, 커널이 끼어들어 페이지를 마련하거나 매핑을 고쳐야 명령을 다시 시도할 수 있다. TLB Miss 는 테이블을 한 번 더 읽으면 풀리는데, 이쪽은 커널 처리를 부른다.
## 첫 번째 단계 — Guest Page Fault
이 fault 는 GVA 를 GPA 로 바꾸는 첫 번째 변환 단계에서 일어난다.
```text label="첫 번째 단계에서 접근이 완료되지 못할 때"
GVA
Guest Page Table
현재 접근을 완료할 수 없음
Guest #PF
Guest Kernel Page Fault Handler
```
게스트 프로세스가 아직 물리 페이지가 붙지 않은 가상 메모리 영역에 처음 접근하면 이 사건이 난다. 게스트 안의 Keycloak 을 예로 들면, 새 영역의 첫 접근에서 fault 가 나고 게스트 커널이 페이지를 확보해 매핑을 갱신한 뒤 명령을 다시 시도한다.
```text label="Guest 프로세스가 새 영역에 처음 접근할 때"
Keycloak
새 Virtual Memory 영역에 첫 접근
Guest Page Table
현재 usable physical mapping 없음
Page Fault
Guest Kernel
Page 확보 / mapping 갱신
Instruction 재시도
```
이 사건 자체가 프로그램 오류를 뜻하지는 않는다. 정상적인 메모리 관리에서도 늘 일어난다.
## Guest Page Fault 를 부르는 다섯 가지
원문은 대표적인 원인을 다섯으로 나눈다. 앞의 넷은 커널이 처리해 실행이 이어지고, 마지막 하나만 프로세스로 신호가 간다.
| 원인 | 게스트 커널이 무엇을 하나 |
|---|---|
| Demand Paging | 아직 물리 페이지가 필요하지 않았던 영역에 첫 접근이 오면 페이지를 준비한다 |
| Swap-in | 필요한 페이지가 게스트 RAM 에 없으면 게스트 swap 에서 읽어 RAM 을 복원하고 페이지 테이블을 갱신한다 |
| Permission Fault | 읽기 전용 페이지에 쓰기가 들어오는 접근을 걸러 낸다 |
| Copy-on-Write | 쓰기에서 난 fault 를 의도적으로 이용해 페이지를 복제하고 쓰기 가능한 매핑을 새로 만든다 |
| Invalid Access | 정상 매핑으로 해결할 수 없는 접근이면 `SIGSEGV` 등으로 이어질 수 있다 |
권한이 fault 를 부르는 까닭은 페이지 테이블 엔트리가 매핑만 담고 있지 않기 때문이다. 같은 엔트리에 읽기·쓰기·실행 권한이 함께 들어 있다.
```text label="Page Table Entry 에 함께 들어 있는 값"
Physical Frame: 1234
Present: 1
Writable: 0
Executable: 0
```
이 엔트리로 매핑된 페이지에 쓰기가 들어가면 fault 가 날 수 있고, Copy-on-Write 는 그 성질을 거꾸로 이용해 쓰기가 들어온 순간에만 페이지를 복제한다. 다섯 가운데 Invalid Access 만 결과가 다르다. 게스트 커널이 정상적인 매핑으로 해결할 수 없는 접근이면 `SIGSEGV` 로 이어질 수 있고, 그래서 Page Fault 와 Segmentation Fault 는 같은 것이 아니다.
## 두 번째 단계 — EPT Violation
이번에는 게스트 페이지 테이블 변환이 성공해 GPA 까지 얻었다고 하자. 그런데 그 GPA 에 대한 두 번째 단계 접근을 지금 EPT(Extended Page Tables) 조건으로 끝낼 수 없으면 EPT Violation 이 된다.
```text label="Guest 변환은 성공했는데 second-stage 에서 막힐 때"
GVA
Guest Page Table
GPA ← Guest translation 성공
EPT
EPT Violation
VM Exit
KVM
```
게스트 안에서는 첫 단계가 정상으로 끝났으므로 게스트 커널의 fault 처리기가 이 사건을 보지 않는다. 제어권이 VM Exit 으로 VMX(Virtual Machine Extensions) Root 쪽으로 넘어가 KVM 이 처리한다.
## 두 사건이 갈리는 다섯 항목
| 무엇을 견주나 | Guest Page Fault | EPT Violation |
|---|---|---|
| 문제 위치 | GVA → GPA | GPA → HPA |
| 관련 테이블 | 게스트 페이지 테이블 | EPT |
| 기본 관점 | 게스트 가상 메모리 | 가상화 메모리 매핑 |
| 주요 처리 계층 | 게스트 커널 | VM Exit 후 KVM 쪽 |
| 앱 오류를 뜻하는가 | 반드시 아님 | 반드시 아님 |
Guest Page Fault 는 게스트가 자기 가상 메모리를 처리하는 사건이고, EPT Violation 은 두 번째 단계 가상화 변환에서 하이퍼바이저 처리가 필요한 사건이다. 마지막 줄이 둘 다 「반드시 아님」인 까닭은 앞에서 본 대로다. demand paging 이나 copy-on-write 처럼 정상 동작이 두 계층 모두에서 fault 를 만든다.
## 세 번째 — Host Page Fault
QEMU 도 호스트의 일반 사용자 공간 프로세스이므로 QEMU 가 마련한 메모리에는 호스트의 가상 메모리 관리가 그대로 적용된다. 게스트 RAM 을 담고 있는 그 영역도 호스트 페이지 테이블을 지나 호스트 물리 주소로 이어지고, 그 경로에서 demand allocation 이나 reclaim, swap 때문에 fault 가 날 수 있다.
```text label="QEMU backing 쪽에서 나는 fault"
QEMU / Guest RAM Backing
Host Virtual Memory
Host Page Fault
Host Kernel
필요한 Host page 처리
```
게스트 안에서는 이 사건이 fault 로 보이지 않는다. 게스트는 자기 메모리 접근이 오래 걸렸다고만 관측하고, 그 지연의 원인은 호스트 쪽 지표에 남는다. 그래서 가상 머신 메모리를 분석할 때는 Guest Page Fault 와 Host Page Fault, 그리고 EPT 관련 가상화 사건을 적어도 셋으로 갈라 놓는다.
![Guest Page Table 에서 갈래가 둘로 나뉘어 하나는 Guest Page Fault 로 Guest Kernel 에 가고 다른 하나는 GPA 를 들고 EPT 로 내려가며, EPT 에서는 EPT Violation 이 VM Exit 을 거쳐 KVM 으로 가고, 따로 놓인 QEMU 의 Guest RAM Backing 에서는 Host Page Fault 가 Host Kernel 로 가는 갈래도. 받는 곳의 이름이 그대로 처리 계층의 이름이다.](../../../final/assets/diagrams/page-fault-layers-in-a-vm/page-fault-layers-in-a-vm.svg)
그림에서 QEMU 쪽 갈래는 위의 두 갈래와 선으로 이어져 있지 않다. 근거 문서가 EPT 로 얻은 HPA 와 QEMU 가 마련한 메모리의 호스트 물리 주소를 같은 것이라고 적지 않아서 그 선을 긋지 않았다.
## 메모리 지연이나 OOM(Out Of Memory) 이 보일 때 셋을 어디에 놓나
증상 하나로 "메모리 부족"을 결론내리지 않고 어느 계층의 사건인지부터 가른다. 세 사건은 이 분류에서 서로 다른 가지에 들어간다.
```text label="메모리 문제를 계층으로 가르는 분류 (fault 가 걸리는 가지)"
문제
├─ Guest Virtual Memory?
│ ├─ Page Fault
│ ├─ Guest reclaim
│ ├─ Guest swap
│ └─ Guest OOM
├─ Virtualization Translation?
│ ├─ EPT-related event
│ ├─ TLB pressure
│ └─ Huge-page/mapping 특성
└─ Host Memory?
├─ Host reclaim
├─ Host swap
├─ Host major fault
└─ Host OOM
```
원문의 분류에는 Dynamic Memory 와 NUMA(Non-Uniform Memory Access) 가지가 더 있고, 위에 옮긴 셋이 fault 가 걸리는 가지다. 게스트 쪽 fault 가 늘었으면 게스트의 reclaim 과 swap 을 같이 보고, 호스트 쪽 major fault 가 늘었으면 호스트의 reclaim 과 swap 을 같이 본다. 어느 쪽 지표를 먼저 여느냐가 이 구분에서 정해진다. 다만 이 호스트에서는 어느 쪽도 아직 열지 않았다. 게스트 쪽 fault 지표도 호스트 쪽 major fault 도 읽은 적이 없어서, 이 분류는 아직 어느 지표부터 열지 정하는 데만 쓰인다.
## 이 글이 확정하지 않는 것
이 호스트에서 잰 값은 하나도 없다. 게스트 쪽 fault 지표도 호스트 쪽 major fault 도 읽지 않았고, 앞의 다섯 원인 가운데 무엇이 이 환경에서 실제로 일어나는지도 세지 않았다. 원문 제2부는 개념 서술이고 실제 서버에 딸린 값은 OQ(Open Question) 번호가 붙은 확인 목록 열넷으로 미뤄져 있다.
게스트 쪽은 OQ-13 이 받는다. page-fault 관련 지표를 관측한 뒤 그 증가가 무엇에서 왔는지를 네 갈래로 갈라 보는 확인이고, 증가 자체를 오류로 읽지 않는 것이 조건이다.
```text label="Guest page fault 가 늘었을 때 갈라 보는 것 (OQ-13)"
정상 demand paging?
COW?
Guest swap-in?
application working-set 증가?
```
호스트 쪽은 OQ-14 가 받는다. 호스트 메모리 압박 실험을 하면서 아래 넷을 같은 시간축에 놓고 견준다.
```text label="같은 시간축에 놓고 견줄 값 (OQ-14)"
Host Fault
+
Swap activity
+
Storage latency
+
Guest application latency
```
가운데 계층은 그 목록이 받지 않는다. 위 분류에는 EPT 관련 사건과 TLB 압박이 한 가지로 들어 있는데, 그것을 이 호스트에서 재라고 적은 항목은 열넷 가운데 없다. 이 글이 갈라 놓은 세 계층에서 확인 계획이 붙은 것은 게스트 쪽과 호스트 쪽 둘이다.
원문이 커널 버전을 적지 않아 이 글도 특정 버전에 고정하지 않았다. 여기 적은 것은 그 문서가 서술한 처리 구조이고, 이 서버에서 어떤 값이 나오는지는 위 두 확인을 돌려야 안다.
<!-- body:end -->
@@ -0,0 +1,106 @@
---
id: ebdf93d7-2dcb-4fc8-91b9-2364ac8196e6
kind: CONCEPT
slug: virtio-balloon-memory-reclaim
title: virtio-balloon 이 Guest 메모리를 회수하고 돌려주는 방식
topic: memory-virtualization
topicName: 메모리 가상화
project: virtualization
status: 초안
basisVersion: virtio-balloon — Guest kernel 쪽 driver 와 QEMU 쪽 device 가 virtqueue 로 맞물린 구성 · libvirt 의 balloon target
studio: "https://hyeonworks.com/studio/documents/ebdf93d7-2dcb-4fc8-91b9-2364ac8196e6/edit"
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
assets:
- key: virtio-balloon-inflate-deflate
file: ../../../final/assets/diagrams/virtio-balloon-inflate-deflate/virtio-balloon-inflate-deflate.svg
source:
- final/document.md#64-ballooning이-필요한-이유
- final/document.md#65-virtio-balloon-구조
- final/document.md#66-balloon-inflate
- final/document.md#67-balloon-page-반환의-의미
- final/document.md#68-balloon-deflate
- final/document.md#69-ballooning을-과도하게-하면-guest가-압박을-받는다
- final/document.md#70-ballooning과-memory-hotplug
- final/document.md#82-핵심-claim-registry
---
# virtio-balloon 이 Guest 메모리를 회수하고 돌려주는 방식
호스트는 QEMU 가 마련한 게스트 RAM 을 볼 수 있지만 그 안에서 어떤 메모리가 중요한지는 알지 못한다. 그 구분을 아는 쪽은 게스트 커널이라, 호스트가 그 메모리를 그냥 스왑으로 밀어내는 대신 게스트와 협력해 필요 없는 몫을 돌려받는 방법이 따로 있다. `virtio-balloon` 이 그 협력에 쓰는 가상 장치다. 게스트 안의 드라이버와 QEMU 쪽 장치가 virtqueue 로 맞물려 있어서, 호스트가 balloon target 을 올리면 게스트 안의 balloon 이 커지고 게스트가 쓸 수 있는 메모리가 줄며 호스트가 회수할 수 있는 몫이 는다. 값을 낮추면 반대 방향으로 움직인다. 이 장치는 게스트에 RAM 을 더 주는 장치가 아니라 이미 준 용량 안에서 쓸 수 있는 양을 옮기는 장치이고, 과도하게 회수하면 호스트 RAM 을 확보하려던 조치가 게스트를 압박한다.
## 관계
- **Host 메모리 압박이 Guest 와 스토리지까지 내려가는 경로**
ballooning 은 그 압박에 대응하는 수단 가운데 하나다. 호스트가 무작정 스왑으로 밀어낼 때 무엇이 일어나는지는 그쪽에 적혀 있다.
- **이 가상 머신들에 virtio-balloon 이 붙어 있는가**
이 프로젝트의 가상 머신에 이 장치가 구성돼 있는지는 근거 문서에 없다. 그것을 확인하는 질문이다.
- **balloon target 을 바꾸면 게스트가 쓸 수 있는 메모리는 어떻게 따라 움직이는가**
부풀리고 줄이는 방향은 이 글이 적었고, 이 환경에서 얼마나 어떤 속도로 반영되는지는 그 질문이 잰다.
- **메모리 증상 하나로 계층을 단정하지 않는다**
balloon target 은 그 기준이 가르는 다섯 갈래 중 Dynamic Memory 갈래다. 게스트 안의 압박을 보고 호스트 RAM 부족으로 넘기기 전에 이 값을 본다.
## 본문
<!-- body:start -->
## 호스트는 게스트 안에서 무엇이 중요한지 알지 못한다
호스트는 QEMU 가 마련한 게스트 RAM 을 볼 수 있다. 그러나 그 안에서 어떤 메모리가 중요한지는 게스트 밖에서 완전히 알 수 없다. 게스트는 자기 메모리를 애플리케이션이 실제로 쓰는 몫(working set)과 JVM heap, 페이지 캐시, 비어 있는 몫처럼 의미가 다른 묶음으로 구분해 알고 있다. 호스트가 보는 것은 같은 크기의 익명 메모리라서 그 안에서 페이지 캐시를 눌렀는지 실제로 쓰는 몫을 눌렀는지 가려낼 수 없다.
그래서 호스트가 그 메모리를 무작정 스왑으로 밀어내기보다 게스트 커널과 협력해 필요 없는 몫을 돌려받는 편이 유리할 수 있다. 그 협력에 쓰는 대표적인 메커니즘이 `virtio-balloon` 이다.
## 게스트 드라이버와 QEMU 장치가 virtqueue 로 맞물린다
`virtio-balloon` 은 게스트 커널 쪽의 balloon 드라이버와 QEMU 쪽의 balloon 장치가 virtqueue 로 이어진 구성이다. virtqueue 는 게스트와 QEMU 가 요청과 응답을 주고받는 공유 큐이고, balloon 관련 요청도 이 큐를 지나 VM 경계를 넘는다. QEMU 쪽 장치는 받은 내용을 호스트의 메모리 관리로 넘긴다.
이 장치는 게스트 RAM 자체를 제공하지 않는다. 이미 존재하는 게스트 RAM 을 호스트와 게스트가 협력해 회수하고 반환하는 데 쓰는 가상 장치다.
이 프로젝트의 가상 머신에 이 장치가 붙어 있는지는 근거 문서에 없다. 아래 두 절이 적는 것은 장치가 있을 때 어느 방향으로 움직이는가다.
## Inflate — 게스트 안의 balloon 이 커진다
호스트가 게스트 메모리를 회수하려고 하면 balloon target 을 조정해 balloon 을 부풀린다. 이 동작을 inflate 라고 한다. 그 요청이 `virtio-balloon` 을 지나 게스트 안의 balloon 드라이버에 닿으면 드라이버가 게스트 페이지를 확보한다. 게스트 안의 balloon 이 커지기 때문에 게스트가 쓸 수 있는 RAM 이 그만큼 줄고, 호스트 쪽에서는 회수할 수 있는 메모리가 는다.
드라이버가 하는 일은 확보한 페이지를 일반적인 게스트 작업이 쓰지 못하도록 붙잡아 두고 그 사실을 호스트 쪽에 알리는 것까지다. 그 뒤 호스트가 그 메모리를 실제로 언제 어떻게 놓아주는지는 QEMU/KVM 버전과 backing 종류 및 설정에 따라 달라질 수 있다. 근거 문서는 그 동작을 하나로 단정하지 않았고 이 환경에서도 확인하지 않았다. 그 문서는 이 호스트의 QEMU/KVM 버전을 한 번도 적지 않았다. 버전과 설정이 갈라 놓는 동작이라 버전을 적기 전에는 이 환경이 어느 쪽인지 말할 수 없다.
![virtio-balloon Driver 와 virtqueue, QEMU virtio-balloon Device, Host Memory Management 네 참여자 사이를 여섯 개의 메시지가 오가는 순서도. 1번부터 3번까지는 Host 쪽에서 정한 balloon target 조정이 QEMU device 와 virtqueue 를 지나 VM Boundary 를 넘어 Guest kernel 의 driver 에 닿는 방향이고, 4번부터 6번까지는 driver 가 확보한 Guest page 가 같은 경계를 반대로 건너 Host 의 회수 가능 backing 이 되는 방향이다.](../../../final/assets/diagrams/virtio-balloon-inflate-deflate/virtio-balloon-inflate-deflate.svg)
## Deflate — target 을 줄이면 되돌아온다
호스트가 게스트에게 메모리를 다시 내줄 수 있으면 balloon target 을 줄인다. 그러면 게스트 balloon 드라이버가 붙잡고 있던 페이지를 반환하고 게스트가 쓸 수 있는 메모리가 늘어난다.
| 호스트가 balloon target 을 | 게스트가 쓸 수 있는 메모리는 |
|---|---|
| 올린다 (Inflate) | 감소 |
| 내린다 (Deflate) | 증가 |
## 과도한 inflate 가 게스트를 압박한다
게스트 애플리케이션이 실제로 쓰는 메모리가 큰데 balloon 을 과도하게 부풀리면, 게스트가 쓸 수 있는 메모리가 줄면서 게스트 안에 메모리 압박이 생긴다. 그러면 게스트 안에서 회수가 돌고 페이지 캐시가 회수되고 게스트 스왑이 시작되며, 심하면 게스트 OOM(Out Of Memory)까지 간다.
호스트 RAM 을 확보하려는 조치가 게스트의 스토리지 I/O 와 애플리케이션 지연을 늘릴 수 있다는 뜻이다. 회수한 만큼 호스트가 편해지는 대신 게스트가 그 압박을 받으므로, balloon target 을 어디까지 올릴지는 게스트가 실제로 쓰는 메모리를 보고 정한다.
## ballooning 과 memory hotplug 는 같은 것이 아니다
ballooning 은 게스트에 이미 설정된 메모리 용량 안에서 호스트와 게스트 사이의 쓸 수 있는 양을 회수하고 반환한다. 용량 자체는 그대로다. memory hotplug 는 기존 게스트 RAM 에 메모리 장치나 영역을 더해 게스트가 늘어난 용량을 인식하게 한다. 하나는 이미 준 용량 안에서 쓸 수 있는 양을 옮기고 다른 하나는 용량을 더한다.
현대 가상화에는 `virtio-mem` 같은 다른 동적 메모리 관리 방식도 있어서, 가상 머신의 동적 메모리 관리를 ballooning 하나로 일반화하지 않는다. 근거 문서에 있는 것은 그런 방식이 존재한다는 사실까지다.
## 근거 문서가 번호를 붙여 둔 문장
근거 문서는 이 장치에서 고정한 문장에 번호를 붙여 두었다. 실험 기록에서 어느 문장을 확인했는지 가리킬 때 이 번호를 쓴다.
| 번호 | 근거 문서가 고정한 문장 |
|---|---|
| CLAIM-MEM-15 | virtio-balloon은 Guest와 Host가 memory 회수/반환에 협력하기 위한 가상 장치이며 RAM 자체를 제공하는 장치는 아니다. |
| CLAIM-MEM-16 | Balloon inflate가 과도하면 Guest reclaim/swap/OOM을 유발할 수 있다. |
## 이 문서가 확정하지 않는 것
이 글이 확정하는 것은 balloon 이 어떤 구조로 어느 방향으로 동작하는가까지다. 이 테스트 서버에서 잰 값은 하나도 없다.
이 프로젝트의 가상 머신에 `virtio-balloon` 이 붙어 있는지조차 근거 문서에 적혀 있지 않다. libvirt 설정에 balloon 관련 요소가 있는지, 있다면 게스트 안에서 그 드라이버가 어떤 이름으로 올라와 있는지를 먼저 확인해야 한다. 장치가 없으면 balloon target 실험은 성립하지 않는다.
값을 한 단계 바꿨을 때 게스트가 쓸 수 있는 메모리가 얼마나 움직이는지, 그 반영이 즉시인지 늦는지, 어느 값부터 게스트 안에서 회수와 스왑이 시작되는지도 재지 않았다. 호스트가 그 메모리를 언제 놓아주는지가 이 QEMU 버전과 이 backing 설정에서 어떻게 되는지도 마찬가지다. 두 확인은 각각 열린 질문으로 두었고 관계에 걸어 두었다.
<!-- body:end -->
@@ -0,0 +1,102 @@
---
id: 6a60abe7-ea2c-46c2-bb9f-8281bfbe9644
kind: QUESTION
slug: balloon-target-vs-guest-available-memory
title: balloon target 을 바꾸면 게스트가 쓸 수 있는 메모리는 어떻게 따라 움직이는가
topic: memory-virtualization
topicName: 메모리 가상화
project: virtualization
status: 초안
questionStatus: OPEN
studio: "https://hyeonworks.com/studio/documents/6a60abe7-ea2c-46c2-bb9f-8281bfbe9644/edit"
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
source:
- final/document.md#83-실제-환경에서-확인할-open-question-oq-9
- final/document.md#66-balloon-inflate
- final/document.md#68-balloon-deflate
- final/document.md#69-ballooning을-과도하게-하면-guest가-압박을-받는다
---
# balloon target 을 바꾸면 게스트가 쓸 수 있는 메모리는 어떻게 따라 움직이는가
virtio-balloon 은 게스트에 RAM 을 새로 붙여 주는 장치가 아니라, 이미 있는 게스트 RAM 의 backing 을 호스트와 게스트가 협력해 회수하고 되돌려주는 가상 장치다. 호스트는 balloon target 을 조정해 그 balloon 을 부풀리거나 줄인다. 개념 문서는 방향까지 적어 두었는데, inflate 하면 게스트가 쓸 수 있는 RAM 이 줄고 deflate 하면 늘어난다.
개념 문서가 방향만 적고 크기는 적지 않아서, 이 물음이 받는 것은 방향이 아니라 크기와 시점이다. 이 호스트에서 목표값을 한 단계 움직였을 때 게스트가 쓸 수 있는 메모리가 얼마나 줄어드는지를 아직 값으로 받아 본 적이 없다. 그 변화가 언제 보이는지, 어느 지점부터 게스트가 reclaim 과 스왑을 시작하는지도 마찬가지다.
## 관계
- **virtio-balloon 이 Guest 메모리를 회수하고 돌려주는 방식**
inflate 와 deflate 가 게스트가 쓸 수 있는 메모리를 어느 방향으로 움직이는지를 그 개념이 설명한다.
- **Host 메모리 압박이 Guest 와 스토리지까지 내려가는 경로**
과도한 inflate 가 게스트를 밀어 넣는 곳이 그 경로다.
- **이 가상 머신들에 virtio-balloon 이 붙어 있는가**
balloon 이 붙어 있다는 답을 받은 뒤에 이 실험을 연다.
- **메모리 실험 결과에는 Host · VM · Workload 조건을 함께 남긴다**
단계마다 받아 적을 조건을 그 기준이 정한다.
## 사실
- 호스트가 게스트 메모리를 회수하려고 할 때 balloon 을 inflate 한다. 게스트 안의 balloon 이 커지기 때문에 게스트가 쓸 수 있는 RAM 이 줄어들고, 호스트가 회수할 수 있는 backing memory 는 늘어난다.
- 호스트가 게스트에게 메모리를 다시 내줄 수 있으면 balloon target 을 줄인다. 그러면 게스트의 balloon 드라이버가 balloon page 를 반환하고 게스트가 쓸 수 있는 메모리가 늘어난다.
- 게스트의 balloon 드라이버는 게스트 page 를 확보하고 그 사실을 호스트 쪽에 알린다. 호스트가 그 backing memory 를 실제로 언제 어떻게 회수하는지는 QEMU 와 KVM 버전, backing 종류, 설정에 따라 달라질 수 있다고 개념 문서가 적어 두었다.
- 게스트 애플리케이션의 working set 이 큰데 balloon 을 과도하게 inflate 하면 다음 순서로 이어질 수 있다. 마지막에 오는 OOM(Out Of Memory)은 reclaim 으로도 필요한 메모리를 확보하지 못해 커널이 프로세스를 종료할 수 있는 상태다.
Guest available memory 감소 → Guest memory pressure → Guest reclaim → page cache 회수 → Guest swap → 심하면 Guest OOM
- 호스트 RAM 을 확보하려던 조치가 게스트의 스토리지 I/O 와 애플리케이션 지연을 늘릴 수 있다.
- 개념 문서가 적은 관측 순서는 호스트와 libvirt 의 메모리 설정을 바꾸고, 게스트에서 free -h 와 /proc/meminfo 를 본 다음, 게스트의 reclaim 과 스왑이 어떻게 달라지는지 보는 것이다.
- 과도한 ballooning 구간의 지연과 스왑, OOM 가능성은 별도 실험으로 보라고 같은 항목이 적어 두었다.
- Ballooning 은 기존 게스트 메모리 용량 안에서 쓸 수 있는 메모리를 회수하고 되돌려준다. 용량 자체를 늘리는 memory hotplug 와 다르고 virtio-mem 같은 다른 동적 메모리 관리 방식도 있으므로, 셋을 하나로 묶어 일반화하지 않는다.
- 이 호스트에서 balloon target 을 움직여 본 기록이 없다. 게스트가 쓸 수 있는 메모리를 찍어 둔 값도 없다.
## 가정
- 이 가상 머신들에 virtio-balloon 이 붙어 있다고 보고 실험을 짠다. 붙어 있는지 자체는 별도 물음이 받는다.
- balloon target 을 낮췄다가 원래 값으로 되돌릴 수 있다고 본다.
- 관측하는 동안 게스트의 워크로드가 일정하다고 전제한다. 그래야 쓸 수 있는 메모리가 달라진 것을 목표값을 바꾼 탓으로 읽을 수 있다.
- 게스트 안에서 읽은 값이 balloon 의 현재 크기를 반영한다고 본다. 두 값을 이 호스트에서 나란히 대조하지는 않았다.
## 미지수
- 목표값을 한 단계 낮췄을 때 게스트가 쓸 수 있는 메모리가 같은 크기만큼 줄어드는지, 그보다 덜 줄어드는지.
- 목표값을 바꾼 직후에 게스트 쪽 값이 바뀌는지, 반영에 시간이 걸리는지.
- 어느 목표값부터 게스트의 reclaim 과 스왑이 시작되는지.
- deflate 로 되돌렸을 때 그 값이 원래대로 돌아오는지.
- balloon target 을 바꾸는 명령. 개념 문서는 호스트와 libvirt 의 메모리 설정이라고만 적고 명령은 적지 않았다.
## 제약
- balloon 이 붙어 있지 않으면 목표값을 움직일 대상이 없으므로, 이 실험은 붙어 있다는 확인이 끝난 뒤에 연다.
- 개념 문서가 그 구간을 별도 실험으로 지정했으니 과도 inflate 구간은 같은 실행에 이어 붙이지 않고 따로 돌린다.
- 단계마다 Host · VM · Workload 조건을 함께 적는다. 조건을 남기지 않은 결과는 다른 환경에서 다시 쓰기 어렵다.
- 게스트 OOM 까지 밀어 볼지는 이 실험에서 정하지 않는다.
- 개념 문서가 목표값을 바꾸는 명령을 적어 주지 않았으므로 실행한 명령과 그 출력을 증거로 함께 남긴다.
- 호스트가 실제로 회수한 backing memory 는 이 실험에서 재지 않는다. 개념 문서가 그 동작을 QEMU 와 KVM 버전, backing 종류, 설정에 따라 달라질 수 있다고만 적고 단정을 피했고, §83 OQ-9 가 준 관측 순서도 호스트 설정을 바꾼 뒤 게스트 쪽 값과 게스트의 reclaim 과 스왑 변화를 보는 데까지다.
## 선택지
### 1. 목표값을 여러 단계로 나눠 낮추고 단계마다 게스트 값을 받아 적는다
한 번에 크게 줄이지 않고 단계를 나누면 목표값과 게스트가 쓸 수 있는 메모리의 대응이 표로 남는다. reclaim 과 스왑이 시작되는 지점도 어느 단계와 어느 단계 사이인지까지 좁혀진다.
단계 수만큼 실행이 늘어나고, 단계마다 게스트 값을 같은 방법으로 받아 적어야 한다.
### 2. 한 단계만 낮췄다가 되돌린다
inflate 와 deflate 가 개념 문서가 적은 방향으로 움직이는지만 이 호스트에서 확인한다. 실행이 짧고 되돌리기도 쉽다.
대신 목표값과 게스트 메모리를 짝지은 값이 한 쌍만 나온다. reclaim 이 시작되는 지점은 이 방법으로 나오지 않아서, 그것까지 알아야 하면 결국 첫 번째 선택지로 돌아간다.
### 3. 과도 inflate 까지 한 번에 밀어 본다 — 제외
게스트가 압박을 받는 구간까지 한 실행으로 내려가서 정상 구간과 압박 구간을 함께 보자는 방법이다.
제외한 이유는 둘이다. 개념 문서가 그 구간을 별도 실험으로 분리해 두었고, 게스트 스왑과 OOM 이 걸리면 그 실행에서 받은 앞 단계 값도 같은 조건으로 읽기 어려워지기 때문이다.
## 다음 검증
1. balloon 이 이 가상 머신들에 붙어 있다는 확인을 먼저 받는다.
2. 게스트에서 free -h 와 /proc/meminfo 와 vmstat 1 을 찍어 기준값으로 둔다.
3. 호스트에서 balloon target 을 한 단계 낮추고 같은 셋을 다시 찍는다.
4. 단계를 몇 번 반복해 목표값과 게스트가 쓸 수 있는 메모리의 대응을 한 표로 만든다.
5. 목표값을 바꾸는 데 쓴 명령과 그 출력을 함께 남긴다. 개념 문서에 적혀 있지 않은 명령이다.
6. 과도 inflate 구간은 실행을 나누고, 그 구간에서는 게스트 애플리케이션 지연을 같이 잰다.
닫는 조건 : 목표값과 게스트가 쓸 수 있는 메모리가 표로 대응되고 reclaim 과 스왑이 시작되는 지점이 적히면 닫는다. 게스트 스왑이나 OOM 까지 갔으면 그 구간이 Case 가 되고, 운영에서 balloon 을 쓸지와 목표값 하한을 얼마로 둘지는 그 Case 가 나온 뒤 Decision 으로 넘긴다.
@@ -0,0 +1,98 @@
---
id: 074a1cb4-cea8-4dad-bc4c-899692ff9aa9
kind: QUESTION
slug: guest-page-fault-vs-workload
title: 게스트 page fault 증가는 workload 변화를 따라가는가
topic: memory-virtualization
topicName: 메모리 가상화
project: virtualization
status: 초안
questionStatus: OPEN
studio: "https://hyeonworks.com/studio/documents/074a1cb4-cea8-4dad-bc4c-899692ff9aa9/edit"
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
source:
- final/document.md#83-실제-환경에서-확인할-open-question-oq-13
- final/document.md#45-guest-page-fault
- final/document.md#46-page-fault의-대표적인-원인
---
# 게스트 page fault 증가는 workload 변화를 따라가는가
게스트 안에서 page fault 가 늘었다는 관측 하나로는 무슨 일이 일어났는지 갈리지 않는다. 개념 문서가 든 대표적인 원인은 다섯인데, 그 가운데 demand paging 과 copy-on-write 는 정상 동작이다. 나머지 중 swap-in 은 게스트 RAM 이 모자란다는 신호이고, invalid access 만 프로그램 오류로 이어진다.
이 물음은 이 호스트의 게스트에서 부하를 올렸을 때 fault 지표가 함께 오르는지를 구간을 나눠 받는다. 올랐다면 그 증가가 다섯 원인 가운데 어느 쪽으로 설명되는지까지 본다.
다섯 원인을 하나씩 묻지 않고 한 물음으로 묶었다. 원인마다 물음을 세우면 개념 문서에서 한 줄이던 것이 다섯 편이 되고, §83 OQ-13 이 갈라 보라고 한 네 갈래는 같은 구간의 같은 지표에서 나온다.
## 관계
- **VM 에서 page fault 는 세 계층에서 따로 일어난다**
게스트 안에서 본 fault 가 어느 계층의 사건인지를 그 개념이 가른다.
- **지금 게스트와 호스트에서 swap 이 실제로 오가고 있는가**
swap-in 이 실제로 있는지는 그 물음이 받는다. 여기서는 같은 구간의 스왑 활동을 함께 적어 fault 증가와 겹치는지만 본다.
- **호스트 major fault 와 스토리지 지연은 같은 시간축에서 함께 움직이는가**
호스트 쪽 fault 는 그 물음이 받고, 이 물음이 보는 것은 게스트 안의 fault 다.
- **메모리 증상 하나로 계층을 단정하지 않는다**
fault 증가 하나로 결론을 내지 않는 기준을 그 문서가 정한다.
## 사실
- 게스트 page fault 는 게스트 프로세스가 쓰는 주소인 GVA(Guest Virtual Address)를 게스트 페이지 테이블로 변환하는 첫 단계에서 그 접근을 지금 끝낼 수 없을 때 발생한다. 게스트 커널의 핸들러가 페이지를 확보하고 매핑을 갱신한 뒤 그 명령을 다시 실행한다.
- page fault 자체가 프로그램 오류를 뜻하지 않는다고 개념 문서가 밝혔다.
- 개념 문서가 든 대표적인 원인은 다섯이다.
demand paging : 영역은 있는데 아직 물리 페이지가 필요하지 않았고, 첫 접근에서 게스트 커널이 페이지를 준비한다.
swap-in : 필요한 페이지가 게스트 RAM 에 없어 게스트 스왑에서 읽어 RAM 을 복원하고 페이지 테이블을 갱신한다.
permission fault : 매핑은 있는데 권한이 맞지 않는다. 읽기 전용 페이지에 쓰면 발생할 수 있다.
copy-on-write : write fault 를 일부러 이용해 페이지를 복제하고 쓰기 가능한 매핑을 새로 만든다.
invalid access : 게스트 커널이 정상 매핑으로 해결할 수 없는 접근이고 SIGSEGV 등으로 이어질 수 있다.
- Page Fault 와 Segmentation Fault 는 다르다.
- 개념 문서의 확인 항목은 게스트에서 page-fault 관련 지표를 보되 정상 demand paging 인지, copy-on-write 인지, 게스트 swap-in 인지, 애플리케이션의 working set 이 커진 것인지를 갈라 보라고 적었다.
- 같은 항목이 page fault 증가만으로 오류라고 판단하지 말라고 적었다.
- 개념 문서는 page fault 지표를 무엇으로 읽는지 적지 않았다. 게스트와 호스트의 스왑 활동을 볼 때 쓰라고 적어 둔 명령은 free -h 와 vmstat 1 이다.
- 이 호스트의 게스트에서 fault 지표를 시간에 따라 찍어 둔 기록이 없다.
## 가정
- idle 구간과 부하 구간을 나눠 만들 수 있고, 두 구간에서 같은 방법으로 지표를 받을 수 있다고 본다.
- 부하를 만드는 방법이 게스트의 메모리 사용량도 함께 올린다고 전제한다. CPU 만 쓰는 부하라면 fault 지표가 움직이지 않을 수 있기 때문이다.
- 두 구간 사이에 게스트의 다른 조건이 바뀌지 않는다고 보고 견준다.
- 게스트 안에서 읽은 fault 지표가 게스트 page fault 를 센 것이라고 본다. EPT(Extended Page Tables) 관련 사건과 호스트 쪽 fault 는 게스트 안에서 보이지 않는다.
## 미지수
- 부하를 올렸을 때 fault 지표가 실제로 함께 오르는지.
- 오른 것이 minor 인지 major 인지.
- 그 증가가 다섯 원인 가운데 어느 쪽으로 설명되는지.
- minor 와 major 를 프로세스 단위로 가르는 방법. 개념 문서가 명령을 적지 않아 실행하는 쪽이 정한다.
- 애플리케이션의 working set 을 이 환경에서 무엇으로 재는지.
## 제약
- 같은 구간의 스왑 활동을 함께 적지 않으면 demand paging 과 swap-in 이 갈리지 않는다.
- fault 지표 하나로 원인을 확정하지 않는다. 개념 문서가 네 갈래를 갈라 보라고 적은 이유가 이것이다.
- 부하를 만드는 방법을 두 실행에서 같게 둔다. 방법이 바뀌면 fault 증가가 부하 때문인지 방법 때문인지 갈리지 않는다.
- 이 호스트에서 잰 값이 없으므로 다른 환경의 fault 수치를 근거로 삼지 않는다.
## 선택지
### 1. idle 구간과 부하 구간을 나눠 시간에 따른 변화를 받는다
두 구간에서 vmstat 1 로 시간에 따른 값을 받고, 같은 구간의 스왑 활동과 애플리케이션의 working set 을 함께 남긴다. 부하가 올라간 시각과 fault 가 오른 시각이 겹치는지가 그대로 보이고, 스왑 활동이 없는데 fault 만 올랐으면 demand paging 쪽으로 좁혀진다.
minor 와 major 를 프로세스 단위로 가르는 것까지는 이 방법으로 나오지 않는다.
### 2. 프로세스 단위로 minor 와 major 를 갈라 받는다
게스트 안의 대상 프로세스를 정해 그 프로세스의 fault 를 minor 와 major 로 나눠 받는다. major 가 함께 오르면 swap-in 쪽으로 좁혀지고, minor 만 오르면 demand paging 이나 copy-on-write 로 좁혀진다.
읽는 방법을 개념 문서가 적어 두지 않아서, 실행하는 쪽이 정하고 그 방법을 증거로 남겨야 한다.
## 다음 검증
1. 게스트에서 idle 구간을 먼저 정하고 그 구간의 fault 지표와 스왑 활동을 vmstat 1 로 받아 기준값으로 둔다.
2. 같은 게스트에 부하를 걸고 같은 방법으로 같은 항목을 다시 받는다.
3. 두 구간에 애플리케이션의 working set 을 함께 적는다.
4. 프로세스 단위로 minor 와 major 를 가른 경우에는 그 값을 읽은 방법을 같이 남긴다.
5. 두 구간을 한 시간축에 놓고 부하가 오른 시각과 fault 가 오른 시각이 겹치는지 본다.
닫는 조건 : 부하 구간의 fault 증가가 minor 위주이고 swap-in 이 없으면 정상 demand paging 으로 설명된다고 적고 닫는다. major fault 가 함께 오르면 게스트 swap-in 을 의심하고 그 구간을 스왑 쪽 물음으로 넘긴다. 어느 쪽으로도 설명되지 않으면 그 관측이 Case 가 된다.
@@ -0,0 +1,101 @@
---
id: a2cbc828-59ab-4e70-916a-d053e71960ae
kind: QUESTION
slug: host-major-fault-vs-storage-latency
title: 호스트 major fault 와 스토리지 지연은 같은 시간축에서 함께 움직이는가
topic: memory-virtualization
topicName: 메모리 가상화
project: virtualization
status: 초안
questionStatus: OPEN
studio: "https://hyeonworks.com/studio/documents/a2cbc828-59ab-4e70-916a-d053e71960ae/edit"
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
source:
- final/document.md#83-실제-환경에서-확인할-open-question-oq-14
- final/document.md#49-host-page-fault도-별도로-존재한다
- final/document.md#62-memory-pressure와-storage-contention의-연결
---
# 호스트 major fault 와 스토리지 지연은 같은 시간축에서 함께 움직이는가
개념 문서는 호스트의 메모리 압박에서 시작해 애플리케이션 지연으로 끝나는 사슬을 그려 두었다. 호스트 메모리 압박이 reclaim 과 스왑을 부르고, 그 스왑 I/O 가 스토리지 I/O 를 늘리고, 늘어난 I/O 가 한 물리 장치에서 경합하면 데이터베이스 지연을 거쳐 애플리케이션 지연까지 간다는 순서다.
사슬의 각 마디는 개념으로 이어져 있지만, 이 호스트에서 네 마디가 같은 구간에 함께 움직이는지는 아직 받아 본 적이 없다. 이 물음은 압박 실험을 한 번 돌릴 때 네 계열을 같은 타임스탬프로 받아 그 사슬이 여기서 이어지는지 끊기는지를 가른다.
## 관계
- **VM 에서 page fault 는 세 계층에서 따로 일어난다**
호스트 쪽 fault 가 게스트 쪽 fault 와 어떻게 다른 사건인지를 그 개념이 가른다.
- **Host 메모리 압박이 Guest 와 스토리지까지 내려가는 경로**
이 물음이 확인하려는 사슬을 그 개념이 그렸다.
- **호스트 메모리 압박이 게스트 애플리케이션 지연을 실제로 밀어 올리는가**
압박을 만드는 실험은 그쪽이 돌리고, 이 물음은 같은 실행에서 계열 넷을 받는다.
- **지금 게스트와 호스트에서 swap 이 실제로 오가고 있는가**
평상시 스왑 활동은 그 물음이 받고, 여기서는 압박 구간의 스왑 활동을 본다.
- **메모리 증상 하나로 계층을 단정하지 않는다**
네 계열을 함께 봐야 하는 근거를 그 기준이 정한다.
## 사실
- QEMU 도 호스트의 일반 userspace 프로세스이므로 QEMU 의 memory backing 에는 호스트의 가상 메모리 관리가 적용된다. demand allocation 이나 reclaim 과 스왑 때문에 호스트 쪽에서도 page fault 가 발생할 수 있다.
- VM 메모리 분석에서는 게스트 page fault 와 호스트 page fault, EPT(Extended Page Tables) 관련 사건을 적어도 셋으로 구분한다.
- 게스트와 호스트가 동시에 메모리 압박을 겪으면 게스트 스왑 I/O 와 호스트 스왑 I/O, 데이터베이스 I/O, filesystem writeback 이 한 물리 NVMe 로 몰릴 수 있다.
- 개념 문서가 그린 사슬은 호스트 메모리 압박에서 reclaim 과 스왑으로, 거기서 스토리지 I/O 증가와 스토리지 경합으로, 다시 데이터베이스 지연과 애플리케이션 지연으로 이어진다. 가능한 경로라고 적었지 이 호스트에서 확인했다고 적지는 않았다.
- CPU 사용률이 낮다고 해서 메모리나 스토리지 문제가 없다고 볼 수 없다.
- 개념 문서는 swap used 값 하나로 장애를 판단하지 말라고 하면서, 대신 swap-in 과 swap-out 이 지속되는지, reclaim pressure 가 증가하는지, major fault 가 증가하는지, 스토리지 지연이 같이 증가하는지를 묻는다. 그 확인에 적어 둔 명령은 free -h 와 vmstat 1 이고, 게스트와 호스트를 동시에 본다.
- 확인 항목은 호스트 fault 와 스왑 활동, 스토리지 지연, 게스트 애플리케이션 지연을 같은 시간축에 놓고 견주라고 적었다.
- 같은 문서의 스토리지 부분은 장치 I/O 관측 명령으로 iostat -xz 1 을 적었다. 메모리 쪽 확인 항목에는 스토리지 지연을 무엇으로 재는지 적혀 있지 않다.
- 호스트 쪽 major fault 를 이 장비에서 찍어 둔 기록이 없다.
## 가정
- 호스트에 메모리 압박을 걸었다가 풀 수 있다고 보고 실험을 짠다. 압박을 만드는 방법 자체는 별도 물음이 정한다.
- 네 계열을 같은 시계에서 받아 적을 수 있다고 본다. 시각이 어긋나면 함께 움직였는지 판단할 수 없기 때문이다.
- 압박 구간에 게스트 쪽 워크로드를 일정하게 유지할 수 있다고 전제한다.
- 호스트의 스왑 영역과 가상 머신 디스크 이미지가 같은 물리 장치 위에 있다고 보고 경합을 예상한다. 이 호스트에서 두 경로가 실제로 같은 장치를 쓰는지는 확인하지 않았다.
## 미지수
- 압박 구간에서 호스트 major fault 가 실제로 오르는지.
- 오른다면 같은 구간에 스왑 활동과 스토리지 지연도 같은 방향으로 움직이는지.
- 그 움직임이 게스트 애플리케이션 지연과 시간적으로 겹치는지.
- major fault 는 오르는데 스토리지 지연은 오르지 않는 구간이 있는지.
- 호스트 쪽 major fault 와 스토리지 지연을 이 환경에서 어떤 명령으로 읽는지. 메모리 쪽 확인 항목이 적지 않았다.
## 제약
- 압박 실험을 이 물음 때문에 따로 한 번 더 돌리지 않고, 지연 쪽 물음의 실행에서 계열을 함께 받는다. §84 의 권장 실험 순서가 압박 실험을 열 번째에 두고 게스트와 호스트의 스왑 및 스토리지 지연 비교를 그 다음 열한 번째에 두었다.
- 한 물리 장치를 여러 가상 머신이 나눠 쓸 때 무슨 일이 생기는지는 스토리지 가상화 주제가 따로 묻고 있다. 여기서는 그 물음을 다시 열지 않고 스토리지 지연을 같은 시간축에 놓는 데까지만 간다.
- swap used 값 하나로 판단하지 않는다.
- baseline 과 압박과 회복 세 구간을 모두 남긴다. 압박 구간만 있으면 오른 것인지 원래 그런 것인지 갈리지 않는다.
- 이 호스트에서 잰 값이 없으므로 다른 환경에서 네 계열이 함께 움직였다는 관찰을 근거로 삼지 않는다.
## 선택지
### 1. 압박 실험 한 번에서 네 계열을 같은 타임스탬프로 받는다
지연 쪽 물음이 돌리는 실행에 계열 넷을 붙여 baseline 과 압박과 회복 구간에서 모두 받는다. 같은 실행이라 시각이 어긋나지 않고, 네 계열을 한 시간축에 놓는 것이 이 물음이 답하려는 것과 같은 모양이다.
받을 항목이 늘어나므로 실행 전에 무엇을 어떤 명령으로 받을지 정해 두어야 한다.
### 2. 호스트 쪽 두 계열을 먼저 받고 스토리지는 뒤에 붙인다
major fault 와 스왑 활동만 먼저 받아 압박이 호스트에 실제로 걸렸는지 확인하고, 스토리지 지연과 게스트 지연은 다음 실행에서 붙인다. 첫 실행이 가볍고, 압박을 만드는 방법이 통하는지도 여기서 걸러진다.
두 실행 사이에 조건이 달라지면 네 계열을 한 시간축에 놓을 수 없어서, 결국 첫 번째 선택지로 다시 돌아가게 된다.
### 3. swap used 값이 오르는지로 판단한다 — 제외
호스트의 swap used 가 오르면 사슬이 이어진 것으로 읽자는 방법이다.
개념 문서가 그 판단을 직접 막았다. 과거에 swap-out 된 cold page 가 남아 있어도 값은 올라가 있어서, 지금 압박이 있는지는 그 값으로 갈리지 않기 때문이다.
## 다음 검증
1. 지연 쪽 물음의 실행 계획에 이 계열 넷을 넣는다.
2. baseline 구간에서 호스트의 major fault 와 스왑 활동을 vmstat 1 로, 스토리지 지연과 게스트 애플리케이션 지연을 각각 정한 방법으로 받는다.
3. 압박 구간에서 같은 넷을 같은 타임스탬프로 다시 받는다.
4. 압박을 푼 회복 구간에서 한 번 더 받는다.
5. 세 구간의 네 계열을 한 시간축에 놓고 어느 계열이 언제 움직였는지 적는다.
6. 스토리지 지연을 읽은 명령을 함께 남긴다. 메모리 쪽 확인 항목에 없는 것이라 실행한 쪽이 정한다.
닫는 조건 : 네 계열이 같은 구간에서 함께 오르면 개념 문서가 그린 사슬이 이 환경에서 이어진다는 것을 확인한 것이 되고, 그 시계열이 Case 가 된다. major fault 는 오르는데 스토리지 지연이 따라 오르지 않으면 이 장치 구성에서는 사슬이 거기서 끊긴다고 적고 닫는다. 어느 쪽이든 장치 분리나 스왑 위치 변경 같은 스토리지 쪽 대응은 그 뒤 Decision 으로 넘긴다.
@@ -0,0 +1,112 @@
---
id: a05fe2a6-eb7c-4bcc-a7da-58e729436468
kind: QUESTION
slug: host-memory-pressure-vs-guest-latency
title: 호스트 메모리 압박이 게스트 애플리케이션 지연을 실제로 밀어 올리는가
topic: memory-virtualization
topicName: 메모리 가상화
project: virtualization
status: 초안
questionStatus: OPEN
studio: "https://hyeonworks.com/studio/documents/a05fe2a6-eb7c-4bcc-a7da-58e729436468/edit"
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
source:
- final/document.md#83-실제-환경에서-확인할-open-question-oq-7
- final/document.md#59-host-memory-pressure와-reclaim
- final/document.md#60-host-swap이-vm에-미치는-영향
- final/document.md#62-memory-pressure와-storage-contention의-연결
---
# 호스트 메모리 압박이 게스트 애플리케이션 지연을 실제로 밀어 올리는가
게스트는 자기가 RAM 에 접근한다고 생각하는데, 그 backing page 가 호스트 RAM 에 없으면 호스트에서는 page fault 와 swap-in I/O 를 기다린 뒤에야 게스트 실행이 이어진다. 개념 문서는 그 경로가 스토리지 경합을 거쳐 애플리케이션 지연까지 이어질 수 있다고 그려 두었을 뿐 이 호스트에서 재지 않았다.
이 물음은 baseline 을 잡고 호스트에 메모리 압박을 유도한 뒤 게스트 지연을 다시 재서, 그 경로가 실제로 이어지는지 확인한다.
## 관계
- **Host 메모리 압박이 Guest 와 스토리지까지 내려가는 경로**
이 실험이 확인하려는 경로를 그 개념이 단계별로 설명한다.
- **VM 에서 page fault 는 세 계층에서 따로 일어난다**
압박 구간에서 늘어나는 fault 가 어느 계층의 것인지를 그 개념이 가른다.
- **지금 게스트와 호스트에서 swap 이 실제로 오가고 있는가**
그 물음이 잰 평상시 상태가 이 실험의 baseline 이 된다.
- **호스트 major fault 와 스토리지 지연은 같은 시간축에서 함께 움직이는가**
압박 구간에서 스토리지 지연이 오르면 그 상관을 그 물음이 따로 본다.
- **메모리 실험 결과에는 Host · VM · Workload 조건을 함께 남긴다**
세 구간 모두에 그 기준이 요구하는 조건을 남겨야 다른 환경과 견줄 수 있다.
## 사실
- §59 는 호스트 RAM 수요가 실제로 쓸 수 있는 물리 메모리에 다다르면 Linux 가 메모리 reclaim 을 시도한다고 적었다.
회수 가능한 캐시와 페이지를 먼저 처리하고, 필요하면 anonymous memory 를 스왑으로 내리고, 그래도 부족하면 심각한 압박이나 OOM 으로 갈 수 있는 순서다.
- §59 는 file-backed clean page 와 anonymous page 를 갈라 적었다.
clean file-backed page 는 원본이 스토리지에 있으므로 RAM 에서 버렸다가 필요할 때 다시 읽을 수 있다. 힙이나 스택 같은 anonymous memory 는 원본을 다시 읽을 수 없어서 스왑 같은 backing 이 필요할 수 있다.
- §60 은 게스트 RAM backing 의 호스트 물리 페이지가 swap-out 될 수 있는 구성을 놓고 두 관점을 나란히 그렸다.
게스트 쪽에서는 Keycloak 이 메모리를 읽는 것으로 끝난다. 호스트 쪽에서는 필요한 backing page 가 RAM 에 없어 호스트 page fault, swap-in I/O, 물리 RAM 으로 복원, 게스트 실행 계속으로 바뀔 수 있다.
- §60 은 그래서 게스트 관점의 RAM 접근이 호스트에서는 스토리지 I/O 를 기다리는 상황으로 바뀔 수 있다고 적었다.
- §62 는 게스트와 호스트가 동시에 메모리 압박을 겪으면 게스트 스왑 I/O · 호스트 스왑 I/O · 데이터베이스 I/O · filesystem writeback 이 한 물리 NVMe 로 몰릴 수 있다고 그렸다.
이어지는 사슬은 호스트 메모리 압박에서 reclaim 과 스왑으로, 거기서 스토리지 I/O 증가와 스토리지 경합으로 간다. 그 다음이 데이터베이스 지연 증가와 애플리케이션 지연 증가다.
- §62 는 CPU 사용률이 낮다고 해서 메모리나 스토리지 문제가 없는 것은 아니라고 밝혔다.
- §83 OQ-7 은 실험 개념을 다섯 단계로 적었다.
Baseline
게스트 애플리케이션 지연 측정
호스트 메모리 압박 유도
호스트 reclaim/swap 관측
게스트 지연 재측정
- §83 OQ-7 은 같은 구간에 CPU 와 스토리지도 동시에 관측하라고 덧붙였다.
- 개념 문서는 호스트 메모리 압박을 어떤 방법으로 유도하는지 적지 않았다. 압박의 크기와 지속 시간도 적혀 있지 않다.
- 이 호스트에서 게스트 애플리케이션 지연을 잰 기록이 개념 문서에 없다.
## 가정
- 호스트에 메모리 압박을 만들었다가 되돌릴 수 있다고 본다. 되돌린 뒤 상태가 baseline 과 같아지는지는 세 번째 구간에서 확인한다.
- 게스트 애플리케이션 지연을 세 구간에서 같은 방법으로 잴 수 있다고 전제한다. 같은 요청 부하를 세 번 거는 것이 이 구성에서 가능한지는 해 보기 전에 알 수 없다.
- 압박 구간에서 reclaim 이나 스왑이 실제로 돌 만큼 압박을 줄 수 있다고 보고 실험을 짠다. 어느 정도가 그 지점인지는 이 호스트에서 확인되지 않았다.
- 세 구간 사이에 게스트 워크로드와 가상 머신 구성이 바뀌지 않는다고 두고 세 기록을 견준다.
- 호스트와 게스트의 시계가 맞아 네 종류 지표를 같은 시간축에 놓을 수 있다고 본다.
## 미지수
- 압박을 유도했을 때 이 호스트에서 reclaim 과 스왑이 실제로 도는지.
- 돈다면 같은 구간의 게스트 애플리케이션 지연이 baseline 과 얼마나 다른지.
- 그 변화가 CPU 쪽 지표가 아니라 메모리와 스토리지 쪽 지표와 같은 방향으로 움직이는지. §62 는 CPU 사용률이 낮아도 메모리나 스토리지 문제가 있을 수 있다고 적었다.
- 호스트 압박을 유도하는 방법. 개념 문서가 적지 않아 실행하는 쪽이 정하고 무엇을 썼는지 남겨야 한다.
- 압박을 걷은 뒤 게스트 지연이 baseline 으로 돌아오는지, 돌아온다면 얼마나 걸리는지.
## 제약
- 세 구간 모두에 §85 가 요구한 호스트 · VM · 워크로드 조건을 남긴다. 조건을 남기지 않으면 이 결과를 다른 환경에 다시 쓸 수 없다.
- 게스트 쪽 스왑은 「지금 게스트와 호스트에서 swap 이 실제로 오가고 있는가」와 같은 방법으로 찍는다. 두 물음의 값이 같은 방식으로 남아야 나란히 놓을 수 있다.
- 압박을 유도한 상태로 운영 실험을 겹쳐 돌리지 않는다. 같은 스토리지를 쓰는 다른 실험이 있으면 시간을 나눈다.
- 지연이 올랐다는 관측만으로 원인을 호스트 메모리로 확정하지 않는다. 같은 구간의 CPU 와 스토리지 지표를 함께 놓고 §86 의 분류로 계층을 가른다.
- 압박의 허용 범위를 정하는 일은 이 물음 밖이다. 여기서는 경로가 이어지는지만 본다.
- 이 실험을 순서에서 앞당기지 않는다. §84 의 권장 실험 순서에서 압박 실험은 열두 단계 가운데 열 번째이고, 앞의 다섯 단계가 호스트와 가상 머신 조건을 채워 두므로 먼저 돌리면 세 구간에 남길 조건을 그때 다시 모아야 한다.
## 선택지
### 1. 세 구간을 한 번에 돌리고 네 종류 지표를 같이 남긴다
baseline, 압박, 압박 해제 세 구간에서 게스트 지연 · 호스트 vmstat · 스토리지 지연 · CPU 를 같은 시각에 기록한다. §83 OQ-7 이 적은 순서 그대로이고, 세 구간이 한 표에 들어가면 지연 변화가 어느 지표와 함께 움직였는지 그 표에서 읽힌다.
압박을 유도하는 방법을 먼저 정해야 하고, 게스트에 같은 부하를 세 번 거는 준비가 필요하다.
### 2. 압박 없이 baseline 만 먼저 여러 번 잰다
같은 워크로드로 게스트 지연을 몇 번 재서 평상시 변동 폭을 먼저 잡는다. 압박 구간의 지연 변화가 그 변동 폭 안인지 밖인지 가릴 기준이 생긴다.
이 순서만으로는 이 물음이 닫히지 않는다. 압박 구간이 없으면 확인할 경로가 시작되지 않기 때문이다.
### 3. 호스트 swappiness 를 바꿔 가며 지연을 비교한다 — 제외
압박을 유도하는 대신 설정을 바꿔 스왑이 도는 정도를 조절하는 방법이다. 지금 필요한 답은 이 구성에서 경로가 이어지는지 하나인데, 설정을 바꾸면 baseline 과 압박 구간이 서로 다른 구성에서 나온 값이 된다. 설정을 어떻게 둘지는 경로가 이어진다는 것이 확인된 뒤의 결정이다.
## 다음 검증
1. baseline 구간에서 게스트 애플리케이션 지연과 호스트 vmstat 1, 스토리지 지연, CPU 를 같은 시각에 기록한다.
2. 호스트에 메모리 압박을 유도하고, 무엇으로 어느 정도의 압박을 얼마나 오래 걸었는지 함께 적는다.
3. 압박 구간에서 같은 네 가지를 다시 기록한다. 게스트 쪽 스왑은 「지금 게스트와 호스트에서 swap 이 실제로 오가고 있는가」와 같은 방법으로 찍는다.
4. 압박을 걷고 세 번째 구간을 같은 방법으로 찍는다.
5. 세 구간 모두에 호스트 · VM · 워크로드 조건을 남긴다.
닫는 조건 : 압박 구간에서 호스트 reclaim 이나 스왑이 돌았는데도 게스트 지연이 baseline 과 다르지 않으면, 이 구성에서는 그 정도의 압박이 게스트까지 오지 않는다고 적고 닫는다. 지연이 오르면 세 구간 표가 Case 가 되고 이 물음은 그 Case 가 닫는다. 압박을 어디까지 허용할지 — swappiness 를 바꿀지, 가상 머신 배치를 바꿀지, ballooning 을 쓸지 — 는 그 Case 가 나온 뒤 Decision 으로 넘긴다.
@@ -0,0 +1,102 @@
---
id: 50730203-df5f-4604-8535-7e93ab6fb724
kind: QUESTION
slug: host-thp-policy
title: 이 호스트의 THP 정책과 huge page 상태는 무엇인가
topic: memory-virtualization
topicName: 메모리 가상화
project: virtualization
status: 초안
questionStatus: OPEN
studio: "https://hyeonworks.com/studio/documents/50730203-df5f-4604-8535-7e93ab6fb724/edit"
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
source:
- final/document.md#83-실제-환경에서-확인할-open-question-oq-4
- final/document.md#53-thp-transparent-huge-pages
- final/document.md#56-thp와-hugetlb-비교
---
# 이 호스트의 THP 정책과 huge page 상태는 무엇인가
THP(Transparent Huge Pages, 투명한 대형 페이지)는 커널이 조건이 맞는 메모리 영역에 Huge Page 를 알아서 활용하려는 기능이다. 그 활용 범위를 정하는 정책 값이 커널과 배포판, 호스트 설정에 따라 다르다 보니, 개념 문서는 값을 적지 않고 실제 시스템에서 읽으라고만 했다.
이 물음은 그 값을 읽는 데서 끝난다. 정책이 always 인지 madvise 인지 never 인지, 그리고 huge page 항목 넷의 값이 호스트와 두 게스트에 대해 적히면 닫힌다.
§83 이 적은 열린 물음 열넷 가운데 확인 명령이 함께 적힌 것은 아홉이고 OQ-4 가 그 아홉에 든다. 명령이 없는 다섯은 전부 구간을 나눠 견주는 실험이고, 이 물음은 명령 두 줄의 출력으로 닫힌다.
## 관계
- **Huge Page 를 VM 에서 볼 때 갈라지는 세 자리**
게스트 단계와 호스트 backing 을 따로 읽어야 한다는 설명이 이 물음을 호스트와 게스트로 나눠 찍는 이유다.
- **가상 머신 RAM 이 HugeTLB 로 명시적으로 backing 되어 있는가**
여기서 읽은 HugePages_Total 이 그 물음의 입력이 된다.
- **메모리 실험 결과에는 Host · VM · Workload 조건을 함께 남긴다**
THP policy 가 그 기준이 요구하는 호스트 조건 목록에 들어 있다.
## 사실
- §53 은 THP 를 Linux 가 가능한 메모리 영역에 Huge Page 를 투명하게 활용하려는 기능으로 적었다.
애플리케이션이 일반 malloc()/mmap() 을 부르면 커널이 조건이 맞을 때 Huge Page 활용을 시도하는 경로다.
- §53 은 상태 확인 명령으로 cat /sys/kernel/mm/transparent_hugepage/enabled 를 들고 출력 예로 always [madvise] never 를 적었다.
대괄호가 붙은 값이 지금 정책이다.
- §53 은 현재 정책이 커널과 배포판, 호스트 설정에 따라 다르므로 실제 시스템에서 확인한다고 밝혔다.
값을 문서에서 옮겨 올 수 없다는 뜻이고, 이 물음이 있는 이유다.
- §56 은 호스트 확인 명령으로 두 줄을 들었다.
grep -i huge /proc/meminfo
cat /sys/kernel/mm/transparent_hugepage/enabled
- §56 은 AnonHugePages 와 HugePages_Total 이 같은 뜻이 아니라고 못 박았다.
같은 표에서 THP 는 커널이 알아서 하는 활용, HugeTLB 는 명시적으로 잡아 두는 풀로 갈라 적었다.
- §83 OQ-4 는 확인할 것으로 다섯을 적었다.
THP policy
AnonHugePages
HugePages_Total
HugePages_Free
Hugepagesize
- 이 호스트에서 그 다섯을 읽은 기록이 개념 문서에 없다. 게스트 쪽 값도 없다.
## 가정
- 호스트에 붙어 sysfs 파일과 /proc/meminfo 를 읽을 수 있다고 본다.
- 두 명령의 출력이 읽는 시점의 상태라고 본다. 그 뒤에 값이 바뀌지 않는다는 근거는 개념 문서에 없으므로 읽은 시각을 함께 남긴다.
- 게스트 커널도 같은 두 파일을 제공한다고 보고 같은 명령을 게스트에서도 찍는다. 게스트 배포판에 그 파일이 실제로 있는지는 확인하지 않았다.
## 미지수
- 이 호스트의 THP 정책이 always 인지 madvise 인지 never 인지.
- AnonHugePages · HugePages_Total · HugePages_Free · Hugepagesize 의 실제 값.
- HugeTLB 풀이 잡혀 있는지. HugePages_Total 이 0 이면 없는 것이고, 0 이 아니면 그 풀을 누가 쓰는지는 이 물음이 답하지 않는다.
- 두 가상 머신 안에서 같은 다섯 항목이 어떻게 보이는지, 그리고 호스트 값과 다른지.
## 제약
- 값을 읽는 데서 끝낸다. 정책을 바꾸거나 풀을 새로 잡는 일은 이 물음에 들어 있지 않다.
- 호스트와 게스트 값을 한 줄에 합쳐 적지 않는다. §52 가 게스트의 page-size 선택과 호스트 backing 을 하나의 동일한 설정으로 취급하지 말라고 적었다.
- AnonHugePages 와 HugePages_Total 을 같은 값으로 읽지 않는다. §56 이 둘을 구분했다.
- 이 호스트에서 잰 값이 없어 다른 장비의 기본 정책을 이 호스트의 값으로 삼지 않는다.
## 선택지
### 1. 호스트 다섯 항목만 먼저 읽는다
명령 두 줄이면 끝나고, 그 다섯 값이 §85 가 요구하는 호스트 조건 가운데 THP policy 칸을 바로 채운다. 실험을 시작하기 전에 한 번 찍어 두는 용도로 충분하다.
게스트 쪽 정책은 남는다. HugeTLB 쪽으로 넘어갈 때 게스트 값을 다시 받으러 붙어야 한다.
### 2. 호스트와 두 게스트를 한 번에 읽는다
같은 두 명령을 호스트에서 한 번, 각 게스트에서 한 번씩 찍어 세 벌을 만든다. §52 가 요구한 구분이 값으로 남고, 게스트가 madvise 인데 호스트가 always 인 것 같은 차이도 세 벌을 나란히 놓으면 바로 보인다.
게스트에 접속해야 하고, 두 가상 머신이 같은 시각에 켜져 있어야 한다.
### 3. 배포판 기본값을 적어 두고 넘어간다 — 제외
정책 값을 조사하는 대신 배포판이 보통 쓰는 기본값을 적는 방법이다. §53 이 정책은 커널과 배포판, 호스트 설정에 따라 다르니 실제 시스템에서 확인하라고 적었으므로, 이 방법으로는 이 물음이 닫히지 않는다.
## 다음 검증
1. 호스트에서 cat /sys/kernel/mm/transparent_hugepage/enabled 를 찍고 대괄호가 붙은 값을 정책으로 적는다.
2. 같은 시각에 호스트에서 grep -i huge /proc/meminfo 를 찍어 AnonHugePages · HugePages_Total · HugePages_Free · Hugepagesize 를 그대로 옮긴다.
3. 같은 두 명령을 가상 머신마다 따로 찍고 호스트 값과 나란히 적는다.
4. 실행한 명령과 출력, 찍은 시각을 함께 증거로 남긴다.
닫는 조건 : 다섯 항목의 값이 호스트와 각 게스트에 대해 적히면 닫는다. HugePages_Total 이 0 이 아니면 그 풀을 가상 머신이 쓰고 있는지는 「가상 머신 RAM 이 HugeTLB 로 명시적으로 backing 되어 있는가」가 받는다. 정책이 always 로 나오고 지연에 민감한 워크로드를 돌리고 있으면 §54 가 든 compaction 영향을 측정할지는 Decision 으로 넘긴다. 이 물음 자체는 정책 값을 확정하는 데서 끝난다.
@@ -0,0 +1,91 @@
---
id: 380d4975-3be5-451d-aede-2e24c5a2a09e
kind: QUESTION
slug: numa-remote-access-vs-workload-latency
title: NUMA remote access 가 이 작업의 지연을 실제로 바꾸는가
topic: memory-virtualization
topicName: 메모리 가상화
project: virtualization
status: 초안
questionStatus: OPEN
studio: "https://hyeonworks.com/studio/documents/380d4975-3be5-451d-aede-2e24c5a2a09e/edit"
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
source:
- final/document.md#83-실제-환경에서-확인할-open-question-oq-12
- final/document.md#74-local-memory와-remote-memory
- final/document.md#77-guest-numa
---
# NUMA remote access 가 이 작업의 지연을 실제로 바꾸는가
NUMA(Non-Uniform Memory Access, 비균일 메모리 접근)는 어느 CPU 가 어느 RAM 에 접근하느냐에 따라 비용이 달라질 수 있는 구조다. 배치가 어긋나 있다는 것과 그 어긋남 때문에 느려진다는 것은 다른 확인이다.
개념 문서는 remote access 가 local access 와 같은 비용이라고 가정할 수 없다고까지 적었는데, 얼마나 다른지는 적지 않았다. 단순 토폴로지만 보고 성능 문제라고 단정하지 말라는 단서도 같은 항목에 달렸다. 이 물음은 이 호스트에서 같은 워크로드를 local 배치와 remote 위주 배치로 각각 돌려 두 값을 견주는 데까지 간다.
§83 의 열린 물음 열넷 가운데 둘은 이 주제로 올리지 않고 CPU 가상화 쪽 물음에 합쳤다. 노드 수와 vCPU 배치는 한 번 읽으면 닫히고 그 물음이 이미 같은 것을 묻고 있어서다. 이 물음은 합치지 않았는데, 토폴로지를 아는 것과 지연이 달라지는 것이 한 번의 측정으로 함께 닫히지 않기 때문이다.
## 관계
- **NUMA 에서는 vCPU 배치와 메모리 배치를 함께 본다**
local 배치와 remote 배치가 무엇으로 갈리는지를 그 개념이 설명한다.
- **QEMU 메모리는 vCPU 가 도는 NUMA node 와 같은 node 에 있는가**
지금 배치가 어떤지는 그 물음이 먼저 적는다. 이 실험은 그 위에서 배치를 일부러 바꿔 견준다.
- **이 호스트의 NUMA 토폴로지는 가상 머신 성능을 고려해야 할 구조인가**
node 가 하나면 이 실험은 열리지 않는다.
- **메모리 실험 결과에는 Host · VM · Workload 조건을 함께 남긴다**
두 구간 모두에 남길 조건을 그 기준이 정한다.
## 사실
- remote access 는 local access 와 동일한 비용이라고 가정할 수 없으며, 추가 지연과 대역폭 비용이 있을 수 있다.
- 큰 가상 머신에서는 게스트에게 NUMA 토폴로지 자체를 노출할 수 있고, 가능하면 게스트가 인식하는 토폴로지와 실제 호스트 배치가 합리적으로 맞물리도록 구성할 수 있다. 개념 문서가 든 예는 노드마다 vCPU 여덟과 RAM 32 GiB 를 둔 가상 머신이고, 이 프로젝트의 가상 머신이 그 크기인지는 적혀 있지 않다.
- 개념 문서가 적은 실험 순서는 local 배치로 baseline 을 잡고 지연과 처리량과 메모리 지표를 받은 뒤, remote 위주 배치로 바꿔 동일 워크로드를 다시 돌려 견주는 것이다.
- 같은 항목이 NUMA node 가 2개 이상인 경우에만 우선순위를 높이라고 적었다.
- 단순 토폴로지만 보고 성능 문제라고 단정하지 않는다는 단서도 같은 항목에 있다.
- 개념 문서는 이 비교에 쓸 워크로드도, 지연과 처리량을 재는 명령도 적지 않았다. 실험 순서만 적혀 있다.
- 이 호스트에서 두 배치를 각각 구성해 본 기록이 없다.
## 가정
- 호스트가 다중 NUMA node 이고 배치를 두 가지로 만들 수 있다고 보고 실험을 짠다.
- 같은 워크로드를 두 번 돌렸을 때 배치 말고 다른 조건이 바뀌지 않는다고 전제한다.
- 두 구간의 차이가 측정 오차보다 크면 배치 때문이라고 읽는다. 오차 범위를 이 호스트에서 재 본 적은 없어서, 그 범위는 baseline 을 여러 번 돌려 정해야 한다.
- Keycloak 처럼 지금 쓰고 있는 작업이 메모리 접근에 민감한지는 확인하지 않았고, 민감하지 않으면 두 배치의 차이가 지표에 나오지 않을 수 있다.
## 미지수
- local 배치와 remote 위주 배치에서 같은 워크로드의 지연과 처리량이 다른지, 다르다면 얼마나 다른지.
- 이 가상 머신들이 게스트에게 NUMA 토폴로지를 노출한 구성인지. 개념 문서에 적혀 있지 않다.
- 어떤 워크로드로 견줄지. 개념 문서는 동일 워크로드라고만 적었다.
- remote 위주 배치를 이 환경에서 어떤 방법으로 만드는지.
- 차이가 나왔을 때 그것이 이 프로젝트가 실제로 돌리는 작업에서도 같은 크기로 나타나는지.
## 제약
- node 가 하나로 확정되면 우선순위를 낮추고 이 실험을 열지 않는다.
- 지금 배치가 적히기 전에는 열지 않는다. 무엇이 local 이고 무엇이 remote 위주인지 정하려면 현재 분포가 먼저 있어야 한다.
- 두 구간 모두에 Host · VM · Workload 조건을 남긴다. 조건 없이 남긴 비교는 다른 환경에서 다시 쓰기 어렵다.
- 토폴로지만 보고 결론을 적지 않는다. 두 구간의 값이 없으면 이 물음은 닫히지 않는다.
## 선택지
### 1. 같은 워크로드를 두 배치에서 돌려 값을 견준다
local 배치에서 지연과 처리량과 메모리 지표를 받고, remote 위주로 바꿔 같은 워크로드를 같은 조건으로 다시 돌린다. 개념 문서가 적은 순서 그대로이고, 차이가 나든 나지 않든 그 값이 다음 판단의 근거가 된다.
배치를 바꾸는 방법을 먼저 정해야 하고, 두 실행 사이에 다른 조건을 고정하는 데 손이 간다.
### 2. 지금 배치 그대로 지표만 받아 둔다 — 제외
배치를 건드리지 않고 현재 상태에서 지연과 처리량을 재 두자는 방법이다.
비교할 다른 배치가 없어서 그 값이 높은지 낮은지 판단할 근거가 없다. 개념 문서가 단순 토폴로지만 보고 단정하지 말라고 한 것도 이 때문이다.
## 다음 검증
1. node 수와 현재 배치가 각각 적힌 뒤에 연다.
2. local 배치로 맞춘 구성에서 같은 워크로드를 돌려 지연과 처리량과 메모리 지표를 기록한다.
3. remote 위주 배치로 바꾸고 같은 워크로드를 같은 조건으로 다시 돌린다.
4. 두 구간 모두에 Host · VM · Workload 조건을 남긴다.
5. 배치를 바꾸는 데 쓴 방법과 지표를 받은 명령을 함께 적는다. 개념 문서에 없는 것이라 실행한 쪽이 정한다.
닫는 조건 : 두 배치의 지연과 처리량이 측정 오차 안에서 다르지 않으면 이 워크로드에서는 NUMA locality 가 우선순위가 아니라고 적고 닫는다. 차이가 나면 그 비교표가 Case 가 되고, vCPU pinning 과 memory binding 을 운영 구성으로 채택할지는 그 Case 가 나온 뒤 Decision 으로 넘긴다. 단일 NUMA node 로 나오면 개념 문서가 적은 대로 우선순위를 낮추고 닫는다.
@@ -0,0 +1,100 @@
---
id: 1b59e6de-16f1-42b2-81bb-01d6198e34bf
kind: QUESTION
slug: qemu-memory-numa-placement
title: QEMU 메모리는 vCPU 가 도는 NUMA node 와 같은 node 에 있는가
topic: memory-virtualization
topicName: 메모리 가상화
project: virtualization
status: 초안
questionStatus: OPEN
studio: "https://hyeonworks.com/studio/documents/1b59e6de-16f1-42b2-81bb-01d6198e34bf/edit"
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
source:
- final/document.md#83-실제-환경에서-확인할-open-question-oq-11
- final/document.md#75-vcpu와-numa의-연결
- final/document.md#76-vcpu-pinning만으로는-numa-최적화가-끝나지-않는다
- final/document.md#78-numa는-실제-장비-topology부터-확인한다
---
# QEMU 메모리는 vCPU 가 도는 NUMA node 와 같은 node 에 있는가
NUMA(Non-Uniform Memory Access)는 어느 CPU 가 어느 RAM 에 닿느냐에 따라 접근 비용이 달라지는 구조를 말한다. 게스트의 vCPU 는 호스트에서 QEMU 의 vCPU 스레드이고, 그 스레드를 어느 논리 CPU 에 올릴지는 호스트 스케줄러가 정한다. 그 가상 머신의 메모리를 실제로 떠받치는 호스트 쪽 페이지가 어느 노드에 있는지는 그것과 따로 정해진다. 둘이 다른 노드로 갈리면 게스트 안에서는 평범한 memory load 로 보이는 동작이 실제 하드웨어에서는 노드 사이 interconnect 를 건넌다.
이 물음이 받는 것은 그 어긋남이 성능을 바꾸는지가 아니다. 이 호스트의 가상 머신마다 vCPU 배치와 메모리 배치가 지금 어디에 놓여 있는지를 값으로 적는 데까지다.
## 관계
- **NUMA 에서는 vCPU 배치와 메모리 배치를 함께 본다**
두 배치를 왜 따로 볼 수 없는지를 그 개념이 설명한다.
- **이 호스트의 NUMA 토폴로지는 가상 머신 성능을 고려해야 할 구조인가**
노드가 몇 개인지는 그 물음이 받는다. 다중 노드라는 답이 나온 뒤에 이 물음을 연다.
- **NUMA remote access 가 이 작업의 지연을 실제로 바꾸는가**
여기서 어긋남을 찾으면 그것이 지연까지 바꾸는지는 그쪽이 받는다.
- **메모리 실험 결과에는 Host · VM · Workload 조건을 함께 남긴다**
배치를 적을 때 함께 남길 조건을 그 기준이 정한다.
## 사실
- 게스트 vCPU 는 호스트에서 QEMU 의 vCPU 스레드이고, 그 스레드는 호스트 Linux 스케줄러를 거쳐 호스트 논리 CPU 에서 실행된다.
- 어떤 가상 머신의 vCPU 스레드가 Node 0 의 CPU 에서 도는데 그 가상 머신의 호스트 쪽 physical backing page 가 Node 1 에 있으면 remote access 가 생길 수 있다. 게스트 안에서는 그것도 단순한 memory load 로 보인다.
- vCPU 를 특정 노드의 CPU 에 pinning 해도 메모리가 다른 노드에 주로 배치되어 있으면 pinning 이후에도 remote memory access 가 많아질 수 있다. 그래서 vCPU 배치와 메모리 배치를 함께 본다.
- 개념 문서가 이상적인 예로 든 구성은 한 가상 머신의 vCPU 넷이 Node 0 의 CPU 에 붙고 그 가상 머신의 메모리 backing 도 Node 0 RAM 인 경우다.
- 개념 문서는 이 확인에 쓸 명령을 적어 두었다.
QEMU 프로세스별 메모리 분포 : numastat -p <QEMU_PID>
vCPU 배치 : virsh vcpupin <VM_NAME> 와 virsh vcpuinfo <VM_NAME>
장비 토폴로지 : lscpu 와 numactl --hardware
- 같은 문서의 확인 항목은 numastat 결과를 vCPU 배치와 나란히 놓고 vCPU 와 메모리가 같은 노드인지 다른 노드인지 가르라고 적었다.
- 같은 문서는 vCPU 배치를 따로 묻는 항목도 두고, 그 확인을 CPU 가상화 SSOT 의 pinning/overcommit 관측과 연결한다고 적었다. 그래서 이 주제는 그 물음을 다시 세우지 않았다. vCPU 배치는 CPU 가상화 쪽 물음이 받고 여기서는 메모리 분포와 둘의 어긋남을 받는다.
- 개념 문서의 권장 실험 순서도 vCPU placement 확인을 여덟째에, QEMU NUMA memory distribution 확인을 아홉째에 두어 앞뒤를 갈라 놓았다.
- 이 호스트에서 numastat 을 QEMU 프로세스에 돌린 기록이 없다. vCPU 배치를 적어 둔 기록도 없다.
## 가정
- 호스트가 다중 NUMA 노드라고 보고 이 실험을 짠다. 노드 수 자체는 CPU 가상화 쪽 물음이 받는다.
- 두 가상 머신이 모두 떠 있는 동안 QEMU 프로세스를 가상 머신마다 가려낼 수 있다고 본다.
- 관측하는 동안 vCPU 스레드가 다른 노드의 CPU 로 옮겨 다니지 않는다고 전제한다. pinning 이 걸려 있지 않으면 이 전제가 깨질 수 있고, 옮겨 다니는지는 CPU 가상화 쪽에서 따로 묻고 있다.
- numastat 이 보고하는 노드별 분포가 그 가상 머신의 게스트 RAM backing 을 대표한다고 본다. QEMU 프로세스에는 게스트 RAM 말고 다른 할당도 들어 있다.
## 미지수
- 각 가상 머신의 QEMU 메모리가 노드별로 얼마씩 나뉘어 있는지.
- 그 분포가 한 노드로 모여 있는지, 두 노드에 걸쳐 있는지.
- vCPU 스레드가 도는 노드와 메모리가 몰린 노드가 같은지 다른지.
- 두 가상 머신이 같은 노드를 함께 쓰고 있는지.
- 지금 구성에서 memory binding 을 걸 수 있는지. 개념 문서는 두 배치를 함께 보라고만 적었고 이 환경에서 binding 을 바꾸는 방법은 적지 않았다.
## 제약
- 노드 수를 여기서 다시 재지 않는다. 단일 노드로 확정되면 이 물음은 이 환경에 적용되지 않는다.
- 배치를 바꾸는 일은 이 물음에 들어 있지 않다. 지금 놓여 있는 상태를 적는 것까지다.
- 두 값은 같은 시각에 받아 적는다. 시각이 어긋나면 스케줄러가 vCPU 스레드를 옮긴 뒤의 배치를 앞서 찍은 메모리 분포와 견주게 된다.
- 이 호스트에서 잰 값이 없으므로 다른 장비의 NUMA 배치를 근거로 삼지 않는다.
## 선택지
### 1. 가상 머신마다 메모리 분포와 vCPU 배치를 한 번에 받아 적는다
QEMU 프로세스마다 numastat -p 를 돌리고, 같은 시각에 virsh vcpuinfo 와 virsh vcpupin 으로 vCPU 배치를 받아 두 값을 한 표에 넣는다. 어긋남이 있으면 그 표에 그대로 드러나고, 없으면 없다는 것도 같은 표로 남는다.
### 2. 부하를 준 상태에서 한 번 더 받는다
idle 상태의 배치만 보면 메모리가 아직 실제로 할당되지 않은 구간을 볼 수 있다. 게스트에서 workload 를 돌린 뒤 같은 두 값을 다시 받으면 실제로 쓰이는 메모리가 어느 노드에 잡히는지까지 나온다.
실행이 두 번으로 늘고 어느 workload 를 쓸지 먼저 정해야 하는데, 그 선택이 결과를 바꾸기 때문에 workload 조건을 함께 적는다.
### 3. 게스트 안에서 numactl 로 확인한다 — 제외
게스트에서도 NUMA 를 볼 수 있으니 게스트 안에서 확인하자는 방법이다.
게스트가 보는 토폴로지는 가상 머신에 노출된 것이고 이 물음이 찾는 것은 호스트 쪽 physical backing 이 어느 노드에 있느냐이므로, 게스트 쪽 값으로는 그 배치를 알 수 없다.
개념 문서는 큰 가상 머신에서 게스트에게 NUMA 토폴로지 자체를 노출하고 게스트 노드와 호스트 배치가 대응되도록 구성할 수 있다고 적어 두었다. 이 프로젝트의 가상 머신이 그런 구성인지는 그 문서에 없어서, 게스트 값이 호스트 배치를 어디까지 반영하는지도 재기 전에는 모른다.
## 다음 검증
1. 호스트가 다중 NUMA 노드라는 확인을 CPU 가상화 쪽 물음에서 먼저 받는다.
2. 가상 머신마다 QEMU 프로세스를 찾아 numastat -p <QEMU_PID> 로 노드별 메모리 분포를 찍는다.
3. 같은 시각에 virsh vcpuinfo <VM_NAME> 와 virsh vcpupin <VM_NAME> 로 vCPU 배치를 적는다.
4. 두 값을 가상 머신마다 한 표로 나란히 놓고, 노드가 같은지 갈리는지 적는다.
5. Host · VM · Workload 조건을 같은 기록에 남긴다.
닫는 조건 : 노드별 메모리 분포와 vCPU 배치가 가상 머신마다 한 표로 적히면 닫는다. 둘이 같은 노드로 모여 있으면 개념 문서가 경고한 어긋남이 이 환경에는 없다고 적고 닫는다. 어긋나 있으면 그 표가 Case 가 되고, 그 어긋남이 지연까지 바꾸는지는 「NUMA remote access 가 이 작업의 지연을 실제로 바꾸는가」가 받는다. memory binding 을 걸지 말지는 그 뒤 Decision 으로 넘긴다. 단일 NUMA 노드로 나오면 이 물음은 이 환경에 적용되지 않는다고 적고 닫는다.
@@ -0,0 +1,111 @@
---
id: fa5b3782-9fcf-4113-8f51-a55c8023b2db
kind: QUESTION
slug: qemu-resident-memory-distribution
title: QEMU 프로세스가 실제로 붙잡고 있는 호스트 메모리는 얼마이고 어떻게 나뉘어 있는가
topic: memory-virtualization
topicName: 메모리 가상화
project: virtualization
status: 초안
questionStatus: OPEN
studio: "https://hyeonworks.com/studio/documents/fa5b3782-9fcf-4113-8f51-a55c8023b2db/edit"
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
source:
- final/document.md#83-실제-환경에서-확인할-open-question-oq-3
- final/document.md#40-qemu는-guest-ram을-어떻게-준비하는가
- final/document.md#41-kvm_set_user_memory_region
- final/document.md#42-configured-memory와-실제-physical-ram-사용량은-같지-않을-수-있다
---
# QEMU 프로세스가 실제로 붙잡고 있는 호스트 메모리는 얼마이고 어떻게 나뉘어 있는가
게스트 RAM 은 QEMU(Quick Emulator, 가상 머신을 실행하는 호스트 userspace 프로그램) 프로세스의 주소 공간 안에 마련된다고 §40 이 적었다. 그래서 「이 가상 머신이 호스트 메모리를 얼마나 쓰고 있는가」는 그 프로세스가 지금 얼마나 큰지를 읽는 물음이 된다.
이 물음은 실행 중인 가상 머신마다 QEMU 프로세스가 호스트에 얼마나 resident 한지, 그 값이 설정한 메모리(configured memory)와 얼마나 벌어져 있는지, 그리고 그 backing 이 anonymous 인지 huge page 인지를 적는다. 이 호스트에서 그 값을 읽은 기록은 없다.
## 관계
- **Guest 메모리 주소가 물리 RAM 에 닿기까지 — GVA · GPA · HPA**
QEMU 가 마련한 호스트 주소 공간이 게스트 물리 주소와 어떻게 이어지는지를 그 기록이 설명한다.
- **이 호스트의 가상 머신들은 설정한 메모리와 지금 잡고 있는 메모리가 얼마나 다른가**
같은 접속에서 값을 받는다. 그쪽이 적는 설정 값을 옆에 놓아야 여기서 벌어짐을 적을 수 있다.
- **가상 머신 RAM 이 HugeTLB 로 명시적으로 backing 되어 있는가**
여기서 huge page 항목이 보이면 그 물음으로 넘긴다.
- **메모리 실험 결과에는 Host · VM · Workload 조건을 함께 남긴다**
여기서 읽는 값은 VM 묶음의 메모리 backing 설정과 짝이 되므로 그 기준을 따라 남긴다.
## 사실
§40 은 QEMU 를 호스트 userspace 프로세스로 두고, QEMU 자신도 호스트 가상 주소 공간을 가지며 게스트 RAM 을 떠받치는 메모리도 그 안에 마련된다고 적었다. QEMU 가 물리 주소 X 부터 8 GiB 를 달라고 RAM 하드웨어를 직접 제어하는 것은 아니라고 같은 절이 못 박았다.
같은 절은 QEMU 메모리도 일반 호스트 프로세스 메모리처럼 관리된다고 적었다. QEMU 의 호스트 가상 주소에서 호스트 페이지 테이블을 지나 호스트 물리 주소로 간다.
§41 은 QEMU 가 마련한 호스트 userspace 메모리 영역이 게스트 GPA 의 어느 범위를 떠받치는지를 KVM 에 등록한다고 적고, 대표 ioctl 로 KVM_SET_USER_MEMORY_REGION 을 들었다. 역할은 셋으로 갈린다. QEMU 는 게스트 RAM 을 위한 호스트 userspace backing 을 내주고, KVM 은 게스트의 메모리 영역과 가상화 매핑을 관리하며, 실행 중의 주소 변환은 CPU 가 한다.
§42 는 설정한 메모리와 게스트가 현재 실제 사용하는 메모리, 호스트에서 현재 resident 한 물리 메모리 셋이 같지 않을 수 있다고 적었다. 그 차이를 만드는 것으로 호스트의 demand paging, backing 종류, HugeTLB, memory locking, preallocation, overcommit 정책을 들었다.
§83 의 OQ-3 은 확인 명령으로 ps -ef | grep qemu 와 ps -o pid,rss,vsz,cmd -p <QEMU_PID> 를 적었다. 필요하면 cat /proc/<QEMU_PID>/status 와 cat /proc/<QEMU_PID>/smaps_rollup 을 보고, 설정한 메모리와 RSS/anonymous/huge-page 상태를 비교하라고 했다.
§42 를 이 호스트에서 재는 물음은 §83 에 둘로 나뉘어 있다. 설정 값과 게스트 사용량은 virsh 와 게스트 안에서 읽는 OQ-2 가 받고, 호스트 프로세스가 실제로 얼마나 붙잡고 있는지는 OQ-3 인 이 물음이 받는다. 어디서 읽는지가 달라 한 물음으로 묶지 않았다. §84 의 권장 실험 순서도 가상 머신 설정 값 확인과 게스트 free/meminfo 확인을 둘째·셋째에 두고 QEMU RSS/HVA backing 상태 확인을 넷째에 두었다.
ps 가 내는 rss 는 RSS(Resident Set Size, 프로세스가 지금 호스트 물리 메모리에 올려 둔 크기)이고 vsz 는 VSZ(Virtual Size, 그 프로세스 가상 주소 공간의 크기)다. 개념 문서는 두 열의 뜻을 따로 풀어 적지 않았다.
## 가정
실행 중인 가상 머신마다 QEMU 프로세스를 하나씩 찾을 수 있다고 전제한다. §40 이 QEMU 를 호스트 프로세스로 서술했지만 이 호스트에서 프로세스 목록을 확인한 기록은 없다.
ps -ef | grep qemu 의 출력에서 가상 머신 이름을 읽어 프로세스와 가상 머신을 짝지을 수 있다고 전제한다. 명령줄에 그 이름이 실려 있지 않으면 짝짓기를 다른 방법으로 해야 한다.
smaps_rollup 을 읽을 권한이 있다고 전제한다. 다른 사용자의 프로세스면 항목이 비어 나올 수 있다.
값을 한 번 찍으면 그 시점의 상태로 충분하다고 전제한다. resident 크기는 게스트가 메모리를 더 건드리면 올라가기 때문에, 한 번 찍은 값은 그 순간의 크기다.
## 미지수
각 가상 머신의 QEMU 프로세스가 지금 호스트에서 얼마나 resident 한가.
그 프로세스의 가상 주소 공간 크기는 얼마이고 resident 크기와 얼마나 차이가 나는가.
두 값이 그 가상 머신에 설정한 메모리와 얼마나 벌어져 있는가.
그 backing 이 anonymous 인가 huge page 인가.
## 제약
이 호스트에서 QEMU 프로세스의 크기를 읽은 값이 없다. §40 의 8 GiB 와 §42 의 16 GiB 는 설명을 위한 예시이므로 이 환경의 값으로 옮겨 쓰지 않는다.
설정한 메모리를 옆에 놓지 않으면 벌어짐을 적을 수 없다. 그 값은 「이 호스트의 가상 머신들은 설정한 메모리와 지금 잡고 있는 메모리가 얼마나 다른가」가 받는다.
§42 가 게스트 사용량과 호스트 resident 를 다른 값으로 갈라 놓았기 때문에, resident 크기 하나로는 게스트가 그 메모리를 지금 쓰고 있는지 알 수 없다.
huge page 항목이 보이더라도 그것이 THP(Transparent Huge Pages, 커널이 조건이 맞는 메모리 영역에 큰 페이지를 자동으로 쓰는 기능)로 붙은 것인지 HugeTLB 로 명시 구성된 것인지는 여기서 닫지 않는다. 그 구분은 HugeTLB backing 을 묻는 물음이 받는다.
## 선택지
### 1. ps 두 명령만으로 먼저 표를 채운다
ps -ef | grep qemu 로 프로세스를 찾고 ps -o pid,rss,vsz,cmd -p <QEMU_PID> 로 가상 머신마다 한 줄을 적는다. 명령이 둘뿐이라 짧고, 설정 값과 견주는 데 필요한 숫자는 이 실행에서 다 나온다.
backing 이 anonymous 인지 huge page 인지는 나오지 않는다. 그 항목이 필요해지면 한 번 더 붙어야 한다.
### 2. status 와 smaps_rollup 까지 같은 실행에서 읽는다
§83 이 「필요하면」으로 둔 두 명령을 처음부터 함께 돌려 anonymous 와 huge page 항목까지 한 번에 적는다. HugeTLB 쪽 물음으로 넘길지 여부가 이 실행에서 정해진다.
출력이 길어서 어느 값을 표에 옮길지 미리 정해 두어야 한다. 정하지 않으면 파일만 쌓이고 표는 채워지지 않는다.
### 3. 게스트 안에서 본 사용량으로 대신한다 — 제외
게스트의 free -h 를 읽어 그 값을 호스트가 잡고 있는 크기로 삼자는 방법이다. 접속 한 번으로 끝난다.
§42 가 게스트 사용량과 호스트 resident 를 서로 다른 값으로 갈라 놓았으므로 한쪽으로 다른 쪽을 대신할 수 없다. 게스트 사용량은 설정한 메모리와 지금 쓰는 메모리를 묻는 물음이 이미 받고 있다.
## 다음 검증
1. ps -ef | grep qemu 로 실행 중인 QEMU 프로세스의 PID 를 찾고 가상 머신 이름과 짝짓는다 (§83 OQ-3).
2. 프로세스마다 ps -o pid,rss,vsz,cmd -p <QEMU_PID> 를 찍는다.
3. cat /proc/<QEMU_PID>/status 와 cat /proc/<QEMU_PID>/smaps_rollup 을 남겨 anonymous 와 huge page 항목을 읽는다.
4. 같은 접속에서 설정한 메모리와 지금 쓰는 메모리를 묻는 물음의 값을 옆에 놓고 벌어짐을 적는다.
5. 남기는 조건은 메모리 실험 조건 기준을 따른다.
닫는 조건 : 가상 머신마다 설정 값 · RSS · VSZ 와 anonymous/huge-page 구성이 한 표에 적히면 닫는다. RSS 가 설정 값에 크게 못 미치면 §42 가 말한 차이를 이 호스트에서 확인한 것이 되므로, 주소 변환 개념 기록의 확인 사례로 넣는다. huge page backing 이 보이면 HugeTLB backing 을 묻는 물음으로 넘긴다.
@@ -0,0 +1,99 @@
---
id: d3e8501f-e1b5-4f8f-aa9a-89f9c89a308e
kind: QUESTION
slug: swap-activity-in-guest-and-host
title: 지금 게스트와 호스트에서 swap 이 실제로 오가고 있는가
topic: memory-virtualization
topicName: 메모리 가상화
project: virtualization
status: 초안
questionStatus: OPEN
studio: "https://hyeonworks.com/studio/documents/d3e8501f-e1b5-4f8f-aa9a-89f9c89a308e/edit"
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
source:
- final/document.md#83-실제-환경에서-확인할-open-question-oq-6
- final/document.md#63-swap-used만-보고-장애를-판단하면-안-된다
- final/document.md#61-guest-swap과-host-swap
---
# 지금 게스트와 호스트에서 swap 이 실제로 오가고 있는가
Swap Used 가 2 GiB 로 나와도 그것이 지금 메모리 압박이 심하다는 뜻은 아니다. 그 값이 과거에 swap-out 된 cold page 때문에 커져 있을 수도 있어서, §63 은 값이 얼마인가 대신 지금 swap-in/out 이 지속되는가를 물으라고 적었다. 이 물음은 그 질문을 이 호스트와 두 게스트에서 같은 시각에 던진다. 게스트 swap 과 호스트 swap 은 서로 다른 경로이므로 한쪽만 보고 다른 쪽을 짐작하지 않는다.
## 관계
- **Host 메모리 압박이 Guest 와 스토리지까지 내려가는 경로**
게스트 swap 과 호스트 swap 이 왜 다른 경로인지를 그 개념이 설명한다.
- **VM 에서 page fault 는 세 계층에서 따로 일어난다**
호스트 쪽 swap-in 은 호스트 page fault 로 시작하고, 그 계층이 게스트 page fault 와 다르다.
- **호스트 메모리 압박이 게스트 애플리케이션 지연을 실제로 밀어 올리는가**
압박을 유도하는 실험은 여기서 잰 평상시 상태를 기준값으로 쓴다.
- **호스트 major fault 와 스토리지 지연은 같은 시간축에서 함께 움직이는가**
§63 이 함께 보라고 든 넷 가운데 major fault 와 storage latency 를 그 물음이 받는다.
- **메모리 증상 하나로 계층을 단정하지 않는다**
Swap Used 값 하나로 판정하지 않는다는 것이 그 기준의 사례다.
## 사실
- §63 은 Swap Used = 2 GiB 라는 값만으로 지금 메모리 압박이 심하다고 단정할 수 없다고 적었다. 과거에 swap-out 된 cold page 가 남아 있을 수도 있기 때문이다.
- §63 은 더 중요한 질문으로 넷을 들었다.
현재 swap-in/out 이 지속되는가
reclaim pressure 가 증가하는가
major fault 가 증가하는가
storage latency 가 같이 증가하는가
- §63 은 게스트와 호스트를 동시에 확인해야 한다고 적고 확인 명령으로 free -h 와 vmstat 1 을 들었다.
- §83 OQ-6 도 게스트와 호스트 양쪽에 같은 두 명령을 적고, swap-used 값 하나보다 지금의 swap-in/out 활동과 메모리 압박을 함께 보라고 했다.
- §61 은 게스트 swap 을 게스트 애플리케이션에서 시작해 게스트 메모리 압박, 게스트 커널, 게스트 swap, /dev/vda, virtio-blk, QEMU 를 거쳐 호스트 스토리지로 내려가는 경로로 그렸다.
- §61 은 호스트 swap 을 게스트 RAM 이 QEMU 의 메모리 backing 을 거쳐 호스트 메모리 압박을 받고 호스트 커널이 호스트 swap 으로 내리는 경로로 그렸다. 같은 절이 두 경로를 같지 않다고 적고, 게스트가 메모리 여유가 있어 보이는데 호스트에서 swap 이나 reclaim 이 심할 수도 있다고 덧붙였다.
- 개념 문서는 vmstat 출력에서 어느 열을 swap-in 과 swap-out 으로 읽는지 적지 않았다.
- 이 호스트와 두 게스트에서 free -h 나 vmstat 를 찍은 기록이 개념 문서에 없다.
## 가정
- 호스트와 두 게스트에 동시에 접속해 같은 구간을 관측할 수 있다고 본다.
- 관측하는 동안 실험용 부하를 따로 걸지 않고 평상시 상태를 재는 것으로 둔다. 그 상태가 이 호스트의 대표적인 상태인지는 한 번의 관측으로 알 수 없다.
- 게스트와 호스트의 시계가 맞아 두 기록을 같은 시간축에 놓을 수 있다고 전제한다.
- 두 가상 머신 모두 swap 영역을 갖고 있다고 보고 게스트 쪽을 읽는다. 설정에 swap 이 없으면 게스트 쪽 관측은 그 사실로 끝난다.
## 미지수
- 관측 구간에서 swap-in 과 swap-out 이 실제로 오가는지, 오간다면 게스트 쪽인지 호스트 쪽인지 둘 다인지.
- Swap Used 가 0 이 아니라면 그 값이 과거에 내려간 cold page 때문인지 지금 진행 중인 swap 활동 때문인지.
- §63 이 든 나머지 셋 가운데 reclaim pressure 와 major fault 를 이 환경에서 어떤 값으로 읽는지. 그 둘을 읽는 명령은 개념 문서에 없다.
- 관측을 얼마나 오래 해야 지속 여부를 말할 수 있는지. 개념 문서는 vmstat 1 만 적고 관측 길이를 적지 않았다.
## 제약
- 게스트 값으로 호스트 상태를 말하거나 그 반대로 말하지 않는다. §61 이 두 경로를 갈라 놓았다.
같은 문서의 결론도 게스트 하나의 free -h 만 보고 메모리 상태를 판단하지 않고 Guest, QEMU, Host, NUMA, Storage 로 이어지는 영향을 같은 시간축에서 관측해야 한다고 적었다.
- Swap Used 값 하나를 결론으로 적지 않는다. §63 이 그 판단을 막았다.
- 부하를 걸어 압박을 만드는 일은 이 물음에 들어 있지 않다. 그 실험은 「호스트 메모리 압박이 게스트 애플리케이션 지연을 실제로 밀어 올리는가」가 받는다.
§84 의 권장 실험 순서가 이미 그렇게 갈라 놓았다. 게스트와 호스트 vmstat 동시 관측이 여섯째이고 memory pressure 실험은 열째, swap 과 storage latency 를 견주는 일은 열한째다. 이 물음은 여섯째까지다.
- 게스트는 가상 머신마다 따로 찍는다. 한 대의 값을 다른 한 대에 옮기지 않는다.
## 선택지
### 1. 게스트와 호스트에서 같은 구간을 동시에 관측한다
세 곳에서 free -h 를 한 번 찍어 기준을 남기고, vmstat 1 을 같은 시각에 시작해 같은 길이로 돌린다. §61 이 갈라 놓은 두 경로가 같은 시간축의 값으로 남아 한쪽만 움직이는 경우와 둘 다 움직이는 경우를 구분할 수 있다.
세 곳에 동시에 붙어야 하고 관측 길이를 먼저 정해야 한다.
### 2. 호스트만 먼저 관측한다
호스트에서 swap 이 전혀 오가지 않으면 §61 의 두 경로 중 호스트 쪽은 지금 문제가 아니라고 그 관측 하나로 정한다. 접속이 한 곳이라 반복해서 찍기 쉽다.
게스트 쪽 swap 은 호스트 값에 드러나지 않는다. 게스트 커널이 /dev/vda 로 내리는 경로라 호스트에서는 스토리지 I/O 로만 보인다. 그래서 게스트를 다시 찍어야 한다.
### 3. Swap Used 값만 세 곳에서 받아 적는다 — 제외
free -h 한 번씩으로 끝내는 방법이다. §63 이 그 값만으로 판단하지 말라고 적은 것이 이 물음의 출발점이므로 이 방법으로는 닫히지 않는다.
## 다음 검증
1. 호스트와 각 게스트에서 free -h 를 한 번씩 찍어 그 시점의 Swap Used 를 기준으로 남긴다.
2. 세 곳에서 vmstat 1 을 같은 시각에 시작해 미리 정한 길이만큼 돌리고, 출력에서 swap-in 과 swap-out 으로 읽은 열의 이름을 함께 적는다.
3. 같은 구간에서 §63 이 든 나머지 셋 가운데 읽을 수 있는 것 — reclaim pressure · major fault · storage latency — 을 어떤 명령으로 읽었는지와 함께 남긴다.
4. 세 기록을 같은 시간축에 놓고 어느 쪽에서 무엇이 움직였는지 적는다.
닫는 조건 : 관측 구간 내내 게스트와 호스트 모두 swap-in 과 swap-out 이 0 이면 지금은 swap 이 오가지 않는다고 적고 닫는다. Swap Used 가 0 이 아니어도 그렇게 적는다. 한쪽에서 swap 이 오가면 §61 의 두 경로 중 어느 쪽인지를 적고, 그 구간의 스토리지 지연과 게스트 지연을 함께 적어 「호스트 메모리 압박이 게스트 애플리케이션 지연을 실제로 밀어 올리는가」로 넘긴다.
@@ -0,0 +1,87 @@
---
id: 63aef96b-03ef-4dbd-9ba6-9e61c6e695e1
kind: QUESTION
slug: virtio-balloon-configured
title: 이 가상 머신들에 virtio-balloon 이 붙어 있는가
topic: memory-virtualization
topicName: 메모리 가상화
project: virtualization
status: 초안
questionStatus: OPEN
studio: "https://hyeonworks.com/studio/documents/63aef96b-03ef-4dbd-9ba6-9e61c6e695e1/edit"
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
source:
- final/document.md#83-실제-환경에서-확인할-open-question-oq-8
- final/document.md#65-virtio-balloon-구조
- final/document.md#64-ballooning이-필요한-이유
---
# 이 가상 머신들에 virtio-balloon 이 붙어 있는가
virtio-balloon 은 게스트 커널 쪽 드라이버와 QEMU 쪽 장치가 virtqueue 로 맞물려 동작한다. 장치가 없거나 게스트 쪽 드라이버가 올라와 있지 않으면 balloon target 을 바꾸는 실험 자체가 성립하지 않으므로, 동적 메모리 회수를 다루기 전에 이 확인이 먼저다. 개념 문서는 확인 명령까지만 적고 이 호스트의 설정은 읽지 않았다.
## 관계
- **virtio-balloon 이 Guest 메모리를 회수하고 돌려주는 방식**
드라이버와 장치가 무엇을 주고받는지를 그 개념이 설명한다. 이 물음은 그 구성이 이 호스트에 있는지만 본다.
- **balloon target 을 바꾸면 게스트가 쓸 수 있는 메모리는 어떻게 따라 움직이는가**
장치가 붙어 있다는 답이 나와야 그 실험이 시작된다.
- **이 호스트의 가상 머신들은 설정한 메모리와 지금 잡고 있는 메모리가 얼마나 다른가**
두 물음이 virsh dommemstat 을 같이 읽으므로 한 번 찍어 나눠 쓴다.
## 사실
- §64 는 호스트가 QEMU 의 게스트 RAM backing 을 볼 수 있지만 게스트 내부에서 어떤 메모리가 중요한지 완전히 알지 못한다고 적었다.
게스트는 자기 메모리를 애플리케이션 working set · JVM heap · page cache · free 로 구분해 알고 있다.
- §64 는 그래서 호스트가 무작정 게스트 backing 을 swap-out 하기보다 게스트 커널과 협력해 불필요한 메모리를 돌려받는 편이 유리할 수 있다고 적고, 대표적인 메커니즘으로 virtio-balloon 을 들었다.
- §65 는 그 구조를 게스트 커널 아래 virtio-balloon 드라이버와 virtqueue, VM 경계 건너편의 QEMU virtio-balloon 장치, 그리고 호스트 메모리 관리로 그렸다.
- §65 는 virtio-balloon 이 게스트 RAM 자체를 제공하는 장치가 아니라고 못 박았다. 이미 존재하는 게스트 RAM backing 을 호스트와 게스트가 협력해 회수하고 반환하는 데 쓰는 가상 장치다.
- §83 OQ-8 은 확인 명령으로 virsh dumpxml <VM_NAME> 을 들고, 게스트에서도 관련 드라이버나 장치 상태를 확인하라고 적었다.
- §83 OQ-8 은 환경에 따라 드라이버 이름과 표시 방식이 달라질 수 있으므로 실제 장비에서 검증하라는 단서를 달았다.
그래서 개념 문서에는 게스트 쪽에서 무엇을 찾아야 하는지가 이름으로 적혀 있지 않다.
- §83 OQ-2 는 가상 머신 메모리 확인 명령으로 virsh dominfo <VM_NAME> · virsh dumpxml <VM_NAME> · virsh dommemstat <VM_NAME> 셋을 들었다.
- 이 호스트의 가상 머신 설정에서 balloon 관련 요소를 읽은 기록이 개념 문서에 없다.
## 가정
- 두 가상 머신 모두 libvirt 로 정의되어 있어 virsh dumpxml 로 설정을 통째로 읽을 수 있다고 본다.
- 설정에 balloon 장치가 있으면 게스트 쪽에도 대응하는 드라이버가 보인다고 전제한다. 그 전제가 이 게스트 배포판에서 맞는지는 확인하지 않았다.
- 게스트에 접속해 장치 목록이나 커널 모듈 상태를 읽을 수 있다고 본다.
## 미지수
- 각 가상 머신의 libvirt 설정에 balloon 장치가 있는지.
- 있다면 게스트 안에서 그 드라이버가 실제로 올라와 있는지.
- 이 환경에서 그 드라이버나 장치가 어떤 이름으로 보이는지. 개념 문서가 환경마다 다를 수 있다고만 적고 이름을 남기지 않았다.
- 게스트 쪽 상태를 어떤 명령으로 읽는지. 개념 문서가 게스트 확인 명령을 적지 않아 실행하는 쪽이 정한다.
## 제약
- 이 물음에서 balloon target 을 움직이지 않는다. 구성 여부를 적는 것이 전부다.
- 설정에 장치가 있다는 것만으로 동작한다고 적지 않는다. §83 OQ-8 이 게스트 쪽 상태도 확인하라고 적었다.
- 설정과 드라이버가 둘 다 보여도 호스트가 backing 을 실제로 언제 회수하는지는 이 확인으로 알 수 없다. §67 이 정확한 Host-side release 동작은 QEMU/KVM 버전, backing 종류 및 설정에 따라 달라질 수 있다고 적고 그 동작을 단정하지 않았다. 개념 문서가 단정하지 않은 것을 이 물음이 대신 단정하지 않는다.
- 게스트 쪽에서 쓴 명령과 그 출력을 그대로 남긴다. 이름이 환경마다 다르므로 다음 사람이 같은 것을 찾으려면 무엇을 봤는지가 필요하다.
- 장치가 없는 것으로 나와도 그것을 결함으로 적지 않는다. 이 실험 기반에서 ballooning 을 쓰기로 한 기록이 개념 문서에 없다.
§70 은 virtio-mem 같은 다른 동적 memory 관리 방식도 존재하므로 모든 동적 VM memory 관리를 ballooning 하나로 일반화하면 안 된다고 적었다. 그래서 balloon 장치가 없다는 답은 이 환경에 동적 메모리 관리가 없다는 뜻이 아니라 이 장치가 없다는 뜻까지다.
## 선택지
### 1. 설정과 게스트 상태를 한 번에 확인한다
가상 머신마다 virsh dumpxml 을 찍어 balloon 관련 요소를 인용하고, 이어서 각 게스트에서 드라이버나 장치 상태를 읽어 실제로 보이는 이름과 함께 적는다. §83 OQ-8 이 요구한 두 확인이 한 번에 끝난다.
게스트 두 대에 접속해야 하고, 게스트 쪽 확인 명령을 실행하는 쪽이 정해야 한다.
### 2. 호스트 설정만 읽고 넘어간다
virsh dumpxml 두 번으로 끝난다. 설정에 balloon 장치가 없으면 게스트 쪽을 볼 이유가 없어 이 물음이 거기서 닫힌다.
장치가 있는 것으로 나오면 게스트 쪽 드라이버가 올라와 있는지를 확인하러 다시 가야 한다.
## 다음 검증
1. 가상 머신마다 virsh dumpxml <VM_NAME> 을 남기고 balloon 관련 요소를 그대로 인용한다.
2. 각 게스트에서 balloon 관련 드라이버나 장치 상태를 확인하고, 실행한 명령과 실제로 보인 이름을 함께 적는다.
3. 같은 실행에서 virsh dommemstat <VM_NAME> 출력도 받아 남긴다. 「이 호스트의 가상 머신들은 설정한 메모리와 지금 잡고 있는 메모리가 얼마나 다른가」가 같은 출력을 읽는다.
닫는 조건 : 설정과 게스트 쪽 상태가 둘 다 적히면 닫는다. 붙어 있지 않으면 이 환경에서는 ballooning 이 동작하지 않는다고 적고 「balloon target 을 바꾸면 게스트가 쓸 수 있는 메모리는 어떻게 따라 움직이는가」는 열지 않은 채로 둔다. 장치가 없으면 그 실험이 성립하지 않는다. 붙어 있으면 그 물음으로 넘긴다.
@@ -0,0 +1,116 @@
---
id: 67285d51-7eaf-4540-9577-cfd0b273b40e
kind: QUESTION
slug: vm-configured-vs-current-memory
title: 이 호스트의 가상 머신들은 설정한 메모리와 지금 잡고 있는 메모리가 얼마나 다른가
topic: memory-virtualization
topicName: 메모리 가상화
project: virtualization
status: 초안
questionStatus: OPEN
studio: "https://hyeonworks.com/studio/documents/67285d51-7eaf-4540-9577-cfd0b273b40e/edit"
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
source:
- final/document.md#83-실제-환경에서-확인할-open-question-oq-2
- final/document.md#42-configured-memory와-실제-physical-ram-사용량은-같지-않을-수-있다
- final/document.md#57-memory-overcommit
---
# 이 호스트의 가상 머신들은 설정한 메모리와 지금 잡고 있는 메모리가 얼마나 다른가
§42 는 가상 머신에 설정한 메모리와 게스트가 지금 실제로 쓰는 메모리, 그리고 호스트에서 지금 resident 한 물리 메모리를 서로 다른 세 값으로 갈라 놓았다. 이 물음은 그 세 값이 이 호스트에서 각각 얼마인지를 가상 머신마다 적는다.
세 값이 벌어져 있는지에 따라 다음에 무엇을 잴지가 갈린다. 설정한 총량이 호스트 RAM 을 넘으면 §57 이 서술한 overcommit 구성에 이 환경이 들어가고, 넘지 않으면 그 절의 시나리오는 여기 걸리지 않는다. 이 호스트의 물리 RAM 도 각 가상 머신에 설정한 메모리도 개념 문서에 적혀 있지 않다.
## 관계
- **Guest 메모리 주소가 물리 RAM 에 닿기까지 — GVA · GPA · HPA**
설정한 값과 resident 값이 왜 갈리는지를 그 기록이 주소 변환 경로로 설명한다.
- **Host 메모리 압박이 Guest 와 스토리지까지 내려가는 경로**
설정 총량이 호스트 RAM 을 넘는 구성에서 실제 수요가 함께 오를 때 무엇이 이어지는지를 그 기록이 다룬다.
- **QEMU 프로세스가 실제로 붙잡고 있는 호스트 메모리는 얼마이고 어떻게 나뉘어 있는가**
같은 실행에서 값을 받는다. 세 값 가운데 호스트 resident 쪽을 프로세스 단위로 재는 물음이 그쪽이다.
- **메모리 실험 결과에는 Host · VM · Workload 조건을 함께 남긴다**
여기서 남기는 값이 이후 메모리 실험의 VM 묶음이 되므로 그 기준을 따라 적는다.
## 사실
§42 는 가상 머신에 16 GiB 를 설정했다고 해서 모든 일반 구성에서 시작 순간 실제 호스트 RAM 16 GiB 가 반드시 모두 즉시 물리적으로 점유되는 것은 아니라고 적었다.
같은 절은 세 값을 나란히 놓고 서로 같지 않을 수 있다고 밝혔다.
Configured Memory
Guest 가 현재 실제 사용하는 Memory
Host 에서 현재 resident 한 Physical Memory
세 값을 갈라 놓는 요인으로 §42 가 든 것은 호스트의 demand paging, backing 종류, HugeTLB, memory locking, preallocation, overcommit 정책이다. 그래서 가상 머신 RAM 16 GiB 를 호스트 RAM 에서 고정된 연속 16 GiB 로 단순화하면 안 된다고 같은 절이 적었다.
§57 은 게스트에 설정한 메모리 총량이 호스트 물리 RAM 보다 큰 구성이 가능할 수 있는 이유를, 설정 용량과 현재 실제 working set 또는 resident 메모리가 같지 않을 수 있다는 데서 찾았다. 다만 모든 가상 머신의 실제 수요가 동시에 증가하면 문제가 발생한다고 이어 적었다.
§83 의 OQ-2 는 확인 명령을 호스트 쪽과 게스트 쪽으로 나눠 적었다. 호스트에서는 virsh dominfo <VM_NAME> 과 virsh dumpxml <VM_NAME>, virsh dommemstat <VM_NAME> 을 쓰고, 게스트에서는 free -h 와 cat /proc/meminfo 를 쓴 뒤 호스트의 QEMU 프로세스 상태와 비교한다.
이 호스트의 물리 RAM 과 각 가상 머신에 설정한 메모리를 적은 값은 개념 문서 어디에도 없다. §57 의 32 GiB 와 16 GiB, §42 의 16 GiB 는 설명을 위한 예시다.
§85 가 실험마다 함께 남기라고 적은 VM 조건에 Configured RAM 과 Current RAM 이 들어 있다. 이 물음이 내는 값은 여기서 한 번 쓰고 마는 것이 아니라 뒤따르는 메모리 실험의 조건 칸으로 그대로 들어간다.
## 가정
가상 머신들이 libvirt 로 관리되고 있어 virsh 로 값을 읽을 수 있다고 전제한다. §83 이 든 명령이 전부 virsh 인데 이 호스트에서 그 명령을 돌린 기록은 없다.
측정하는 동안 각 가상 머신이 실행 중이라고 전제한다. 꺼져 있으면 현재 메모리와 게스트 사용량이 나오지 않고 설정 값만 읽힌다.
호스트 쪽 세 명령과 게스트 쪽 두 명령을 사실상 같은 시각에 찍을 수 있다고 전제한다. 사이가 벌어지면 세 값이 서로 다른 시점의 상태가 된다.
이 호스트에 있는 가상 머신 전부를 셀 수 있다고 전제한다. 몇 대가 실행 중인지도 개념 문서에 없다.
## 미지수
이 호스트의 물리 RAM 은 얼마인가.
각 가상 머신에 설정한 메모리는 얼마이고, 그 합이 호스트 RAM 을 넘는가.
넘든 넘지 않든, 지금 각 게스트가 실제로 쓰고 있는 메모리는 얼마이고 호스트에서 그 가상 머신 몫으로 resident 한 메모리는 얼마인가.
세 값이 설정 값과 얼마나 벌어져 있는가.
## 제약
이 호스트에서 잰 값이 없다. §42 와 §57 이 든 숫자는 개념을 보이려고 든 예시이므로 이 환경의 값으로 옮겨 쓰지 않는다.
§84 의 권장 실험 순서는 가상 머신 설정 값 확인과 게스트 free/meminfo 확인을 둘째와 셋째로 나눠 적었다. 여기서는 그 둘을 한 물음으로 묶는다. 이 물음이 답할 것이 세 값의 차이라서 호스트 쪽과 게스트 쪽을 다른 시각에 찍으면 그 차이가 어느 시점의 것인지 말할 수 없다.
세 값을 서로 다른 시각에 찍으면 비교가 성립하지 않는다. 게스트 사용량은 workload 가 달라지면 같이 움직인다.
이 물음은 설정한 값과 실제 사용량의 차이를 적는 데까지다. 그 차이가 있을 때 무엇이 일어나는지는 swap 과 호스트 메모리 압박을 다루는 물음들이 받는다.
QEMU 프로세스 쪽 값은 여기서 닫지 않는다. virsh 가 보고하는 값과 프로세스가 실제로 잡고 있는 크기는 다른 관측이고, 그것은 「QEMU 프로세스가 실제로 붙잡고 있는 호스트 메모리는 얼마이고 어떻게 나뉘어 있는가」가 받는다.
## 선택지
### 1. 실행 중인 가상 머신 전부를 한 번에 찍는다
virsh list 로 실행 중인 가상 머신을 세고, 각각에 대해 호스트 세 명령과 게스트 두 명령을 같은 시각에 돌린다. 설정 총량과 호스트 RAM 을 그 자료 하나로 견줄 수 있어서 overcommit 여부가 이 실행에서 정해진다.
가상 머신이 여럿이면 명령 수가 늘어 같은 시각을 지키기 어려워진다. 그럴 때는 호스트 쪽을 먼저 한 번에 돌리고 게스트 쪽을 이어서 돌린 뒤 두 시각을 함께 적는다.
### 2. 가상 머신 하나로 표 모양을 먼저 정하고 나머지로 넓힌다
한 대에 대해 다섯 명령을 돌려 세 값을 어디서 읽는지 확정한 다음 나머지에 같은 순서를 적용한다. 값을 잘못 읽어 표를 다시 만드는 일을 줄인다.
두 번 붙어야 하고, 첫 실행과 두 번째 실행 사이에 게스트 사용량이 달라진다. overcommit 판정에 필요한 설정 값은 그 사이에 바뀌지 않으므로 판정 자체는 갈리지 않는다.
### 3. QEMU 프로세스 값까지 이번에 함께 읽는다 — 제외
호스트에 한 번 붙는 김에 ps 로 QEMU 프로세스의 크기까지 받아 적자는 방법이다. 실행 횟수는 줄어든다.
QEMU 쪽은 별도의 물음이 이미 받고 있고 그쪽은 anonymous 인지 huge page 인지까지 보기 때문에, 여기서 함께 읽으면 어느 물음의 기준으로 닫혔는지가 남지 않는다. 두 물음의 실행 시각을 맞추고 싶으면 같은 접속에서 순서대로 돌리고 각각의 기록에 남긴다.
## 다음 검증
1. virsh list 로 실행 중인 가상 머신 이름을 적는다.
2. 가상 머신마다 호스트에서 virsh dominfo <VM_NAME> 으로 configured/current memory 를, virsh dumpxml <VM_NAME> 으로 memory backing 설정을, virsh dommemstat <VM_NAME> 으로 balloon 계열 값을 남긴다 (§83 OQ-2).
3. 같은 시각에 각 게스트에서 free -h 와 cat /proc/meminfo 를 찍는다.
4. 호스트의 free -h 도 함께 남겨 물리 RAM 과 현재 여유를 적는다.
5. 남기는 조건은 메모리 실험 조건 기준을 따른다. QEMU 프로세스의 resident 값은 QEMU 쪽 물음이 받는다.
닫는 조건 : 설정 값 · 게스트 사용량 · 호스트 resident 세 값을 가상 머신마다 한 표로 적으면 닫는다. 설정 총량이 호스트 RAM 을 넘으면 이 환경이 overcommit 상태라는 사실을 확정한 것이 되고, 그 상태에서 무엇이 일어나는지는 swap 을 묻는 물음과 호스트 메모리 압박을 묻는 물음이 받는다. 넘지 않으면 §57 의 시나리오가 이 환경에 적용되지 않는다.
@@ -0,0 +1,98 @@
---
id: f460e9a2-35ef-46ff-81cf-b36627a1b231
kind: QUESTION
slug: vm-ram-backed-by-hugetlb
title: 가상 머신 RAM 이 HugeTLB 로 명시적으로 backing 되어 있는가
topic: memory-virtualization
topicName: 메모리 가상화
project: virtualization
status: 초안
questionStatus: OPEN
studio: "https://hyeonworks.com/studio/documents/f460e9a2-35ef-46ff-81cf-b36627a1b231/edit"
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
source:
- final/document.md#83-실제-환경에서-확인할-open-question-oq-5
- final/document.md#55-hugetlb
- final/document.md#52-vm에서-huge-page를-볼-때-주의할-점
---
# 가상 머신 RAM 이 HugeTLB 로 명시적으로 backing 되어 있는가
HugeTLB 는 관리자가 미리 잡아 둔 Huge Page pool 을 가상 머신이나 애플리케이션이 명시적으로 지정해 쓰는 방식이다. 이 호스트의 두 가상 머신이 그 방식으로 RAM 을 받고 있는지는 libvirt 설정 한 곳만 봐서는 끝나지 않는다. §83 OQ-5 가 설정을 읽은 뒤 호스트 /proc/meminfo, QEMU smaps 계열과 교차 검증하라고 적었기 때문이다. 세 자료가 서로 맞는지까지 확인해야 이 환경이 어느 쪽으로 backing 되어 있는지가 정해진다.
## 관계
- **Huge Page 를 VM 에서 볼 때 갈라지는 세 자리**
§52 가 나눈 세 단계 가운데 호스트 backing 쪽만 이 물음이 확정한다.
- **이 호스트의 THP 정책과 huge page 상태는 무엇인가**
HugePages_Total 과 HugePages_Free 를 그쪽이 읽는다. 그 값이 이 물음의 교차 검증 재료가 된다.
- **QEMU 프로세스가 실제로 붙잡고 있는 호스트 메모리는 얼마이고 어떻게 나뉘어 있는가**
두 물음이 같은 smaps_rollup 출력을 읽으므로 한 번 찍어 나눠 쓴다.
- **메모리 실험 결과에는 Host · VM · Workload 조건을 함께 남긴다**
memory backing 설정이 그 기준의 VM 조건 목록에 들어 있다.
## 사실
- §55 는 HugeTLB 를 명시적인 Huge Page pool 을 쓸 수 있는 Linux 메커니즘으로 적었다.
THP 는 애플리케이션이 일반 메모리 할당을 하면 커널이 알아서 Huge Page 를 활용하는 경로이고, HugeTLB 는 관리자가 풀을 준비한 뒤 애플리케이션이나 가상 머신이 명시적으로 쓰는 경로다.
- §55 는 물리 RAM 안에 일반 메모리 영역과 2 MiB 단위 HugeTLB Pool 이 나뉘어 있는 그림으로 그 풀을 그렸다.
- §55 는 사전 확보로 예측 가능성을 높일 수 있지만 일반 메모리 할당의 유연성이 감소하는 trade-off 가 있다고 밝혔다.
- §52 는 가상 머신에 translation 단계가 둘이라 "Huge Page 를 사용한다"는 말만으로는 부족하다고 적고 셋을 구분하라고 했다.
게스트 page-table 단계에서 큰 페이지를 쓰는가
호스트 backing 이 Huge Page 인가
EPT mapping 에서 큰 mapping 을 활용하는가
- §52 는 게스트와 호스트의 page-size 선택을 하나의 동일한 설정으로 취급하면 안 된다고 못 박았다.
- §83 OQ-5 는 확인 명령으로 virsh dumpxml <VM_NAME> 을 들고, libvirt memory backing 관련 설정을 확인한 뒤 호스트 /proc/meminfo, QEMU smaps 계열과 교차 검증하라고 적었다.
- §83 OQ-3 이 QEMU 프로세스 쪽 명령으로 cat /proc/<QEMU_PID>/smaps_rollup 을 들었다.
smaps 계열에서 huge page 사용량을 어느 항목 이름으로 읽는지는 개념 문서가 적지 않았다.
- 세 자료 가운데 호스트 쪽은 항목 이름이 적혀 있다. §56 이 호스트 확인 명령을 적으면서 AnonHugePages 와 HugePages_Total 은 같은 의미가 아니라고 못 박았고, §83 OQ-4 도 THP policy · AnonHugePages · HugePages_Total · HugePages_Free · Hugepagesize 를 확인 항목으로 들었다. 이름이 없는 것은 QEMU smaps 쪽 하나다.
- 이 호스트의 libvirt 설정을 읽은 기록이 개념 문서에 없다.
## 가정
- 두 가상 머신 모두 libvirt 로 정의되어 있어 virsh dumpxml 로 설정을 통째로 읽을 수 있다고 본다.
- 설정에 memory backing 관련 요소가 없으면 명시적 HugeTLB backing 을 쓰지 않는 것으로 읽는다. 그 해석이 이 libvirt 버전에서 맞는지는 확인하지 않았다.
- 세 자료를 같은 시각에 찍으면 같은 순간의 상태를 가리킨다고 전제한다.
- QEMU 프로세스 번호를 가상 머신마다 가려낼 수 있다고 본다.
## 미지수
- 각 가상 머신의 libvirt 설정에 memory backing 항목이 있는지, 있다면 HugeTLB 를 가리키는지.
- 있다면 그 요구량이 호스트 HugePages_Total 및 HugePages_Free 와 맞물리는지.
- QEMU smaps 계열에서 huge page 사용량을 어느 항목으로 읽는지. 개념 문서가 항목 이름을 적지 않아 실행하는 쪽이 정한다.
- 설정과 호스트 값이 어긋나는 상태가 이 호스트에 실제로 있는지.
## 제약
- 게스트 안에서 본 huge page 상태를 호스트 backing 의 증거로 쓰지 않는다. §52 가 두 단계를 구분하라고 적었다.
- 세 자료 중 하나만으로 결론을 적지 않는다. OQ-5 가 교차 검증을 지시했다.
- 이 물음에서 풀을 새로 잡거나 backing 설정을 바꾸지 않는다. 현재 상태를 확정하는 것이 전부다.
- §84 의 권장 실험 순서는 THP/HugeTLB 상태 확인을 다섯째 한 단계로 묶었지만 §83 은 호스트 정책을 OQ-4 로, 가상 머신 backing 을 OQ-5 로 갈라 물었다. 앞의 것은 호스트 하나에 대해 한 번 읽는 값이고 뒤의 것은 가상 머신마다 갈릴 수 있는 설정이다. 이 물음이 받는 것은 뒤쪽이고, 앞쪽 값은 「이 호스트의 THP 정책과 huge page 상태는 무엇인가」가 읽어 여기에 교차 검증 재료로 들어온다.
- 개념 문서가 smaps 항목 이름을 적지 않았으므로 실행한 명령과 출력을 그대로 남겨야 다음 사람이 같은 값을 다시 읽는다.
## 선택지
### 1. libvirt 설정부터 읽고 backing 항목이 없으면 거기서 끝낸다
virsh dumpxml 을 두 번 찍는 것으로 시작한다. 두 설정 모두 memory backing 관련 요소가 없으면 명시적 HugeTLB backing 이 아니라는 답이 거의 정해지고, 호스트 HugePages_Total 이 0 인지만 확인하면 닫힌다.
설정에 항목이 있으면 결국 세 자료를 다 받아야 하므로 호스트와 QEMU 쪽을 다시 찍으러 간다.
### 2. 세 자료를 한 번에 찍어 나란히 놓는다
설정과 호스트 /proc/meminfo, QEMU smaps_rollup 을 같은 시각에 받아 나란히 둔다. OQ-5 가 요구한 교차 검증이 한 번의 실행으로 끝나고, 설정과 실제 값이 어긋나는 경우에도 그 어긋남이 같은 시각의 자료로 남는다.
QEMU 프로세스 번호를 먼저 가려내야 하고, 두 가상 머신이 같은 시각에 켜져 있어야 한다.
### 3. 호스트 HugePages_Total 하나로 가른다 — 제외
풀이 잡혀 있지 않으면 어떤 가상 머신도 HugeTLB 로 backing 될 수 없으니 그 값 하나로 끝내자는 방법이다. 0 이 아닌 경우에는 그 풀을 쓰는 것이 이 가상 머신들인지 다른 프로세스인지 가리지 못한다. 그 구분이 이 물음이 답해야 하는 내용이다.
## 다음 검증
1. 가상 머신마다 virsh dumpxml <VM_NAME> 을 남기고 memory backing 관련 요소를 그대로 인용한다.
2. 같은 시각에 호스트에서 grep -i huge /proc/meminfo 를 찍는다.
3. 같은 시각에 각 QEMU 프로세스에 대해 cat /proc/<QEMU_PID>/smaps_rollup 을 찍고, huge page 로 읽은 항목의 이름을 함께 적는다.
4. 세 출력을 나란히 놓고 서로 맞는지, 어긋나면 어느 값이 어떻게 다른지 적는다.
닫는 조건 : 세 자료가 서로 맞으면 이 환경이 어느 쪽으로 backing 되어 있는지를 확정하고 닫는다. 설정에 backing 이 없고 HugePages_Total 도 0 이면 이 가상 머신들은 명시적 HugeTLB backing 을 쓰지 않는다고 적고 닫는다. 그 경우 남는 것은 §54 가 적은 THP 쪽 특성이고, 정책 값은 「이 호스트의 THP 정책과 huge page 상태는 무엇인가」가 읽는다. 설정과 호스트 값이 어긋나면 그 어긋남이 Case 가 된다.
@@ -0,0 +1,120 @@
---
id: 63fa33e5-9957-4d28-8d3c-c75735a73bd6
kind: REFERENCE
slug: memory-symptom-needs-layer-separation
title: 메모리 증상 하나로 계층을 단정하지 않는다
topic: memory-virtualization
topicName: 메모리 가상화
project: virtualization
status: 초안
studio: "https://hyeonworks.com/studio/documents/63fa33e5-9957-4d28-8d3c-c75735a73bd6/edit"
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
source:
- final/document.md#86-문제를-진단할-때의-분류
- final/document.md#63-swap-used만-보고-장애를-판단하면-안-된다
- final/document.md#78-numa는-실제-장비-topology부터-확인한다
- final/document.md#88-결론
- final/document.md#61-guest-swap과-host-swap
- final/document.md#72-guest-oom과-host-oom
---
# 메모리 증상 하나로 계층을 단정하지 않는다
§86 은 메모리 지연이나 OOM(Out Of Memory, 커널이 필요한 메모리를 확보하지 못해 프로세스를 종료할 수 있는 상태)이 보일 때 한 번에 「메모리 부족」이라고 결론내리지 말라고 적었다. 대신 증상을 먼저 다섯 갈래로 가르는 분류를 두었다. Guest Virtual Memory · Virtualization Translation · Host Memory · Dynamic Memory · NUMA 다섯이다. 마지막의 NUMA(Non-Uniform Memory Access, 비균일 메모리 접근)는 어느 CPU 가 어느 RAM 에 접근하느냐에 따라 비용이 달라질 수 있는 구조다.
같은 요구가 개념 문서 안에서 세 번 더 나온다. Swap Used 값 하나로 판단하지 않고(§63), NUMA 최적화를 토폴로지를 재기 전에 정하지 않으며(§78), 게스트 하나의 free -h 만 보고 메모리 상태를 판단하지 않는다(§88). 이 기준은 그 넷을 한 판독 절차로 묶는다. 뒤의 셋을 따로 규칙으로 세우지 않은 것은 보는 대상만 다를 뿐 §86 과 같은 말을 하기 때문이다. 규칙이 세 벌이면 판독하는 사람이 어느 것을 따르는지가 갈린다.
## 관계
- **VM 에서 page fault 는 세 계층에서 따로 일어난다**
Guest Virtual Memory 갈래와 Virtualization Translation 갈래, Host Memory 갈래가 각각 어떤 fault 로 보이는지를 그 기록이 푼다.
- **Host 메모리 압박이 Guest 와 스토리지까지 내려가는 경로**
Host Memory 갈래로 좁힌 뒤에 무엇이 이어지는지를 그 기록이 설명한다.
- **virtio-balloon 이 Guest 메모리를 회수하고 돌려주는 방식**
Dynamic Memory 갈래에서 볼 balloon target 과 Guest pressure 가 그 기록에서 나온다.
- **NUMA 에서는 vCPU 배치와 메모리 배치를 함께 본다**
NUMA 갈래로 좁혔을 때 vCPU placement 와 memory placement 를 왜 따로 읽으면 안 되는지를 그 기록이 설명한다.
- **메모리 실험 결과에는 Host · VM · Workload 조건을 함께 남긴다**
이 기준은 갈래를 좁히는 데까지만 쓰이고, 원인을 확정하는 측정은 그 기준이 요구한 조건과 함께 남긴다.
- **지금 게스트와 호스트에서 swap 이 실제로 오가고 있는가**
2 번 규칙과 3 번 규칙이 요구한 관측을 이 호스트에서 실제로 수행하는 물음이다.
- **게스트 page fault 증가는 workload 변화를 따라가는가**
Guest Virtual Memory 갈래로 좁힌 증상을 워크로드와 견주는 물음이다.
## 목적
이 기준은 증상 하나를 원인으로 바로 옮기는 판독을 막는다. §86 이 든 예가 메모리 지연과 OOM 인데, 둘 다 다섯 계층 어디서든 나올 수 있다.
다섯 갈래를 개념 한 편 안에 넣지 않고 따로 세운 것은 갈래마다 받는 개념이 다르기 때문이다. 어느 한 개념 안에 두면 그 글이 나머지 넷을 가리키지 못한다.
좁히는 것과 확정하는 것을 갈라 둔 이유도 여기 있다. 이 기준은 어느 갈래인지까지만 좁히고, 원인 확정은 §85 의 조건을 함께 남긴 측정이 한다.
## 규칙
### 1. 증상을 다섯 갈래로 먼저 가른다
§86 의 분류는 메모리 지연이나 OOM 을 다섯으로 나누고, 갈래마다 볼 것을 함께 적었다.
Guest Virtual Memory : Page Fault · Guest reclaim · Guest swap · Guest OOM
Virtualization Translation : EPT-related event · TLB pressure · Huge-page/mapping 특성
Host Memory : Host reclaim · Host swap · Host major fault · Host OOM
Dynamic Memory : Balloon target · Guest pressure · Hotplug/virtio-mem 여부
NUMA : vCPU placement · memory placement · remote access
### 2. Swap Used 값 하나로 메모리 압박을 단정하지 않는다
§63 은 Swap Used = 2 GiB 라는 값만으로 지금 메모리 압박이 심하다고 단정할 수 없다고 적었다. 과거에 swap-out 된 cold page 가 남아 있을 수도 있기 때문이다.
같은 절이 대신 물으라고 한 것은 넷이다.
현재 swap-in/out 이 지속되는가?
reclaim pressure 가 증가하는가?
major fault 가 증가하는가?
storage latency 가 같이 증가하는가?
이 넷은 게스트와 호스트를 동시에 확인해야 한다고 §63 이 못박았고, 확인 명령으로 free -h 와 vmstat 1 을 들었다.
### 3. 게스트 스왑과 호스트 스왑을 한 값으로 세지 않는다
§61 은 두 스왑이 서로 다른 경로라고 적었다. 게스트 스왑은 게스트 메모리 압박에서 시작해 게스트 커널과 게스트 스왑을 지나 /dev/vda 로 내려가고, virtio-blk 와 QEMU 를 거쳐 호스트 스토리지에 닿는다. 호스트 스왑은 게스트 RAM 의 QEMU memory backing 이 호스트 메모리 압박을 만나 호스트 커널의 스왑으로 내려가는 경로다.
경로가 이렇게 갈리다 보니, 게스트는 메모리에 여유가 있어 보이는데 호스트에서는 스왑과 reclaim 이 심할 수도 있다.
### 4. OOM 은 발생 계층을 확인한 뒤에 부른다
§72 는 게스트 OOM 과 호스트 OOM 을 갈랐다. 게스트 RAM 이 모자라서 게스트 커널이 프로세스를 죽이면 Keycloak 프로세스 종료 같은 결과가 된다. 호스트 물리 RAM 이 모자라서 호스트 커널이 QEMU 를 victim 으로 고르면 그 VM 전체가 중단될 수 있다.
cgroup memory limit 이 걸린 환경은 예외로 둔다. 호스트 전체 RAM 에 여유가 있어도 그 cgroup 경계에서 OOM 이 발생할 수 있다고 같은 절이 적었다.
### 5. NUMA 갈래는 토폴로지를 잰 뒤에 순위를 매긴다
§78 은 호스트가 NUMA node 한 개면 cross-node remote-memory 문제가 주요 이슈가 아닐 수 있어서, 실제 환경에서는 먼저 토폴로지를 재고 NUMA 최적화가 필요한지 판단하라고 했다.
확인 명령으로는 lscpu 와 numactl --hardware 를 들었다. QEMU 프로세스별 메모리 분포는 numastat -p <QEMU_PID>, vCPU 배치는 virsh vcpupin <VM_NAME> 과 virsh vcpuinfo <VM_NAME> 을 들었다.
### 6. 게스트 하나의 free -h 로 닫지 않는다
§88 은 실제 테스트 서버에서 게스트 하나의 free -h 만 보고 메모리 상태를 판단하지 않는다고 적었다. Guest → QEMU → Host → NUMA → Storage 영향을 같은 시간축에서 관측해야 한다고 했다.
## 적용 조건
메모리 증상을 원인으로 옮기려는 판독 전부에 걸린다. 지연 증가, 스왑 관측, OOM, fault 증가가 여기 들어간다.
가르는 축은 다섯이고, 갈래마다 무엇을 볼지는 1 번 규칙에 적혀 있다.
## 예외
cgroup memory limit 이 걸린 환경에서는 호스트 전체 RAM 에 여유가 있어도 그 경계에서 OOM 이 나기 때문에, Host Memory 갈래를 곧바로 지우면 안 된다(§72).
NUMA node 가 하나인 호스트에서는 NUMA 갈래의 우선순위를 낮춰도 된다고 §78 이 적었다. 그것도 토폴로지를 잰 뒤의 이야기다.
이 기준은 갈래를 좁힐 뿐 원인을 확정하지 않는다. 확정은 §85 의 조건을 남긴 측정이 한다.
## 예시
- 게스트에서 Swap Used 만 보고 「메모리 부족」으로 닫은 판독 : swap-in/out 이 지속되는지를 재지 않았다
- 게스트 free -h 에 여유가 보여 호스트를 보지 않은 판독 : 호스트 reclaim 과 호스트 스왑을 확인하지 않았다
- Keycloak 프로세스가 죽었는데 호스트 커널 로그를 보지 않은 판독 : 게스트 OOM 인지 호스트 OOM 인지 갈리지 않는다
- 호스트 RAM 에 여유가 있다는 이유로 OOM 을 다른 갈래로 넘긴 판독 : cgroup 경계를 확인하지 않았다
- 토폴로지를 재지 않고 vCPU pinning 부터 손댄 조치 : NUMA 갈래가 이 호스트에 걸리는지 모른다
- 게스트와 호스트를 다른 시각에 찍어 한 표로 놓은 기록 : 같은 시간축이 아니다
@@ -0,0 +1,106 @@
---
id: ded41b52-ec08-4231-a54c-86d6c80d38f0
kind: REFERENCE
slug: record-the-conditions-with-every-memory-experiment
title: 메모리 실험 결과에는 Host · VM · Workload 조건을 함께 남긴다
topic: memory-virtualization
topicName: 메모리 가상화
project: virtualization
status: 초안
studio: "https://hyeonworks.com/studio/documents/ded41b52-ec08-4231-a54c-86d6c80d38f0/edit"
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
source:
- final/document.md#85-실험-시-반드시-같이-기록할-것
- final/document.md#84-권장-실험-순서
- final/document.md#83-실제-환경에서-확인할-open-question
---
# 메모리 실험 결과에는 Host · VM · Workload 조건을 함께 남긴다
메모리 측정은 값만 남기면 다음 사람이 그 값을 어디에 쓸 수 있는지 알 수 없다. §85 는 실험마다 함께 적을 조건을 Host 여덟 · VM 일곱 · Workload 다섯으로 못박았다. 이유도 한 줄로 적었는데, 조건을 남기지 않으면 「Memory pressure에서 느려졌다」는 결과를 다른 환경에 재사용하기 어렵다는 것이다.
이 기준은 그 세 묶음을 메모리 측정 기록의 필수 항목으로 둔다. 어느 단계에서 그 값을 얻는지는 §84 의 권장 실험 순서에서 가져왔다. 이 주제의 열린 물음 열둘이 전부 같은 규칙에 걸려서, 조건 목록을 물음마다 되풀이하는 대신 여기 한 편에 두고 각 물음이 가리킨다.
## 관계
- **메모리 증상 하나로 계층을 단정하지 않는다**
증상을 어느 갈래로 좁힐지는 그 기준이 정하고, 좁힌 뒤에 재는 값을 어떤 조건과 함께 남길지는 이 기준이 정한다.
- **Guest 메모리 주소가 물리 RAM 에 닿기까지 — GVA · GPA · HPA**
VM 묶음의 Configured RAM 과 Current RAM 을 왜 따로 적는지가 그 기록의 세 값 구분에서 나온다.
- **Huge Page 를 VM 에서 볼 때 갈라지는 세 자리**
Host 묶음의 THP policy 와 VM 묶음의 Memory backing 설정이 그 기록에서 갈라지는 두 값이다.
- **Host 메모리 압박이 Guest 와 스토리지까지 내려가는 경로**
Host 묶음에 Swap 설정과 Physical storage 가 들어간 이유를 그 기록이 설명한다.
- **호스트 메모리 압박이 게스트 애플리케이션 지연을 실제로 밀어 올리는가**
baseline 과 부하 구간을 비교하는 실험이라 세 묶음을 두 시점에 각각 남겨야 한다.
- **NUMA remote access 가 이 작업의 지연을 실제로 바꾸는가**
Host 묶음의 NUMA topology 를 먼저 적지 않으면 이 물음의 결과를 다른 장비에 옮길 수 없다.
## 목적
이 기준은 두 가지를 막는다. 조건 없이 남긴 측정값을 다른 장비의 판단 근거로 쓰는 일과, baseline 과 부하 구간을 비교해 놓고 두 시점 사이에 무엇이 달라져서 값이 달라졌는지 되짚지 못하는 일이다.
메모리 값은 장비 설정에 크게 기댄다. §53 은 THP(Transparent Huge Pages, 커널이 조건이 맞는 메모리 영역에 큰 page 를 자동으로 쓰는 기능) 정책을 실제 시스템에서 확인하라고 적었다. 정책 값이 커널과 배포판, 호스트 설정에 따라 다르기 때문이다. §78 은 호스트가 NUMA(Non-Uniform Memory Access, 비균일 메모리 접근) node 한 개면 cross-node remote-memory 문제가 주요 이슈가 아닐 수 있다고 적었다. 같은 워크로드를 돌려도 이 두 설정이 다르면 두 측정을 나란히 놓고 읽을 수 없다.
## 규칙
### 1. Host 조건 여덟 항목을 실험마다 적는다
CPU model, Core / Thread 수, RAM, NUMA topology, Swap 설정, Kernel version, THP policy, Physical storage 여덟을 적는다.
이 가운데 넷은 개념 문서가 따로 이유를 적어 두었다. THP policy 는 §53 이 커널과 배포판, 호스트 설정에 따라 다르니 실제 시스템에서 확인하라고 했고, NUMA topology 는 §78 이 최적화가 필요한지 판단하기 전에 먼저 재라고 했다.
Swap 설정과 Physical storage 가 들어간 이유는 §62 가 이어 놓은 경로에 있다. 호스트 메모리 압박이 reclaim 과 스왑을 부르면 그 I/O 가 데이터베이스 I/O, filesystem writeback 과 같은 물리 장치로 몰리기 때문에 애플리케이션 지연까지 올라갈 수 있다.
### 2. VM 조건 일곱 항목을 적되 Configured RAM 과 Current RAM 을 따로 적는다
vCPU, Configured RAM, Current RAM, Memory backing 설정, Balloon device, Guest swap, Guest kernel 일곱이다.
§42 는 configured memory 와 게스트가 지금 실제로 쓰는 메모리, 호스트에서 지금 resident 인 물리 메모리 셋이 같지 않을 수 있다고 적었다. RAM 을 한 값으로 줄여 적으면 나중에 그 숫자가 셋 가운데 무엇이었는지 알 수 없다.
### 3. Workload 조건 다섯 항목을 적는다
Application, Heap/Memory 설정, Request concurrency, DB workload, 측정 시간 다섯이다.
측정 시간은 §88 이 요구한 관측을 가능하게 한다. 게스트와 QEMU, 호스트, NUMA, 스토리지 영향을 같은 시간축에 놓으려면 각 값이 언제 찍혔는지 알아야 하기 때문이다.
### 4. baseline 과 부하 구간을 비교하는 실험은 두 시점 모두에 세 묶음을 남긴다
세 묶음 안에는 실험 중에 달라지는 항목이 들어 있다. Current RAM 이 그렇고(§42), 게스트 스왑과 호스트 스왑도 부하 구간에서만 오갈 수 있다(§61 · §63).
부하 전 값이 없으면 부하 중 값 하나만으로는 그것이 증가인지 원래 그런 값인지 갈리지 않으니, 두 시점을 모두 남긴다.
### 5. 조건이 먼저 채워지는 순서로 실험을 배치한다
§84 의 권장 순서는 호스트 물리 메모리와 NUMA 확인에서 시작한다. 이어서 VM configured memory, Guest free/meminfo, QEMU RSS 와 HVA backing 상태, THP/HugeTLB 상태를 차례로 본다. 압박 실험은 열 번째다.
앞의 다섯 단계가 Host 묶음과 VM 묶음을 그대로 채우기 때문에, 순서를 지키면 압박 실험을 시작할 때 조건을 다시 모으지 않아도 된다.
§84 를 따로 뽑아 기록 한 편으로 만들지는 않았다. 그 순서는 어느 물음을 먼저 여느냐를 정할 뿐이라 순서만 읽을 사람이 없고, 조건이 먼저 채워진다는 뜻만 이 규칙으로 옮겼다.
## 적용 조건
메모리 관련 측정을 남기는 실험 전부에 걸린다. §83 의 OQ-1 부터 OQ-14 까지가 여기 들어가고, 이 주제의 열린 물음 열둘도 같은 규칙을 따른다.
남길 조건은 세 묶음이다.
Host : CPU model · Core/Thread 수 · RAM · NUMA topology · Swap 설정 · Kernel version · THP policy · Physical storage
VM : vCPU · Configured RAM · Current RAM · Memory backing 설정 · Balloon device · Guest swap · Guest kernel
Workload : Application · Heap/Memory 설정 · Request concurrency · DB workload · 측정 시간
## 예외
단일 값을 한 번 읽고 끝나는 확인에는 Workload 묶음이 비어 있어도 된다. THP 정책 문자열 하나를 읽는 §83 OQ-4 가 그렇다.
baseline 과 부하 구간을 비교하는 실험에서는 세 묶음을 두 시점에 각각 남긴다. 한 번만 남기면 비교한 두 시점 가운데 한쪽의 조건이 빈다.
이 프로젝트는 조건 목록을 실제 장비 값으로 채운 적이 없다. §85 가 준 것은 항목 이름이고 값은 아직 없다.
## 예시
- 호스트 RAM 을 적지 않고 남긴 「부하 중 게스트가 느려졌다」 : 다른 장비에서 다시 쓸 수 없다
- THP 정책 문자열 하나만 읽는 확인 : Workload 묶음을 비워 둔다
- Configured RAM 만 적고 Current RAM 을 비운 기록 : §42 가 갈라 놓은 값을 하나로 합친 것이라 다시 재야 한다
- 부하 전 값 없이 부하 중 vmstat 만 남긴 기록 : 증가폭을 적을 수 없다
- 측정 시간을 빼고 게스트 값과 호스트 값을 따로 남긴 기록 : 같은 시간축에 놓을 수 없다
- 권장 순서 1 번부터 5 번까지를 먼저 돌린 뒤 시작한 압박 실험 : Host 묶음과 VM 묶음이 이미 채워져 있다