feat: 가상화 문서들 추가
This commit is contained in:
+336
@@ -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) 노드로 이어진다.
|
||||
|
||||

|
||||
|
||||
위쪽 점선 상자 안에서 끝나는 것이 첫 번째 변환이고, 그 상자를 나온 뒤의 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 -->
|
||||
+179
@@ -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 -->
|
||||
+118
@@ -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 -->
|
||||
+123
@@ -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 에 있는 모양이다.
|
||||
|
||||

|
||||
|
||||
이 호스트가 노드 몇 개인지는 근거 문서에 없다. 하나로 나오면 지금 말한 어긋남은 이 환경에 성립하지 않는다.
|
||||
|
||||
## 큰 가상 머신에서는 게스트에게 토폴로지를 보여 준다
|
||||
|
||||
가상 머신이 커지면 그 안에서도 같은 문제가 생긴다. 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 -->
|
||||
+237
@@ -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 관련 가상화 사건을 적어도 셋으로 갈라 놓는다.
|
||||
|
||||

|
||||
|
||||
그림에서 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 -->
|
||||
+106
@@ -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 버전을 한 번도 적지 않았다. 버전과 설정이 갈라 놓는 동작이라 버전을 적기 전에는 이 환경이 어느 쪽인지 말할 수 없다.
|
||||
|
||||

|
||||
|
||||
## 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 -->
|
||||
+102
@@ -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 으로 넘긴다.
|
||||
+98
@@ -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 가 된다.
|
||||
+101
@@ -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 으로 넘긴다.
|
||||
+112
@@ -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 으로 넘긴다.
|
||||
+102
@@ -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 으로 넘긴다. 이 물음 자체는 정책 값을 확정하는 데서 끝난다.
|
||||
+91
@@ -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 로 나오면 개념 문서가 적은 대로 우선순위를 낮추고 닫는다.
|
||||
+100
@@ -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 노드로 나오면 이 물음은 이 환경에 적용되지 않는다고 적고 닫는다.
|
||||
+111
@@ -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 을 묻는 물음으로 넘긴다.
|
||||
+99
@@ -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 의 두 경로 중 어느 쪽인지를 적고, 그 구간의 스토리지 지연과 게스트 지연을 함께 적어 「호스트 메모리 압박이 게스트 애플리케이션 지연을 실제로 밀어 올리는가」로 넘긴다.
|
||||
+87
@@ -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 을 바꾸면 게스트가 쓸 수 있는 메모리는 어떻게 따라 움직이는가」는 열지 않은 채로 둔다. 장치가 없으면 그 실험이 성립하지 않는다. 붙어 있으면 그 물음으로 넘긴다.
|
||||
+116
@@ -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 의 시나리오가 이 환경에 적용되지 않는다.
|
||||
+98
@@ -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 가 된다.
|
||||
+120
@@ -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 갈래가 이 호스트에 걸리는지 모른다
|
||||
- 게스트와 호스트를 다른 시각에 찍어 한 표로 놓은 기록 : 같은 시간축이 아니다
|
||||
+106
@@ -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 묶음이 이미 채워져 있다
|
||||
Reference in New Issue
Block a user