feat: 가상화 문서들 추가
This commit is contained in:
+197
@@ -0,0 +1,197 @@
|
||||
---
|
||||
id: 5bcae89b-0873-4a2b-af4a-10e5234c085b
|
||||
kind: CONCEPT
|
||||
slug: kvm-vcpu-to-physical-cpu
|
||||
title: KVM 에서 vCPU 가 물리 CPU 에서 실행되기까지
|
||||
topic: cpu-virtualization
|
||||
topicName: CPU 가상화
|
||||
project: virtualization
|
||||
status: 초안
|
||||
basisVersion: x86_64 Intel VMX(VT-x) · Linux KVM(kvm · kvm_intel) · QEMU/libvirt · VM 안의 K3s cgroup CPU limit
|
||||
studio: "https://hyeonworks.com/studio/documents/5bcae89b-0873-4a2b-af4a-10e5234c085b/edit"
|
||||
assets:
|
||||
- key: vm-exit-handling-cycle
|
||||
file: ../../../final/assets/diagrams/vm-exit-handling-cycle/vm-exit-handling-cycle.svg
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#1-이-문서의-범위
|
||||
- final/document.md#2-전체-구조
|
||||
- final/document.md#3-각-구성요소의-역할
|
||||
- final/document.md#4-vcpu와-vcpu-thread
|
||||
- final/document.md#5-host-linux-scheduler와-실제-cpu
|
||||
- final/document.md#6-kvm_run과-guest-실행
|
||||
- final/document.md#7-vm-entry와-vm-exit
|
||||
- final/document.md#8-무엇이-실제로-vm-exit을-발생시키는가
|
||||
- final/document.md#9-vm-exit-이후-처리
|
||||
- final/document.md#10-guest가-idle이면-물리-cpu는-어떻게-되는가
|
||||
- final/document.md#11-vm의-4-vcpu는-정확히-무엇을-의미하는가
|
||||
- final/document.md#12-cpu-contention과-overcommit
|
||||
- final/document.md#13-steal-time
|
||||
- final/document.md#14-실제-linux에서-확인할-수-있는-것
|
||||
- final/document.md#15-cpu-가상화-관점에서-장애를-보는-방법
|
||||
- final/document.md#16-현재-keycloak-k3s-실험과의-관계
|
||||
- final/document.md#18-bare-metal-k3s와-vm-기반-k3s의-차이
|
||||
- final/document.md#19-이-ssot에서-파생될-concept
|
||||
- final/document.md#22-현재-단계의-핵심-claim
|
||||
- final/document.md#24-cpu-가상화-계층에서-발생할-수-있는-문제
|
||||
- final/document.md#25-cpu-문제를-계층별로-구분하는-진단표
|
||||
- final/document.md#26-현재-keycloak-실험에서-cpu-문제를-오판하지-않기-위한-기준
|
||||
- final/document.md#28-concept-->-open-question-->-case-적용-기준
|
||||
---
|
||||
|
||||
# KVM 에서 vCPU 가 물리 CPU 에서 실행되기까지
|
||||
|
||||
`virsh` 로 가상 머신을 시작하면 그 뒤로 CPU 를 쓰는 쪽은 QEMU 프로세스다. vCPU 하나마다 QEMU vCPU 스레드가 하나씩 있다. 그 스레드를 호스트 Linux 스케줄러가 다른 호스트 스레드와 나란히 놓고 논리 CPU 에 배치한다. 게스트 코드는 그 스레드가 `KVM_RUN` 을 호출한 뒤 VM Entry 를 지나 물리 CPU 에서 직접 실행된다. 하이퍼바이저가 개입해야 하는 조건을 만나면 VM Exit 으로 KVM 에 제어권이 넘어간다. Keycloak 멀티 노드 실험을 가상 머신 두 대 위에서 돌리다 보니, 이 경로를 알아 두면 게스트 안의 지연을 애플리케이션·저장소 문제와 호스트 자원 문제로 갈라 볼 수 있다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **pinning 없이 vCPU 스레드는 호스트 논리 CPU 사이를 옮겨 다니는가**
|
||||
이 글은 vCPU 스레드가 호스트 스케줄러에 따라 다른 논리 CPU 에서 실행될 수 있다고만 적었다. 그 이동을 `PSR` 로 실제로 재는 질문이다.
|
||||
- **실제 작업에서 주로 발생하는 VM Exit 은 무엇인가**
|
||||
무엇이 VM Exit 을 부르는지는 여기 적었지만, 어떤 작업에서 어떤 이유의 Exit 이 얼마나 나오는지는 이 호스트에서 세지 않았다.
|
||||
- **가상 머신 두 대에 동시에 부하를 주면 이 호스트에서 vCPU 경쟁이 실제로 생기는가**
|
||||
경쟁과 초과 할당은 예시 숫자로 설명했다. 이 테스트 환경의 논리 CPU 수와 두 가상 머신의 vCPU 합은 SSOT 에 적혀 있지 않다.
|
||||
- **cgroup CPU 제한과 호스트 vCPU 경쟁을 지표로 가를 수 있는가**
|
||||
진단표는 두 원인을 다른 행으로 갈라 놓았다. 그 구분이 지표로도 갈리는지는 같은 크기의 지연을 두 원인으로 각각 재현해야 안다.
|
||||
- **운영 서버는 CPU 가상화 계층의 영향을 받는 구조인가**
|
||||
진단표의 어느 행부터 보는지가 이 답에 달려 있다.
|
||||
- **이 호스트의 NUMA 토폴로지는 가상 머신 성능을 고려해야 할 구조인가**
|
||||
이 글은 NUMA 를 진단표의 한 행으로만 두고 메모리 배치는 범위 밖으로 미뤘다. 단일 NUMA node 면 이 실험에서 우선순위를 낮추고, 다중이면 vCPU 와 메모리 배치를 따로 확인한다.
|
||||
|
||||
## 본문
|
||||
|
||||
<!-- body:start -->
|
||||
|
||||
## virsh 뒤에서 가상 머신을 실행하는 프로세스
|
||||
|
||||
`virsh` 는 libvirt 기반 가상 머신을 관리하는 명령줄 도구다. `virsh start ubuntu-vm` 을 실행하면 명령이 libvirt 로 전달되고 libvirt 가 QEMU 프로세스를 실행한다. 명령 전달이 끝나면 `virsh` 자체는 종료될 수 있고, 가상 머신을 실제로 실행하는 QEMU 프로세스는 계속 살아 있다.
|
||||
|
||||
libvirt 는 가상 머신의 수명 주기와 구성을 관리하는 계층이다. 가상 머신 정의에는 RAM 8 GiB, vCPU 4 같은 값이 들어 있다. libvirt 는 그 정의를 바탕으로 QEMU 를 적절한 옵션과 함께 실행하고 관리한다.
|
||||
|
||||
QEMU 는 호스트의 사용자 공간에서 실행되는 프로세스다. 4 vCPU 가상 머신이라면 메인·제어 스레드 하나와 vCPU 스레드 넷이 만들어진다. KVM 가속을 쓸 때 게스트의 일반 CPU 명령을 QEMU 가 하나씩 소프트웨어로 번역해 실행하는 경로가 핵심은 아니다. vCPU 스레드가 KVM 을 통해 게스트 실행을 요청하면 게스트 코드는 VMX 를 이용해 실제 CPU 에서 직접 실행된다.
|
||||
|
||||
`/dev/kvm` 은 프로세스가 아니라 Linux 가 사용자 공간 프로그램에 KVM API 를 노출하는 문자 장치(character device) 인터페이스다. QEMU 는 이 파일을 열고 `ioctl` 로 `KVM_CREATE_VM`, `KVM_CREATE_VCPU`, `KVM_SET_USER_MEMORY_REGION`, `KVM_RUN` 같은 동작을 요청한다.
|
||||
|
||||
커널 안쪽은 두 층으로 갈린다. KVM Core 가 CPU 제조사에 독립적인 공통 가상화 로직을 맡고, 제조사별 구현은 `kvm_intel` 과 `kvm_amd` 로 나뉜다. `kvm_intel` 은 Intel CPU 환경에서 KVM 이 Intel 의 하드웨어 가상화 기능을 쓸 수 있게 하는 커널 모듈이다. AMD 환경에서는 `kvm_amd` 가 SVM 을 쓴다.
|
||||
|
||||
VMX(Virtual Machine Extensions)는 Intel CPU 자체가 제공하는 하드웨어 가상화 기능이어서 프로세스도 커널 모듈도 아니다. VMX 는 실행 영역을 호스트·하이퍼바이저 쪽인 VMX Root Operation 과 게스트 쪽인 VMX Non-Root Operation 으로 구분한다. 여기서 Root 는 Linux 의 `root` 사용자와 무관하다. 게스트 Linux 에서 `root` 권한으로 프로그램을 실행하더라도 게스트 전체는 VMX 관점에서 여전히 Non-Root Operation 에서 실행된다.
|
||||
|
||||
## vCPU 하나마다 QEMU 스레드 하나
|
||||
|
||||
가상 머신에 4 vCPU 를 설정하면 게스트 OS 는 그것을 자신의 CPU 처럼 인식한다. 호스트에는 각 vCPU 의 실행 주체에 대응하는 QEMU vCPU 스레드가 하나씩 존재한다. 4 vCPU 는 게스트 OS 가 최대 4개의 CPU 실행 흐름을 가질 수 있도록 4개의 가상 CPU 실행 컨텍스트를 제공한다는 의미에 가깝다. 호스트의 물리 CPU 4개가 그 가상 머신으로 영구히 넘어가지는 않는다.
|
||||
|
||||
vCPU 스레드가 실행될 논리 CPU 를 특정 집합으로 제한하는 설정을 CPU pinning 이라고 한다. pinning 도 별도의 CPU 격리도 하지 않았다면 vCPU 스레드는 호스트 Linux 스케줄러가 스케줄링한다. 호스트가 6 Core / 12 Thread 라면 Linux 에서는 일반적으로 12개의 논리 CPU 가 스케줄링 대상으로 보인다. vCPU 스레드도 Chrome 스레드나 Java 스레드와 마찬가지로 그 12개 위에 배치된다. 그래서 시간에 따라 같은 스레드가 서로 다른 논리 CPU 에서 실행될 수도 있다. 다만 이 호스트에서 그 이동을 재지는 않았다. 스레드가 최근 실행된 논리 CPU 를 알려 주는 `PSR` 값을 시간에 따라 여러 번 보면 확인되는데, 그 관찰을 아직 돌리지 않았다.
|
||||
|
||||
호스트 CPU 가 부족하면 QEMU vCPU 스레드와 호스트의 다른 작업이 같은 논리 CPU 자원을 두고 경쟁한다.
|
||||
|
||||
## KVM_RUN 이 게스트 실행을 시작한다
|
||||
|
||||
QEMU 의 vCPU 스레드가 게스트 vCPU 를 실행하려면 KVM 에 `KVM_RUN` 을 요청한다.
|
||||
|
||||
```c label="vCPU thread 가 Guest 실행을 요청하는 호출"
|
||||
ioctl(vcpu_fd, KVM_RUN, 0);
|
||||
```
|
||||
|
||||
KVM 은 이 요청을 받아 VM Entry 로 CPU 가 게스트 실행을 시작하거나 재개하게 한다. 이 상태에서 `ADD`, `MOV`, `SUB`, `CMP`, `JMP` 같은 게스트의 일반 명령은 실제 CPU 에서 직접 실행된다. 명령 하나마다 QEMU 까지 돌아갔다가 다시 실행하는 구조가 아니다.
|
||||
|
||||
## 무엇이 VM Exit 을 부르는가
|
||||
|
||||
VM Exit 은 가상 머신 종료가 아니다. CPU 가 VMX Non-Root 에서 게스트를 실행하다가 하이퍼바이저가 개입해야 하는 조건을 만나면, 게스트 실행에서 빠져나와 VMX Root 쪽 KVM 으로 제어권을 넘긴다. QEMU 가 종료되지도, 가상 머신 메모리가 지워지지도 않는다. 필요한 처리가 끝나면 다시 VM Entry 를 통해 게스트 실행을 이어 간다.
|
||||
|
||||
무엇을 가로챌지는 Intel VMX 의 VMCS(Virtual Machine Control Structure)에 있는 VM-Execution Control 등이 정한다. 특권 명령이면 전부 VM Exit 이라거나 `root` 가 실행하면 VM Exit 이라는 규칙은 맞지 않는다. Exit 여부는 VMX 설정과 해당 동작의 종류에 따라 정해진다.
|
||||
|
||||
| 무엇이 실행되나 | 가로채는 설정과 그 이유 |
|
||||
|---|---|
|
||||
| `HLT` 계열 | 게스트 커널이 유휴 경로로 들어갔다. `HLT` exiting 을 쓰면 KVM 이 vCPU 를 재울 수 있다 |
|
||||
| `IN` · `OUT` | I/O port 접근을 QEMU 의 가상 장치 동작으로 처리하도록 설정할 수 있다 |
|
||||
| `CPUID` | 게스트에게 보여 줄 CPU 모델과 기능이 호스트 CPU 와 달라 CPU 정보를 가상화한다 |
|
||||
| `CR0` · `CR3` · `CR4` 접근 | VMX 설정으로 어떤 접근을 가로챌지 고른다. 모든 접근이 Exit 을 부르지는 않는다 |
|
||||
| `RDMSR` · `WRMSR` | 특정 MSR(Model-Specific Register) 접근을 가로채도록 설정했을 때 발생한다 |
|
||||
| 예외 | Exception Bitmap 설정에 따라 게스트가 직접 처리할 수도, 하이퍼바이저가 가로챌 수도 있다 |
|
||||
| 외부 인터럽트 | 호스트가 처리해야 할 물리 인터럽트가 들어왔고 인터럽트 설정이 그렇게 돼 있다 |
|
||||
|
||||
`CR3` 처럼 페이지 테이블과 관련된 레지스터는 게스트 커널도 자주 쓴다. 그런 접근까지 전부 가로채면 Exit 이 늘어나기 때문에 현대 가상화에서는 불필요한 Exit 을 줄이는 쪽으로 설정한다.
|
||||
|
||||
## Exit 을 KVM 이 처리할 때와 QEMU 까지 돌아갈 때
|
||||
|
||||
VM Exit 이 발생하면 KVM 은 먼저 Exit Reason 을 확인한다. KVM 이 커널 안에서 처리할 수 있는 Exit 은 처리한 뒤 바로 게스트로 재진입한다. 사용자 공간에서 가상 장치를 흉내 내야 할 때처럼 사용자 공간 처리가 필요할 때만 `KVM_RUN` 이 반환된다. 그러면 QEMU 가 필요한 가상 장치 동작을 처리한 뒤 다시 `KVM_RUN` 을 호출해 게스트 실행을 재개한다.
|
||||
|
||||

|
||||
|
||||
VM Exit 자체는 정상적인 가상화 동작이어서 Exit 이 있다고 문제가 되지 않는다. 어떤 작업에서 하이퍼바이저가 개입해야 하는 Exit 이 지나치게 빈번하고 그 처리 비용이 커지면 성능에 영향을 줄 수 있다. 그래서 Exit 횟수만 세지 않는다. Exit 이 난 이유와 작업 종류, 처리한 곳이 KVM 인지 QEMU 사용자 공간인지, 애플리케이션 지연이 Exit 증가와 같은 시점에 나타나는지를 같이 본다.
|
||||
|
||||
## 게스트에 할 일이 없으면 물리 CPU 가 풀린다
|
||||
|
||||
게스트에 실행할 작업이 없으면 게스트 커널은 유휴 경로로 들어가고 `HLT` 계열 동작이 사용될 수 있다. KVM 과 VMX 가 `HLT` exiting 을 쓴다면 여기서 VM Exit 이 발생한다. KVM 은 vCPU 가 당장 할 일이 없는 상태를 처리하면서 그 스레드를 재우거나 대기 상태로 둘 수 있다. 그동안 호스트 스케줄러는 그 논리 CPU 를 호스트의 다른 작업에 쓴다.
|
||||
|
||||
나중에 타이머, 인터럽트, I/O 완료처럼 vCPU 를 다시 실행해야 할 이유가 생기면 vCPU 가 깨어나 실행 대기 상태가 된다. 호스트 스케줄러가 그 스레드를 논리 CPU 에 배치하면 KVM 이 VM Entry 로 게스트 실행을 재개한다. 가상 머신에 4 vCPU 를 설정했어도 그 가상 머신에 할 일이 없는 동안에는 호스트가 CPU 자원을 다른 작업에 쓴다.
|
||||
|
||||
## CPU 가 모자랄 때 게스트 안에서 보이는 값
|
||||
|
||||
호스트에 논리 CPU 가 12개 있고 8 vCPU 가상 머신을 네 대 올리면 32 vCPU 가 12개의 논리 CPU 위에서 실행될 수 있다. 그 가상 머신들이 대부분 유휴 상태면 이 구성 자체가 장애를 뜻하지 않는다. 여러 가상 머신의 vCPU 가 동시에 실행 대기 상태가 될 때 CPU 경쟁과 스케줄링 지연이 늘어난다.
|
||||
|
||||
vCPU 스레드끼리만 경쟁하지도 않는다. 호스트에 Nginx 를 직접 설치하고 가상 머신 두 대를 실행하는 테스트 환경이라면 호스트 쪽 Nginx 와 K3s, DB, Redis, 그 밖의 호스트 프로세스도 같은 Linux 스케줄러를 거친다.
|
||||
|
||||
게스트 안에서 이 경쟁이 드러나는 값이 steal time 이다. 게스트 Linux 의 `top` 에서 `%st` 로 확인할 수 있다. 게스트 vCPU 에 실행할 작업이 있고 호스트에서 그 스레드가 CPU 를 필요로 하는데, 다른 작업 때문에 즉시 실행되지 못한 시간을 나타낸다. 이 값이 높으면 호스트 CPU 경쟁이나 논리 CPU 보다 많은 vCPU 를 할당한 초과 할당을 의심할 수 있지만, 단독으로 원인을 확정하는 지표는 아니다. 호스트 CPU 포화와 실행 대기열, 친화도, 어떤 작업이 돌고 있었는지를 함께 확인한다.
|
||||
|
||||
vCPU 스레드가 실행 대기 상태가 되어도 호스트 스케줄러가 실제 논리 CPU 에 배치할 때까지 기다릴 수 있다. 호스트가 포화될수록 이 대기 시간이 길어지고 게스트에서는 애플리케이션 지연 증가로 보인다. 그래서 vCPU 수를 늘린다고 항상 빨라지지 않는다. 게스트 쪽 작업이 그만큼의 병렬성을 쓰지 못하거나 호스트 전체 CPU 에 비해 vCPU 가 지나치게 많으면 스케줄링 대상만 늘어난다. pinning 도 잘못 설정하면 특정 CPU 에 작업이 몰리기 때문에 pinning 여부만 보지 않고 CPU 별 사용률과 친화도도 본다.
|
||||
|
||||
## 어느 계층에서 느려졌는지 가르는 표
|
||||
|
||||
가상 머신 안의 Keycloak 이 느려졌을 때 CPU 경로는 애플리케이션에서 물리 CPU 까지 여러 층을 지난다. Keycloak 파드는 K3s 의 cgroup CPU 상한 아래에서 실행된다. 그 아래로 게스트 Linux 와 vCPU, QEMU vCPU 스레드, 호스트 Linux 스케줄러, KVM 과 VMX, 물리 CPU 와 NUMA 가 이어진다. 같은 「CPU 가 느리다」는 현상도 층마다 원인이 다르다.
|
||||
|
||||
| 어떤 문제인가 | 주로 어느 계층인가 | 무엇부터 확인하나 |
|
||||
|---|---|---|
|
||||
| 게스트 CPU 포화 | 게스트 | 게스트 CPU, 프로세스·스레드, load |
|
||||
| CPU 제한(throttling) | K3s / cgroup | CPU 상한, throttled time |
|
||||
| vCPU 과다 할당 | 가상 머신 구성 | vCPU 수, 작업 병렬성 |
|
||||
| CPU 초과 할당 | 호스트 구성 | 전체 vCPU, 호스트 논리 CPU |
|
||||
| CPU 경쟁 | 호스트 스케줄러 | 호스트 CPU, 실행 대기열, CPU 별 사용률 |
|
||||
| steal time 증가 | 게스트에서 관측 | `%st`, 호스트 경쟁 |
|
||||
| 스케줄링 지연 | 호스트 스케줄러 | 실행 대기열, 스케줄러 관찰 |
|
||||
| 잘못된 pinning | 호스트 / 가상 머신 설정 | 친화도, CPU 별 사용률 |
|
||||
| 과도한 VM Exit | KVM / VMX | Exit 횟수와 이유, 작업 |
|
||||
| 호스트 CPU 포화 | 호스트 | 호스트 CPU·load·실행 대기열 |
|
||||
| NUMA locality | 하드웨어 / 메모리 | NUMA 토폴로지, CPU·메모리 배치 |
|
||||
|
||||
이 표로 하나의 지표를 골라 원인을 확정하지 않는다. 어느 계층부터 조사할지 범위를 줄이는 데 쓴다.
|
||||
|
||||
특히 두 원인이 겹쳐 보인다. 호스트 CPU 를 제때 받지 못하는 문제는 경쟁과 steal time, 스케줄링 쪽이고, 파드가 자신에게 걸린 CPU 상한을 넘겨 제한되는 문제는 K3s cgroup 쪽이다. 호스트 CPU 에 여유가 있어도 Keycloak 파드는 설정된 상한 때문에 실행이 제한될 수 있다. cgroup 제한은 KVM 문제가 아니지만 가상 머신 안에서 K3s 를 운영하는 이 실험에서는 같은 애플리케이션 지연으로 관찰되기 때문에 진단 경계에 포함한다.
|
||||
|
||||
호스트 OS 에 K3s 를 직접 설치했다면 컨테이너의 프로세스는 호스트 커널을 공유하고 호스트 스케줄러가 직접 스케줄링하는 대상이어서 QEMU 와 `/dev/kvm`, KVM, VMX 를 지나지 않는다. 가상 머신 안에 K3s 를 구성하면 게스트 Linux 와 vCPU, QEMU vCPU 스레드, KVM 과 VMX 계층이 그 아래에 더해진다. 같은 부하 테스트라도 가상 머신 기반 환경에서는 호스트 가상화 자원 병목을 추가로 확인한다. 운영 서버에서는 위 표의 어느 행부터 보는지가 달라질 수 있다. 운영 서버가 호스트에 K3s 를 직접 설치한 구성인지 상위 하이퍼바이저나 클라우드 가상 머신 위에 있는지를 아직 확인하지 않았기 때문이다. 부하를 걸어 재야 아는 것이 아니라 서버 구성을 한 번 확인하면 답이 정해진다.
|
||||
|
||||
## 호스트에서 이 경로를 확인하는 명령
|
||||
|
||||
이 경로가 실제 호스트에서도 그대로인지는 명령으로 하나씩 확인할 수 있다. CPU 가 하드웨어 가상화를 지원하는지는 플래그로 본다.
|
||||
|
||||
```bash label="Intel 은 vmx, AMD 는 svm flag 를 확인한다"
|
||||
grep -E 'vmx|svm' /proc/cpuinfo
|
||||
```
|
||||
|
||||
커널 모듈은 `lsmod | grep kvm` 으로, KVM API 진입점은 `ls -l /dev/kvm` 으로 확인한다. 실행 중인 가상 머신은 `virsh list` 로 본다. `ps -ef | grep '[q]emu'` 를 실행하면 가상 머신이 살아 있는 동안 함께 남아 있는 쪽이 `virsh` 가 아니라 QEMU 프로세스임을 확인할 수 있다.
|
||||
|
||||
QEMU 프로세스의 스레드는 `ps -T -p <QEMU_PID>` 나 `top -H -p <QEMU_PID>` 로 본다. 스레드 이름은 환경과 QEMU 버전에 따라 다를 수 있지만 vCPU 관련 스레드를 호스트에서 관찰할 수 있다. 그 스레드가 최근 어느 논리 CPU 에서 실행됐는지는 `PSR` 열이 알려 준다.
|
||||
|
||||
```bash label="vCPU thread 가 최근 실행된 logical CPU"
|
||||
ps -eLo pid,tid,psr,pcpu,comm | grep qemu
|
||||
```
|
||||
|
||||
여기 보이는 값은 vCPU 가 물리 CPU 에 영구 고정돼 있다는 뜻이 아니라, pinning 을 하지 않았을 때 스케줄링에 따라 달라지는 값이다. 게스트 쪽에서는 `top` 등으로 steal time 을 확인하고, 환경이 지원하면 `perf kvm` 으로 KVM 관련 실행 통계를 본다.
|
||||
|
||||
```bash label="KVM Exit 관찰"
|
||||
sudo perf kvm stat live
|
||||
```
|
||||
|
||||
지원되는 명령과 표시되는 Exit 이유는 커널과 `perf` 버전, CPU 아키텍처 및 설정에 따라 다르기 때문에 그 환경에서 무엇이 되는지 `perf kvm --help` 로 확인한다. 필요하면 KVM tracepoint 를 이용한 별도 추적도 검토한다.
|
||||
|
||||
## 이 문서가 확정하지 않는 것
|
||||
|
||||
CPU 가상화 개념에서 확정하는 수준은 셋까지다. 구조적으로 어떤 문제가 발생할 수 있는가, 어떤 지표로 그 문제를 의심할 수 있는가, 어느 계층에서 확인해야 하는가. 현재 테스트 서버에서 실제로 그 문제가 일어나는지는 여기서 사실로 확정하지 않는다.
|
||||
|
||||
이 호스트에서 잰 값은 하나도 없다. 관찰 명령은 무엇을 볼 수 있는지 적어 둔 목록이다. VM Exit 분포도 steal time 도, pinning 없이 vCPU 스레드가 논리 CPU 사이를 옮겨 다니는지도 아직 측정하지 않았다. 검증은 VMX 와 SVM 확인에서 시작한다. 그다음 KVM 모듈과 `/dev/kvm`, `virsh` 가상 머신, QEMU 프로세스, vCPU 스레드, 호스트 논리 CPU 스케줄링, 게스트가 유휴일 때와 부하를 받을 때의 비교, steal time, VM Exit 관찰로 이어진다. 값이 나오면 각각 재현 조건을 붙인 기록으로 남기고, 그때도 답하지 못한 항목은 열린 질문으로 유지한다.
|
||||
|
||||
범위 밖으로 미룬 영역도 있다. 네트워크·메모리·스토리지 가상화와 PCIe/VFIO/IOMMU 상세, K3s 네트워크와 컨테이너 런타임 상세는 이 문서가 다루지 않는다. NUMA 는 문제가 생길 수 있다는 것과 CPU 친화도와의 연관까지만 적었다. 메모리 배치와 NUMA 튜닝은 메모리 가상화 개념 문서에서 다룬다.
|
||||
|
||||
Keycloak refresh token 경쟁도 CPU 가상화 문제와 같은 문제가 아니다. 다만 가상 머신 기반 실험 환경의 CPU 경쟁이 실험 결과를 왜곡할 수 있어서 두 문제를 분리해 측정한다. 그래서 그 경쟁을 검증하는 첫 실험에서는 CPU 자원을 여유 있게 유지한 채 같은 세션과 같은 refresh token 에 대한 동시 요청을 만든다. 트래픽을 올려 자원 포화를 보는 부하·스트레스 실험은 따로 돌린다. 나눠 돌리는 이유는 결과를 「동일 상태에 동시에 접근해서 발생한 문제」와 「시스템 자원이 부족해져서 발생한 문제」로 갈라 읽기 위해서다. 한 실험에서 둘을 같이 일으키면 원인을 분리하기 어려워진다.
|
||||
|
||||
<!-- body:end -->
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
---
|
||||
id: d103bb81-45df-402f-86e5-e41d66ed7f8d
|
||||
kind: QUESTION
|
||||
slug: exit-distribution-for-keycloak-workload
|
||||
title: Keycloak 작업의 VM Exit 분포는 idle · CPU-bound · I/O-bound 와 어떻게 다른가
|
||||
topic: cpu-virtualization
|
||||
topicName: CPU 가상화
|
||||
project: virtualization
|
||||
status: 초안
|
||||
questionStatus: OPEN
|
||||
studio: "https://hyeonworks.com/studio/documents/d103bb81-45df-402f-86e5-e41d66ed7f8d/edit"
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#27-문제-영역에서-파생되는-추가-open-question-oq-11
|
||||
- final/document.md#24-cpu-가상화-계층에서-발생할-수-있는-문제-24-9
|
||||
- final/document.md#14-실제-linux에서-확인할-수-있는-것-14-9
|
||||
---
|
||||
|
||||
# Keycloak 작업의 VM Exit 분포는 idle · CPU-bound · I/O-bound 와 어떻게 다른가
|
||||
|
||||
VM Exit 은 게스트를 실행하던 CPU 가 하이퍼바이저가 개입해야 하는 조건을 만나 제어권을 KVM 쪽으로 넘기는 전환이다. 정상적인 가상화 동작이라 Exit 이 있다는 것만으로 문제가 되지 않는다. 다만 하이퍼바이저가 개입해야 하는 Exit 이 특정 작업에서 지나치게 빈번하고 처리 비용이 커지면 성능에 영향을 줄 수 있다. Keycloak 을 돌리는 구간이 그런 작업인지 보려면 다른 구간의 Exit 분포와 견줘야 하는데, 이 호스트에서 Exit 을 관측하는 도구가 열리는지부터 확인하지 않았다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **KVM 에서 vCPU 가 물리 CPU 에서 실행되기까지**
|
||||
무엇이 VM Exit 을 부르고 그 처리가 커널에서 끝나는지 QEMU 사용자 공간까지 가는지를 이 개념이 설명한다.
|
||||
- **실제 작업에서 주로 발생하는 VM Exit 은 무엇인가**
|
||||
그쪽은 작업 다섯의 분포를 서로 견주는 물음이고, 이 질문은 Keycloak 구간의 분포가 같은 구간의 지연과 같이 움직이는지를 묻는다.
|
||||
다섯 구간을 한 번 받으면 두 질문의 자료가 함께 나오지만 닫는 기준이 달라 기록은 따로 둔다. 도구가 이 환경에서 열리는지 보는 첫 단계도 둘이 같이 쓴다.
|
||||
|
||||
## 사실
|
||||
|
||||
- VM Exit 은 정상적인 가상화 동작이어서 Exit 이 존재한다는 것만으로 문제가 되지 않는다.
|
||||
- 하이퍼바이저가 개입해야 하는 Exit 이 특정 작업에서 지나치게 빈번하고 처리 비용이 커지면 성능에 영향을 줄 수 있다.
|
||||
- 확인할 때 함께 보는 것은 넷이다.
|
||||
Exit 이유
|
||||
작업 종류
|
||||
Exit 처리 위치가 KVM 인지 QEMU 사용자 공간인지
|
||||
애플리케이션 지연과 Exit 증가가 함께 나타나는지
|
||||
- Exit 이 많다는 관측 하나로 장애라고 판단하지 않는다.
|
||||
- 환경이 지원하면 perf kvm 으로 KVM 관련 실행 통계를 확인할 수 있고, 예로 든 명령은 sudo perf kvm stat live 다.
|
||||
- 지원되는 명령과 표시되는 Exit 이유는 커널과 perf 버전, CPU 아키텍처와 설정에 따라 다를 수 있어 perf kvm --help 를 함께 확인한다. 필요하면 KVM tracepoint 를 이용한 별도 추적도 검토한다.
|
||||
- 비교 후보로 적어 둔 구간은 idle, CPU-bound 작업, I/O 가 많은 작업, Keycloak 정상 요청, Keycloak 부하 테스트 다섯이다.
|
||||
이 목록은 개념 문서가 앞선 물음 자리에 적어 둔 것이고, 이 질문의 제목이 든 것은 그 가운데 앞쪽 셋이다. 받아야 하는 구간은 제목이 든 것보다 둘 많다.
|
||||
|
||||
## 가정
|
||||
|
||||
- 이 호스트에서 perf kvm 이나 KVM tracepoint 가운데 하나는 열린다고 보고 측정 순서를 짠다.
|
||||
둘 다 열리지 않는 결과도 이 질문의 정상적인 답이다.
|
||||
- 다섯 구간을 같은 도구로 같은 길이만큼 잴 수 있다고 전제한다.
|
||||
- Keycloak 부하 구간의 지연은 실험이 이미 재고 있는 값에서 가져올 수 있다고 본다.
|
||||
- I/O 가 많은 구간을 이 환경에서 따로 만들 수 있다고 보고 짰다. 만드는 방법은 개념 문서에 적혀 있지 않다.
|
||||
|
||||
## 미지수
|
||||
|
||||
- 이 호스트에서 perf kvm 의 어떤 하위 명령이 열리는지, 열리지 않으면 tracepoint 쪽이 열리는지.
|
||||
- Keycloak 구간의 Exit 이유 분포가 나머지 네 구간과 얼마나 다른지.
|
||||
- 그 차이가 같은 구간의 Keycloak 지연과 같이 움직이는지.
|
||||
- Exit 처리 위치가 KVM 인지 QEMU 사용자 공간인지를 이 환경의 출력으로 가를 수 있는지.
|
||||
- 관측이 열린다면 얼마나 오래 받아야 구간 사이의 차이가 잡히는지.
|
||||
|
||||
## 제약
|
||||
|
||||
- 표시되는 Exit 이유는 커널과 perf 버전, CPU 아키텍처와 설정에 따라 달라지므로 다른 환경에서 나온 분포와 이 호스트의 분포를 바로 견주지 않는다.
|
||||
- Exit 횟수만 세지 않는다. Exit 이유와 작업 종류, 처리 위치, 같은 구간의 지연을 함께 받아 적는다.
|
||||
- 도구가 열려야 측정이 시작된다. 열리지 않으면 이 질문은 값 없이 닫힌다.
|
||||
- sudo 가 필요한 명령이라 이 호스트에서 그 권한으로 실행할 수 있어야 한다.
|
||||
- 이 호스트에서 Exit 을 실제로 받아 본 기록이 없다.
|
||||
|
||||
## 선택지
|
||||
|
||||
### 1. perf kvm stat live 로 다섯 구간을 차례로 받는다
|
||||
|
||||
perf kvm --help 로 열리는 하위 명령을 먼저 확인하고, 열리면 다섯 구간을 각각 돌리며 Exit 이유 분포를 받는다.
|
||||
같은 도구로 다섯을 받으므로 구간 사이의 차이를 그대로 견줄 수 있다.
|
||||
|
||||
구간마다 부하를 따로 만들어야 하고, 분포가 관측 길이에 따라 흔들리면 같은 구간을 여러 번 받아야 한다.
|
||||
|
||||
### 2. KVM tracepoint 를 직접 켜서 받는다
|
||||
|
||||
perf kvm 이 이 커널과 perf 버전에서 열리지 않을 때 검토하는 방법이다.
|
||||
tracepoint 를 직접 켜면 Exit 이유를 더 세밀하게 받을 수 있다.
|
||||
|
||||
받은 결과를 다섯 구간에 걸쳐 같은 형태로 정리하는 일이 첫 번째 방법보다 손이 많이 가서, perf kvm 이 열리지 않는 것을 확인한 뒤에 간다.
|
||||
|
||||
### 3. Keycloak 구간만 받아 절대값으로 판단한다 — 제외
|
||||
|
||||
Keycloak 구간의 Exit 수가 크면 Exit 처리 비용이 있다고 읽는 방법이다.
|
||||
Exit 이 많다는 관측 하나로 장애라고 판단하지 않기로 했고, 무엇과 견줘야 「많다」인지도 이 호스트에는 아직 기준이 없다. 그래서 다른 구간 없이 Keycloak 구간만 받지 않는다.
|
||||
|
||||
## 다음 검증
|
||||
|
||||
1. perf kvm --help 로 이 환경이 지원하는 하위 명령을 확인한다.
|
||||
2. 열리면 sudo perf kvm stat live 로 idle, CPU-bound, I/O 가 많은 구간, Keycloak 정상 요청, Keycloak 부하 다섯의 Exit 이유 분포를 받는다.
|
||||
3. 같은 다섯 구간의 Keycloak 지연을 실험이 이미 재는 값에서 가져와 분포 옆에 적는다.
|
||||
4. perf kvm 이 열리지 않으면 KVM tracepoint 를 쓰는 쪽을 검토하고, 검토 결과와 그 판단 근거도 함께 기록한다.
|
||||
|
||||
닫는 조건 : Keycloak 구간의 Exit 분포가 다른 구간과 다르고 그 차이가 지연과 같이 움직이면 그 측정이 Case 가 되고 닫는다. 분포가 다르지 않거나 지연과 따로 움직이면, 이 작업에서 Exit 처리 비용은 조사 대상이 아니라고 적고 진단표의 「과도한 VM Exit」 행을 뒤로 미루는 근거로 남긴 뒤 닫는다.
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
---
|
||||
id: 688c6c02-c1bd-4cd2-a615-2396db145386
|
||||
kind: QUESTION
|
||||
slug: host-numa-topology
|
||||
title: 이 호스트의 NUMA 토폴로지는 가상 머신 성능을 고려해야 할 구조인가
|
||||
topic: cpu-virtualization
|
||||
topicName: CPU 가상화
|
||||
project: virtualization
|
||||
status: 초안
|
||||
questionStatus: OPEN
|
||||
studio: "https://hyeonworks.com/studio/documents/688c6c02-c1bd-4cd2-a615-2396db145386/edit"
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#27-문제-영역에서-파생되는-추가-open-question-oq-12
|
||||
- final/document.md#24-cpu-가상화-계층에서-발생할-수-있는-문제-24-11
|
||||
---
|
||||
|
||||
# 이 호스트의 NUMA 토폴로지는 가상 머신 성능을 고려해야 할 구조인가
|
||||
|
||||
멀티소켓이나 NUMA(Non-Uniform Memory Access) 구조의 호스트에서는 vCPU 가 어느 노드의 CPU 에서 실행되고 그 가상 머신의 메모리가 어느 노드에 놓였는지가 성능에 영향을 줄 수 있다. 이 물음은 그 영향을 재는 것이 아니라, 이 호스트가 애초에 그 조건에 들어가는지를 먼저 가른다. 단일 노드로 나오면 지금 실험에서 우선순위를 낮추고, 다중 노드로 나오면 vCPU 와 메모리 배치를 따로 다룬다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **KVM 에서 vCPU 가 물리 CPU 에서 실행되기까지**
|
||||
vCPU 스레드가 어느 논리 CPU 에서 실행될지를 호스트 스케줄러가 정한다는 것이 이 물음의 전제다.
|
||||
|
||||
## 사실
|
||||
|
||||
- 멀티소켓 또는 NUMA 구조의 호스트에서는 CPU 가 실행되는 NUMA 노드와 가상 머신 메모리가 놓인 NUMA 노드가 어디냐에 따라 성능이 달라질 수 있다.
|
||||
- vCPU 가 노드 0 의 CPU 에서 실행되는데 필요한 메모리가 주로 노드 1 에 배치되어 있으면 다른 노드의 메모리를 읽는 접근(remote memory access)이 생길 수 있다.
|
||||
- NUMA 는 CPU 가상화와 메모리 가상화의 경계에 걸쳐 있다. 그래서 지금 개념 문서는 문제가 있다는 것과 CPU 친화도와 어떻게 얽히는지까지만 기록했고, 상세한 메모리 배치와 NUMA 튜닝은 메모리 가상화를 다루는 개념 문서로 넘겼다.
|
||||
- 이 물음의 종료 기준은 개념 문서가 직접 적어 두었다.
|
||||
단일 NUMA 노드 : 지금 실험에서 우선순위를 낮춘다
|
||||
다중 NUMA 노드 : vCPU 와 메모리 배치를 별도 Case 후보로 올린다
|
||||
개념 문서가 남긴 열린 물음 열둘 가운데 닫는 갈래를 스스로 적어 둔 것은 이 하나다.
|
||||
- 개념 문서는 이 확인에 쓸 명령을 적지 않았다. 다른 확인 항목과 달리 NUMA 쪽에는 예로 든 명령이 없다.
|
||||
- 이 호스트의 NUMA 노드 수를 확인한 기록이 없다.
|
||||
|
||||
## 가정
|
||||
|
||||
- 호스트에 붙어 노드 수를 읽을 수 있다고 본다.
|
||||
- 단일 노드로 나오면 vCPU 가 실행되는 노드와 메모리가 놓인 노드가 갈리지 않으므로 다른 노드의 메모리를 읽을 일이 지금 실험에서 성능 차이를 만들지 않는다고 본다.
|
||||
그 추론을 이 호스트에서 확인하지는 않았다.
|
||||
- 두 가상 머신의 메모리 배치가 실행 중에 바뀌지 않는다고 전제한다.
|
||||
|
||||
## 미지수
|
||||
|
||||
- 이 호스트가 단일 NUMA 노드인지 다중 NUMA 노드인지.
|
||||
- 다중이라면 두 가상 머신의 vCPU 와 메모리가 각각 어느 노드에 배치되어 있는지.
|
||||
- 노드 수와 배치를 이 환경에서 어떤 명령으로 읽는지. 개념 문서가 그 명령을 적지 않아 실행하는 쪽이 정한다.
|
||||
- 다중으로 나왔을 때 배치를 바꾸는 일이 지금 가상 머신 구성에서 가능한지.
|
||||
|
||||
## 제약
|
||||
|
||||
- 이 질문이 다중 노드로 닫혀도 여기서 NUMA 튜닝을 정하지 않는다. 상세한 메모리 배치는 메모리 가상화를 다루는 개념 문서가 받는다.
|
||||
- 쓸 명령을 개념 문서가 정해 주지 않으므로, 실행한 명령과 그 출력을 함께 증거로 남겨야 다음 사람이 같은 값을 다시 읽을 수 있다.
|
||||
- 이 호스트에서 잰 값이 없어 다른 장비의 노드 구성을 근거로 삼지 않는다.
|
||||
|
||||
## 선택지
|
||||
|
||||
### 1. 노드 수만 먼저 읽고 거기서 갈린다
|
||||
|
||||
호스트의 NUMA 노드 수 하나로 우선순위 판정이 끝나기 때문에 확인이 짧고, 단일로 나오면 더 볼 것이 없다.
|
||||
|
||||
다중으로 나오면 vCPU 와 메모리 배치를 읽으려고 한 번 더 붙어야 한다.
|
||||
|
||||
### 2. 노드 수와 두 가상 머신의 배치를 한 번에 읽는다
|
||||
|
||||
호스트에 붙은 김에 노드 수와 각 가상 머신의 vCPU · 메모리 배치를 같이 받아 적는다. 다중으로 나왔을 때 바로 다음 단계로 넘어갈 수 있다.
|
||||
|
||||
단일로 나오면 함께 받은 배치는 쓰지 않게 되는데, 대신 실행이 한 번으로 끝난다.
|
||||
|
||||
### 3. 메모리 가상화 개념 문서를 쓸 때 함께 본다 — 제외
|
||||
|
||||
상세한 메모리 배치를 그쪽에서 다루기로 했으니 확인도 그때 하자는 방법이다.
|
||||
지금 필요한 판단은 이 항목을 실험 목록의 어디에 둘지 하나이고, 그것은 노드 수만으로 갈린다. 메모리 가상화 쪽을 기다리면 그동안 우선순위를 정하지 못한 채로 둔다.
|
||||
개념 문서가 적은 다음 기반 영역은 메모리 가상화가 아니라 네트워크 가상화이고, 메모리 쪽을 언제 정리하는지는 적혀 있지 않다.
|
||||
|
||||
## 다음 검증
|
||||
|
||||
1. 호스트의 NUMA 노드 구성을 확인해 노드 수를 적는다.
|
||||
2. 이 확인에 쓴 명령과 그 출력을 함께 증거로 남긴다. 개념 문서가 명령을 적어 두지 않아 실행한 쪽이 무엇을 썼는지 기록해야 한다.
|
||||
3. 다중 노드로 나오면 vCPU 가 실행되는 노드와 가상 머신 메모리가 배치된 노드까지 이어서 적는다.
|
||||
|
||||
닫는 조건 : 단일 NUMA 노드로 나오면 지금 실험에서 우선순위를 낮추고 닫는다. 다중 NUMA 노드로 나오면 vCPU 와 메모리 배치를 별도 Case 후보로 올리고, 이 프로젝트가 메모리 가상화 쪽 정리를 가질 때 그리로 넘긴다.
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
---
|
||||
id: 4a2d3332-81c3-44fa-a4fb-216a41abb504
|
||||
kind: QUESTION
|
||||
slug: idle-vcpu-thread-appearance
|
||||
title: 게스트가 유휴 상태일 때 vCPU 스레드는 이 테스트 환경에서 어떻게 보이는가
|
||||
topic: cpu-virtualization
|
||||
topicName: CPU 가상화
|
||||
project: virtualization
|
||||
status: 초안
|
||||
questionStatus: OPEN
|
||||
studio: "https://hyeonworks.com/studio/documents/4a2d3332-81c3-44fa-a4fb-216a41abb504/edit"
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#20-이-concept에서-파생되는-open-question-oq-3
|
||||
- final/document.md#10-guest가-idle이면-물리-cpu는-어떻게-되는가
|
||||
- final/document.md#14-실제-linux에서-확인할-수-있는-것-14-6
|
||||
---
|
||||
|
||||
# 게스트가 유휴 상태일 때 vCPU 스레드는 이 테스트 환경에서 어떻게 보이는가
|
||||
|
||||
게스트에 할 일이 없으면 vCPU 스레드가 잠들거나 대기 상태로 들어가고, 그동안 호스트는 그 물리 CPU 를 다른 작업에 쓸 수 있다. 이 서술이 이 환경에서도 그대로 보이는지는 QEMU 프로세스의 스레드 목록을 유휴와 부하 두 상태에서 찍어 봐야 아는데, 아직 어느 쪽도 찍지 않았다. 이 QEMU 버전에서 vCPU 스레드가 어떤 이름으로 나오는지도 모른다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **KVM 에서 vCPU 가 물리 CPU 에서 실행되기까지**
|
||||
vCPU 스레드가 왜 잠들 수 있는지, 무엇이 그것을 깨우는지를 이 개념이 설명한다.
|
||||
- **pinning 없이 vCPU 스레드는 호스트 논리 CPU 사이를 옮겨 다니는가**
|
||||
같은 `ps -eLo` 출력의 `psr` 값을 본다. 여기서 스레드를 가려내는 방법이 정해지면 그쪽 측정이 바로 이어진다.
|
||||
|
||||
## 사실
|
||||
|
||||
가상 머신에 4 vCPU 를 설정했다고 해서 4개의 호스트 논리 CPU 가 계속 예약되지는 않는다 : §10
|
||||
|
||||
§10 이 그린 유휴 흐름
|
||||
|
||||
Guest 에 실행할 작업 없음
|
||||
Guest Kernel idle
|
||||
HLT 등
|
||||
VM Exit
|
||||
KVM
|
||||
vCPU Thread block/sleep
|
||||
|
||||
이때 호스트 스케줄러는 물리 CPU 를 호스트의 다른 작업에 쓸 수 있다 : §10
|
||||
|
||||
§10 이 그린 깨어나는 흐름
|
||||
|
||||
vCPU wake-up
|
||||
runnable
|
||||
Host Linux Scheduler
|
||||
Logical CPU 에서 vCPU Thread 실행
|
||||
KVM / VM Entry
|
||||
Guest 실행 재개
|
||||
|
||||
§10 은 깨우는 이유로 타이머, 인터럽트, I/O 완료를 든다.
|
||||
|
||||
§14.6 은 ps -T -p <QEMU_PID> 또는 top -H -p <QEMU_PID> 로 vCPU 관련 스레드를 호스트에서 관찰할 수 있다고 적으면서, 환경과 QEMU 버전에 따라 이름은 다를 수 있다는 단서를 함께 단다.
|
||||
|
||||
§20 은 게스트의 유휴 상태와 CPU 부하 상태를 비교하라고 적고 확인 명령으로 둘을 든다.
|
||||
|
||||
top -H -p <QEMU_PID>
|
||||
ps -eLo pid,tid,psr,pcpu,stat,comm
|
||||
|
||||
## 가정
|
||||
|
||||
QEMU 프로세스의 PID 를 먼저 찾아야 한다. 가상 머신 두 대가 도는 호스트에서 어느 PID 가 어느 가상 머신인지 가리는 방법을 아직 정하지 않았다.
|
||||
|
||||
vCPU 스레드를 이름으로 못 가리면 스레드 수를 그 가상 머신의 vCPU 수와 맞춰 가린다고 전제한다.
|
||||
|
||||
유휴라고 부를 상태를 이 가상 머신에서 만들 수 있다. 가상 머신 안에서 도는 것이 있으면 완전한 유휴가 아니다.
|
||||
|
||||
pcpu 와 stat 를 한 번씩 찍은 값으로 두 상태를 가를 수 있다.
|
||||
|
||||
## 미지수
|
||||
|
||||
이 환경의 QEMU 에서 vCPU 스레드가 어떤 이름으로 보이는지.
|
||||
|
||||
유휴 상태에서 그 스레드의 pcpu 와 stat 값.
|
||||
|
||||
CPU 부하를 걸었을 때 같은 두 값이 어떻게 바뀌는지.
|
||||
|
||||
vCPU 스레드 말고 어떤 스레드가 같은 QEMU 프로세스 아래에 함께 보이는지, 그 스레드들이 유휴 상태에서도 CPU 를 쓰는지.
|
||||
|
||||
유휴인데도 주기적으로 깨어나는 구간이 있는지. §10 은 타이머와 인터럽트를 깨우는 이유로 들었다.
|
||||
|
||||
## 제약
|
||||
|
||||
이 호스트에서 찍은 출력이 없다. §10 은 개념 흐름을 그린 것이고 이 환경의 실제 출력이 아니다. 결과가 §10 과 같으면 개념에 흡수되고 다르면 Case 가 된다는 갈림을 처음에는 이 물음을 따로 세우지 않을 이유로 봤다. 지금은 그것을 그대로 닫는 조건으로 쓴다 — 어느 쪽으로 갈리든 물음은 닫힌다.
|
||||
|
||||
§14.6 이 스레드 이름은 환경과 QEMU 버전에 따라 다를 수 있다고 적기 때문에, 이름으로 거르는 절차를 미리 굳혀 둘 수 없다.
|
||||
|
||||
§20 이 든 확인 명령 둘은 그 시점의 스레드 목록과 값을 보여 준다. §10 이 적은 HLT 에서 VM Exit 을 거쳐 block/sleep 으로 가는 전이를 단계마다 짚어 주지는 않는다.
|
||||
|
||||
상태를 바꾸는 것 말고 가상 머신 구성은 건드리지 않는다. vCPU 수를 조정하면 유휴와 부하의 차이 대신 구성 차이를 보게 된다.
|
||||
|
||||
## 선택지
|
||||
|
||||
### 1. 스레드 이름부터 확정한다
|
||||
|
||||
§14.6 의 ps -T -p <QEMU_PID> 로 그 QEMU 프로세스의 스레드 목록을 받아, vCPU 스레드로 보이는 것이 그 가상 머신의 vCPU 수와 같은 수로 나오는지 맞춰 본다. 이름을 확정해 두면 뒤의 두 출력에서 어느 줄을 읽을지 정해진다.
|
||||
|
||||
### 2. 유휴와 부하를 같은 명령 두 개로 찍어 나란히 둔다
|
||||
|
||||
§20 이 확인 명령으로 top -H -p <QEMU_PID> 와 ps -eLo pid,tid,psr,pcpu,stat,comm 둘을 들었다. 두 상태에서 같은 두 명령을 돌리면 pcpu 와 stat 두 열을 그대로 견줄 수 있다. §14.7 이 스레드가 어느 논리 CPU 에서 돌았는지 볼 때 든 ps 에는 stat 열이 없다. §20 은 이 물음의 확인 명령에만 그 열을 더했고, 스레드가 자고 있는지 실행 상태인지는 그 열에서 읽는다.
|
||||
|
||||
### 3. 호스트 쪽 CPU 사용량도 같이 본다
|
||||
|
||||
§10 은 vCPU 스레드가 잠들면 호스트가 그 물리 CPU 를 다른 작업에 쓸 수 있다고 적는다. vCPU 스레드의 pcpu 만 보면 그 스레드가 CPU 를 안 쓴다는 데까지만 확인되고, 비워 준 CPU 를 호스트가 실제로 다른 작업에 썼는지는 안 보인다.
|
||||
|
||||
### 4. 제외 — 추적으로 전이 시점을 직접 잡는다
|
||||
|
||||
HLT 로 VM Exit 이 나고 스레드가 잠드는 순간을 추적하면 §10 의 흐름을 단계마다 확인할 수 있다. 그러나 이 질문은 두 상태의 출력이 갈리는지를 묻고 §20 도 확인 명령을 두 줄로 적어 두었기 때문에, 더 무거운 도구는 두 출력이 갈리지 않을 때 꺼낸다.
|
||||
|
||||
## 다음 검증
|
||||
|
||||
1. 가상 머신을 유휴 상태로 둔 채 top -H -p <QEMU_PID> 와 ps -eLo pid,tid,psr,pcpu,stat,comm 을 찍는다.
|
||||
2. 같은 가상 머신에 CPU 부하를 걸고 같은 두 명령을 다시 찍는다.
|
||||
3. 두 출력을 나란히 둔다. §20 이 적은 확인 명령 그대로다.
|
||||
|
||||
닫는 조건 : 유휴 상태에서 vCPU 스레드의 pcpu 가 0 에 가깝고 stat 가 sleep 계열이며 부하에서 실행 상태로 바뀌면, §10 의 서술이 이 환경에서도 성립하는 것으로 개념의 확인 사례에 흡수하고 닫는다. 두 상태에서 출력이 갈리지 않거나 §10 과 반대로 나오면 그 출력이 Case 가 된다.
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
---
|
||||
id: 2acec7d5-3eaf-4115-9d61-45647c4ec01e
|
||||
kind: QUESTION
|
||||
slug: is-production-on-a-hypervisor
|
||||
title: 운영 서버는 CPU 가상화 계층의 영향을 받는 구조인가
|
||||
topic: cpu-virtualization
|
||||
topicName: CPU 가상화
|
||||
project: virtualization
|
||||
status: 초안
|
||||
questionStatus: OPEN
|
||||
studio: "https://hyeonworks.com/studio/documents/2acec7d5-3eaf-4115-9d61-45647c4ec01e/edit"
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#20-이-concept에서-파생되는-open-question-oq-6
|
||||
- final/document.md#18-bare-metal-k3s와-vm-기반-k3s의-차이
|
||||
- final/document.md#25-cpu-문제를-계층별로-구분하는-진단표
|
||||
---
|
||||
|
||||
# 운영 서버는 CPU 가상화 계층의 영향을 받는 구조인가
|
||||
|
||||
§18 과 §20 은 K3s 가 호스트 스케줄러로 바로 가는 경로와 게스트 · vCPU · 하이퍼바이저를 더 지나는 경로를 나란히 적었다.
|
||||
§25 의 진단표는 문제마다 주된 계층을 갈라 놓아서, Virtualization 과 Host 계층에만 걸리는 행이 따로 있다.
|
||||
운영 서버가 두 경로 중 어느 쪽인지는 SSOT 에 적혀 있지 않은데, 그 구조가 정해져야 진단표의 어느 행을 운영 진단에 쓸지 고를 수 있다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **KVM 에서 vCPU 가 물리 CPU 에서 실행되기까지**
|
||||
운영 서버가 가상 머신 위라면 무엇이 더 끼어드는지를 설명한 기록이다.
|
||||
|
||||
## 사실
|
||||
|
||||
§18 은 호스트 OS 에 K3s 를 직접 설치했을 때의 CPU 경로를 적었다.
|
||||
컨테이너에서 도는 작업은 K3s 와 컨테이너 런타임을 지나 호스트 Linux 스케줄러로, 다시 물리 CPU 로 간다.
|
||||
|
||||
그 호스트 위에 별도 가상 머신이 없다면 그 작업이 QEMU 와 /dev/kvm, KVM, VMX 경로를 타지는 않는다.
|
||||
컨테이너의 프로세스는 호스트 커널을 공유하고 호스트 스케줄러가 직접 스케줄링한다.
|
||||
|
||||
가상 머신 안에 K3s 를 구성하면 계층이 더 붙는다.
|
||||
§18 이 든 것은 게스트 Linux 와 K3s, vCPU, QEMU vCPU 스레드, 호스트 Linux 스케줄러, KVM / VMX 다.
|
||||
그래서 같은 부하 테스트라도 가상 머신 기반 환경에서는 호스트 가상화 자원 병목을 추가로 확인해야 한다.
|
||||
|
||||
§20 의 OQ-6 은 두 경로를 K3s -> Host Scheduler -> Physical CPU 와 K3s -> Guest -> vCPU -> Hypervisor -> Physical CPU 로 나란히 적고, 구조에 따라 진단 지표가 달라진다고 밝혔다.
|
||||
|
||||
§25 의 진단표는 문제마다 주된 계층을 갈라 적었다.
|
||||
|
||||
vCPU 과다 할당 : VM 구성
|
||||
CPU overcommit : Host 구성
|
||||
CPU contention · Scheduling latency : Host Scheduler
|
||||
잘못된 pinning : Host / VM 설정
|
||||
과도한 VM Exit : KVM / VMX
|
||||
Steal time 증가 : Guest 에서 관측
|
||||
Host CPU saturation : Host
|
||||
|
||||
운영 서버가 bare-metal 인지 상위 하이퍼바이저나 클라우드 가상 머신 위인지는 SSOT 에 적혀 있지 않다.
|
||||
|
||||
## 가정
|
||||
|
||||
운영 서버도 K3s 로 Keycloak 을 돌린다고 전제한다. §20 이 나눈 두 경로는 둘 다 K3s 에서 시작한다.
|
||||
|
||||
운영 서버가 그 두 갈래 중 하나라고 전제한다. 중첩 가상화처럼 계층이 더 들어가는 구성은 이 물음에 넣지 않았다.
|
||||
|
||||
구조가 정해지면 §25 의 진단표를 운영 진단에도 그대로 쓴다고 전제한다. 진단표를 만든 근거는 이 테스트 환경이다.
|
||||
|
||||
## 미지수
|
||||
|
||||
운영 서버가 bare-metal 호스트에 직접 K3s 를 설치한 것인가, 상위 하이퍼바이저나 클라우드 가상 머신 위에 있는가.
|
||||
|
||||
하이퍼바이저 위라면 그 호스트의 CPU 지표를 우리가 볼 수 있는가.
|
||||
클라우드 가상 머신이면 호스트 쪽 실행 대기열과 사용률을 읽을 수 없어 §25 의 Host 행을 그대로 쓰기 어려워진다.
|
||||
|
||||
운영 서버에 직접 붙어 명령을 돌릴 수 있는가.
|
||||
|
||||
## 제약
|
||||
|
||||
이 물음은 부하를 걸어 재는 것이 아니라 구성을 확인해서 닫는다.
|
||||
|
||||
§14.1~§14.3 은 그 장비가 가상 머신을 직접 돌리는 호스트 인지를 보여 준다.
|
||||
그 장비 자신이 어떤 하이퍼바이저의 게스트 인지는 이 세 명령이 말해 주지 않는다.
|
||||
|
||||
구조가 정해지기 전에는 §25 의 어느 행을 운영 진단에 넣을지 고를 수 없다.
|
||||
|
||||
§25 는 그 표의 목적을 「하나의 지표로 장애 원인을 확정하는 것이 아니라, 어느 계층부터 조사해야 하는지 범위를 줄이는 것」이라고 적었다.
|
||||
그래서 이 물음이 닫혀도 운영 장애의 원인이 정해지는 것은 아니다. 정해지는 것은 어느 계층부터 조사할지다.
|
||||
|
||||
## 선택지
|
||||
|
||||
### 1. 구성 기록과 도입 경로로 확인한다
|
||||
|
||||
서버를 어떻게 마련했는지 남은 기록으로 bare-metal 인지 하이퍼바이저나 클라우드 가상 머신 위인지 가른다.
|
||||
운영 서버에 붙지 않아도 되고 부하도 걸지 않는다.
|
||||
|
||||
기록이 실제 구성과 어긋나 있으면 어긋난 채로 적히므로 2번과 함께 쓴다.
|
||||
|
||||
### 2. 서버에 붙어 명령으로 확인한다
|
||||
|
||||
§14.1~§14.3 의 grep -E 'vmx|svm' /proc/cpuinfo · lsmod | grep kvm · ls -l /dev/kvm 로 그 장비가 가상 머신을 직접 돌리는 호스트인지부터 적는다.
|
||||
이어서 게스트 안에서 top 의 %st 가 관측되는지 본다.
|
||||
|
||||
세 명령이 모두 비어 있으면 그 장비가 가상 머신을 직접 돌리지는 않는다는 뜻이고, 그 위에 하이퍼바이저가 있는지는 1번의 구성 기록으로 가른다.
|
||||
|
||||
### 3. 제외 — 테스트 환경의 구조를 운영에 옮겨 적는다
|
||||
|
||||
테스트 환경이 가상 머신 위라는 것이 운영 서버의 구조를 말해 주지 않는다.
|
||||
두 환경을 같다고 적으면 §25 의 Virtualization 행을 근거 없이 운영 진단에 넣게 된다.
|
||||
|
||||
## 다음 검증
|
||||
|
||||
1. 운영 서버의 구성 기록이나 도입 경로로 bare-metal 인지 하이퍼바이저 / 클라우드 가상 머신 위인지 확인한다.
|
||||
2. 서버에 붙을 수 있으면 §14.1~§14.3 의 grep -E 'vmx|svm' /proc/cpuinfo · lsmod | grep kvm · ls -l /dev/kvm 으로 그 장비가 가상 머신을 직접 돌리는 호스트인지 적는다.
|
||||
3. 게스트 안에서 top (§14.8) 의 %st 가 관측되는지 함께 본다.
|
||||
|
||||
닫는 조건 : 운영 서버가 bare-metal 로 확인되면 §25 의 Virtualization · Host 행을 운영 진단에서 빼도 된다고 적고 닫는다. 하이퍼바이저나 클라우드 가상 머신 위로 확인되면 그 사실이 운영 진단의 전제가 되고, §25 의 어느 행을 운영에서 쓸지는 그 뒤 Decision 이 정한다.
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
---
|
||||
id: 62319064-17ef-4b2f-bf8b-e363cd0e36a6
|
||||
kind: QUESTION
|
||||
slug: pinning-before-and-after
|
||||
title: CPU pinning 전후로 Keycloak 지연과 vCPU 스케줄링 변동이 달라지는가
|
||||
topic: cpu-virtualization
|
||||
topicName: CPU 가상화
|
||||
project: virtualization
|
||||
status: 초안
|
||||
questionStatus: OPEN
|
||||
studio: "https://hyeonworks.com/studio/documents/62319064-17ef-4b2f-bf8b-e363cd0e36a6/edit"
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#27-문제-영역에서-파생되는-추가-open-question-oq-10
|
||||
- final/document.md#24-cpu-가상화-계층에서-발생할-수-있는-문제-24-7
|
||||
- final/document.md#14-실제-linux에서-확인할-수-있는-것-14-7
|
||||
---
|
||||
|
||||
# CPU pinning 전후로 Keycloak 지연과 vCPU 스케줄링 변동이 달라지는가
|
||||
|
||||
vCPU 스레드가 실행될 호스트 논리 CPU 를 특정 집합으로 제한하는 설정을 CPU pinning 이라고 한다. 적절히 걸면 스케줄링 변동이 줄지만 잘못 걸면 특정 CPU 에만 작업이 몰린다. 이 호스트에서는 pinning 을 걸지 않은 상태의 Keycloak 지연도, 건 상태의 지연도 아직 재지 않았으므로 pinning 이 지금 작업에 이점을 주는지는 말할 수 없다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **KVM 에서 vCPU 가 물리 CPU 에서 실행되기까지**
|
||||
pinning 도 CPU 격리도 하지 않았을 때 vCPU 스레드를 누가 어디에 배치하는지를 이 개념이 설명한다.
|
||||
- **pinning 없이 vCPU 스레드는 호스트 논리 CPU 사이를 옮겨 다니는가**
|
||||
그쪽이 이동 자체를 관측하고, 이 질문은 그 이동을 없앴을 때 Keycloak 지연이 달라지는지를 받는다.
|
||||
|
||||
## 사실
|
||||
|
||||
- CPU pinning 은 특정 vCPU 스레드를 특정 호스트 논리 CPU 에 제한하는 설정이다.
|
||||
- 적절히 쓰면 스케줄링 변동을 줄일 수 있고, 잘못 설정하면 특정 CPU 에 작업이 집중될 수 있다.
|
||||
- 그래서 pinning 을 걸었는지만 보지 않고 CPU 별 사용률과 친화도를 함께 확인한다.
|
||||
- 개념 문서가 잘못 건 예로 그린 것은 vCPU 스레드 둘과 호스트 프로세스 하나가 같은 논리 CPU 에 묶이고 나머지 논리 CPU 는 상대적으로 유휴한 그림이다.
|
||||
치우침을 만드는 쪽에 호스트 프로세스가 함께 들어 있다.
|
||||
- 스레드가 최근 실행된 논리 CPU 는 ps -eLo pid,tid,psr,pcpu,comm | grep qemu 의 PSR 로 볼 수 있다.
|
||||
- PSR 값 하나가 vCPU 가 물리 CPU 에 영구 고정되어 있다는 뜻은 되지 않는다. pinning 을 걸지 않았다면 스케줄링에 따라 달라질 수 있다.
|
||||
- pinning 이 지금 작업에서 실제 이점을 주는지는 실험으로 확인하기로 했고, 아직 그 실험을 돌리지 않았다.
|
||||
|
||||
## 가정
|
||||
|
||||
- 지금 두 가상 머신에 pinning 도 별도의 친화도 설정도 걸려 있지 않다고 보고 「전」 실행을 잡는다.
|
||||
걸려 있는지는 개념 문서에 적혀 있지 않아 실행할 때 함께 확인한다.
|
||||
- pinning 없이 돌리면 같은 tid 의 PSR 이 시간에 따라 바뀐다고 본다.
|
||||
이 호스트에서 그 이동을 아직 관측하지 않았다.
|
||||
- 두 실행에 같은 Keycloak 부하를 같은 방식으로 걸 수 있다고 전제한다.
|
||||
- 두 실행 사이에 vCPU 수와 가상 머신 배치를 바꾸지 않는다고 두고 짠다. 그래야 차이를 pinning 쪽으로 읽을 수 있다.
|
||||
|
||||
## 미지수
|
||||
|
||||
- pinning 뒤 Keycloak 지연이 달라지는지, 달라진다면 어느 방향인지.
|
||||
- 같은 tid 의 PSR 변동이 실제로 줄어드는지.
|
||||
- 변동이 줄어드는 대신 특정 논리 CPU 로 작업이 몰리는지.
|
||||
- 어떤 논리 CPU 집합에 묶을지. 이 호스트의 논리 CPU 수와 각 가상 머신의 vCPU 수를 아직 적지 않았다.
|
||||
- 호스트 쪽 Nginx 와 다른 호스트 프로세스가 그 집합을 함께 쓰는지.
|
||||
|
||||
## 제약
|
||||
|
||||
- pinning 여부만으로 판정하지 않는다. 지연과 PSR 변동, CPU 별 사용률 셋을 같은 실행에서 받아 적는다.
|
||||
- 「전」과 「후」 두 실행의 부하가 같아야 지연 차이를 pinning 쪽으로 읽을 수 있다.
|
||||
- 이 호스트에서 잰 값이 하나도 없어 견줄 값을 이번 실행이 함께 만든다.
|
||||
- 이 실행 전에 pinning 을 쓸지 말지를 먼저 정하지 않는다. 잰 값이 없는 상태로 정하면 그 선택이 감수하는 비용을 적을 수 없다.
|
||||
- 묶는 대상은 vCPU 스레드다. 게스트 안의 Keycloak 파드 배치는 이 질문이 다루지 않는다.
|
||||
|
||||
## 선택지
|
||||
|
||||
### 1. 같은 부하로 전후 두 실행을 재고 세 항목을 나란히 놓는다
|
||||
|
||||
pinning 없이 Keycloak 부하를 걸어 지연과 PSR 변동과 CPU 별 사용률을 남기고, vCPU 스레드를 논리 CPU 에 묶은 뒤 같은 부하로 같은 세 항목을 다시 남긴다.
|
||||
지연이 좋아졌는지와 그 대가로 특정 CPU 에 몰렸는지를 한 실행 쌍에서 같이 볼 수 있다.
|
||||
|
||||
가상 머신 설정을 바꿔 다시 부하를 거는 만큼 시간이 든다. 두 실행 사이에 다른 조건이 바뀌면 그 실행 쌍은 버린다.
|
||||
|
||||
### 2. 부하 없이 PSR 변동만 견준다
|
||||
|
||||
유휴 구간에서 PSR 을 여러 번 찍어 pinning 전후를 견주는 방법이다. 부하를 만들지 않아도 되고 실행이 짧다.
|
||||
|
||||
이 방법으로는 질문의 절반만 답한다. pinning 이 스케줄링 변동을 줄이는지는 보이지만 Keycloak 지연이 달라지는지는 보이지 않고, 부하가 없으면 특정 CPU 에 작업이 몰리는지도 드러나지 않는다.
|
||||
|
||||
### 3. 지연만 견주고 CPU 별 사용률은 뒤로 미룬다 — 제외
|
||||
|
||||
전후 지연만 재면 실행 하나가 짧아진다.
|
||||
다만 잘못 건 pinning 도 짧은 구간에서는 지연이 좋아진 것으로 보일 수 있다. 그 경우를 가려내려고 CPU 별 사용률과 친화도를 함께 보기로 했으므로 이 방법은 쓰지 않는다.
|
||||
|
||||
## 다음 검증
|
||||
|
||||
1. pinning 을 걸지 않은 상태에서 Keycloak 부하를 걸고, 지연과 ps -eLo pid,tid,psr,pcpu,comm | grep qemu 의 PSR 변동, CPU 별 사용률을 남긴다.
|
||||
2. 같은 실행에서 그 가상 머신에 친화도 설정이 이미 걸려 있는지도 함께 적는다.
|
||||
3. vCPU 스레드를 논리 CPU 에 묶고 같은 부하를 다시 걸어 같은 세 항목을 남긴다.
|
||||
4. 두 실행을 나란히 놓고 지연 차이와 CPU 별 사용률의 치우침을 함께 읽는다.
|
||||
|
||||
닫는 조건 : pinning 뒤 지연이 좋아지고 CPU 별 사용률이 특정 CPU 로 몰리지 않으면, pinning 을 쓸지 말지를 Decision 으로 넘긴다. 그 Decision 이 감수할 비용은 잘못 건 pinning 이 특정 CPU 에 작업을 집중시킨다는 것이다. 두 실행에서 지연 차이가 없으면 이 작업에서는 pinning 이 이점을 주지 않는다고 적고 닫는다.
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
---
|
||||
id: 2d058fb5-01f1-4be8-80bb-0e377868573c
|
||||
kind: QUESTION
|
||||
slug: steal-time-increase-under-two-cpu-bound-vms
|
||||
title: 가상 머신 두 대를 CPU-bound 로 만들면 게스트 steal time 은 얼마나 오르는가
|
||||
topic: cpu-virtualization
|
||||
topicName: CPU 가상화
|
||||
project: virtualization
|
||||
status: 초안
|
||||
questionStatus: OPEN
|
||||
studio: "https://hyeonworks.com/studio/documents/2d058fb5-01f1-4be8-80bb-0e377868573c/edit"
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#27-문제-영역에서-파생되는-추가-open-question-oq-7
|
||||
- final/document.md#13-steal-time
|
||||
- final/document.md#24-cpu-가상화-계층에서-발생할-수-있는-문제-24-4
|
||||
---
|
||||
|
||||
# 가상 머신 두 대를 CPU-bound 로 만들면 게스트 steal time 은 얼마나 오르는가
|
||||
|
||||
§24.4 는 steal time 을 게스트에 실행할 작업이 있는데도 하이퍼바이저나 호스트가 그 vCPU 스레드를 즉시 실행시키지 못한 시간으로 적었다.
|
||||
게스트의 `top` 에서 `%st` 로 보이는 값이지만, §13 과 §24.4 는 그 값 하나로 원인을 확정해서는 안 된다고 함께 못 박았다.
|
||||
§27 은 이 물음에서 호스트의 CPU 사용률과 실행 대기열, QEMU vCPU 스레드, 각 게스트의 `%st` 를 함께 재라고 적었다.
|
||||
이 호스트에서 `%st` 를 잰 값이 없어서, 부하 전 기준값부터 남겨야 증가폭을 숫자로 적을 수 있다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **KVM 에서 vCPU 가 물리 CPU 에서 실행되기까지**
|
||||
게스트가 관측하는 steal time 이 호스트에서 무엇 때문에 생기는지를 설명한 기록이다.
|
||||
- **가상 머신 두 대에 동시에 부하를 주면 이 호스트에서 vCPU 경쟁이 실제로 생기는가**
|
||||
같은 부하 실행을 쓴다. 경쟁이 있는지를 묻는 쪽과 그것이 `%st` 로 얼마나 보이는지를 묻는 쪽으로 갈린다.
|
||||
- **vCPU 를 늘릴수록 이 호스트에서 Keycloak 처리량도 계속 오르는가**
|
||||
총 vCPU 를 늘린 구성에서 `%st` 가 어떻게 달라지는지가 그쪽 실행에서 다시 나온다.
|
||||
|
||||
## 사실
|
||||
|
||||
§13 은 게스트 Linux 에서 top 등의 CPU 지표를 볼 때 st(steal time)를 확인할 수 있다고 적었다.
|
||||
|
||||
§13 이 적은 steal time 은 이런 상황을 가리키는 단서다.
|
||||
게스트 vCPU 에 실행할 작업이 있고, 호스트에서 vCPU 스레드가 CPU 를 필요로 하는데, 다른 작업 때문에 즉시 실행되지 못한다.
|
||||
|
||||
높은 steal time 은 가상화 환경에서 호스트 CPU 경쟁이나 CPU 초과 할당을 의심할 수 있는 지표 중 하나다.
|
||||
다만 그 값 하나로 원인을 확정해서는 안 되고, 호스트 CPU 포화와 실행 대기열, 친화도, 어떤 작업이 돌고 있었는지를 함께 확인해야 한다고 같은 절이 적었다.
|
||||
|
||||
§24.4 는 게스트에 실행할 작업이 있는데 하이퍼바이저나 호스트가 그 vCPU 스레드를 즉시 실행시키지 못한 시간을 게스트가 steal time 으로 관찰한다고 적었다.
|
||||
그 값은 게스트의 top 등에서 %st 로 본다.
|
||||
같은 절은 높은 steal time 을 호스트 CPU 경쟁 또는 초과 할당을 조사하라는 단서로 두되, 단독으로 원인을 확정하는 지표는 아니라고 적었다.
|
||||
|
||||
§24.3 은 경쟁 대상이 QEMU vCPU 스레드만은 아니라고 적었다.
|
||||
호스트에 Nginx 를 직접 설치하고 가상 머신 두 대를 실행하는 테스트 환경에서는 가상 머신 밖의 호스트 작업도 CPU 경쟁에 포함된다.
|
||||
|
||||
§27 의 OQ-7 은 함께 측정할 항목으로 넷을 들었다.
|
||||
|
||||
Host CPU utilization
|
||||
Host run queue
|
||||
QEMU vCPU thread
|
||||
각 Guest 의 %st
|
||||
|
||||
이 호스트에서 %st 를 실제로 잰 값은 없다.
|
||||
|
||||
## 가정
|
||||
|
||||
두 가상 머신을 동시에 CPU-bound 로 만들면 vCPU 스레드끼리 호스트 CPU 시간을 두고 경쟁하게 된다고 전제한다.
|
||||
이 호스트의 논리 CPU 수와 두 가상 머신의 vCPU 합계가 SSOT 에 적혀 있지 않아서, 경쟁이 생기는 구성인지도 아직 전제로만 두었다.
|
||||
|
||||
두 게스트에 같은 정도의 부하를 걸 수 있다고 전제한다.
|
||||
한쪽이 더 세게 걸리면 두 %st 를 나란히 놓고 읽을 수 없다.
|
||||
|
||||
부하 전과 부하 중을 각각 한 번씩 찍으면 비교할 수 있다고 전제한다.
|
||||
값이 흔들리면 한 번씩 찍은 두 값만으로는 증가인지 흔들림인지 갈리지 않는다.
|
||||
|
||||
## 미지수
|
||||
|
||||
두 가상 머신을 동시에 CPU-bound 로 만들었을 때 각 게스트의 %st 가 부하 전 대비 얼마나 오르는가.
|
||||
|
||||
그 증가가 호스트 실행 대기열과 같은 방향으로 움직이는가.
|
||||
|
||||
이 호스트의 논리 CPU 수는 얼마이고 두 가상 머신의 vCPU 합계는 그 수에 견줘 어느 정도인가.
|
||||
|
||||
## 제약
|
||||
|
||||
§13 이 steal time 하나로 원인을 확정하지 않는다고 적었으므로 %st 만 재서는 이 물음이 닫히지 않는다.
|
||||
|
||||
부하 전 기준값을 먼저 남기지 않으면 부하 중 값만으로 증가를 적을 수 없다.
|
||||
|
||||
네 항목을 같은 시각에 찍어야 서로 견줄 수 있다. 따로 찍은 값을 한 표에 놓으면 부하 구간이 어긋난다.
|
||||
|
||||
부하 구간에 호스트에서 Nginx 같은 다른 작업이 함께 돌면 %st 의 증가분을 두 가상 머신 사이의 경쟁으로만 읽을 수 없다.
|
||||
|
||||
OQ-1 과 이 물음은 같은 부하 실행에서 값을 얻는다. 그래도 한 물음으로 합치지 않았다.
|
||||
합치면 경쟁이 있었다는 결론과 그것이 %st 로 얼마나 보였다는 결론 가운데 어느 쪽 기준으로 닫혔는지가 남지 않는다.
|
||||
|
||||
## 선택지
|
||||
|
||||
### 1. 부하 전후로 네 항목을 같은 실행에서 남긴다
|
||||
|
||||
§27 이 든 네 항목을 부하 전에 한 번, 두 가상 머신을 동시에 CPU-bound 로 만든 뒤에 한 번 같은 시각으로 기록한다.
|
||||
%st 가 올랐을 때 호스트 실행 대기열도 함께 올랐는지를 같은 표에서 읽을 수 있다.
|
||||
|
||||
실행이 한 번이라 값이 흔들리는 정도는 알 수 없다. 흔들림이 의심되면 부하 구간을 늘려 여러 번 찍는다.
|
||||
|
||||
### 2. 제외 — %st 만 부하 전후로 비교한다
|
||||
|
||||
찍을 것이 하나뿐이라 실행은 가볍다.
|
||||
다만 §13 이 함께 확인하라고 든 호스트 CPU 포화와 실행 대기열이 빠져서, %st 가 올라도 그것이 호스트 경쟁 때문인지 게스트 쪽 작업 때문인지 가를 수 없다.
|
||||
|
||||
### 3. 제외 — 가상 머신 한 대만 CPU-bound 로 만들어 비교군을 하나 더 만든다
|
||||
|
||||
이 물음이 묻는 것은 두 대를 동시에 부하로 만든 상태이고, 부하 전 값이 이미 비교군이다.
|
||||
한 대 부하가 필요해지면 1번 실행을 마친 뒤에 따로 더한다.
|
||||
|
||||
## 다음 검증
|
||||
|
||||
1. 부하 전에 Host CPU utilization 과 run queue, ps -eLo pid,tid,psr,pcpu,comm | grep qemu (§14.7) 의 QEMU vCPU thread 사용량, 각 Guest 의 top (§14.8) %st 를 기준값으로 남긴다.
|
||||
2. 가상 머신 두 대를 동시에 CPU-bound 로 만든다.
|
||||
3. 같은 네 항목을 같은 시각에 다시 기록한다.
|
||||
|
||||
닫는 조건 : 부하 전후의 %st 와 호스트 실행 대기열을 한 표로 놓을 수 있으면 그 표가 Case 가 되고 닫는다. %st 가 부하 전과 다르지 않으면 이 호스트 구성에서는 두 가상 머신 동시 부하가 steal time 으로 나타나는 경쟁을 만들지 않는다고 적는다. 그 결론은 OQ-1 과 함께 닫는다.
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
---
|
||||
id: 116b806c-b176-401c-9363-1489fffb721c
|
||||
kind: QUESTION
|
||||
slug: throttling-vs-contention
|
||||
title: cgroup CPU 제한과 호스트 vCPU 경쟁을 지표로 가를 수 있는가
|
||||
topic: cpu-virtualization
|
||||
topicName: CPU 가상화
|
||||
project: virtualization
|
||||
status: 초안
|
||||
questionStatus: OPEN
|
||||
studio: "https://hyeonworks.com/studio/documents/116b806c-b176-401c-9363-1489fffb721c/edit"
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#27-문제-영역에서-파생되는-추가-open-question-oq-9
|
||||
- final/document.md#24-cpu-가상화-계층에서-발생할-수-있는-문제-24-8
|
||||
- final/document.md#25-cpu-문제를-계층별로-구분하는-진단표
|
||||
---
|
||||
|
||||
# cgroup CPU 제한과 호스트 vCPU 경쟁을 지표로 가를 수 있는가
|
||||
|
||||
가상 머신 안에서 K3s 를 돌리는 지금 구성에서는 Keycloak 파드가 자신에게 걸린 CPU 상한에 막혀 느려진 것(cgroup throttling)과 호스트 CPU 를 제때 받지 못해 느려진 것(Host vCPU contention)이 같은 애플리케이션 지연으로 관찰될 수 있다. 계층별 진단표는 두 원인을 다른 계층에 갈라 놓고 먼저 볼 항목도 따로 적어 두었다. 다만 이 호스트에서 두 원인을 각각 재현해 그 항목들이 실제로 다르게 움직이는지는 확인하지 않았다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **KVM 에서 vCPU 가 물리 CPU 에서 실행되기까지**
|
||||
호스트 CPU 경쟁이 게스트 안에서 어떤 값으로 드러나는지를 이 개념이 설명한다.
|
||||
- **Keycloak 동시 refresh 실험 중 CPU 가상화 계층이 결과를 흔들 만큼 포화되는가**
|
||||
그쪽에서 가상화 계층 지표가 움직인다는 답이 나오면, 그 움직임을 cgroup 쪽 제한과 가를 수 있는지는 이 질문이 받는다.
|
||||
|
||||
## 사실
|
||||
|
||||
- 가상 머신 안에서 K3s 를 돌리면 물리 CPU 와 Keycloak 파드 사이에 호스트·하이퍼바이저, 게스트 Linux, K3s, cgroup CPU limit 이 차례로 놓인다.
|
||||
- 호스트 CPU 에 여유가 있어도 Keycloak 파드는 설정된 CPU 상한 때문에 실행이 제한될 수 있다.
|
||||
- 호스트 CPU 를 받지 못해 생긴 문제와 파드가 자신의 CPU 상한을 넘겨 생긴 문제는 서로 다른 계층에 있다.
|
||||
앞의 것은 경쟁과 steal time, 스케줄링 쪽이고 뒤의 것은 cgroup 제한 쪽이다.
|
||||
- cgroup 제한 자체는 KVM 문제가 아니다.
|
||||
그래도 가상 머신 안에서 K3s 를 운영하는 지금 실험에서는 두 원인이 같은 애플리케이션 지연으로 관찰될 수 있어 진단 경계에 넣었다.
|
||||
개념 문서가 다루기로 한 범위는 CPU 가상화 하나인데, cgroup 제한은 그 범위 밖에서 진단 경계 안으로 들여온 항목이다.
|
||||
- 계층별 진단표가 적은 우선 확인 항목은 이렇게 갈린다.
|
||||
CPU throttling : CPU limit · throttled time
|
||||
CPU contention : 호스트 CPU · 실행 대기열 · CPU 별 사용률
|
||||
- 같은 표가 두 원인의 대표적인 현상도 갈라 적었다.
|
||||
cgroup 쪽은 파드가 CPU 를 더 쓰려 해도 상한에 막힌 것이고, 경쟁 쪽은 실행 가능한 작업이 늘면서 지연이 늘어난 것이다.
|
||||
갈라 적힌 현상은 각 계층에서 보이는 모습이라, 애플리케이션 지연만 보고 있으면 그 구분이 드러나지 않는다.
|
||||
- 그 표는 지표 하나로 원인을 확정하려고 만든 것이 아니라 어느 계층부터 조사할지 범위를 줄인다.
|
||||
- 이 호스트에서 throttled time 도 steal time 도 잰 값이 없다.
|
||||
|
||||
## 가정
|
||||
|
||||
- 같은 크기의 Keycloak 지연 증가를 두 원인으로 각각 만들어 낼 수 있다고 보고 비교를 짠다.
|
||||
두 재현이 실제로 같은 크기가 되는지는 해 보기 전에 알 수 없다.
|
||||
- 다른 가상 머신에 CPU 부하를 걸면 호스트 쪽 경쟁이 생긴다고 본다.
|
||||
이 호스트에서 두 가상 머신 동시 부하가 경쟁을 만드는지는 아직 관측하지 않았다.
|
||||
- Keycloak 파드의 cgroup CPU limit 은 실험을 위해 낮췄다가 되돌릴 수 있다고 본다.
|
||||
- 두 재현 사이에 다른 조건이 바뀌지 않는다는 것을 전제로 두 실행을 견준다.
|
||||
|
||||
## 미지수
|
||||
|
||||
- 두 원인을 각각 재현했을 때 throttled time · CPU limit · 게스트 CPU 와 %st, 호스트 CPU 와 실행 대기열이 서로 다른 조합으로 움직이는지.
|
||||
- 갈린다면 어느 항목이 가장 먼저 갈리는지.
|
||||
- 지금 Keycloak 파드에 CPU limit 이 걸려 있는지, 걸려 있다면 얼마인지. 개념 문서에 적혀 있지 않다.
|
||||
- throttled time 을 이 환경에서 어떤 경로로 읽는지. 개념 문서는 확인 항목으로만 적고 실제로 읽는 명령을 적지 않았다.
|
||||
- 두 원인이 동시에 걸린 구간에서도 지표가 갈리는지. 이번 검증은 하나씩 만든 두 실행만 견준다.
|
||||
|
||||
## 제약
|
||||
|
||||
- 두 재현이 비슷한 크기의 지연 증가를 만들어야 지표 조합을 견줄 수 있다.
|
||||
- 지표 하나로 원인을 확정하지 않는다. 게스트 · 호스트 · K3s 세 쪽을 같은 시각에 받아 적는다.
|
||||
- refresh 경쟁 실험은 CPU 자원을 여유 있게 둔 상태에서 먼저 돌리므로 이 재현은 그 실험과 같은 시간에 걸지 않는다.
|
||||
- 이 호스트의 논리 CPU 수와 두 가상 머신의 vCPU 수를 개념 문서가 적어 두지 않았다.
|
||||
부하 쪽 재현이 실제로 경쟁을 만드는지는 그 두 값을 적은 뒤에 판단한다.
|
||||
|
||||
## 선택지
|
||||
|
||||
### 1. 두 원인을 따로 재현해 지표 조합을 나란히 놓는다
|
||||
|
||||
Keycloak 파드의 cgroup CPU limit 을 낮춰 한 번, 다른 가상 머신에 CPU 부하를 걸어 한 번, 같은 크기의 지연을 두 번 만든다.
|
||||
두 실행에서 게스트 · 호스트 · K3s 쪽 항목을 같은 이름으로 받아 적으면 어느 항목이 두 재현에서 다르게 움직이는지 그대로 보인다.
|
||||
|
||||
셋 가운데 손이 가장 많이 간다. 부하 쪽 재현은 다른 가상 머신을 함께 써야 하고, cgroup 쪽 재현은 파드 설정을 바꿨다가 되돌려야 한다.
|
||||
|
||||
### 2. 호스트 쪽 항목부터 보고 범위를 줄인다
|
||||
|
||||
호스트 CPU 와 실행 대기열, CPU 별 사용률을 먼저 본다.
|
||||
호스트에 여유가 있는데 파드만 느리면 조사 범위가 cgroup 쪽으로 줄어든다.
|
||||
|
||||
호스트가 포화된 것으로 나오면 거기서 끝나지 않는다. 그 구간에 파드의 상한도 함께 걸려 있었는지는 이 순서로 가려낼 수 없어서, 결국 첫 번째 선택지의 두 재현으로 돌아간다.
|
||||
|
||||
### 3. throttled time 하나로 가른다 — 제외
|
||||
|
||||
throttled time 이 0 이 아니면 cgroup 제한, 0 이면 호스트 경쟁으로 읽는 방법이다.
|
||||
진단표는 두 행에 각각 두 항목 이상을 적어 두었다. 지표 하나로 원인을 확정하지 않기로 하고 만든 표이므로 이 순서는 쓰지 않는다.
|
||||
|
||||
## 다음 검증
|
||||
|
||||
1. 부하를 걸기 전에 파드의 throttled time 과 CPU limit, 게스트 CPU 와 게스트에서 top 으로 본 %st, 호스트 CPU 와 실행 대기열을 한 번 찍어 기준값으로 둔다.
|
||||
2. Keycloak 파드의 cgroup CPU limit 을 낮춰 Keycloak 지연을 올리고 같은 다섯 항목을 같은 시각에 받아 적는다.
|
||||
3. limit 을 되돌린 뒤, 이번에는 다른 가상 머신에 CPU 부하를 걸어 비슷한 크기의 지연을 만들고 같은 다섯 항목을 다시 받아 적는다.
|
||||
4. 두 실행의 기록을 기준값과 함께 한 표에 나란히 놓는다.
|
||||
|
||||
닫는 조건 : 두 재현에서 지표 조합이 갈리면 그 대조표가 Case 가 되고, 진단표의 두 행을 어떤 순서로 보는지 정하는 근거가 된다. 갈리지 않으면 「이 지표들로는 두 원인이 구분되지 않는다」를 적고, 무엇을 더 봐야 하는지를 새 Question 으로 낸 뒤 이 질문은 닫는다.
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
---
|
||||
id: 723d8930-9e82-4552-8376-038a6446ec71
|
||||
kind: QUESTION
|
||||
slug: throughput-vs-vcpu-count
|
||||
title: vCPU 를 늘릴수록 이 호스트에서 Keycloak 처리량도 계속 오르는가
|
||||
topic: cpu-virtualization
|
||||
topicName: CPU 가상화
|
||||
project: virtualization
|
||||
status: 초안
|
||||
questionStatus: OPEN
|
||||
studio: "https://hyeonworks.com/studio/documents/723d8930-9e82-4552-8376-038a6446ec71/edit"
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#27-문제-영역에서-파생되는-추가-open-question-oq-8
|
||||
- final/document.md#11-vm의-4-vcpu는-정확히-무엇을-의미하는가
|
||||
- final/document.md#24-cpu-가상화-계층에서-발생할-수-있는-문제-24-6
|
||||
---
|
||||
|
||||
# vCPU 를 늘릴수록 이 호스트에서 Keycloak 처리량도 계속 오르는가
|
||||
|
||||
§11 은 4 vCPU 를 실행 컨텍스트 4개를 주는 것으로 적었지, 물리 CPU 4개를 가상 머신이 영구적으로 소유하는 것으로 적지 않았다.
|
||||
§24.6 은 vCPU 를 많이 할당한다고 항상 성능이 좋아지지는 않는다고 적었다.
|
||||
§27 은 비교 구성으로 2 vCPU · 4 vCPU · 8 vCPU 를 들었다.
|
||||
세 구성에 같은 Keycloak 부하를 걸어 본 기록이 이 호스트에 없어서, 처리량이 어디서부터 더 오르지 않는지는 모른다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **KVM 에서 vCPU 가 물리 CPU 에서 실행되기까지**
|
||||
vCPU 수가 무엇을 뜻하고 호스트 CPU 가 부족할 때 무엇이 경쟁하는지를 설명한 기록이다.
|
||||
- **가상 머신 두 대를 CPU-bound 로 만들면 게스트 steal time 은 얼마나 오르는가**
|
||||
총 vCPU 를 늘린 구성에서 `%st` 가 어떻게 나오는지를 그쪽과 같은 방법으로 읽는다.
|
||||
|
||||
## 사실
|
||||
|
||||
§11 은 4 vCPU 를 게스트 OS 가 최대 4개의 CPU 실행 흐름을 가질 수 있도록 가상 CPU 실행 컨텍스트 4개를 제공하는 뜻으로 적었다.
|
||||
호스트의 물리 CPU 4개를 가상 머신이 영구적으로 소유한다는 뜻은 아니다.
|
||||
호스트 CPU 가 부족하면 QEMU vCPU 스레드와 호스트의 다른 작업이 같은 논리 CPU 자원을 두고 경쟁할 수 있다고 같은 절이 덧붙였다.
|
||||
|
||||
§24.6 은 특정 가상 머신에 vCPU 를 많이 할당한다고 항상 성능이 좋아지는 것은 아니라고 적었다.
|
||||
게스트 쪽 작업이 실제로 그만큼의 병렬성을 쓰지 못하거나 호스트 전체 CPU 에 비해 지나치게 많은 vCPU 를 할당하면 스케줄링 대상만 늘 수 있다.
|
||||
§24.6 은 이것을 「vCPU 수가 많다 = 항상 빠르다로 판단하지 않는다」는 한 줄로 못 박았다.
|
||||
그래서 실제 작업의 병렬성과 호스트 전체 CPU 를 함께 확인해야 한다고 같은 절이 적었다.
|
||||
|
||||
§27 의 OQ-8 은 vCPU 추가가 실제 처리량과 지연에 어떤 영향을 주는지 확인하는 비교 구성으로 2 vCPU / 4 vCPU / 8 vCPU 를 들었다.
|
||||
|
||||
이 호스트에서 vCPU 수를 바꿔 가며 Keycloak 처리량을 잰 값은 없다.
|
||||
|
||||
## 가정
|
||||
|
||||
세 구성에서 Keycloak 설정과 부하 도구, 부하 크기를 같게 둘 수 있다고 전제한다.
|
||||
vCPU 수만 달라져야 처리량 차이를 vCPU 수 탓으로 읽을 수 있다.
|
||||
|
||||
Keycloak 작업이 vCPU 를 늘린 만큼 병렬로 쓴다고는 전제하지 않는다.
|
||||
§24.6 이 든 두 원인 가운데 병렬성 부족 쪽은 이 작업에서 확인하지 않았다.
|
||||
|
||||
2 · 4 · 8 세 점으로 처리량이 꺾이는 구간을 짚을 수 있다고 전제한다.
|
||||
상한이 4 와 8 사이에 있으면 세 점만으로는 몇 vCPU 인지 나오지 않는다.
|
||||
§27 은 그 세 구성을 「예를 들어」로 들었다. 사이 값이 필요해지면 점을 더해도 그 절이 든 비교 구성을 벗어나지 않는다.
|
||||
|
||||
## 미지수
|
||||
|
||||
vCPU 를 2 에서 4, 8 로 올렸을 때 처리량과 지연이 어디서부터 더 좋아지지 않는가.
|
||||
|
||||
좋아지지 않는다면 원인이 작업의 병렬성 부족인가 호스트 전체 CPU 부족인가.
|
||||
|
||||
이 호스트의 논리 CPU 수는 얼마이고, 8 vCPU 구성이 그 수를 넘는가.
|
||||
|
||||
## 제약
|
||||
|
||||
vCPU 수만 바꾸고 Keycloak 설정과 부하는 세 실행에서 고정한다.
|
||||
|
||||
8 vCPU 가 호스트 논리 CPU 수를 넘는지에 따라 재는 것이 작업의 병렬성인지 초과 할당인지 달라지므로, 호스트 논리 CPU 수를 먼저 적는다.
|
||||
|
||||
§24.6 이 든 두 원인을 가르려면 처리량과 지연만으로는 부족하다. 게스트의 %st 를 같은 실행에서 남긴다.
|
||||
|
||||
## 선택지
|
||||
|
||||
### 1. 2 · 4 · 8 세 구성에 같은 부하를 걸고 처리량과 지연, %st 를 남긴다
|
||||
|
||||
§27 이 든 비교 구성 그대로다. 세 값이 한 표에 놓이면 어느 구간부터 처리량이 더 오르지 않는지를 그 표에서 읽는다.
|
||||
%st 를 함께 남기므로 처리량이 멈춘 구간에서 호스트가 포화됐는지 아닌지를 가를 수 있다.
|
||||
|
||||
가상 머신을 세 번 다시 구성하고 부하를 세 번 걸어야 해서 실행이 가장 무겁다.
|
||||
|
||||
### 2. 제외 — 2 와 4 만 재고 8 을 건너뛴다
|
||||
|
||||
실행이 둘로 줄지만 §24.6 이 말한 지나치게 많은 vCPU 쪽은 8 구성에서만 나타난다.
|
||||
2 에서 4 로 오르는 것만 보고 vCPU 를 더 줘도 된다고 적으면 §24.6 이 막으려던 판단을 그대로 하게 된다.
|
||||
|
||||
### 3. 제외 — 호스트 CPU 사용률만 보고 vCPU 수를 정한다
|
||||
|
||||
사용률이 낮아도 게스트 쪽 작업이 병렬성을 쓰지 못하면 처리량은 오르지 않는다.
|
||||
§24.6 이 병렬성과 호스트 전체 CPU 를 함께 확인하라고 적었으므로 처리량을 직접 잰다.
|
||||
|
||||
## 다음 검증
|
||||
|
||||
1. 가상 머신 구성을 2 vCPU · 4 vCPU · 8 vCPU 로 바꿔 가며 같은 Keycloak 부하를 걸고 처리량과 지연을 각각 기록한다.
|
||||
2. 세 실행 모두에서 호스트 논리 CPU 수 대비 총 vCPU 를 함께 적는다.
|
||||
3. 세 실행 모두에서 게스트의 top (§14.8) %st 를 남겨 §24.6 이 말한 병렬성 부족과 호스트 전체 CPU 부족을 갈라 둔다.
|
||||
|
||||
닫는 조건 : 처리량이 더 오르지 않기 시작하는 vCPU 수가 나오면 그 지점을 이 작업의 상한으로 적고 그 세 실행이 Case 가 된다. 세 구성이 모두 같은 방향으로 오르면 이 호스트에서는 상한에 닿지 않았다고 적고 닫는다. 어느 vCPU 수로 운영할지는 그 뒤 Decision 이 정한다.
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
---
|
||||
id: eba4a877-276a-4050-9a97-d32614546c71
|
||||
kind: QUESTION
|
||||
slug: vcpu-contention-under-two-vm-load
|
||||
title: 가상 머신 두 대에 동시에 부하를 주면 이 호스트에서 vCPU 경쟁이 실제로 생기는가
|
||||
topic: cpu-virtualization
|
||||
topicName: CPU 가상화
|
||||
project: virtualization
|
||||
status: 초안
|
||||
questionStatus: OPEN
|
||||
studio: "https://hyeonworks.com/studio/documents/eba4a877-276a-4050-9a97-d32614546c71/edit"
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#20-이-concept에서-파생되는-open-question-oq-1
|
||||
- final/document.md#12-cpu-contention과-overcommit
|
||||
- final/document.md#16-현재-keycloak-k3s-실험과의-관계
|
||||
---
|
||||
|
||||
# 가상 머신 두 대에 동시에 부하를 주면 이 호스트에서 vCPU 경쟁이 실제로 생기는가
|
||||
|
||||
Keycloak 멀티 노드 실험은 물리 호스트 한 대 위의 가상 머신 두 대에서 돌아간다. 두 가상 머신이 동시에 CPU 를 많이 쓰면 QEMU vCPU 스레드끼리 호스트 CPU 시간을 두고 경쟁할 수 있다. 그런데 이 호스트에 논리 CPU 가 몇 개인지도, 두 가상 머신에 vCPU 를 몇 개씩 줬는지도 문서에 적혀 있지 않아 경쟁이 생길 조건인지부터 모른다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **KVM 에서 vCPU 가 물리 CPU 에서 실행되기까지**
|
||||
vCPU 스레드가 호스트 Linux 스케줄러를 거쳐 논리 CPU 에 배치되는 경로를 이 개념이 설명한다.
|
||||
- **가상 머신 두 대를 CPU-bound 로 만들면 게스트 steal time 은 얼마나 오르는가**
|
||||
같은 부하를 걸고 게스트 쪽 지표만 따로 묻기 때문에, 호스트 쪽 지표까지 한 번에 찍으면 두 질문이 같은 측정으로 닫힌다. 한 물음으로 합칠지는 값을 잰 뒤에야 알 수 있어 지금은 둘로 두고 관계로만 이었다.
|
||||
|
||||
## 사실
|
||||
|
||||
§12 는 호스트에 논리 CPU 가 12개 있고 8 vCPU 짜리 가상 머신 넷을 올린 예를 드는데, 그러면 총 32 vCPU 가 12개의 논리 CPU 위에서 실행될 수 있다.
|
||||
|
||||
모든 가상 머신이 동시에 CPU 를 많이 사용하면 vCPU 스레드끼리 호스트 CPU 시간을 두고 경쟁한다 : §12
|
||||
이런 상황에서는 게스트 안의 애플리케이션이 느려졌더라도 원인이 그 애플리케이션이 아니라 호스트 CPU 경쟁일 수 있다 : §12
|
||||
|
||||
§16 이 그린 테스트 환경은 Physical Host 아래 Host Nginx 와 가상 머신 두 대이고, VM 1 과 VM 2 가 각각 K3s 노드와 Keycloak 을 돌린다.
|
||||
|
||||
§16 이 결과 해석에서 분리하라고 든 원인 일곱
|
||||
|
||||
Keycloak refresh/session 동시성
|
||||
PostgreSQL contention/locking
|
||||
Redis 상태 관리
|
||||
K3s resource scheduling
|
||||
VM vCPU scheduling
|
||||
Host CPU saturation
|
||||
Nginx/LB
|
||||
|
||||
§20 이 이 물음의 확인 대상으로 든 다섯
|
||||
|
||||
Host logical CPU 수
|
||||
각 VM vCPU 수
|
||||
QEMU vCPU thread CPU 사용량
|
||||
Host run queue
|
||||
Guest steal time
|
||||
|
||||
이 호스트의 논리 CPU 수 : 문서에 없음
|
||||
두 가상 머신의 vCPU 수 : 문서에 없음
|
||||
|
||||
§12 의 12 와 8 과 32 는 경쟁이 어떻게 생기는지 설명하려고 든 예시 숫자이지 이 호스트에서 읽은 값이 아니다.
|
||||
|
||||
## 가정
|
||||
|
||||
두 가상 머신은 같은 물리 호스트의 CPU 를 나눠 쓴다. §16 이 그린 구조가 지금 실험 환경과 같다고 전제한다.
|
||||
|
||||
두 가상 머신에 같은 시각에 CPU 부하를 걸 수 있고, 그때 호스트와 두 게스트의 지표를 같은 시각에 기록할 수 있다.
|
||||
|
||||
게스트 Linux 의 top 이 이 환경에서 %st 를 실제로 채워 준다. %st 열이 보여 주는 steal time 은 게스트의 vCPU 에 실행할 작업이 있는데도 다른 작업 때문에 곧바로 실행되지 못한 상황을 가리킨다(§13). 이 열이 비면 게스트 쪽에서는 경쟁의 단서를 못 본다.
|
||||
|
||||
호스트 쪽 Nginx 도 같은 물리 CPU 를 쓰는데, 부하 구간의 호스트 CPU 사용량을 전부 QEMU vCPU 스레드 몫으로 읽으면 이 전제가 깨진다.
|
||||
|
||||
## 미지수
|
||||
|
||||
이 호스트의 논리 CPU 수.
|
||||
|
||||
두 가상 머신에 준 vCPU 수와 그 합. 그 합이 논리 CPU 수를 넘는지.
|
||||
|
||||
부하를 걸기 전 QEMU vCPU 스레드의 CPU 사용량과 호스트의 실행 대기열(run queue), 그리고 두 게스트의 %st. 부하 전 값이 없으면 부하 중 값만 가지고는 그 값이 움직인 것인지 판정할 수 없다.
|
||||
|
||||
두 가상 머신에 동시에 부하를 걸었을 때 그 세 지표가 어떻게 움직이는지.
|
||||
|
||||
vCPU 합이 논리 CPU 수 이하인데도 %st 가 오르는 구간이 있는지.
|
||||
|
||||
## 제약
|
||||
|
||||
이 호스트에서 잰 값이 하나도 없다. 근거는 개념을 정리한 문서 한 편이고 §12 의 숫자는 예시다.
|
||||
|
||||
§20 이 확인 대상을 다섯으로 잡았으므로 steal time 하나만 찍고 닫을 수 없다. 호스트 쪽 사용량과 실행 대기열도 함께 본다. 이 물음은 닫는 조건이 없다고 보아 한 번 기록에서 뺐다가 되돌렸다. §20 이 이미 적어 둔 확인 대상 다섯이 그대로 닫는 조건이 되기 때문이다.
|
||||
|
||||
§16 은 CPU 가상화가 Keycloak refresh token 경쟁의 원인은 아니라고 못박는다. 이 질문은 원인을 찾으려는 것이 아니라 실험 결과를 어느 계층 쪽으로 읽을지 가르는 데 쓴다.
|
||||
|
||||
측정하는 동안 실험 환경 구성은 바꾸지 않는다. pinning 을 걸거나 vCPU 수를 줄이면 지금 구성에서 경쟁이 생기는지를 못 본다.
|
||||
|
||||
## 선택지
|
||||
|
||||
### 1. 호스트 논리 CPU 수와 각 가상 머신의 vCPU 수를 먼저 읽는다
|
||||
|
||||
이 두 값은 부하를 걸지 않고도 지금 읽을 수 있다. 둘만 있으면 vCPU 합이 논리 CPU 수를 넘는지 갈리고, 넘지 않는 구성이라면 §12 가 든 32 대 12 같은 초과 할당 상태는 아니라는 것까지 확정된다. 움직이는 나머지 셋은 그 뒤에 잰다.
|
||||
|
||||
### 2. 다섯 지표를 부하 전과 부하 중 두 시점에 찍는다
|
||||
|
||||
부하를 걸기 전에 한 번, 두 가상 머신에 동시에 부하를 건 뒤에 한 번, 같은 명령으로 기록한다. 호스트 쪽은 ps -eLo pid,tid,psr,pcpu,comm | grep qemu 로 QEMU vCPU 스레드를 보고, 게스트 쪽은 top 으로 %st 를 본다.
|
||||
|
||||
### 3. 가상 머신 한 대에만 먼저 부하를 걸어 본다
|
||||
|
||||
한 대만 걸었을 때 지표가 어떻게 움직이는지 알아 두면, 두 대를 동시에 걸었을 때 늘어난 값이 vCPU 경쟁 때문인지 그 가상 머신 안의 CPU 포화 때문인지 갈린다. 다만 이 질문은 두 대를 동시에 돌렸을 때를 묻기 때문에 이 측정만으로는 닫히지 않는다.
|
||||
|
||||
### 4. 제외 — pinning 을 걸어 놓고 잰다
|
||||
|
||||
vCPU 스레드를 특정 논리 CPU 에 고정하면 경쟁 구간을 만들기 쉽다. 그러나 지금 구성에서 경쟁이 생기는지를 묻는 질문이라, 구성을 바꾸고 잰 값은 답이 되지 않는다. pinning 은 별도 질문으로 뺀다.
|
||||
|
||||
## 다음 검증
|
||||
|
||||
1. 호스트 논리 CPU 수와 각 가상 머신의 vCPU 수를 먼저 적는다. §20 이 든 확인 대상 다섯 가운데 부하 없이 읽을 수 있는 둘이다.
|
||||
2. 부하를 걸기 전에 호스트에서 ps -eLo pid,tid,psr,pcpu,comm | grep qemu (§14.7) 로 QEMU vCPU 스레드의 CPU 사용량을, 호스트 실행 대기열을, 두 게스트에서 top (§14.8) 으로 %st 를 같은 시각에 기록한다.
|
||||
3. VM 1 과 VM 2 에 동시에 CPU 부하를 건다.
|
||||
4. 부하 중에 2번의 세 지표를 같은 방법으로 다시 기록한다.
|
||||
|
||||
닫는 조건 : 두 가상 머신의 vCPU 합이 호스트 논리 CPU 이하이고 동시 부하에서도 게스트 %st 와 호스트 실행 대기열이 부하 전과 다르지 않으면, 이 호스트 구성에서는 경쟁이 관측되지 않는다고 적고 닫는다. %st 나 실행 대기열이 움직이면 그 부하 전후 표가 Case 가 되고 이 질문은 그 Case 가 닫는다. vCPU 수를 줄일지 가상 머신 배치를 바꿀지는 그 Case 가 나온 뒤 Decision 으로 넘긴다.
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
---
|
||||
id: cd58d35d-3a93-4682-82b8-b64ce3a8813c
|
||||
kind: QUESTION
|
||||
slug: vcpu-thread-migration-without-pinning
|
||||
title: pinning 없이 vCPU 스레드는 호스트 논리 CPU 사이를 옮겨 다니는가
|
||||
topic: cpu-virtualization
|
||||
topicName: CPU 가상화
|
||||
project: virtualization
|
||||
status: 초안
|
||||
questionStatus: OPEN
|
||||
studio: "https://hyeonworks.com/studio/documents/cd58d35d-3a93-4682-82b8-b64ce3a8813c/edit"
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#20-이-concept에서-파생되는-open-question-oq-5
|
||||
- final/document.md#5-host-linux-scheduler와-실제-cpu
|
||||
- final/document.md#14-실제-linux에서-확인할-수-있는-것-14-7
|
||||
---
|
||||
|
||||
# pinning 없이 vCPU 스레드는 호스트 논리 CPU 사이를 옮겨 다니는가
|
||||
|
||||
§5 는 QEMU vCPU 스레드도 다른 호스트 스레드와 마찬가지로 Linux 스케줄러가 실행할 논리 CPU 를 정한다고 적었다.
|
||||
관찰 수단은 §14.7 이 든다. `PSR` 은 `ps` 가 찍어 주는 값이고, 그 스레드가 최근 실행된 논리 CPU 를 가리킨다.
|
||||
다만 이 호스트에서 `PSR` 을 찍어 본 기록이 없어서, 같은 vCPU 스레드가 시간에 따라 다른 논리 CPU 로 옮겨 가는지는 아직 모른다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **KVM 에서 vCPU 가 물리 CPU 에서 실행되기까지**
|
||||
vCPU 스레드가 호스트 Linux 스케줄러의 스케줄링 대상이라는 설명이 이 물음의 전제다.
|
||||
- **CPU pinning 전후로 Keycloak 지연과 vCPU 스케줄링 변동이 달라지는가**
|
||||
이동이 실제로 일어나야 pinning 을 걸어 볼 이유가 생긴다.
|
||||
- **게스트가 유휴 상태일 때 vCPU 스레드는 이 테스트 환경에서 어떻게 보이는가**
|
||||
두 물음이 같은 `ps` 출력을 읽으므로 한 번 찍어 둘을 같이 본다.
|
||||
|
||||
## 사실
|
||||
|
||||
§5 는 QEMU vCPU 스레드도 다른 호스트 스레드와 마찬가지로 Linux 스케줄러가 실행할 논리 CPU 를 정한다고 적었다.
|
||||
그래서 같은 vCPU 스레드라도 시간에 따라 서로 다른 논리 CPU 에서 실행될 수 있다.
|
||||
같은 절은 그 이동을 세 시점으로 그려 두었다. T1 에 CPU7 에서 돌던 vCPU 스레드 0 이 T2 에는 CPU7 을 다른 스레드에게 내주고, T3 에는 CPU3 에서 돈다.
|
||||
CPU pinning 을 적용하면 실행 위치를 특정 논리 CPU 집합으로 제한할 수 있다고 같은 절이 덧붙였다.
|
||||
|
||||
§14.7 은 ps -eLo pid,tid,psr,pcpu,comm | grep qemu 를 들고, PSR 로 스레드가 최근 실행된 논리 CPU 를 관찰할 수 있다고 적었다.
|
||||
같은 절은 그 값이 vCPU 가 물리 CPU 에 영구 고정되어 있다는 뜻은 아니라고 못 박고, pinning 을 하지 않았다면 스케줄링에 따라 달라질 수 있다고 덧붙였다.
|
||||
|
||||
§20 의 OQ-5 는 PSR 과 스케줄러 추적으로 관찰한다고만 적었을 뿐 관찰한 결과는 남기지 않았다.
|
||||
|
||||
이 호스트에서 PSR 을 실제로 찍은 기록은 SSOT 에 없다.
|
||||
|
||||
## 가정
|
||||
|
||||
두 가상 머신에 pinning 이나 CPU 친화도를 걸지 않았다고 전제하고 이 물음을 세웠다.
|
||||
실제로 걸었는지는 SSOT 에 적혀 있지 않다.
|
||||
|
||||
일정 간격으로 여러 번 찍으면 이동이 PSR 값의 변화로 잡힌다고 전제한다.
|
||||
다만 표본과 표본 사이에 다른 논리 CPU 로 갔다가 돌아오면 두 표본은 같은 값으로 나온다.
|
||||
|
||||
게스트 부하가 있을 때와 없을 때 호스트 스케줄러가 스레드를 놓는 논리 CPU 가 달라질 수 있다고 전제한다.
|
||||
§5 는 스케줄러가 실행할 논리 CPU 를 정한다고만 적었지 부하에 따라 무엇이 달라지는지는 적지 않았다.
|
||||
|
||||
## 미지수
|
||||
|
||||
이 호스트에서 pinning 없이 같은 vCPU 스레드의 PSR 이 시간에 따라 실제로 바뀌는가.
|
||||
|
||||
바뀐다면 게스트가 유휴일 때와 CPU 부하가 있을 때가 서로 다른가.
|
||||
|
||||
현재 두 가상 머신에 친화도가 걸려 있는가.
|
||||
|
||||
스케줄러 추적을 이 호스트에서 쓸 수 있는가. §20 이 관찰 수단으로 들었지만 지원 여부는 확인하지 않았다.
|
||||
|
||||
## 제약
|
||||
|
||||
이 프로젝트에는 이 호스트에서 잰 값이 하나도 없어서, 지금 이동 여부를 적으면 관측이 아니라 추측이 된다.
|
||||
|
||||
PSR 은 최근 실행된 논리 CPU 하나만 보여 주므로 두 표본 사이에 일어난 이동은 잡히지 않는다.
|
||||
|
||||
§20 이 든 관찰 수단은 PSR 과 스케줄러 추적 둘이고, 이 물음은 그 둘 안에서 닫는다.
|
||||
|
||||
이 물음을 pinning 전후를 비교하는 쪽의 관측 항목으로 넣지 않고 따로 세웠다. 이동이 없으면 그 비교를 걸어 볼 이유가 없어져서 이쪽이 먼저 닫혀야 한다.
|
||||
그 대신 이동이 성능에 얼마나 걸리는지는 여기서 재지 않는다.
|
||||
|
||||
## 선택지
|
||||
|
||||
### 1. ps 출력을 일정 간격으로 여러 번 찍는다
|
||||
|
||||
§14.7 이 든 ps -eLo pid,tid,psr,pcpu,comm | grep qemu 를 그대로 쓰고 같은 tid 의 PSR 을 시간순으로 늘어놓는다.
|
||||
게스트를 건드리지 않고 호스트에서만 찍으면 되고 따로 설치할 도구도 없다.
|
||||
|
||||
표본 사이의 이동은 보이지 않으니 몇 초 간격으로 몇 번 찍었는지를 값과 함께 적는다.
|
||||
|
||||
### 2. 스케줄러 추적으로 이동 시점을 본다
|
||||
|
||||
§20 이 PSR 과 함께 든 수단이다. 표본 간격에 기대지 않고 스레드가 옮겨 간 시점을 볼 수 있다.
|
||||
|
||||
다만 이 호스트에서 추적을 쓸 수 있는지 아직 확인하지 않았고, 호스트에 도구와 권한이 더 필요하다.
|
||||
1번으로 이동이 이미 보이면 여기까지 가지 않는다.
|
||||
|
||||
### 3. 제외 — 친화도 설정만 읽고 답한다
|
||||
|
||||
설정에 pinning 이 없다는 것과 스케줄러가 실제로 스레드를 옮겼다는 것은 다르다.
|
||||
설정 확인은 따로 두지 않고 1번 실행에서 함께 적는 항목으로 넣는다.
|
||||
|
||||
## 다음 검증
|
||||
|
||||
1. ps -eLo pid,tid,psr,pcpu,comm | grep qemu 를 일정 간격으로 여러 번 찍어 같은 tid 의 PSR 이 바뀌는지 본다.
|
||||
2. 게스트가 유휴일 때와 CPU 부하가 있을 때를 나눠 각각 찍는다.
|
||||
3. 같은 실행에서 그 가상 머신에 친화도 설정이 있는지도 함께 적는다.
|
||||
|
||||
닫는 조건 : 관측 구간에서 같은 tid 의 PSR 이 바뀌면 이동한다는 사실을 개념의 확인 사례로 흡수하고 닫는다. 그 이동이 성능에 영향을 주는지는 pinning 전후를 비교하는 OQ-10 이 받는다. PSR 이 고정으로 나오면 친화도 설정을 먼저 확인하고, 설정이 없는데도 고정이면 그 관측이 Case 가 된다.
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
---
|
||||
id: da3a7b56-5691-4a9f-90c5-3a1a5d0f0013
|
||||
kind: QUESTION
|
||||
slug: virtualization-layer-saturation-during-refresh
|
||||
title: Keycloak 동시 refresh 실험 중 CPU 가상화 계층이 결과를 흔들 만큼 포화되는가
|
||||
topic: cpu-virtualization
|
||||
topicName: CPU 가상화
|
||||
project: virtualization
|
||||
status: 초안
|
||||
questionStatus: OPEN
|
||||
studio: "https://hyeonworks.com/studio/documents/da3a7b56-5691-4a9f-90c5-3a1a5d0f0013/edit"
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#20-이-concept에서-파생되는-open-question-oq-2
|
||||
- final/document.md#16-현재-keycloak-k3s-실험과의-관계
|
||||
- final/document.md#26-현재-keycloak-실험에서-cpu-문제를-오판하지-않기-위한-기준
|
||||
---
|
||||
|
||||
# Keycloak 동시 refresh 실험 중 CPU 가상화 계층이 결과를 흔들 만큼 포화되는가
|
||||
|
||||
Keycloak refresh token 경쟁 실험은 가상 머신 두 대 위에서 돌기 때문에 원래 검증하려던 구조 아래에 KVM 계층이 하나 더 붙는다. 실험 구간에서 요청이 느려지거나 실패했을 때 그것을 refresh 경쟁 문제로 읽어도 되는지는 같은 구간의 호스트 CPU 사용량과 steal time 을 봐야 갈린다. steal time 은 게스트의 vCPU 에 실행할 작업이 있는데도 다른 작업 때문에 곧바로 실행되지 못한 상황을 가리키는 지표다(§13). 그런데 §20 이 함께 관찰하라고 든 다섯 지표를 아직 한 번도 같이 찍지 않았다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **KVM 에서 vCPU 가 물리 CPU 에서 실행되기까지**
|
||||
게스트 안의 지연이 가상화 계층까지 내려가는 경로를 이 개념이 설명한다.
|
||||
- **가상 머신 두 대에 동시에 부하를 주면 이 호스트에서 vCPU 경쟁이 실제로 생기는가**
|
||||
경쟁이 생길 수 있는 구성인지를 먼저 묻는다. 그쪽이 닫히면 여기서 기준값을 다시 만들지 않아도 된다.
|
||||
- **cgroup CPU 제한과 호스트 vCPU 경쟁을 지표로 가를 수 있는가**
|
||||
§26 이 나눈 여섯 경계 가운데 K3s 와 Virtualization 을 어떻게 가르는지를 따로 묻는다.
|
||||
|
||||
## 사실
|
||||
|
||||
§16 이 그린 원래 검증 대상 구조
|
||||
|
||||
Client
|
||||
Nginx / Load Balancer
|
||||
K3s
|
||||
Keycloak Node 1 · Keycloak Node 2
|
||||
Session / Token State
|
||||
PostgreSQL · Redis
|
||||
|
||||
테스트 환경에서는 이 구조 아래에 KVM 계층이 추가된다 : §16
|
||||
Physical Host 아래에 Host Nginx 와 가상 머신 두 대가 있고 가상 머신마다 K3s 노드와 Keycloak 이 돈다 : §16
|
||||
|
||||
§16 이 테스트 결과를 해석할 때 분리하라고 든 원인 일곱
|
||||
|
||||
Keycloak refresh/session 동시성
|
||||
PostgreSQL contention/locking
|
||||
Redis 상태 관리
|
||||
K3s resource scheduling
|
||||
VM vCPU scheduling
|
||||
Host CPU saturation
|
||||
Nginx/LB
|
||||
|
||||
§16 은 KVM CPU 가상화를 이해하는 목적을 이렇게 적었다.
|
||||
|
||||
Keycloak 멀티 노드 실험에서 발생한 지연이나 실패가 application/storage 문제인지, VM/Host 자원 문제인지 구분할 수 있도록 실험 기반을 이해하기 위해서다.
|
||||
|
||||
§26 은 같은 원인들을 여섯 경계로 다시 나눈다.
|
||||
|
||||
Application / Auth : Refresh Token 경쟁 · Session 상태 경쟁 · Keycloak 내부 처리
|
||||
Storage : PostgreSQL lock / latency · Redis latency / consistency
|
||||
K3s : Pod CPU throttling · Pod scheduling/resource limit
|
||||
Guest : Guest CPU saturation
|
||||
Virtualization : vCPU scheduling · Steal time · VM Exit overhead
|
||||
Host : CPU contention · CPU overcommit · Host saturation
|
||||
|
||||
§26 은 refresh 경쟁을 검증하는 첫 실험에서 가능하면 CPU 자원을 여유 있게 유지하고, 그 상태에서 같은 세션과 같은 refresh token 에 대한 동시 요청을 만들어 동시성 문제를 먼저 확인하라고 적는다. 트래픽을 늘려 CPU/DB/Redis/K3s 자원 포화를 관찰하는 것은 별도의 부하·스트레스 Case 로 나눈다.
|
||||
|
||||
§20 이 refresh 경쟁 실험 중 동시에 관찰하라고 든 다섯
|
||||
|
||||
Host CPU
|
||||
Guest CPU
|
||||
steal time
|
||||
Keycloak latency
|
||||
DB/Redis latency
|
||||
|
||||
§20 은 그 목적이 refresh 경쟁과 호스트 자원 경쟁을 분리하는 것이라고 밝힌다.
|
||||
|
||||
## 가정
|
||||
|
||||
refresh 경쟁 실험이 Keycloak latency 와 DB/Redis latency 를 이미 재고 있다고 전제한다. 그 실험이 어떤 값을 어떤 주기로 내는지는 이 근거 문서에 적혀 있지 않다.
|
||||
|
||||
기준값을 찍는 구간과 실험 구간 사이에 호스트의 다른 작업이 달라지지 않는다.
|
||||
|
||||
다섯 지표의 시각을 맞춰 읽을 수 있다고 전제한다. 호스트 쪽과 게스트 쪽 시계가 어긋나면 부하 구간을 겹쳐 놓을 수 없기 때문이다.
|
||||
|
||||
§26 이 말한 「CPU 자원을 여유 있게 유지한」 상태를 이 호스트에서 만들 수 있다.
|
||||
|
||||
## 미지수
|
||||
|
||||
실험을 걸기 전 다섯 지표의 기준값.
|
||||
|
||||
실험 구간의 호스트 CPU 와 게스트 CPU, 그리고 각 게스트의 steal time.
|
||||
|
||||
같은 구간의 Keycloak latency 와 DB/Redis latency.
|
||||
|
||||
가상화 쪽 지표가 얼마나 움직여야 실험 결과를 다르게 읽어야 하는지. 그 문턱을 아직 정하지 않았다.
|
||||
|
||||
두 가상 머신이 같은 호스트에 있으므로 한쪽 Keycloak 노드에 몰린 요청이 다른 쪽 게스트의 steal time 을 올리는지.
|
||||
|
||||
## 제약
|
||||
|
||||
§26 이 첫 실험을 CPU 여유 상태로 못박기 때문에, 이 질문은 실험 조건을 바꾸지 않고 관찰 지표만 덧붙여 답해야 한다. §17.1 이 refresh token 경쟁을 확인하는 데 반드시 호스트 CPU 를 100% 까지 밀 필요는 없다고 적었으므로, 실험을 그대로 두고 관찰만 덧붙이는 것이 §26 의 조건과도 어긋나지 않는다.
|
||||
|
||||
이 호스트에서 잰 값이 없어 기준값부터 만들어야 한다. 실험 구간의 값만 있으면 그 값이 평소 값인지 실험 때문에 오른 값인지 갈리지 않는다.
|
||||
|
||||
§22 의 Claim 12·13 은 이 환경에서 실제로 그러한지를 주장하는 것이라, 재기 전에는 Case 도 Question 도 되지 않는다고 보고 그대로 두었다. 이 물음을 그와 갈라 먼저 올린 것은 아는 것과 모르는 것이 실험 전에도 갈리기 때문이다.
|
||||
|
||||
§16 은 CPU 가상화가 refresh token 경쟁의 원인은 아니라고 적는다. 여기서 「포화되지 않았다」가 나와도 refresh 경쟁 실험의 결론이 바뀌지는 않고, 그 결론을 애플리케이션·저장소 쪽으로 읽어도 되는지만 갈린다.
|
||||
|
||||
§20 이 든 다섯 가운데 Keycloak latency 와 DB/Redis latency 는 실험 쪽 도구가 낸다. 가상화 쪽에서 새로 만들 값은 Host CPU · Guest CPU · steal time 셋이다.
|
||||
|
||||
## 선택지
|
||||
|
||||
### 1. 실험을 그대로 두고 관찰만 덧붙인다
|
||||
|
||||
§26 이 첫 실험의 CPU 조건을 정해 두었으므로 실험 자체는 건드리지 않는다. 같은 실험을 돌리면서 호스트 쪽 QEMU vCPU 스레드 사용량과 게스트 쪽 %st 만 함께 기록한다. 이 방법으로는 이번 실험 구간에서 가상화 계층이 움직였는지 하나만 알 수 있다.
|
||||
|
||||
### 2. 실험 직전 구간을 기준값으로 따로 찍는다
|
||||
|
||||
부하를 걸기 전 같은 다섯 지표를 한 번 찍어 두면 실험 구간의 값을 견줄 대상이 생긴다. 기준값 없이 실험 구간만 찍으면 steal time 이 어떤 값으로 나오든 그것이 평소 값인지 실험 때문에 오른 값인지 판정할 수 없다.
|
||||
|
||||
### 3. 여섯 경계 가운데 가상화 쪽 둘만 먼저 잰다
|
||||
|
||||
§26 은 Application / Auth 부터 Host 까지 여섯 경계를 든다. 여섯을 한 번에 분리하려면 경계마다 지표를 붙여야 하는데, 이 질문은 Virtualization 과 Host 두 경계가 실험 결과를 흔들었는지만 묻는다. 나머지 넷은 실험 쪽 기록을 그대로 쓰고 이 둘만 새로 잰다.
|
||||
|
||||
### 4. 제외 — CPU 를 일부러 포화시켜 견준다
|
||||
|
||||
포화 구간과 여유 구간을 나란히 놓으면 가상화 계층이 결과를 얼마나 흔드는지 바로 보인다. 그러나 §26 이 첫 refresh 실험을 CPU 여유 상태로 정해 두어서 같은 실험 안에서는 할 수 없다. 별도의 부하·스트레스 Case 로 뺀다.
|
||||
|
||||
## 다음 검증
|
||||
|
||||
1. 실험을 걸기 전 §20 이 든 다섯 지표를 한 번 찍어 기준값으로 둔다.
|
||||
2. refresh 경쟁 실험을 돌리면서 같은 다섯을 같은 시각에 기록한다. 게스트의 steal time 은 top (§14.8), 호스트 쪽 QEMU vCPU 스레드는 ps -eLo pid,tid,psr,pcpu,comm | grep qemu (§14.7) 로 본다.
|
||||
3. Keycloak 과 DB/Redis latency 는 실험이 이미 재는 값을 그대로 쓴다.
|
||||
|
||||
닫는 조건 : 실험 구간의 호스트 CPU 와 steal time 이 기준값과 다르지 않으면, 그 실험 결과를 애플리케이션·저장소 문제로 읽어도 된다고 적고 닫는다. 둘 중 하나라도 움직이면 §26 의 여섯 경계를 분리해 다시 재고, refresh 경쟁 실험을 CPU 여유 상태에서 따로 돌릴지는 Decision 으로 넘긴다.
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
---
|
||||
id: 61b7486f-5dab-41df-8f4f-e8fc21d88617
|
||||
kind: QUESTION
|
||||
slug: vm-exit-distribution-by-workload
|
||||
title: 실제 작업에서 주로 발생하는 VM Exit 은 무엇인가
|
||||
topic: cpu-virtualization
|
||||
topicName: CPU 가상화
|
||||
project: virtualization
|
||||
status: 초안
|
||||
questionStatus: OPEN
|
||||
studio: "https://hyeonworks.com/studio/documents/61b7486f-5dab-41df-8f4f-e8fc21d88617/edit"
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#20-이-concept에서-파생되는-open-question-oq-4
|
||||
- final/document.md#8-무엇이-실제로-vm-exit을-발생시키는가
|
||||
- final/document.md#14-실제-linux에서-확인할-수-있는-것-14-9
|
||||
---
|
||||
|
||||
# 실제 작업에서 주로 발생하는 VM Exit 은 무엇인가
|
||||
|
||||
VM Exit 은 게스트를 실행하던 CPU 가 하이퍼바이저가 개입해야 하는 조건을 만나 KVM 쪽으로 제어권을 넘기는 전환이고, 가상 머신이 꺼지는 것이 아니다(§7.2). §8 은 Exit 을 낼 수 있는 동작을 일곱 가지로 들지만, 그 가운데 무엇이 실제로 Exit 을 내는지는 이 호스트의 VMX 설정과 돌리는 작업이 정한다. 유휴 구간과 CPU-bound 구간, Keycloak 부하 구간이 서로 다른 Exit 분포를 낼지는 재 봐야 알고, 그 전에 `perf kvm` 이 이 환경에서 열리는지부터 확인하지 않았다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **KVM 에서 vCPU 가 물리 CPU 에서 실행되기까지**
|
||||
VM Entry 와 VM Exit 이 무엇이고 Exit 뒤에 무엇이 처리하는지를 이 개념이 설명한다.
|
||||
- **Keycloak 작업의 VM Exit 분포는 idle · CPU-bound · I/O-bound 와 어떻게 다른가**
|
||||
같은 측정에서 Keycloak 쪽 분포만 떼어 묻는다. 도구가 열리지 않으면 두 질문이 같은 이유로 닫힌다.
|
||||
|
||||
## 사실
|
||||
|
||||
§8 이 든, VM Exit 을 발생시킬 수 있는 동작
|
||||
|
||||
HLT
|
||||
I/O Port 접근 - IN / OUT
|
||||
CPUID
|
||||
Control Register 접근
|
||||
MSR 접근
|
||||
Exception
|
||||
External Interrupt
|
||||
|
||||
Intel VMX 에는 VMCS(Virtual Machine Control Structure)가 있고 하이퍼바이저는 VM-Execution Control 등을 통해 어떤 동작을 가로챌지 설정한다 : §8
|
||||
|
||||
§8 은 「특권 명령이면 전부 VM Exit」이나 「root 가 실행하면 VM Exit」 같은 규칙이 맞지 않는다고 적는다. VM Exit 여부는 VMX 설정과 해당 동작의 종류에 따라 결정된다.
|
||||
|
||||
모든 CR(control register) 접근이 항상 VM Exit 을 발생시키지는 않는다 : §8.4
|
||||
Page Fault, Breakpoint, Debug Exception 같은 CPU 예외도 Exception Bitmap 등의 설정에 따라 게스트가 직접 처리할 수도 있고 하이퍼바이저가 가로챌 수도 있다 : §8.6
|
||||
|
||||
CPU 에는 MSR(Model-Specific Register)이 있고 RDMSR 과 WRMSR 로 접근한다. 특정 MSR 접근을 하이퍼바이저가 가로채도록 설정했다면 VM Exit 이 발생할 수 있다 : §8.5
|
||||
|
||||
§8.2 는 I/O port 접근에서 나온 Exit 을 KVM 이 받고, 사용자 공간에서 가상 장치를 흉내 내야 하면 KVM_RUN 이 반환되어 QEMU 가 처리한 뒤 다시 KVM_RUN 을 호출한다고 적는다.
|
||||
|
||||
§14.9 는 환경이 지원하면 perf kvm 으로 KVM 관련 실행 통계를 확인할 수 있다고 하면서 sudo perf kvm stat live 를 예로 든다. 지원되는 명령과 표시되는 Exit 이유는 커널, perf 버전, CPU 아키텍처 및 설정에 따라 다를 수 있다. 그래서 perf kvm --help 를 함께 확인하고, 필요하면 KVM tracepoint 를 이용한 별도 추적도 검토하라고 적는다.
|
||||
|
||||
§20 이 든 비교 후보 다섯
|
||||
|
||||
idle
|
||||
CPU-bound workload
|
||||
I/O-heavy workload
|
||||
Keycloak 정상 요청
|
||||
Keycloak 부하 테스트
|
||||
|
||||
## 가정
|
||||
|
||||
다섯 작업을 이 호스트에서 각각 만들 수 있다. Keycloak 정상 요청과 부하 테스트는 이미 돌리는 실험을 그대로 쓴다고 전제한다.
|
||||
|
||||
호스트에서 sudo 로 perf 를 돌릴 수 있다.
|
||||
|
||||
한 작업을 재는 동안 다른 가상 머신이 내는 Exit 이 결과에 섞이지 않는다고 전제하는데, perf kvm 이 호스트 전체를 보는지 프로세스 하나만 보는지는 확인하지 않았다.
|
||||
|
||||
Exit 이유가 다섯 작업 사이에서 같은 이름으로 표시된다.
|
||||
|
||||
## 미지수
|
||||
|
||||
이 환경에서 perf kvm 이 어떤 하위 명령을 지원하는지.
|
||||
|
||||
열린다면 어떤 Exit 이유가 표시되는지.
|
||||
|
||||
다섯 작업의 Exit 이유 분포가 서로 얼마나 다른지.
|
||||
|
||||
perf kvm 이 열리지 않을 때 KVM tracepoint 로 같은 것을 볼 수 있는지.
|
||||
|
||||
한 작업을 얼마나 오래 재야 분포가 더 흔들리지 않는지.
|
||||
|
||||
## 제약
|
||||
|
||||
이 호스트에서 잰 값이 없기 때문에, 분포를 재기 전에 도구가 열리는지부터 확인해야 한다. 도구가 이 환경에서 되는지부터 봐야 한다는 것은 이 물음을 접을 이유가 아니라 다음 검증의 첫 단계다. 열리지 않는 것으로 확인되는 것도 이 물음을 닫는 결과이기 때문이다.
|
||||
|
||||
§8 에 따르면 어떤 동작이 Exit 을 내는지는 VMX 설정이 정하기 때문에, 나온 분포는 이 호스트의 설정에 딸린 값이라 다른 호스트로 옮겨 읽을 수 없다.
|
||||
|
||||
§14.9 는 표시되는 Exit 이유가 커널 · perf 버전 · CPU 아키텍처 및 설정에 따라 다를 수 있다고 적는다. 표의 열 이름도 이 환경에서 나온 그대로 쓴다.
|
||||
|
||||
Exit 총 횟수만으로는 작업을 견줄 수 없다. §8.2 처럼 QEMU 까지 돌아가는 Exit 과 KVM 안에서 끝나는 Exit 은 처리 경로가 다르다. §24.9 도 확인할 때 Exit 횟수만 보지 말라고 적으면서 「VM Exit이 많다 = 장애로 바로 판단하지 않는다」로 닫는다.
|
||||
|
||||
## 선택지
|
||||
|
||||
### 1. perf kvm --help 로 열리는 하위 명령부터 확인한다
|
||||
|
||||
§14.9 가 지원 여부는 환경마다 다르다고 적어 두었기 때문에 도구부터 확인한다. 여기서 안 열리면 뒤의 계획이 통째로 바뀐다.
|
||||
|
||||
### 2. 열리면 sudo perf kvm stat live 로 다섯 작업을 각각 돌린다
|
||||
|
||||
§20 이 든 다섯을 하나씩 돌리며 Exit 이유 분포를 받아 적는다. 유휴 구간을 먼저 재 두면 나머지 넷에서 늘어난 Exit 이 무엇인지 견줄 수 있다.
|
||||
|
||||
### 3. 열리지 않으면 KVM tracepoint 추적을 검토한다
|
||||
|
||||
§14.9 가 대안으로 든 방법이다. 이쪽도 안 되면 「이 호스트에서는 Exit 분포를 관측할 수 없다」가 이 질문의 답이 된다.
|
||||
|
||||
### 4. 제외 — Exit 총 횟수만 세어 작업을 견준다
|
||||
|
||||
숫자 하나로 다섯을 늘어놓을 수 있어 간단하다. 그러나 §8.2 가 적은 대로 Exit 마다 처리 경로가 달라서, 총 횟수는 작업 사이의 차이를 설명하지 못한다.
|
||||
|
||||
## 다음 검증
|
||||
|
||||
1. perf kvm --help 로 이 환경이 지원하는 하위 명령을 확인한다.
|
||||
2. 열리면 sudo perf kvm stat live 로 §20 의 비교 후보 다섯을 각각 돌리며 Exit 이유 분포를 받아 적는다.
|
||||
3. 열리지 않으면 §14.9 가 말한 KVM tracepoint 추적을 검토하고 그 결과도 기록한다.
|
||||
|
||||
닫는 조건 : 다섯 작업의 Exit 이유 분포를 한 표로 놓을 수 있으면 그 표가 Case 가 되고 닫는다. perf kvm 도 tracepoint 도 이 환경에서 열리지 않으면, 「이 호스트에서는 Exit 분포를 관측할 수 없다」를 그대로 적고 §24.9 의 확인 항목에서 Exit 이유를 빼는 근거로 남긴 뒤 닫는다.
|
||||
+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 묶음이 이미 채워져 있다
|
||||
+341
@@ -0,0 +1,341 @@
|
||||
---
|
||||
id: 5b1de31c-0495-4226-8607-73ed7284b314
|
||||
kind: CONCEPT
|
||||
slug: guest-packet-path-to-physical-nic
|
||||
title: Guest 의 packet 이 Host Physical NIC 에 닿기까지 — virtio-net · virtqueue · vhost-net · TAP · Bridge
|
||||
topic: network-virtualization
|
||||
topicName: 네트워크 가상화
|
||||
project: virtualization
|
||||
status: 초안
|
||||
basisVersion: Linux KVM/QEMU/libvirt · virtio-net frontend · vhost-net kernel backend · TAP · Linux Bridge
|
||||
studio: "https://hyeonworks.com/studio/documents/5b1de31c-0495-4226-8607-73ed7284b314/edit"
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
assets:
|
||||
- key: packet-control-and-data-paths
|
||||
file: ../../../final/assets/diagrams/packet-control-and-data-paths/packet-control-and-data-paths.svg
|
||||
source:
|
||||
- final/document.md#94-전체-네트워크-계층
|
||||
- final/document.md#121-이-ssot에서-파생될-concept
|
||||
- final/document.md#125-최종-기준-구조
|
||||
- final/document.md#89-문서-목적
|
||||
- final/document.md#90-virsh-libvirt-virtio-구분
|
||||
- final/document.md#91-virtio-net은-정확히-어디에-있는가
|
||||
- final/document.md#92-frontend와-backend
|
||||
- final/document.md#93-guest-os는-왜-qemu가-아니라-virtio-net을-사용하는가
|
||||
- final/document.md#95-physical-nic의-역할
|
||||
- final/document.md#96-linux-bridge의-역할
|
||||
- final/document.md#97-routing의-역할
|
||||
- final/document.md#98-nat의-역할
|
||||
- final/document.md#99-tap의-역할
|
||||
- final/document.md#100-virtqueue의-역할
|
||||
- final/document.md#101-guest-tcp-ip-stack의-역할
|
||||
- final/document.md#102-packet이-keycloak까지-올라오는-과정
|
||||
- final/document.md#103-qemu-virtio-device-model의-역할
|
||||
- final/document.md#104-왜-tap-→-vhost-net-→-qemu-→-virtqueue-라고-일반화하면-안-되는가
|
||||
- final/document.md#105-control-path와-data-path
|
||||
- final/document.md#106-qemu가-userspace인데-packet이-qemu를-안-거칠-수-있는-이유
|
||||
- final/document.md#107-vhost-net-최적화
|
||||
- final/document.md#108-vhost-net은-qemu를-제거하지-않는다
|
||||
- final/document.md#109-fast-path와-slow-control-path
|
||||
- final/document.md#110-data-copy-최적화
|
||||
- final/document.md#111-interrupt-notification-최적화
|
||||
- final/document.md#112-multi-queue-최적화
|
||||
- final/document.md#113-offload-최적화
|
||||
- final/document.md#114-linux-bridge가-항상-host-tcp-ip-stack을-거치는-것은-아니다
|
||||
- final/document.md#115-host-physical-nic로-나갈-때-virtio를-다시-거치지-않는다
|
||||
- final/document.md#117-이-구조에서-발생할-수-있는-문제-117-4
|
||||
- final/document.md#117-이-구조에서-발생할-수-있는-문제-117-5
|
||||
- final/document.md#124-핵심-claim
|
||||
---
|
||||
|
||||
# Guest 의 packet 이 Host Physical NIC 에 닿기까지 — virtio-net · virtqueue · vhost-net · TAP · Bridge
|
||||
|
||||
가상 머신 안의 Keycloak 이 HTTP 요청 하나를 받으려면 그 패킷이 먼저 호스트의 물리 NIC 에 닿아야 한다. 거기서 Linux Bridge 와 TAP, vhost-net, virtqueue 를 지나 게스트 커널의 TCP/IP 스택까지 올라온다. virtio-net 은 그 경로 어딘가에 놓인 프로그램 하나가 아니다. 게스트 쪽 프런트엔드 드라이버와 호스트 쪽 백엔드를 잇는 I/O 계약이고, 그 백엔드 자리는 QEMU 사용자 공간이 맡을 수도 호스트 커널의 vhost-net 이 맡을 수도 있다. 가상 머신 두 대에서 Keycloak 멀티 노드 실험을 돌리면 요청이 느려지거나 아예 닿지 않는 일이 생긴다. 이 경로를 알아 두면 그 증상을 애플리케이션·저장소 쪽 문제와 네트워크 가상화 계층 문제로 갈라 볼 수 있다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **packet 이 어디서 끊겼는지는 계층마다 capture 해서 가른다**
|
||||
이 글이 세운 계층을 그대로 관측 지점으로 쓰는 절차다. 어느 계층까지 패킷이 보였는지로 의심 구간을 좁힌다.
|
||||
- **Keycloak 실험 결과를 애플리케이션 원인으로 읽기 전에 network 계층을 따로 검증한다**
|
||||
이 경로가 실험의 여러 노드에 공유되기 때문에 생기는 오귀속을 막는 기준이다.
|
||||
- **이 호스트의 가상 머신 네트워크는 Bridge 인가 NAT 인가 Routing 인가**
|
||||
이 글은 Bridge 와 라우팅, NAT 가 각각 무엇을 하는지까지만 적었다. 이 호스트가 그중 무엇으로 구성돼 있는지는 확인하지 않았다.
|
||||
- **VM1 과 VM2 의 TAP/vnet interface 는 무엇이고 어디에 붙어 있는가**
|
||||
TAP 이 게스트의 이더넷 프레임과 호스트 네트워크를 잇는 접점이라는 설명이 이 물음의 전제다.
|
||||
- **이 호스트에서 vhost-net 이 실제로 packet datapath 를 맡고 있는가**
|
||||
백엔드가 QEMU 사용자 공간일 수도 vhost-net 일 수도 있다고 적었다. 이 호스트가 어느 쪽인지는 아직 모른다.
|
||||
- **QEMU backend 와 vhost-net 의 차이가 이 호스트에서 실제로 보이는가**
|
||||
두 백엔드의 경로 차이는 여기 적었지만, 그 차이가 지연이나 CPU 사용량으로 드러나는지는 재지 않았다.
|
||||
- **이 가상 머신들의 virtio-net 에 multi-queue 가 켜져 있는가**
|
||||
큐를 하나만 쓰면 부하가 몰릴 수 있다고만 적었다. 지금 큐가 몇 개인지는 확인하지 않았다.
|
||||
- **Host Nginx 에서 Keycloak 까지 packet 은 실제로 어디를 지나는가**
|
||||
여기 그린 경로는 가장 기본적인 조합을 기준으로 한 것이고, 이 테스트 환경의 실제 경로는 추적하지 않았다.
|
||||
- **부하 중 network 가상화가 지연을 바꿀 만큼 호스트 CPU 를 쓰는가**
|
||||
vhost-net 과 QEMU 스레드, softirq 도 호스트 CPU 를 쓴다는 것까지가 이 글의 범위다.
|
||||
- **KVM 에서 vCPU 가 물리 CPU 에서 실행되기까지**
|
||||
QEMU 가 장치를 만들지만 반복 실행은 커널이 맡는다는 구조가 CPU 쪽과 같다. 그쪽 글이 같은 구분을 명령 실행으로 설명한다.
|
||||
|
||||
## 본문
|
||||
|
||||
<!-- body:start -->
|
||||
|
||||
## virtio-net 은 어디에 있는가
|
||||
|
||||
virtio 는 명령어가 아니고 프로그램 하나도 커널 모듈 하나도 아니다. 게스트와 호스트/하이퍼바이저가 가상 I/O 장치를 효율적으로 쓰려고 정해 둔 표준화된 인터페이스이자 프로토콜이다. 그 계열에는 네트워크의 virtio-net 과 블록 I/O 의 virtio-blk 가 있다. SCSI(Small Computer System Interface) 의 virtio-scsi 와 메모리 벌룬의 virtio-balloon 도 같은 계열이고, 이 글은 그중 virtio-net 을 다룬다.
|
||||
|
||||
가상 NIC 를 성립시키는 구현은 게스트와 호스트로 나뉘어 있다. 게스트 커널 쪽에는 TCP/IP 스택과 virtio-net 프런트엔드 드라이버, virtqueue 가 들어 있다.
|
||||
|
||||
```text label="Guest 측"
|
||||
Guest Kernel
|
||||
├─ TCP/IP Stack
|
||||
├─ virtio-net Frontend Driver
|
||||
└─ virtqueue
|
||||
```
|
||||
|
||||
호스트 쪽은 사용자 공간과 커널로 다시 갈린다. QEMU 프로세스 안에 virtio-net 장치 모델이 있고, 커널에는 vhost-net 과 TAP, Linux Bridge 와 라우팅·NAT, 물리 NIC 드라이버가 있다.
|
||||
|
||||
```text label="Host 측"
|
||||
Host Userspace
|
||||
└─ QEMU virtio-net Device Model
|
||||
|
||||
Host Kernel
|
||||
├─ vhost-net (사용하는 경우)
|
||||
├─ TAP
|
||||
├─ Linux Bridge / Routing / NAT
|
||||
└─ Physical NIC Driver
|
||||
```
|
||||
|
||||
그래서 virtio 는 특정 커널 계층 자체가 아니라 게스트 쪽 프런트엔드(frontend)와 호스트 쪽 백엔드(backend) 사이의 I/O 계약이다. 프런트엔드는 게스트 커널의 virtio-net 드라이버이고, 백엔드는 게스트가 넘긴 패킷 버퍼를 호스트 쪽에서 처리하는 구현이다. 이 백엔드 자리에는 QEMU 사용자 공간이 올 수도 있고 호스트 커널의 vhost-net 이 올 수도 있다.
|
||||
|
||||
둘 사이에 놓인 virtqueue 는 NIC 도 아니고 Linux 네트워크 인터페이스도 아니다. 게스트와 호스트 백엔드가 I/O 버퍼를 주고받으려고 쓰는 디스크립터 기반 공유 큐다. 네트워크에서는 보통 게스트에서 호스트로 가는 TX(transmit) virtqueue 와 호스트에서 게스트로 가는 RX(receive) virtqueue 를 쓴다. 페이로드를 매번 전통적인 사용자 공간 API 호출로 넘기는 방식이 아니라, 게스트 메모리의 버퍼와 디스크립터를 효율적으로 공유하고 참조하도록 설계돼 있다.
|
||||
|
||||
게스트 입장에서는 이 구조가 보이지 않는다. 가상 머신을 시작할 때 QEMU 가 가상 PCI(Peripheral Component Interconnect) 버스에 virtio NIC 를 노출한다. 그러면 게스트 Linux 가 그 장치를 발견하고 virtio-net 드라이버를 붙여 `ens3` 나 `eth0` 같은 인터페이스를 만든다. 그다음부터 게스트는 QEMU 를 호출한다고 동작하지 않고 자기 NIC 를 쓴다고 동작한다.
|
||||
|
||||
## 장치를 만드는 쪽과 패킷을 나르는 쪽
|
||||
|
||||
`virsh` 는 사용자가 libvirt 에 가상 머신 관리 명령을 전달하는 명령줄 도구여서 패킷이 지나는 길에는 직접 참여하지 않는다. libvirt 는 그 아래에서 vCPU 와 메모리, 디스크, NIC 모델, MAC 주소, 가상 네트워크, 브리지, QEMU 인자 같은 가상 머신의 수명 주기와 구성을 관리한다. 실제 프로세스로 실행되는 것은 QEMU 다.
|
||||
|
||||
QEMU 안의 virtio 장치 모델이 하는 일은 둘로 갈라서 봐야 뒤가 이어진다. 하나는 장치를 만들고 설정하고 관리하는 일이다. virtio-net 장치 모델을 만들어 게스트에게 노출하고, 기능 협상(feature negotiation)을 하고, virtqueue 를 설정하고, 백엔드를 연결한다. 다른 하나는 패킷을 실제로 나르는 처리인데, 이쪽은 QEMU 가 맡을 수도 맡지 않을 수도 있다. QEMU 백엔드를 직접 쓰면 패킷이 QEMU 를 지난다.
|
||||
|
||||
```text label="QEMU backend 를 직접 사용하는 경우"
|
||||
TAP
|
||||
↓
|
||||
QEMU virtio backend
|
||||
↓
|
||||
virtqueue
|
||||
↓
|
||||
Guest
|
||||
```
|
||||
|
||||
vhost-net 을 쓰면 반복되는 패킷 I/O 를 호스트 커널에서 처리하고 QEMU 사용자 공간을 우회한다.
|
||||
|
||||
```text label="vhost-net 을 사용하는 경우"
|
||||
TAP
|
||||
↓
|
||||
vhost-net
|
||||
↓
|
||||
virtqueue
|
||||
↓
|
||||
Guest
|
||||
```
|
||||
|
||||
이 구분에는 경로 이름이 따로 붙어 있다. 설정 경로(control/setup path)와 데이터 경로(data path)다. 여기서 control 은 Kubernetes 의 Control Plane 을 뜻하지 않고, 일반적인 시스템 용어로 설정·제어 경로를 가리킨다. `virsh` 에서 libvirt 와 QEMU, virtio-net 장치 모델로 내려가면서 기능 협상과 virtqueue 설정, vhost-net 설정이 일어나는 쪽이 설정 경로다. 그 설정이 끝난 뒤 패킷이 반복해서 흐르는 쪽이 데이터 경로다.
|
||||
|
||||
## 밖에서 들어온 패킷이 Keycloak 까지 올라오는 순서
|
||||
|
||||
가장 기본적인 virtio-net 과 vhost-net, TAP, Linux Bridge 조합을 기준으로 하면 수신 방향은 이 순서다.
|
||||
|
||||
```text label="수신 방향"
|
||||
Internet / Client
|
||||
↓
|
||||
Physical NIC
|
||||
↓
|
||||
Physical NIC Driver
|
||||
↓
|
||||
Linux Bridge / Routing / NAT
|
||||
↓
|
||||
TAP
|
||||
↓
|
||||
vhost-net
|
||||
↓
|
||||
RX virtqueue
|
||||
↓
|
||||
virtio-net Frontend Driver
|
||||
↓
|
||||
Guest TCP/IP Stack
|
||||
↓
|
||||
Socket
|
||||
↓
|
||||
Keycloak
|
||||
```
|
||||
|
||||
송신은 이 순서를 거꾸로 밟는다. Keycloak 이 소켓에 쓰면 게스트 TCP/IP 스택과 virtio-net 프런트엔드 드라이버를 지나 TX virtqueue 에 실린다. 거기서부터는 vhost-net 과 TAP, Linux Bridge 와 라우팅·NAT, 물리 NIC 드라이버를 차례로 지나 물리 NIC 로 나간다.
|
||||
|
||||
맨 앞의 물리 NIC(Network Interface Card, 네트워크 인터페이스 카드) 는 실제 네트워크 링크와 서버를 잇는 하드웨어다. 그 하드웨어를 호스트 커널의 드라이버가 제어하고 Linux 가 인터페이스로 노출하기 때문에, `ip link` 에 보이는 `enp3s0` 같은 인터페이스와 NIC 하드웨어를 같은 것으로 세지 않는다.
|
||||
|
||||
그다음의 Linux Bridge 는 호스트 커널 안에 있는 L2 소프트웨어 스위치다. 이더넷 프레임의 목적지 MAC(Media Access Control) 주소를 보고 어느 포트로 넘길지 정하고, MAC 주소를 학습하고, 여러 가상·물리 포트를 잇는다.
|
||||
|
||||
라우팅은 다른 계층에서 다른 값을 본다. 브리지가 L2 에서 MAC 주소를 보고 같은 이더넷 네트워크를 잇는다면, 라우팅은 L3 에서 목적지 IP(Internet Protocol) 주소를 보고 어느 인터페이스나 next-hop 으로 패킷을 보낼지 정한다. NAT(Network Address Translation) 는 또 다른 일을 해서 패킷의 IP 와 포트 정보를 바꾼다. 가상 머신이 사설 서브넷을 쓰면 호스트가 NAT 게이트웨이처럼 동작할 수 있다.
|
||||
|
||||
경로 그림에서는 셋이 한 줄에 나란히 적히지만 보는 값도 바꾸는 값도 서로 다르다. 그래서 실제 가상 머신 네트워크를 분석할 때는 브리지 기반인지 라우팅 기반인지 NAT 기반인지를 먼저 가른다.
|
||||
|
||||
TAP 은 호스트 Linux 커널이 제공하는 가상 이더넷 인터페이스이고 물리 장치가 아니다. `tap0` 나 `vnet0` 같은 이름으로 보이며, 가상 머신의 이더넷 프레임과 호스트 Linux 네트워크를 잇는 접점이 된다. 수신할 때는 Linux Bridge 에서 TAP 을 거쳐 가상 머신으로 들어가고, 송신할 때는 가상 머신에서 TAP 을 거쳐 Linux Bridge 로 나온다.
|
||||
|
||||
TAP 을 지나 게스트 안으로 들어오면 그다음은 평범한 Linux 네트워크 스택이다. 게스트의 TCP/IP 스택도 게스트 Linux 커널의 실제 스택이어서, 게스트 커널에는 소켓과 TCP, UDP, IP, 라우팅, neighbor/ARP, 방화벽, 네트워크 드라이버가 그대로 있다. TCP 는 연결 관리와 포트, 시퀀스, 순서 보장, 재전송, 중복 처리, 흐름 제어, 혼잡 제어를 맡는다. IP 계층은 IP 주소와 라우팅을 담당하고, NIC 에 가까운 계층에서는 이더넷 프레임과 MAC 주소를 다룬다.
|
||||
|
||||
그 위에서 애플리케이션이 보는 것은 소켓 하나다. 이더넷 프레임이 IP 패킷과 TCP 세그먼트를 지나 소켓에 닿고 그 위에서 HTTP 로 읽히면 Keycloak 이 요청을 처리한다.
|
||||
|
||||
```text label="Keycloak 이 요청 하나를 받기까지의 계층"
|
||||
Ethernet Frame
|
||||
↓
|
||||
IP Packet
|
||||
↓
|
||||
TCP Segment / Stream
|
||||
↓
|
||||
Socket
|
||||
↓
|
||||
HTTP
|
||||
↓
|
||||
Keycloak
|
||||
```
|
||||
|
||||
그래서 Keycloak 은 virtqueue 와 vhost-net, TAP, Bridge, 물리 NIC 를 직접 알 필요가 없고, 게스트 Linux 가 주는 TCP 소켓 위에서만 동작한다. 아래쪽 계층이 어긋나도 애플리케이션 로그에는 느렸다는 것과 실패했다는 것만 남는 이유가 여기에 있다.
|
||||
|
||||
## vhost-net 이 우회하는 것
|
||||
|
||||
QEMU 사용자 공간이 패킷마다 I/O 를 처리하면 호스트 커널과 사용자 공간을 오가는 전환 비용이 쌓인다. 초당 지나는 패킷이 많아질수록 그 전환과 스케줄링, 복사, 알림 비용이 커질 수 있다. vhost-net 은 그 반복 경로를 호스트 커널 쪽으로 옮겨서 컨텍스트 스위치와 사용자 공간 오버헤드를 줄인다.
|
||||
|
||||
QEMU 가 장치를 만들고 관리하는 주체라는 것과, 흐르는 패킷을 전부 QEMU 가 처리한다는 것은 다른 말이다. CPU 가상화에서 같은 구분을 이미 한 번 한다. QEMU 가 vCPU 를 만들지만 게스트의 `ADD`, `MOV`, `SUB` 를 전부 QEMU 가 실행하지는 않는다. 그 명령은 KVM(Kernel-based Virtual Machine) 과 VMX(Virtual Machine Extensions) 가 실행한다. 네트워크에서도 QEMU 가 가상 NIC 를 만들지만, 패킷 100만 개를 반드시 QEMU 가 하나씩 처리할 필요는 없다.
|
||||
|
||||
vhost-net 을 써도 QEMU 는 계속 필요하다. 가상 머신 수명 주기와 가상 하드웨어 모델, virtio 장치 생성, 기능 협상, 큐 설정, 백엔드 연결, 장치 reset, 설정 처리가 QEMU 에 남는다. 그래서 vhost-net 은 QEMU 를 없애는 것이 아니라, QEMU 가 맡던 반복적인 virtio 데이터 경로의 상당 부분을 호스트 커널로 넘긴다고 읽는다.
|
||||
|
||||
두 경로에는 이름이 따로 있다. 자주 반복되는 패킷 전달과 데이터 전송이 fast path 다. 장치 초기화와 기능 협상, 큐 설정, 설정 변경, 장치 reset 처럼 드물게 일어나면서 설정과 예외 처리를 맡는 쪽이 control/slow path 다. QEMU 는 뒤쪽에서 계속 중요한 역할을 한다.
|
||||
|
||||

|
||||
|
||||
## 이 경로를 일반화할 때 어긋나는 것
|
||||
|
||||
경로를 한 줄로 굳혀 놓으면 틀리는 그림이 셋 있다. 첫째는 패킷이 vhost-net 다음에 QEMU 를 반드시 지나는 것처럼 그린 것이다.
|
||||
|
||||
```text label="이렇게 일반화하지 않는다"
|
||||
TAP
|
||||
↓
|
||||
vhost-net
|
||||
↓
|
||||
QEMU
|
||||
↓
|
||||
virtqueue
|
||||
```
|
||||
|
||||
vhost-net 의 중요한 목적 하나가 데이터 경로에서 QEMU 사용자 공간을 우회하는 데 있다. 그래서 vhost-net 을 쓸 때의 fast path 는 TAP 에서 vhost-net 과 virtqueue 를 지나 게스트로 간다. QEMU 는 사라지지 않고 장치의 수명 주기와 설정을 관리한다.
|
||||
|
||||
둘째는 Bridge 를 지나는 프레임이 항상 호스트의 TCP/IP 스택을 거친다고 본 것이다. Bridge 가 L2 전달만 하는 경우 프레임이 호스트의 일반적인 L3 TCP/IP 스택을 반드시 거치지는 않는다. VM1 의 TAP 에서 Linux Bridge 를 지나 VM2 의 TAP 으로 가는 경로가 그렇다. 호스트가 라우팅이나 NAT, host-local termination, 방화벽 역할을 하면 그때 L3/Netfilter 경로가 개입한다. 그래서 물리 NIC 에서 호스트 TCP/IP 스택을 거쳐 Bridge 로 간다는 순서를 고정된 경로로 두지 않는다. 실제 경로는 브리지와 라우팅, NAT 구성에 따라 달라진다.
|
||||
|
||||
셋째는 호스트 물리 NIC 로 나갈 때 virtio 를 한 번 더 거친다고 본 것이다.
|
||||
|
||||
```text label="Host 물리 NIC 로 나가는 경로"
|
||||
Guest
|
||||
virtio-net
|
||||
↓
|
||||
vhost-net
|
||||
↓
|
||||
TAP
|
||||
↓
|
||||
Linux Bridge
|
||||
↓
|
||||
Intel NIC Driver
|
||||
↓
|
||||
Intel Physical NIC
|
||||
```
|
||||
|
||||
게스트 virtio 에서 호스트 virtio 를 거쳐 물리 NIC 로 가는 구조가 아니다. virtio 는 게스트의 가상 I/O 장치와 호스트 백엔드 사이의 인터페이스이고, 물리 NIC 로 나갈 때는 그 카드의 실제 드라이버를 쓴다.
|
||||
|
||||
## 복사와 알림, 큐 개수와 오프로드
|
||||
|
||||
네트워크 성능에서 큰 비용 하나가 패킷 데이터를 복사하는 일이다. virtio 와 virtqueue, vhost 구조는 버퍼 디스크립터를 써서 불필요한 복사와 컨텍스트 스위치를 줄이는 방향으로 설계돼 있다. 다만 이것을 항상 zero-copy 라고 일반화하지 않는다. 실제로 복사가 일어나는지는 커널 버전과 QEMU 버전, vhost 설정, 오프로드, NIC 의 기능, 패킷이 지나는 경로, GSO/GRO/TSO 에 따라 달라질 수 있다.
|
||||
|
||||
게스트와 호스트는 큐에 새 패킷이나 버퍼가 들어왔다는 것을 서로 알려야 한다. 게스트가 보낼 때는 virtqueue 에 디스크립터를 등록하고 호스트 백엔드에 알리면 백엔드가 처리하고, 받을 때는 호스트가 virtqueue 에 버퍼를 반영하고 게스트에 알리면 게스트 드라이버가 처리한다. 패킷마다 인터럽트와 알림이 지나치게 많이 발생하면 오버헤드가 커지기 때문에 batching 과 interrupt moderation, queueing 을 쓴다.
|
||||
|
||||
큐를 하나만 쓰면 특정 vCPU 나 처리 경로에 부하가 몰릴 수 있다. virtio-net 은 multi-queue 를 쓸 수 있고, RX 큐를 vCPU 마다 하나씩 붙여 패킷 처리를 병렬로 돌리고 큐 하나에 몰리는 병목을 완화한다. 효과는 부하의 성격과 CPU affinity, IRQ(Interrupt Request) 배치, 큐 설정에 따라 달라진다.
|
||||
|
||||
오프로드는 작은 패킷을 하나씩 처리하는 CPU 부담과 나누고 합치는 비용을 줄인다. TSO(TCP Segmentation Offload)와 GSO(Generic Segmentation Offload), GRO(Generic Receive Offload), 그리고 checksum offload 가 대표적이다. 오프로드가 켜져 있으면 `tcpdump` 에서 보이는 패킷 크기나 checksum 이 실제 wire 에서 보이는 것과 다르게 보일 수 있다.
|
||||
|
||||
## 이 구조에서 성능이 갈리는 두 조건
|
||||
|
||||
초당 지나는 패킷이 많은데 QEMU 사용자 공간이 데이터 경로를 직접 처리하면 CPU 부담이 커질 수 있다. 그때는 아래 다섯 가지를 함께 본다.
|
||||
|
||||
```text label="vhost-net 미사용 또는 비효율적 datapath 일 때 관찰"
|
||||
QEMU CPU usage
|
||||
vhost thread
|
||||
packet rate
|
||||
latency
|
||||
context switch
|
||||
```
|
||||
|
||||
큐 하나나 vCPU 하나에 패킷 처리가 몰릴 수도 있다. 이때는 큐 개수와 분산 설정을 확인 대상으로 둔다.
|
||||
|
||||
```text label="single queue bottleneck 일 때 확인 대상"
|
||||
virtio multi-queue
|
||||
IRQ distribution
|
||||
per-vCPU CPU usage
|
||||
RSS/RPS/XPS
|
||||
```
|
||||
|
||||
두 조건 모두 호스트 CPU 를 함께 쓴다. vhost-net 과 QEMU 스레드, softirq 가 호스트 CPU 를 쓰기 때문에, 네트워크 문제처럼 보이는 지연이 실은 CPU 스케줄링 문제일 수도 있다.
|
||||
|
||||
## 호스트에서 이 경로를 확인하는 명령
|
||||
|
||||
여기까지가 구조이고, 이 호스트가 실제로 그 구조인지는 명령으로 하나씩 확인한다. 물리 NIC 와 Bridge 는 인터페이스 목록과 전달 상태로 본다.
|
||||
|
||||
```bash label="Physical NIC 와 Linux Bridge"
|
||||
ip link
|
||||
ip addr
|
||||
ethtool <interface>
|
||||
ip link show type bridge
|
||||
bridge link
|
||||
bridge fdb show
|
||||
```
|
||||
|
||||
TAP 과 가상 머신의 NIC 연결, libvirt 가상 네트워크는 libvirt 쪽에서 확인한다. `virsh domiflist` 가 어느 가상 머신이 어느 TAP 에 붙어 있는지 알려 주고, `virsh net-dumpxml` 이 그 네트워크가 어떤 구성인지 알려 준다.
|
||||
|
||||
```bash label="TAP 과 libvirt VM NIC · 가상 network"
|
||||
ip tuntap show
|
||||
virsh domiflist <domain>
|
||||
virsh net-list --all
|
||||
virsh net-info <network>
|
||||
virsh net-dumpxml <network>
|
||||
```
|
||||
|
||||
라우팅과 게스트 안쪽, 그리고 virtio 장치와 vhost 모듈이 실제로 올라와 있는지는 각각 따로 본다.
|
||||
|
||||
```bash label="Routing · Guest NIC · virtio 와 vhost 모듈"
|
||||
ip route
|
||||
ip rule
|
||||
ip neigh
|
||||
lspci
|
||||
lsmod | grep virtio
|
||||
lsmod | grep vhost
|
||||
```
|
||||
|
||||
경로 위의 어느 지점까지 패킷이 보이는지는 계층마다 캡처해서 가른다.
|
||||
|
||||
```bash label="계층마다 packet 이 보이는지"
|
||||
sudo tcpdump -ni <physical-nic>
|
||||
sudo tcpdump -ni <bridge>
|
||||
sudo tcpdump -ni <tap-or-vnet>
|
||||
sudo tcpdump -ni <guest-interface>
|
||||
```
|
||||
|
||||
## 이 문서가 확인하지 않은 것
|
||||
|
||||
이 호스트에서 잰 값은 하나도 없다. 위의 명령들은 무엇을 볼 수 있는지 적어 둔 목록이고 아직 실행하지 않았다. 이 가상 머신들의 네트워크가 Bridge 인지 NAT 인지 라우팅인지, VM1 과 VM2 의 TAP 또는 vnet 인터페이스가 무엇인지를 아직 확인하지 않았다. vhost-net 이 실제로 데이터 경로를 맡고 있는지, multi-queue 가 켜져 있는지도 마찬가지다. 그래서 이 글은 「이 구조에서는 이렇게 동작한다」까지만 말하고 「이 서버가 그 구조다」라고는 말하지 않는다. QEMU 백엔드와 vhost-net 의 성능 차이가 이 호스트에서 관찰되는지, Keycloak 부하 시험에서 네트워크 가상화가 지연에 영향을 줄 만큼 호스트 CPU 를 쓰는지도 같은 상태다.
|
||||
|
||||
이 문서가 Keycloak 테스트 환경에 얹어 그린 경로도 마찬가지다. 호스트 Nginx 아래에 가상 머신 두 대가 있고 그 안에서 K3s 와 Keycloak 노드가 돈다는 앞부분은 실험 구성으로 밝혀 두었다. 그 아래를 브리지와 라우팅·NAT, TAP, vhost-net, virtqueue 로 펼친 뒷부분은 네트워크 구성과 TAP 이름, vhost-net 사용 여부를 확인하기 전의 가정이다. 이 환경이 실제로 그렇다는 주장이어서, 재기 전에는 이 글의 그림으로 올리지 않고 근거가 모자란 후보로 남겼다.
|
||||
|
||||
여기 적은 것은 가장 기본적인 조합 안에서만 성립한다. 실제 환경은 Bridge 와 NAT, routed network, macvtap, SR-IOV, VFIO(Virtual Function I/O) passthrough, Open vSwitch, Kubernetes CNI(Container Network Interface) 에 따라 달라질 수 있다. 이 문서에 커널과 QEMU, libvirt 버전이 한 번도 적혀 있지 않아서 특정 버전에 고정하지도 못한다. 복사 동작 하나만 봐도 커널 버전과 QEMU 버전, vhost 설정, 오프로드, NIC 의 기능에 따라 달라지므로, 버전이 바뀌어 이 서술이 낡았는지는 위 확인 명령을 실제 호스트에서 돌릴 때 드러난다.
|
||||
|
||||
가상 머신 위에 올린 K3s 안쪽도 이 문서 밖이다. K3s 의 CNI 와 Service, Pod 네트워크는 이 네트워크 가상화 위에 더해지는 계층이라 따로 분석한다.
|
||||
|
||||
확인 순서는 물리 NIC 에서 시작한다. 그다음 libvirt 가상 네트워크와 Bridge·NAT·Route 구성, 가상 머신별 TAP 또는 vnet, virtio-net 장치, vhost-net 사용 여부, 게스트 NIC 와 route 를 차례로 본다. 이어서 호스트 Nginx 에서 가상 머신까지 패킷이 지나는 길을 `tcpdump` 로 추적하고, VM1 과 VM2 사이의 경로와 Keycloak 요청 때의 패킷 흐름을 확인한다. 마지막으로 부하를 걸었을 때 QEMU 와 vhost 의 CPU 사용량을 비교하고 multi-queue 와 오프로드 설정을 본다. 검증되지 않은 항목은 열린 질문으로 두고, 실제 결과가 나오면 재현 조건을 붙인 기록으로 옮긴다.
|
||||
|
||||
<!-- body:end -->
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
---
|
||||
id: 600a2621-f6d8-42df-9629-7db65011545d
|
||||
kind: QUESTION
|
||||
slug: actual-packet-path-nginx-to-keycloak
|
||||
title: Host Nginx 에서 Keycloak 까지 packet 은 실제로 어디를 지나는가
|
||||
topic: network-virtualization
|
||||
topicName: 네트워크 가상화
|
||||
project: virtualization
|
||||
status: 초안
|
||||
questionStatus: OPEN
|
||||
studio: "https://hyeonworks.com/studio/documents/600a2621-f6d8-42df-9629-7db65011545d/edit"
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#122-open-question-oq-6
|
||||
- final/document.md#116-현재-keycloak-k3s-테스트-환경과-연결
|
||||
- final/document.md#119-실제-packet-path-추적
|
||||
- final/document.md#94-전체-네트워크-계층
|
||||
- final/document.md#125-최종-기준-구조
|
||||
---
|
||||
|
||||
# Host Nginx 에서 Keycloak 까지 packet 은 실제로 어디를 지나는가
|
||||
|
||||
§116 은 이 테스트 환경의 요청 경로를 클라이언트에서 Keycloak 까지 한 줄로 그렸다. 가상 머신 네트워크까지 펼치면 물리 NIC 에서 브리지와 TAP 을 지나 vhost-net 과 virtqueue 를 거쳐 게스트 안으로 들어간다고 적었다. 그 펼친 그림은 §94 가 기준으로 삼은 구조를 이 환경에 얹은 것이고, 이 호스트에서 패킷을 잡아 본 결과가 아니다. 여기서 묻는 것은 호스트 Nginx 를 지난 요청이 실제로 어느 브리지와 어느 TAP 을 거쳐 어느 가상 머신으로 들어가는가 하나다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **Guest 의 packet 이 Host Physical NIC 에 닿기까지 — virtio-net · virtqueue · vhost-net · TAP · Bridge**
|
||||
§94 와 §125 가 그린 기준 경로를 이 개념이 설명한다. 이 물음은 그 경로가 이 호스트에서도 같은지를 묻는다.
|
||||
- **packet 이 어디서 끊겼는지는 계층마다 capture 해서 가른다**
|
||||
§119 가 든 계층별 캡처 방법을 규칙으로 편 기준이다. 요청이 중간에서 끊기면 그쪽이 받는다.
|
||||
- **이 호스트의 가상 머신 네트워크는 Bridge 인가 NAT 인가 Routing 인가**
|
||||
구성이 셋 중 무엇이냐에 따라 캡처를 걸 지점이 달라진다.
|
||||
- **VM1 과 VM2 의 TAP/vnet interface 는 무엇이고 어디에 붙어 있는가**
|
||||
tcpdump 를 걸 인터페이스의 이름을 그쪽이 댄다.
|
||||
- **Keycloak 실험 결과를 애플리케이션 원인으로 읽기 전에 network 계층을 따로 검증한다**
|
||||
§120 이 요구한 별도 검증에서 이 경로 확인이 첫 항목이 된다.
|
||||
|
||||
## 사실
|
||||
|
||||
- §116 이 그린 테스트 환경의 요청 경로
|
||||
Client → Host Physical NIC → Host Nginx → Host Network → VM1 / VM2 → K3s → Keycloak Node 1 / 2
|
||||
- §116 이 가상 머신 네트워크까지 펼친 경로
|
||||
Client → Physical NIC → Host Network Stack / Bridge / Route / NAT → TAP(vm1) / TAP(vm2) → vhost-net → virtqueue → virtio-net → Guest Network Stack → K3s networking → Keycloak
|
||||
- §116 은 그 뒤에 K3s 내부의 CNI · Service · Pod network 가 추가되므로 별도 계층으로 분석한다고 적었다.
|
||||
- §94 는 이 문서가 기준으로 삼은 구조를 virtio-net + vhost-net + TAP + Linux Bridge 로 놓고 수신 방향과 송신 방향을 각각 그렸으며, 실제 환경이 Bridge · NAT · Routed Network · macvtap · SR-IOV · VFIO passthrough · Open vSwitch · Kubernetes CNI 에 따라 달라질 수 있다고 밝혔다.
|
||||
- §125 는 같은 경로를 vhost-net 을 쓸 때와 QEMU backend 를 쓸 때로 나눠 다시 그렸다. 두 그림은 TAP 다음이 vhost-net 인지 QEMU virtio backend 인지에서 갈리고 나머지 구간은 같다.
|
||||
- §119 는 경로를 확인하는 방법으로 호스트의 physical NIC · bridge · tap 또는 vnet 세 곳과 게스트의 guest interface 한 곳에 sudo tcpdump -ni 를 걸고, 어디까지 보이는지로 의심 구간을 좁히라고 적었다.
|
||||
- §119 가 든 판정 예 셋
|
||||
Physical NIC O · Bridge O · TAP X : Guest 내부보다 먼저 Host Bridge/TAP mapping 을 의심한다
|
||||
TAP O · Guest NIC X : virtio/vhost/Guest NIC 계층을 의심한다
|
||||
Guest NIC O · Socket X : Guest routing/firewall/listen 상태를 의심한다
|
||||
- §122 OQ-6 은 Host NIC · Bridge · TAP · Guest NIC 에서 tcpdump 로 추적한다고만 적었다. 어느 이름의 인터페이스인지는 적혀 있지 않다.
|
||||
- §116 의 그림은 앞부분과 뒷부분의 근거가 다르다. 호스트 Nginx 아래에 가상 머신 두 대가 있고 그 안에서 K3s 와 Keycloak 노드가 돈다는 앞부분은 §89 가 밝힌 실험 구성이고, 브리지와 라우팅·NAT, TAP, vhost-net 으로 펼친 뒷부분은 OQ-1 과 OQ-2, OQ-3 를 확인하기 전의 가정이다.
|
||||
- 이 호스트에서 패킷을 잡아 본 기록이 없다. §116 이 펼친 경로는 확인한 결과가 아니라 §94 의 기준 구조를 이 환경에 얹은 그림이다.
|
||||
|
||||
## 가정
|
||||
|
||||
- 호스트 Nginx 를 통해 Keycloak 요청을 한 번 보낼 수 있고, 그 요청이 두 가상 머신 가운데 어느 쪽으로 갔는지 확인하는 쪽이 알 수 있다고 본다.
|
||||
- 네 지점에 동시에 캡처를 걸어 둘 수 있다고 전제한다. 지점마다 따로 걸면 같은 요청을 본 것인지 갈리지 않는다.
|
||||
- §116 이 그린 구조가 지금 실험 환경과 같다고 전제한다. 가상 머신이 둘이고 각각 K3s 노드와 Keycloak 을 돌린다는 것까지가 근거 문서에 적힌 전부다.
|
||||
- 오프로드 설정이 캡처하는 동안 바뀌지 않는다.
|
||||
|
||||
## 미지수
|
||||
|
||||
- 호스트 Nginx 를 지난 요청이 어느 브리지를 거치는지, 그 브리지에서 어느 TAP 으로 나가는지.
|
||||
- 그 TAP 이 두 가상 머신 가운데 어느 쪽의 인터페이스인지.
|
||||
- 게스트 안의 인터페이스에서 같은 요청이 보이는지.
|
||||
- 네 지점의 결과가 §116 이 펼친 그림과 같은지, 다르다면 어느 지점부터 다른지.
|
||||
- 지금 오프로드 설정이 무엇인지. 캡처에서 본 패킷 크기와 체크섬을 wire 값으로 읽어도 되는지가 이것으로 갈린다.
|
||||
|
||||
## 제약
|
||||
|
||||
- 캡처 지점의 이름을 먼저 확정해야 한다. 어느 브리지와 어느 TAP 인지 모르면 tcpdump 를 걸 곳을 고르지 못한다. 그 이름은 네트워크 구성을 묻는 물음과 인터페이스를 묻는 물음이 댄다.
|
||||
- K3s 안에서 Keycloak Pod 까지 가는 구간은 이 물음이 닫는 범위 밖이다. §116 이 CNI · Service · Pod network 를 별도 계층으로 미뤄 두었다.
|
||||
- 요청이 중간 지점에서 끊기면 그것은 이 물음의 답이 아니라 장애다. §119 가 든 세 판정 예를 따라 의심 구간을 적고 계층별 캡처 기준으로 넘긴다.
|
||||
- 이 호스트에서 잰 값이 없어 §116 의 그림을 확인된 경로로 삼지 않는다. 이 환경이 실제로 그렇다는 주장이라 재기 전에는 Case 도 Concept 도 아니라고 보고 근거가 모자란 후보로 남겨 두었으며, OQ-1 과 OQ-2, OQ-3, OQ-6 이 답하면 다시 판정한다.
|
||||
|
||||
## 선택지
|
||||
|
||||
### 1. 네 지점에 한 번에 걸고 요청을 한 번 보낸다
|
||||
|
||||
§119 가 든 호스트 세 지점과 게스트 한 지점에 동시에 tcpdump 를 걸어 두고, 호스트 Nginx 를 통해 Keycloak 요청을 한 번 보낸다. 같은 요청 하나가 네 지점에서 각각 보이는지로 경로가 확정되기 때문에 실행이 한 번으로 끝난다.
|
||||
|
||||
지점 이름을 미리 확정해 두어야 하고 네 곳을 동시에 열어 두어야 한다.
|
||||
|
||||
### 2. 바깥에서 안쪽으로 한 지점씩 옮겨 간다
|
||||
|
||||
물리 NIC 에서 시작해 브리지, TAP, 게스트 인터페이스 순으로 한 지점씩 옮기며 같은 요청을 반복해서 보낸다. 여러 곳을 동시에 열지 않아도 되고 어디서부터 안 보이는지가 바로 드러난다.
|
||||
|
||||
요청을 여러 번 보내야 해서 매번 같은 경로로 갔다고 전제해야 한다. 두 가상 머신에 요청이 번갈아 가면 그 전제가 깨진다.
|
||||
|
||||
### 3. 두 가상 머신 쪽 TAP 을 모두 열어 놓고 한 번 보낸다
|
||||
|
||||
VM1 과 VM2 의 TAP 을 둘 다 열어 두면 요청이 어느 쪽으로 갔는지까지 같은 실행에서 나온다. §120 이 든 「Node1 요청만 지연」 같은 증상을 나중에 볼 때 어느 경로를 먼저 열어야 하는지도 이 기록에서 나온다.
|
||||
|
||||
캡처 지점이 다섯으로 늘어난다.
|
||||
|
||||
### 4. 제외 — 호스트 Nginx 설정에서 경로를 읽는다
|
||||
|
||||
Nginx 가 어느 주소로 요청을 넘기는지 설정만 읽어도 어느 가상 머신으로 가는지는 알 수 있다. 그러나 이 물음은 설정이 말하는 경로가 아니라 패킷이 실제로 지나는 계층을 묻는다. §116 이 그린 그림이 확인된 것이 아니라는 데서 물음이 시작했으므로, 설정을 읽으면 같은 종류의 근거가 하나 더 늘어난다.
|
||||
|
||||
## 다음 검증
|
||||
|
||||
1. 캡처를 걸 지점의 이름을 먼저 확정한다. 어느 브리지와 어느 TAP 인지는 네트워크 구성을 묻는 물음과 인터페이스를 묻는 물음이 답한다.
|
||||
2. 호스트에서 sudo tcpdump -ni 뒤에 physical NIC 이름 · bridge 이름 · tap 또는 vnet 이름을 넣어 세 곳에 걸고, 게스트에서 같은 명령을 guest interface 이름에 건다 (§122 OQ-6 · §119).
|
||||
3. 호스트 Nginx 를 통해 Keycloak 요청을 한 번 보낸다.
|
||||
4. 네 지점에서 그 요청이 보였는지를 §119 처럼 O 와 X 로 적는다.
|
||||
5. 캡처하는 동안의 오프로드 상태를 함께 적는다. §113 은 오프로드가 켜져 있으면 tcpdump 에서 보이는 패킷 크기나 체크섬이 실제 wire 에서 보이는 것과 다르게 보일 수 있다고 적었다.
|
||||
|
||||
닫는 조건 : 네 지점의 캡처 결과로 실제 경로를 한 줄로 적으면 닫는다. §116 이 펼친 그림과 같으면 그 그림을 이 호스트에서 확인된 경로로 올리고, §116 을 근거로 삼는 후보를 다시 판정한다. 다르면 어느 지점부터 다른지를 적고 §119 의 판정 예대로 그 구간을 의심 구간으로 넘긴다. 요청이 중간 지점에서 끊기면 그것은 이 물음의 답이 아니므로 계층별 캡처 기준으로 넘긴다.
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
---
|
||||
id: 484fd832-c7d3-413e-897a-97892e4e10ac
|
||||
kind: QUESTION
|
||||
slug: is-vhost-net-actually-in-use
|
||||
title: 이 호스트에서 vhost-net 이 실제로 packet datapath 를 맡고 있는가
|
||||
topic: network-virtualization
|
||||
topicName: 네트워크 가상화
|
||||
project: virtualization
|
||||
status: 초안
|
||||
questionStatus: OPEN
|
||||
studio: "https://hyeonworks.com/studio/documents/484fd832-c7d3-413e-897a-97892e4e10ac/edit"
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#122-open-question-oq-3
|
||||
- final/document.md#103-qemu-virtio-device-model의-역할
|
||||
- final/document.md#104-왜-tap-→-vhost-net-→-qemu-→-virtqueue-라고-일반화하면-안-되는가
|
||||
- final/document.md#106-qemu가-userspace인데-packet이-qemu를-안-거칠-수-있는-이유
|
||||
- final/document.md#107-vhost-net-최적화
|
||||
- final/document.md#108-vhost-net은-qemu를-제거하지-않는다
|
||||
- final/document.md#117-이-구조에서-발생할-수-있는-문제-117-4
|
||||
---
|
||||
|
||||
# 이 호스트에서 vhost-net 이 실제로 packet datapath 를 맡고 있는가
|
||||
|
||||
§103 은 패킷이 TAP 에서 게스트로 올라오는 길을 두 가지로 갈라 적었다. QEMU 백엔드를 직접 쓰면 QEMU virtio backend 가 그 사이에 들어가고, vhost-net 을 쓰면 호스트 커널의 vhost-net 이 들어간다. 둘 중 어느 쪽이 이 호스트에서 돌고 있는지는 SSOT 에 없어서, 이 물음은 가상 머신 두 대 각각의 데이터 경로 백엔드를 확정한다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **Guest 의 packet 이 Host Physical NIC 에 닿기까지 — virtio-net · virtqueue · vhost-net · TAP · Bridge**
|
||||
그 경로 그림이 vhost-net 을 쓰는 구성을 기준으로 그려져 있어서, 이 호스트가 그 기준에 해당하는지를 이 물음이 정한다.
|
||||
- **QEMU backend 와 vhost-net 의 차이가 이 호스트에서 실제로 보이는가**
|
||||
비교하려면 지금 어느 백엔드로 돌고 있는지가 먼저 정해져야 한다.
|
||||
- **부하 중 network 가상화가 지연을 바꿀 만큼 호스트 CPU 를 쓰는가**
|
||||
§117.4 가 든 CPU 오버헤드는 QEMU 사용자 공간이 데이터 경로를 직접 처리할 때의 이야기다.
|
||||
- **Host Nginx 에서 Keycloak 까지 packet 은 실제로 어디를 지나는가**
|
||||
TAP 과 virtqueue 사이에 무엇이 있는지가 그 경로 서술의 한 칸을 채운다.
|
||||
|
||||
## 사실
|
||||
|
||||
- §103 은 QEMU 의 virtio Device Model 이 호스트 사용자 공간의 QEMU 프로세스 안에 있다고 적고, 그 역할을 둘로 나눴다. 하나는 장치 생성/설정/관리이고 다른 하나는 실제로 패킷을 나르는 처리다.
|
||||
- §103 이 적은 데이터 경로 두 가지는 이렇게 갈린다.
|
||||
QEMU backend 를 직접 쓰는 경우 : TAP → QEMU virtio backend → virtqueue → Guest
|
||||
vhost-net 을 쓰는 경우 : TAP → vhost-net → virtqueue → Guest
|
||||
- §104 는 TAP → vhost-net → QEMU → virtqueue 를 일반적인 경로로 그리면 안 된다고 못 박았다. vhost-net 의 목적 하나가 데이터 경로에서 QEMU 사용자 공간을 우회하는 것이기 때문이다.
|
||||
- §106 은 장치를 만들고 관리하는 주체와 실제로 패킷을 나르는 주체가 다르다는 것을 CPU 가상화에 견주어 적었다. QEMU 가 vCPU 를 만들어도 게스트의 ADD · MOV · SUB 를 전부 QEMU 가 실행하지는 않는다.
|
||||
- §107 은 QEMU 사용자 공간이 패킷마다 I/O 를 처리하면 호스트 커널과 사용자 공간 사이의 전환이 쌓이고, 초당 지나는 패킷이 많아질수록 userspace/kernel transition · scheduling · copy · notification 비용이 커질 수 있다고 적었다.
|
||||
- §108 은 vhost-net 을 써도 QEMU 가 남아서 맡는 일을 열거했다.
|
||||
VM lifecycle · Virtual hardware model · virtio device 생성
|
||||
Feature negotiation · Queue configuration · Backend 연결
|
||||
Device reset · Control/configuration handling
|
||||
- §117.4 는 초당 지나는 패킷이 많은데 QEMU 사용자 공간이 데이터 경로를 직접 처리하면 CPU 오버헤드가 커질 수 있다고 밝히고, 관찰 대상으로 QEMU CPU usage · vhost thread · packet rate · latency · context switch 를 들었다.
|
||||
- 확인 명령으로 §122 OQ-3 과 §118 이 든 것은 lsmod | grep vhost 하나다. §122 OQ-3 은 그 뒤에 QEMU arguments 와 libvirt domain XML 을 추가로 확인한다고 적었지만, 그 둘을 읽는 명령은 제3부 어디에도 적혀 있지 않다.
|
||||
- §123 은 개념에서 열린 질문을 거쳐 Case 로 가는 순서를 설명하면서 이 물음을 예로 들었다. 개념 자리에 「vhost-net은 QEMU userspace를 우회해 packet datapath를 처리할 수 있다」를, 물음 자리에 「현재 테스트 Host에서 vhost-net이 실제 활성화되어 있는가?」를 놓고, 답이 나오면 「libvirt/QEMU virtio-net backend 구성 확인 및 vhost-net 사용 검증」이 Case 가 된다고 적었다.
|
||||
- 이 호스트의 가상 머신 두 대가 어느 백엔드로 도는지, vhost 모듈이 올라와 있는지를 확인한 기록은 SSOT 에 없다.
|
||||
|
||||
## 가정
|
||||
|
||||
- 호스트에 붙어 lsmod 를 실행하고 libvirt 설정을 읽을 수 있다고 본다.
|
||||
- 모듈이 올라와 있다는 것과 그 가상 머신이 그 백엔드를 쓴다는 것을 다른 사실로 놓고 물음을 세웠다. §103 은 백엔드 선택을 가상 머신의 구성으로 적었지 모듈 적재로 적지 않았다.
|
||||
- 두 가상 머신이 같은 백엔드를 쓴다고 전제하지 않는다. 가상 머신마다 따로 읽어 각각 적는다.
|
||||
- 읽는 동안 가상 머신을 재시작하지 않는다고 전제한다. 백엔드는 §105 가 적은 설정 경로에서 정해지므로 재시작하면 달라질 수 있다.
|
||||
|
||||
## 미지수
|
||||
|
||||
- 이 호스트에 vhost 커널 모듈이 올라와 있는지.
|
||||
- 두 가상 머신의 libvirt domain 설정과 QEMU arguments 가 vhost 백엔드를 쓰도록 되어 있는지.
|
||||
- 그래서 지금 흐르는 패킷이 §103 이 그린 두 경로 가운데 어느 쪽으로 가는지.
|
||||
- QEMU arguments 와 libvirt domain XML 을 이 환경에서 어떤 명령으로 읽는지. 제3부가 그 명령을 적지 않아 실행하는 쪽이 정한다.
|
||||
|
||||
## 제약
|
||||
|
||||
- 백엔드가 무엇인지를 확정하는 데서 끊는다. 두 백엔드의 성능 차이는 그것을 비교하는 물음이 받는다.
|
||||
- 이 호스트에서 읽은 설정이 없어 §94 와 §125 가 기준으로 삼은 vhost-net 구성을 이 호스트의 값으로 쓰지 않는다.
|
||||
- 명령을 SSOT 가 하나만 주었으므로, 나머지 둘을 무엇으로 읽었는지 실행한 명령과 출력을 함께 남긴다.
|
||||
|
||||
## 선택지
|
||||
|
||||
### 1. 모듈 적재부터 보고 가상 머신 설정으로 좁힌다
|
||||
|
||||
lsmod | grep vhost 로 호스트에 그 기능이 있는지 먼저 보고, 나오면 가상 머신마다 설정을 읽어 실제로 쓰는지 좁힌다. 모듈이 아예 없으면 두 대 모두 QEMU 백엔드라는 답이 한 번에 나온다.
|
||||
|
||||
모듈이 있을 때는 이 명령만으로 아무것도 정해지지 않아서 결국 가상 머신 설정을 읽어야 한다.
|
||||
|
||||
### 2. 가상 머신 설정부터 읽고 모듈 적재로 뒷받침한다
|
||||
|
||||
두 가상 머신의 libvirt domain 설정과 QEMU arguments 에서 백엔드 지정을 먼저 읽고, lsmod 출력으로 그 설정이 실제로 성립할 수 있는지 뒷받침한다. 가상 머신마다 다른 답이 나오는 경우까지 한 번에 잡힌다.
|
||||
|
||||
설정을 읽는 명령을 SSOT 가 주지 않아서 무엇으로 읽었는지 따로 적어야 한다.
|
||||
|
||||
### 3. §117.4 의 관찰 대상으로 되짚는다 — 제외
|
||||
|
||||
호스트에 vhost 스레드가 보이는지, 부하 중 QEMU CPU 가 어떻게 움직이는지로 백엔드를 역추정하자는 방법이다. 그 관찰은 부하를 걸어야 값이 생기고, §117.4 는 그 목록을 성능 문제를 진단할 때 볼 것으로 적었다. 지금 필요한 것은 설정이 무엇으로 되어 있는가 하나다. 그것은 부하 없이 읽힌다.
|
||||
|
||||
## 다음 검증
|
||||
|
||||
1. lsmod | grep vhost 로 vhost 커널 모듈이 올라와 있는지 본다.
|
||||
2. 두 가상 머신의 libvirt domain 설정에서 인터페이스의 driver 지정을 읽는다. 무엇으로 읽었는지 명령을 함께 적는다.
|
||||
3. 같은 가상 머신의 QEMU 실행 인자에서 백엔드 지정을 읽는다. 이것도 명령을 함께 적는다.
|
||||
4. 세 출력을 가상 머신별로 짝지어 적고 실행한 명령과 함께 증거로 남긴다.
|
||||
|
||||
닫는 조건 : 가상 머신 두 대 각각의 데이터 경로 백엔드가 vhost-net 인지 QEMU 사용자 공간인지 설정으로 확정되면 닫는다. vhost-net 이면 §125 의 「Data Path - vhost-net 사용」 이 이 호스트의 경로라고 적고, QEMU 백엔드면 「Data Path - QEMU backend 사용」 쪽이라고 적는다. 어느 쪽이든 그 결과가 QEMU backend 와 vhost-net 을 견주는 물음의 비교 대상 하나를 고정한다. 두 대가 서로 다르게 나오면 그 사실을 적고, 이후 측정에서 둘을 같은 조건으로 묶지 않는다.
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
---
|
||||
id: 79ac61d6-cbe6-483b-9961-02ca8c02b5c2
|
||||
kind: QUESTION
|
||||
slug: network-virtualization-cpu-cost-under-load
|
||||
title: 부하 중 network 가상화가 지연을 바꿀 만큼 호스트 CPU 를 쓰는가
|
||||
topic: network-virtualization
|
||||
topicName: 네트워크 가상화
|
||||
project: virtualization
|
||||
status: 초안
|
||||
questionStatus: OPEN
|
||||
studio: "https://hyeonworks.com/studio/documents/79ac61d6-cbe6-483b-9961-02ca8c02b5c2/edit"
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#122-open-question-oq-7
|
||||
- final/document.md#117-이-구조에서-발생할-수-있는-문제-117-7
|
||||
- final/document.md#107-vhost-net-최적화
|
||||
- final/document.md#111-interrupt-notification-최적화
|
||||
- final/document.md#120-keycloak-refresh-token-실험과의-관계
|
||||
---
|
||||
|
||||
# 부하 중 network 가상화가 지연을 바꿀 만큼 호스트 CPU 를 쓰는가
|
||||
|
||||
§117.7 은 vhost-net 과 QEMU 스레드와 softirq 도 호스트 CPU 를 쓰므로, 네트워크 문제처럼 보이는 것이 CPU 스케줄링 문제일 수 있다고 적었다. 그 셋 가운데 QEMU 스레드와 게스트 쪽 지표는 CPU 가상화를 다루는 물음 둘이 이미 같은 실험 구간에서 재기로 해 두었다. 그래서 여기서는 그 둘이 보지 않는 vhost 커널 스레드와 softirq 만 본다. Keycloak 부하 시험 구간에서 이 둘이 호스트 CPU 를 얼마나 쓰는지, 그 사용량이 네트워크 지연과 같이 움직이는지로 물음을 좁힌다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **Guest 의 packet 이 Host Physical NIC 에 닿기까지 — virtio-net · virtqueue · vhost-net · TAP · Bridge**
|
||||
vhost-net 이 데이터 경로의 어느 구간을 맡는지를 이 개념이 설명한다. 여기서 재려는 커널 스레드가 그 구간을 돌린다.
|
||||
- **Keycloak 실험 결과를 애플리케이션 원인으로 읽기 전에 network 계층을 따로 검증한다**
|
||||
§120 이 요구한 별도 검증을 규칙으로 편 기준이다. 이 측정이 없으면 그 검증을 마쳤다고 적을 수 없다.
|
||||
- **QEMU backend 와 vhost-net 의 차이가 이 호스트에서 실제로 보이는가**
|
||||
백엔드를 바꿔 견주는 물음이라 §122 가 든 관찰 대상을 상당 부분 함께 쓴다. 여기서 찍은 기준값을 그쪽이 그대로 받는다.
|
||||
- **이 가상 머신들의 virtio-net 에 multi-queue 가 켜져 있는가**
|
||||
큐가 하나로 나오면 §117.5 가 든 쏠림이 이 측정의 vCPU 별 사용량에서 드러난다.
|
||||
- **Keycloak 동시 refresh 실험 중 CPU 가상화 계층이 결과를 흔들 만큼 포화되는가**
|
||||
Guest steal time 과 QEMU vCPU 스레드는 그쪽이 잰다. 같은 실험 구간에서 두 기록을 함께 남기고 여기서는 vhost 커널 스레드와 softirq 만 새로 잰다.
|
||||
|
||||
## 사실
|
||||
|
||||
- §117.7 은 vhost-net · QEMU thread · softirq 도 호스트 CPU 를 쓰고, 따라서 네트워크 문제처럼 보여도 CPU 스케줄링 문제일 수 있다고 적었다.
|
||||
- §107 은 QEMU 사용자 공간이 패킷마다 I/O 를 처리하면 호스트 커널과 사용자 공간을 오가는 전환 비용이 쌓이고, 초당 지나는 패킷이 많아질수록 userspace/kernel transition · scheduling · copy · notification 비용이 커질 수 있다고 밝혔다.
|
||||
- §111 은 게스트와 호스트가 큐에 새 패킷이나 버퍼가 있음을 서로 알려야 한다고 적고, 패킷마다 인터럽트나 알림이 지나치게 많이 발생하면 오버헤드가 커지므로 batching · interrupt moderation · queueing 이 중요하다고 밝혔다.
|
||||
- §120 은 Refresh Token 경쟁 자체가 virtio-net 문제는 아니지만 클라이언트부터 PostgreSQL/Redis 까지 같은 경로를 공유하므로 네트워크 경로를 별도로 검증한다고 적었다.
|
||||
- §120 이 Refresh Token 경쟁이나 DB lock 으로 오해할 수 있다고 든 다섯
|
||||
Node1 요청만 지연
|
||||
VM2 packet loss
|
||||
Host bridge misconfiguration
|
||||
NAT/conntrack issue
|
||||
Host CPU contention으로 vhost 처리 지연
|
||||
- §122 OQ-7 이 든 관찰 대상 여섯
|
||||
QEMU CPU
|
||||
vhost thread
|
||||
softirq
|
||||
Host CPU
|
||||
Guest CPU
|
||||
network latency
|
||||
- 이 여섯 가운데 QEMU CPU 와 Guest CPU 는 CPU 가상화 쪽 물음이 같은 실험 구간에서 이미 잰다. §14.7 이 호스트 스레드를 보는 명령으로 적은 ps -eLo pid,tid,psr,pcpu,comm | grep qemu 는 이름이 qemu 인 스레드만 걸러 내므로 vhost 커널 스레드는 그 출력에 나오지 않는다.
|
||||
- softirq 시간을 읽는 명령이 근거 문서에 없다. §118 의 네트워크 확인 명령 목록에도, CPU 관측 명령을 모아 둔 §14 에도 softirq 항목이 없고 이 낱말은 §117.7 과 §122 OQ-7 두 곳에만 나온다.
|
||||
- §108 은 vhost-net 을 써도 QEMU 가 VM lifecycle · virtio device 생성 · feature negotiation · queue configuration · backend 연결 · device reset 을 계속 맡는다고 적었다.
|
||||
- 이 호스트에서 부하 구간의 vhost 스레드 CPU 사용량이나 softirq 시간을 잰 기록이 없다.
|
||||
|
||||
## 가정
|
||||
|
||||
- Keycloak 부하 시험을 돌릴 수 있고, 부하 구간과 그 직전 구간을 나눠 기록할 수 있다고 본다.
|
||||
- 호스트에서 vhost 커널 스레드를 스레드 단위로 구분해 볼 수 있다고 전제한다. §108 이 vhost-net 을 써도 QEMU 가 설정과 수명 주기를 계속 맡는다고 적었으므로, QEMU 프로세스의 CPU 사용량과 vhost 커널 스레드의 CPU 사용량을 따로 세야 한다.
|
||||
- 부하 도구가 네트워크 지연을 이미 내고 있다고 전제한다. 그 도구가 어떤 값을 어떤 주기로 내는지는 근거 문서에 적혀 있지 않다.
|
||||
- 호스트와 게스트 둘의 시각을 맞춰 읽을 수 있다. 시계가 어긋나면 세 값을 같은 부하 구간에 겹쳐 놓지 못한다.
|
||||
- 호스트 쪽 Nginx 도 같은 물리 CPU 를 쓴다. 부하 구간의 호스트 CPU 상승을 전부 네트워크 가상화 몫으로 읽으면 이 전제가 깨진다.
|
||||
|
||||
## 미지수
|
||||
|
||||
- 부하를 걸기 전 vhost 커널 스레드의 CPU 사용량과 softirq 시간.
|
||||
- 부하 구간에서 그 둘이 얼마나 오르는지.
|
||||
- 오른 몫이 QEMU vCPU 스레드 사용량과 어떻게 나뉘는지.
|
||||
- vhost 커널 스레드와 softirq 의 움직임이 네트워크 지연과 같은 시간축에서 함께 움직이는지.
|
||||
- 두 지표가 얼마나 움직여야 실험 결과를 다르게 읽어야 하는지. 그 문턱을 아직 정하지 않았다.
|
||||
- softirq 시간을 이 환경에서 어떤 도구로 읽는지. 근거 문서가 명령을 적지 않아 실행하는 쪽이 정한다.
|
||||
|
||||
## 제약
|
||||
|
||||
- 실험 조건을 바꾸지 않고 관찰만 덧붙인다. CPU 가상화 쪽 물음이 같은 실험 구간을 쓰기로 되어 있어 부하 수준을 바꾸면 두 기록을 겹쳐 읽지 못한다.
|
||||
- 기준값을 먼저 찍는다. 부하 구간의 값만 있으면 그것이 평소 값인지 부하 때문에 오른 값인지 판정할 수 없다.
|
||||
- 이 물음이 새로 만드는 값은 vhost 커널 스레드 사용량과 softirq 시간 둘이다. QEMU CPU 와 Guest CPU 와 steal time 은 CPU 가상화 쪽 기록을 그대로 쓰고, 네트워크 지연은 부하 도구가 낸 값을 쓴다.
|
||||
- 두 지표가 움직이지 않았다는 결과가 나와도 Refresh Token 실험의 결론이 바뀌지는 않는다. §120 이 Refresh Token 경쟁과 네트워크 가상화를 별개 문제로 놓았기 때문에, 여기서 갈리는 것은 그 실험 결과를 애플리케이션과 저장소 쪽으로 읽어도 되는지 하나다.
|
||||
- 이 호스트에서 잰 값이 없어 다른 장비의 수치를 근거로 삼지 않는다.
|
||||
|
||||
## 선택지
|
||||
|
||||
### 1. 실험을 그대로 두고 vhost 스레드와 softirq 만 덧붙여 잰다
|
||||
|
||||
CPU 가상화 쪽 물음이 이미 같은 구간에서 Guest steal time 과 QEMU vCPU 스레드를 기록하므로, 여기서는 그 기록에 없는 둘을 같은 시각에 붙인다. 실험 조건을 건드리지 않아서 두 기록을 겹쳐 읽을 수 있다.
|
||||
|
||||
이 방법으로는 이번 부하 수준에서 두 지표가 움직였는지 하나만 알 수 있다.
|
||||
|
||||
### 2. 부하 직전 구간을 기준값으로 따로 찍는다
|
||||
|
||||
부하를 걸기 전에 §122 OQ-7 의 여섯을 한 번 찍어 두면 부하 구간의 값을 견줄 대상이 생긴다. 기준값 없이 부하 구간만 찍으면 vhost 스레드 사용량이 어떤 값으로 나오든 그것이 평소 값인지 부하 때문에 오른 값인지 판정하지 못한다.
|
||||
|
||||
### 3. network latency 를 같은 시간축에 올려 함께 본다
|
||||
|
||||
vhost 스레드와 softirq 가 올라도 지연이 그대로면 이 부하 수준에서는 지연을 바꾸지 않았다고 적을 수 있다. §122 OQ-7 이 network latency 를 여섯째 관찰 대상으로 둔 이유가 여기에 있다. 대신 호스트와 부하 도구의 시각을 맞춰야 세 값을 겹쳐 놓을 수 있다.
|
||||
|
||||
### 4. 제외 — 초당 패킷 수를 올려 vhost 처리를 포화시켜 견준다
|
||||
|
||||
§107 은 초당 지나는 패킷이 많아질수록 전환과 복사와 알림 비용이 커질 수 있다고 적었으므로, 그 수를 올리면 vhost 처리 비용이 더 잘 드러난다. 그러나 이 물음은 Keycloak 부하 시험 구간에서 네트워크 가상화가 결과를 흔들었는지를 묻는 것이라, 부하를 다르게 걸고 잰 값은 그 답이 되지 않는다. 포화 구간과 견주는 측정은 별도 부하 Case 로 뺀다.
|
||||
|
||||
## 다음 검증
|
||||
|
||||
1. 부하를 걸기 전 §122 OQ-7 이 든 여섯을 한 번 찍어 기준값으로 둔다.
|
||||
2. Keycloak 부하 시험을 돌리면서 같은 여섯을 같은 시각에 기록한다. 호스트에서는 QEMU 프로세스와 vhost 커널 스레드의 CPU 사용량을 스레드 단위로, softirq 시간을, 호스트 전체 CPU 를 적는다.
|
||||
3. 각 게스트의 CPU 사용량과 부하 도구가 낸 네트워크 지연을 같은 구간에서 적는다.
|
||||
4. vhost 커널 스레드와 softirq 를 읽는 데 쓴 명령을 함께 남긴다. §14.7 의 ps -eLo pid,tid,psr,pcpu,comm | grep qemu 는 qemu 로 걸러 내 vhost 커널 스레드를 내지 않고, softirq 를 읽는 명령은 근거 문서에 없다.
|
||||
5. Guest steal time 과 QEMU vCPU 스레드 쪽은 「Keycloak 동시 refresh 실험 중 CPU 가상화 계층이 결과를 흔들 만큼 포화되는가」가 같은 구간에서 재므로, 두 기록을 한 실험에서 함께 남긴다.
|
||||
|
||||
닫는 조건 : 부하 구간의 vhost 커널 스레드 사용량과 softirq 시간이 기준값과 다르지 않고 네트워크 지연도 움직이지 않으면, 이 부하 수준에서는 네트워크 가상화가 지연에 영향을 줄 만큼 호스트 CPU 를 쓰지 않는다고 적고 닫는다. 셋 중 하나라도 움직이면 그 부하 전후 표가 Case 가 되고, vhost 스레드를 어느 CPU 에 둘지나 multi-queue 를 켤지는 그 Case 뒤에 Decision 으로 넘긴다. 어느 쪽이든 이 결과가 없으면 §120 이 경고한 오귀속, 곧 네트워크 지연을 Refresh Token 경쟁으로 읽는 것을 배제했다고 적을 근거가 없다.
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
---
|
||||
id: bded6560-dc8d-473d-966c-5b037bb84c4c
|
||||
kind: QUESTION
|
||||
slug: qemu-backend-vs-vhost-net-on-this-host
|
||||
title: QEMU backend 와 vhost-net 의 차이가 이 호스트에서 실제로 보이는가
|
||||
topic: network-virtualization
|
||||
topicName: 네트워크 가상화
|
||||
project: virtualization
|
||||
status: 초안
|
||||
questionStatus: OPEN
|
||||
studio: "https://hyeonworks.com/studio/documents/bded6560-dc8d-473d-966c-5b037bb84c4c/edit"
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#122-open-question-oq-4
|
||||
- final/document.md#107-vhost-net-최적화
|
||||
- final/document.md#110-data-copy-최적화
|
||||
- final/document.md#111-interrupt-notification-최적화
|
||||
- final/document.md#117-이-구조에서-발생할-수-있는-문제-117-4
|
||||
---
|
||||
|
||||
# QEMU backend 와 vhost-net 의 차이가 이 호스트에서 실제로 보이는가
|
||||
|
||||
backend 는 virtqueue 를 사이에 두고 게스트와 버퍼를 주고받는 호스트 쪽을 가리킨다. §107 은 패킷 처리를 QEMU 사용자 공간(userspace)에서 호스트 커널로 옮기면 컨텍스트 스위치와 사용자 공간 오버헤드가 줄어든다고 적었다. 줄어든다는 방향만 적혀 있을 뿐 이 호스트에서 두 backend 를 나란히 재 본 값은 없다. §110 이 실제로 복사가 일어나는지는 커널 버전과 offload 를 비롯한 여러 조건에 따라 달라질 수 있다고 밝혔으므로, 다른 환경에서 나온 수치를 이 호스트의 값으로 옮겨 쓸 수도 없다. 이 물음은 §122 OQ-4 가 든 여섯 축을 이 호스트에서 나란히 재서 차이가 보이는지 가른다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **Guest 의 packet 이 Host Physical NIC 에 닿기까지 — virtio-net · virtqueue · vhost-net · TAP · Bridge**
|
||||
그 경로에서 두 backend 가 갈리는 곳은 TAP 다음 한 칸이고, 이 물음은 거기서 무엇이 달라지는지를 잰다.
|
||||
- **이 호스트에서 vhost-net 이 실제로 packet datapath 를 맡고 있는가**
|
||||
지금 어느 쪽으로 돌고 있는지가 정해져야 견줄 두 값 가운데 한쪽이 고정된다.
|
||||
- **부하 중 network 가상화가 지연을 바꿀 만큼 호스트 CPU 를 쓰는가**
|
||||
두 물음이 QEMU CPU 와 Host CPU 를 같은 부하에서 읽으므로 측정을 한 번으로 묶을 수 있다.
|
||||
- **Keycloak 실험 결과를 애플리케이션 원인으로 읽기 전에 network 계층을 따로 검증한다**
|
||||
backend 차이가 지연에 얼마나 들어오는지를 이 물음이 재면, 그 기준이 그 값을 가져다 쓴다.
|
||||
|
||||
## 사실
|
||||
|
||||
- §107 은 QEMU 가 사용자 공간에서 패킷마다 I/O 를 처리하면 호스트 커널과 QEMU 사용자 공간 사이를 오가는 전환이 쌓인다고 적었다. 초당 패킷 수(packet rate)가 높아질수록 사용자 공간과 커널 사이의 전환 · 스케줄링 · 복사 · 알림 비용이 커질 수 있다.
|
||||
- §107 이 적은 두 구성은 TAP 다음에 무엇이 오는지로 갈린다.
|
||||
QEMU userspace backend : TAP → QEMU → virtqueue
|
||||
vhost-net kernel backend : TAP → vhost-net → virtqueue
|
||||
- §125 는 최종 기준 구조에 두 Data Path 를 나란히 그렸다. 열한 칸 가운데 다른 곳은 TAP 다음 한 칸이고, 거기에 vhost-net 이 오느냐 QEMU virtio backend 가 오느냐로 갈린다.
|
||||
- §107 이 든 최적화 방향은 패킷마다 QEMU 사용자 공간이 끼어들던 처리를 커널 backend 로 옮겨 컨텍스트 스위치와 사용자 공간 오버헤드를 줄이는 것이다.
|
||||
- §110 은 virtio · virtqueue · vhost 구조가 버퍼 디스크립터(descriptor)로 불필요한 복사와 컨텍스트 스위치를 줄이도록 설계되어 있다고 적고, 그렇다고 이를 항상 zero-copy 라고 일반화하면 안 된다고 못 박았다. 실제로 복사가 일어나는지는 아래에 따라 달라질 수 있다.
|
||||
Kernel version · QEMU version · vhost configuration
|
||||
offload · NIC capability · packet path · GSO/GRO/TSO
|
||||
- §111 은 게스트와 호스트가 큐에 새 패킷이 들어왔음을 서로 알려야 한다고 적고, 패킷마다 인터럽트와 알림이 지나치게 많이 나가면 오버헤드가 커질 수 있다고 덧붙였다. 그래서 batching 과 interrupt moderation 과 queueing 이 중요하다.
|
||||
- §117.4 는 초당 패킷 수가 높은 구간에서 QEMU 사용자 공간이 처리 경로를 직접 맡으면 CPU 오버헤드가 커질 수 있다고 밝히고, 관찰할 것으로 QEMU CPU usage · vhost thread · packet rate · latency · context switch 를 들었다.
|
||||
- §122 OQ-4 는 비교할 축 여섯을 적었다.
|
||||
Latency
|
||||
Throughput
|
||||
QEMU CPU
|
||||
Host CPU
|
||||
Context Switch
|
||||
Packet rate
|
||||
- §122 OQ-4 는 축의 이름만 적어 두었고, 여섯을 무슨 도구로 어떻게 재는지는 제3부에 없다. 같은 §122 에서 OQ-1 과 OQ-2 는 돌릴 명령을 그대로 적었고 OQ-3 은 lsmod | grep vhost 를 확인 후보로 들었다.
|
||||
- 이 호스트에서 두 backend 를 재 본 값은 SSOT 에 없다.
|
||||
|
||||
## 가정
|
||||
|
||||
- 같은 가상 머신을 다른 backend 로 다시 구성해 띄울 수 있다고 본다. 그것이 이 실험 환경에서 되는지는 SSOT 에 적혀 있지 않다.
|
||||
- 두 구성에 같은 부하를 걸 수 있다고 전제한다. 부하 도구와 요청 구성이 같아야 여섯 축을 견줄 수 있기 때문이다.
|
||||
- 부하를 걸기 전 값과 부하 중 값의 차이가 backend 차이보다 작다고 전제하지 않는다. 그래서 부하 전 값을 먼저 찍어 둔다.
|
||||
- 두 구성을 같은 시각에 나란히 돌릴 수 없다고 보고 차례로 잰다. 그 사이에 호스트의 다른 부하가 달라지면 값이 흔들릴 수 있다.
|
||||
|
||||
## 미지수
|
||||
|
||||
- 같은 부하를 두 backend 로 돌렸을 때 여섯 축이 실제로 얼마나 달라지는지.
|
||||
- 그 차이가 이 실험의 결과를 다르게 읽어야 할 만큼인지, 아니면 측정 흔들림 안인지.
|
||||
- 이 호스트가 내는 초당 패킷 수가 §107 이 말한 「높아질수록 비용이 커지는」 구간에 들어가는지.
|
||||
- 여섯 축을 이 환경에서 무엇으로 재는지. 제3부가 도구를 적지 않아 실행하는 쪽이 정한다.
|
||||
|
||||
## 제약
|
||||
|
||||
- 지금 backend 가 무엇인지는 이 물음이 정하지 않는다. 그것을 확정하는 물음이 닫힌 뒤에 시작한다. §116 은 이 테스트 환경의 경로를 펼치면서 TAP 다음 칸에 vhost-net 을 적어 두었는데, 그 칸이 실제로 그런지를 §122 가 OQ-3 으로 아직 묻고 있으므로 그 그림을 지금 backend 의 근거로 쓰지 않는다.
|
||||
- §110 이 든 조건들(kernel version · QEMU version · vhost configuration · offload · NIC capability · packet path · GSO/GRO/TSO)을 함께 적지 않으면 이 측정을 다른 환경에 재사용할 수 없다.
|
||||
- 이 호스트에서 잰 값이 없어 다른 장비의 backend 비교 수치를 근거로 삼지 않는다.
|
||||
- 여기서 어느 backend 로 실험을 고정할지는 정하지 않는다. 그것은 측정 결과가 나온 뒤의 Decision 이다.
|
||||
|
||||
## 선택지
|
||||
|
||||
### 1. 지금 backend 만 먼저 찍어 나중에 견줄 값을 만든다
|
||||
|
||||
구성을 바꾸지 않고 부하 전과 부하 중의 여섯 축을 한 번씩 기록한다. 가상 머신을 다시 구성하지 않으므로 지금 돌고 있는 Keycloak 실험을 멈추지 않아도 되고, 나중에 다른 backend 를 잴 때 견줄 값이 생긴다.
|
||||
|
||||
한 구성의 값만으로는 이 물음이 닫히지 않는다.
|
||||
|
||||
### 2. 두 backend 로 바꿔 가며 같은 부하를 건다
|
||||
|
||||
§122 OQ-4 가 요구하는 비교가 이것이다. 같은 가상 머신을 다른 backend 로 다시 구성하고 같은 부하를 양쪽에 걸어 여섯 축을 같은 시각에 기록한다. 차이가 나면 그 표가 그대로 Case 가 된다.
|
||||
|
||||
가상 머신을 다시 구성하고 재시작해야 하므로 그동안 Keycloak 실험을 멈춰야 하고, 두 측정 사이에 호스트 상태가 달라지지 않도록 관리해야 한다.
|
||||
|
||||
### 3. 문헌의 backend 비교 수치를 가져다 쓴다 — 제외
|
||||
|
||||
§110 은 실제로 복사가 일어나는지가 여러 조건에 따라 달라질 수 있다고 밝혔다. 그 조건은 kernel version · QEMU version · vhost configuration · offload · NIC capability · packet path · GSO/GRO/TSO 다. 조건이 이만큼 걸려 있으니 다른 환경에서 나온 수치는 이 호스트의 값이 되지 못한다. 이 물음은 이 호스트에서 잰 값으로만 닫힌다.
|
||||
|
||||
## 다음 검증
|
||||
|
||||
1. 지금 backend 가 무엇인지 먼저 확정한다. 그것을 묻는 물음이 닫히기 전에는 견줄 두 값 가운데 한쪽이 무엇인지 알 수 없다.
|
||||
2. 부하를 걸기 전에 여섯 축을 한 번 찍어 둔다.
|
||||
3. 지금 구성에 부하를 걸고 여섯 축을 같은 시각에 기록한다. Latency 와 Throughput 과 Packet rate 는 부하 도구가 내는 값을 쓰고, QEMU CPU 와 Host CPU 와 Context Switch 는 호스트에서 읽는다.
|
||||
4. 같은 가상 머신을 다른 backend 로 구성하고 같은 부하를 걸어 3 을 되풀이한다.
|
||||
5. 두 구성의 여섯 축을 나란히 적고, §110 이 든 조건들과 실행한 명령을 함께 증거로 남긴다.
|
||||
|
||||
닫는 조건 : 두 구성의 여섯 축을 나란히 놓으면 닫는다. 차이가 부하 전 값의 흔들림 안이면 이 호스트가 내는 초당 패킷 수에서는 backend 선택이 결과를 바꾸지 않는다고 적고 닫는다. 차이가 나면 그 측정이 Case 가 되고, 어느 backend 로 실험을 고정할지는 그 Case 뒤에 Decision 으로 넘긴다.
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
---
|
||||
id: 397c4789-0272-449b-86d6-2c4a3a122401
|
||||
kind: QUESTION
|
||||
slug: tap-interface-to-vm-mapping
|
||||
title: VM1 과 VM2 의 TAP/vnet interface 는 무엇이고 어디에 붙어 있는가
|
||||
topic: network-virtualization
|
||||
topicName: 네트워크 가상화
|
||||
project: virtualization
|
||||
status: 초안
|
||||
questionStatus: OPEN
|
||||
studio: "https://hyeonworks.com/studio/documents/397c4789-0272-449b-86d6-2c4a3a122401/edit"
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#122-open-question-oq-2
|
||||
- final/document.md#99-tap의-역할
|
||||
- final/document.md#117-이-구조에서-발생할-수-있는-문제-117-1
|
||||
- final/document.md#118-실제-linux에서-확인할-명령어
|
||||
---
|
||||
|
||||
# VM1 과 VM2 의 TAP/vnet interface 는 무엇이고 어디에 붙어 있는가
|
||||
|
||||
§99 는 TAP 을 가상 머신의 이더넷 프레임과 호스트 리눅스 네트워크를 잇는 접점으로 놓았다. 그 접점의 이름은 호스트마다 다르게 붙는데, 이 호스트에서 두 가상 머신에 각각 무엇이 붙었는지는 SSOT 에 없다. 이름을 모르면 §119 가 적은 계층별 tcpdump 도 대상을 채우지 못하고, §117.1 이 든 「특정 VM 만 통신 불가」가 어느 가상 머신을 가리키는지도 가릴 수 없다. 이 물음은 두 가상 머신의 호스트 쪽 인터페이스 이름과 그것이 붙어 있는 곳을 확정한다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **Guest 의 packet 이 Host Physical NIC 에 닿기까지 — virtio-net · virtqueue · vhost-net · TAP · Bridge**
|
||||
그 경로에서 TAP 이 무엇을 하는지 이미 설명해 두었으므로 이 물음은 거기서 이어진다.
|
||||
- **packet 이 어디서 끊겼는지는 계층마다 capture 해서 가른다**
|
||||
그 기준이 요구하는 캡처 지점의 이름을 이 물음이 댄다.
|
||||
- **이 호스트의 가상 머신 네트워크는 Bridge 인가 NAT 인가 Routing 인가**
|
||||
ip link 와 bridge link 를 두 물음이 같이 쓰므로 한 번 찍어 둘을 함께 읽는다.
|
||||
- **Host Nginx 에서 Keycloak 까지 packet 은 실제로 어디를 지나는가**
|
||||
tcpdump 를 어느 인터페이스에 걸지가 이 물음의 답에서 나온다.
|
||||
|
||||
## 사실
|
||||
|
||||
- §99 는 TAP 을 호스트 리눅스 커널이 제공하는 가상 이더넷 네트워크 인터페이스로 적었다. 물리 장치가 아니고, 이름의 예로 tap0 과 vnet0 을 들었다.
|
||||
- §99 가 적은 TAP 의 역할은 가상 머신의 이더넷 프레임과 호스트 리눅스 네트워크를 연결하는 접점이다.
|
||||
수신 : Linux Bridge → TAP → VM
|
||||
송신 : VM → TAP → Linux Bridge
|
||||
- §99 는 확인 명령으로 ip link · ip tuntap show · bridge link · virsh domiflist 넷을 들었고, §118 이 같은 명령을 계층별 목록으로 다시 적었다.
|
||||
TAP/vnet : ip link · ip tuntap show
|
||||
libvirt VM NIC : virsh domiflist 에 domain 이름을 넣는다
|
||||
Linux Bridge : ip link show type bridge · bridge link · bridge fdb show
|
||||
- §122 OQ-2 는 이 호스트에서 돌릴 명령을 네 줄로 적었다.
|
||||
virsh domiflist vm1
|
||||
virsh domiflist vm2
|
||||
ip link
|
||||
bridge link
|
||||
- §99 와 §118 이 TAP 확인 명령으로 든 ip tuntap show 는 OQ-2 의 네 줄에 없다.
|
||||
- §117.1 은 TAP/Bridge 연결 오류의 증상 셋을 들었다.
|
||||
VM 외부 통신 불가
|
||||
Host ↔ VM 통신 불가
|
||||
특정 VM 만 통신 불가
|
||||
- §117.1 이 그 증상에서 확인하라고 든 명령은 ip link · bridge link · bridge fdb show · virsh domiflist 다.
|
||||
- §116 은 이 테스트 환경의 경로를 펼치면서 TAP 칸을 TAP(vm1) 과 TAP(vm2) 로 적었다. 호스트에서 읽은 이름은 그 그림에도 없다.
|
||||
- 두 가상 머신의 호스트 쪽 인터페이스 이름도, 그 인터페이스가 어느 브리지에 붙어 있는지도 SSOT 에는 없다.
|
||||
|
||||
## 가정
|
||||
|
||||
- 호스트에 붙어 virsh 와 ip 계열 명령을 실행할 수 있다고 본다.
|
||||
- §122 OQ-2 가 명령에 적은 vm1 과 vm2 가 이 호스트에 실재하는 domain 이름이라고 본다. 실제 이름이 다르면 그 이름으로 바꿔 돌린다. §99 와 §118 은 같은 명령에 넣을 domain 을 비워 두었고, 가상 머신 이름을 그대로 적은 곳은 §90.1 의 virsh domiflist vm1 과 §122 OQ-2 다.
|
||||
- virsh domiflist 출력에 인터페이스 이름과 MAC 주소가 함께 나온다고 전제한다. §99 도 §118 도 이 명령의 출력 형식은 적지 않았다.
|
||||
- 두 가상 머신이 켜져 있는 동안 읽는다고 전제한다. 꺼진 가상 머신의 TAP 이 호스트에 남아 있는지는 SSOT 에 적혀 있지 않다.
|
||||
|
||||
## 미지수
|
||||
|
||||
- VM1 과 VM2 의 호스트 쪽 인터페이스 이름이 각각 무엇인지.
|
||||
- 각 인터페이스의 MAC 주소와 NIC model 이 무엇인지.
|
||||
- 두 인터페이스가 같은 브리지에 붙어 있는지, 서로 다른 곳에 붙어 있는지.
|
||||
- ip tuntap show 에 나오는 TAP 목록과 virsh domiflist 가 대는 이름이 그대로 맞아떨어지는지.
|
||||
|
||||
## 제약
|
||||
|
||||
- 이름과 어디에 붙어 있는지를 적는 데서 끊는다. 그 경로로 패킷이 실제로 흘렀는지는 tcpdump 를 쓰는 물음이 받는다.
|
||||
- 두 가상 머신을 같은 시점에 읽는다. 한쪽을 재시작한 뒤 다른 쪽을 읽으면 이름이 바뀌어도 알 수 없기 때문이다.
|
||||
- 이 호스트에서 읽은 출력이 없어 tap0 이나 vnet0 같은 §99 의 예시 이름을 이 호스트의 값으로 쓰지 않는다.
|
||||
|
||||
## 선택지
|
||||
|
||||
### 1. libvirt 가 대는 이름을 먼저 받아 호스트에서 대조한다
|
||||
|
||||
virsh domiflist 로 가상 머신마다 붙은 인터페이스를 받고, 그 이름이 ip link 목록에 실재하는지 확인한 뒤 bridge link 로 어느 브리지의 port 인지 잡는다. §122 OQ-2 가 적은 네 줄이 이 순서다. 가상 머신과 인터페이스의 짝이 처음부터 정해져 나오므로 두 대의 것을 헷갈리지 않는다.
|
||||
|
||||
libvirt 가 모르는 인터페이스는 이 순서에서 빠진다.
|
||||
|
||||
### 2. 호스트의 인터페이스를 전부 세우고 가상 머신으로 되짚는다
|
||||
|
||||
ip link 와 ip tuntap show 로 호스트에 있는 TAP 을 모두 적고, bridge link 로 어느 브리지에 붙었는지 잡는다. 그다음 virsh domiflist 로 각각이 어느 가상 머신의 것인지 되짚는다. libvirt 밖에서 만들어진 TAP 이 있어도 목록에 남는다.
|
||||
|
||||
가상 머신이 두 대뿐인데 호스트에 TAP 이 여럿이면 짝짓는 데 MAC 주소를 다시 대조해야 한다.
|
||||
|
||||
### 3. 통신이 안 될 때 §117.1 의 확인 목록으로 함께 본다 — 제외
|
||||
|
||||
§117.1 이 증상과 확인 명령을 이미 묶어 두었으니 장애가 났을 때 같이 보자는 방법이다. 지금 필요한 것은 정상일 때의 이름과 붙어 있는 곳이고, 그것이 있어야 장애 때 무엇이 달라졌는지 견줄 수 있다. 증상이 난 뒤에 처음 읽으면 그 값이 원래 그랬는지 그때 바뀐 것인지 가릴 수 없다.
|
||||
|
||||
## 다음 검증
|
||||
|
||||
1. virsh domiflist vm1 과 virsh domiflist vm2 로 각 가상 머신에 붙은 인터페이스를 읽는다.
|
||||
2. ip link 로 그 이름이 호스트에 실재하는지 대조한다.
|
||||
3. bridge link 로 각 인터페이스가 어느 브리지의 port 인지 적는다.
|
||||
4. 두 가상 머신의 결과를 인터페이스 이름 · MAC 주소 · 붙어 있는 브리지로 나란히 적고, 실행한 명령과 출력을 함께 증거로 남긴다.
|
||||
|
||||
닫는 조건 : 가상 머신마다 인터페이스 이름과 MAC 주소와 붙어 있는 브리지를 적어 두 대를 나란히 놓으면 닫는다. 이 목록이 계층별 캡처 기준이 요구하는 지점의 이름이 되고, 이것이 없으면 실제 패킷 경로를 묻는 물음의 tcpdump 를 어느 인터페이스에 걸지 정할 수 없다. 두 가상 머신이 서로 다른 브리지에 붙어 있으면 §117.1 의 「특정 VM 만 통신 불가」를 진단할 때 그 사실을 먼저 본다고 적는다.
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
---
|
||||
id: 1a996f8d-b04e-49d2-88ef-4877e21f7c0e
|
||||
kind: QUESTION
|
||||
slug: virtio-net-multi-queue-enabled
|
||||
title: 이 가상 머신들의 virtio-net 에 multi-queue 가 켜져 있는가
|
||||
topic: network-virtualization
|
||||
topicName: 네트워크 가상화
|
||||
project: virtualization
|
||||
status: 초안
|
||||
questionStatus: OPEN
|
||||
studio: "https://hyeonworks.com/studio/documents/1a996f8d-b04e-49d2-88ef-4877e21f7c0e/edit"
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#122-open-question-oq-5
|
||||
- final/document.md#112-multi-queue-최적화
|
||||
- final/document.md#100-virtqueue의-역할
|
||||
- final/document.md#117-이-구조에서-발생할-수-있는-문제-117-5
|
||||
---
|
||||
|
||||
# 이 가상 머신들의 virtio-net 에 multi-queue 가 켜져 있는가
|
||||
|
||||
multi-queue 는 virtio-net 이 송수신 큐를 여러 개 두고 쓰는 구성이다. §112 는 큐를 하나만 쓰면 패킷 처리가 한 vCPU 나 한 처리 경로에 몰릴 수 있어서 이것을 최적화 방향으로 들었다. 이 물음은 그 쏠림이 실제로 일어나는지를 재지 않고, 두 가상 머신이 애초에 큐를 몇 개 쓰도록 구성되어 있는지를 읽는다. 근거 문서는 multi-queue 를 쓸 수 있다고만 적었을 뿐 이 가상 머신들의 큐 수는 적지 않았다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **Guest 의 packet 이 Host Physical NIC 에 닿기까지 — virtio-net · virtqueue · vhost-net · TAP · Bridge**
|
||||
게스트와 호스트 backend 가 virtqueue 로 무엇을 주고받는지를 이 개념이 설명한다. 큐를 몇 개 두느냐는 그 구조 위의 설정이다.
|
||||
- **이 호스트에서 vhost-net 이 실제로 packet datapath 를 맡고 있는가**
|
||||
큐를 실제로 돌리는 backend 가 어느 쪽이냐에 따라 큐 수를 읽을 곳이 달라진다.
|
||||
- **부하 중 network 가상화가 지연을 바꿀 만큼 호스트 CPU 를 쓰는가**
|
||||
큐가 하나로 나왔을 때 그것이 실제 병목인지는 부하 구간의 vCPU 별 사용량이 답한다.
|
||||
|
||||
## 사실
|
||||
|
||||
- §112 는 큐를 하나만 쓰면 특정 vCPU 나 처리 경로에 부하가 몰릴 수 있다고 적었고, virtio-net 이 multi-queue 를 쓸 수 있다고 밝혔다. 든 예는 RX Queue 0 부터 3 까지를 vCPU 0 부터 3 까지에 하나씩 대응시킨 구성이다.
|
||||
- §112 가 적은 multi-queue 의 목적 셋
|
||||
Packet processing 병렬화
|
||||
Single queue bottleneck 완화
|
||||
Multi-core 활용
|
||||
- §112 는 그 효과가 workload · CPU affinity · IRQ placement · queue configuration 에 따라 달라진다고 덧붙였다. IRQ(Interrupt Request, 인터럽트 요청)는 장치가 처리할 일이 생겼음을 CPU 에 알리는 신호다. §117.5 가 그 분포를 확인 대상으로 든 것을 보면, 큐를 나눠 두어도 그 신호를 한 vCPU 가 몰아서 받으면 처리는 한 곳에 몰릴 수 있다.
|
||||
- §100 은 virtqueue 를 게스트와 호스트 backend 가 디스크립터(descriptor)를 써서 I/O 버퍼를 주고받는 공유 큐 구조로 놓고, 네트워크에서는 보통 TX/RX 큐를 쓴다고 적었다. TX virtqueue 는 게스트에서 호스트로, RX virtqueue 는 호스트에서 게스트로 버퍼를 넘긴다.
|
||||
- §117.5 는 single queue bottleneck 을 큐 하나나 vCPU 하나에 패킷 처리가 몰리는 문제로 놓고, 확인 대상 넷을 들었다.
|
||||
virtio multi-queue
|
||||
IRQ distribution
|
||||
per-vCPU CPU usage
|
||||
RSS/RPS/XPS
|
||||
- §122 OQ-5 가 적은 확인 대상 넷
|
||||
QEMU/libvirt NIC configuration
|
||||
Guest ethtool
|
||||
queue count
|
||||
IRQ distribution
|
||||
- 같은 §122 에서 OQ-1 과 OQ-2 는 돌릴 명령을 그대로 적었고, OQ-5 는 확인 대상 넷의 이름만 적었다. §117.5 가 든 넷도 이름이다.
|
||||
- 두 가상 머신에 설정된 큐 수가 근거 문서에 없다. 게스트가 몇 개를 쓰고 있는지도, IRQ 가 어느 vCPU 에 붙어 있는지도 적혀 있지 않다.
|
||||
- §126 이 적은 실습 순서 열둘 가운데 multi-queue / offload 확인은 마지막 열두째다.
|
||||
- 네트워크 계층의 확인 명령을 모아 둔 §118 에는 큐 수나 IRQ 분포를 읽는 명령이 없다. Guest NIC 항목에 적힌 것은 ip link · ip addr · ip route · ip neigh 이고, virtio 장치 항목은 lspci 와 lsmod | grep virtio 다. ethtool 은 Physical NIC 항목에서 인터페이스 이름을 받는 형태로만 나온다.
|
||||
|
||||
## 가정
|
||||
|
||||
- 두 가상 머신의 구성과 게스트 내부를 지금 읽을 수 있다고 본다.
|
||||
- §112 가 든 RX Queue 넷과 vCPU 넷의 짝은 multi-queue 를 설명하려고 든 예시이지 이 호스트에서 읽은 값이 아니다.
|
||||
- 설정에 적힌 큐 수와 게스트가 실제로 쓰는 큐 수를 따로 읽어야 한다고 전제한다. 설정에 여럿을 적어 두면 게스트 드라이버가 그만큼 쓴다는 서술이 근거 문서에 없기 때문이다.
|
||||
- 두 가상 머신의 NIC 구성이 확인하는 동안 바뀌지 않는다.
|
||||
|
||||
## 미지수
|
||||
|
||||
- 두 가상 머신의 virtio-net 에 설정된 큐 수.
|
||||
- 게스트가 실제로 쓰고 있는 큐 수, 그리고 그 수가 설정값과 같은지.
|
||||
- 각 큐의 IRQ 가 여러 vCPU 에 흩어져 있는지 한 vCPU 에 몰려 있는지.
|
||||
- 두 가상 머신의 vCPU 수. §112 가 큐와 vCPU 를 하나씩 짝지어 든 예는 두 수를 나란히 놓아야 읽히는데, vCPU 수도 네트워크 쪽 근거에는 없다.
|
||||
- 이 환경의 RSS/RPS/XPS 설정. §117.5 가 확인 대상으로 들었지만 값을 읽는 방법은 적지 않았다.
|
||||
|
||||
## 제약
|
||||
|
||||
- 이 호스트에서 잰 값이 하나도 없다. 근거는 개념을 정리한 문서 한 편이고 §112 의 큐 넷과 vCPU 넷은 예시 숫자다.
|
||||
- 이 물음은 큐 구성이 무엇인지까지만 답한다. 쏠림이 실제 병목인지는 부하를 걸어야 갈리고, 그 부하 측정은 다른 물음이 가져간다.
|
||||
- 확인하는 동안 NIC 구성을 바꾸지 않는다. 큐 수를 늘려 놓고 읽으면 지금 실험이 어떤 구성에서 돌았는지 못 본다.
|
||||
- §118 이 큐 수와 IRQ 분포를 읽는 명령을 적지 않았으므로, 실행한 명령과 그 출력을 함께 증거로 남겨야 다음 사람이 같은 값을 다시 읽는다.
|
||||
|
||||
## 선택지
|
||||
|
||||
### 1. 호스트 쪽 설정값만 먼저 읽는다
|
||||
|
||||
libvirt 와 QEMU 쪽 NIC 구성에 큐 수가 적혀 있는지부터 본다. §90.2 가 libvirt 의 관리 대상 예로 든 여덟은 vCPU · Memory · Disk · NIC model · MAC address · Virtual network · Bridge · QEMU arguments 이고, 큐 수는 그 목록에 없다. 가상 머신에 들어가지 않고 호스트에서 끝나고, 큐가 하나로 적혀 있으면 그 구성은 single queue 로 확정된다.
|
||||
|
||||
설정에 큐를 여럿 적어 두었을 때 게스트가 그만큼 쓰는지는 이 확인으로 알 수 없다.
|
||||
|
||||
### 2. 설정값과 게스트 쪽 채널 수를 함께 읽는다
|
||||
|
||||
§122 OQ-5 가 든 넷 가운데 앞의 셋에 해당한다. 호스트의 NIC 구성과 게스트 안에서 본 채널 수를 같이 적으면 둘이 어긋나는 경우까지 잡힌다. 가상 머신 두 대에 각각 들어가야 해서 실행 횟수가 늘어난다.
|
||||
|
||||
### 3. IRQ 분포까지 한 번에 읽는다
|
||||
|
||||
큐가 여럿이어도 IRQ 가 한 vCPU 에 몰려 있으면 §117.5 가 든 쏠림은 그대로 생길 수 있다. §122 OQ-5 가 IRQ distribution 을 넷째 확인 대상으로 둔 이유가 여기에 있다. 세 항목을 한 번에 받아 적으면 큐 수만으로 판정을 끝내지 않게 된다.
|
||||
|
||||
### 4. 제외 — 큐 수를 바꿔 가며 견준다
|
||||
|
||||
multi-queue 를 켜고 끄면서 재면 이 환경에서 큐 수가 무엇을 바꾸는지 바로 보인다. 그러나 지금 물음은 실험이 어떤 구성에서 돌고 있는지를 읽는 것이라, 구성을 바꾸고 잰 값은 답이 되지 않는다. 켜고 끈 두 구성을 견주는 측정은 별도 Case 로 뺀다.
|
||||
|
||||
## 다음 검증
|
||||
|
||||
1. 호스트에서 두 가상 머신의 libvirt/QEMU NIC configuration 을 열어 큐 수가 적혀 있는지 읽는다. §122 에서 libvirt domain XML 을 확인하라고 적은 곳은 OQ-3 이고 OQ-5 에는 그 문장이 없다. §118 이 이 확인의 명령을 적지 않았으므로 무엇을 실행했는지 함께 기록한다.
|
||||
2. 각 게스트에서 ethtool 로 채널 수를 읽고, 실제 queue count 를 그 옆에 적는다. §122 OQ-5 가 Guest ethtool 과 queue count 를 나눠 적었으므로 둘을 따로 남긴다.
|
||||
3. 각 게스트의 IRQ 분포를 읽어 큐마다 어느 vCPU 에 붙어 있는지 적는다.
|
||||
4. 두 가상 머신의 결과를 vCPU 수와 나란히 한 표로 정리한다.
|
||||
|
||||
닫는 조건 : 가상 머신마다 설정된 큐 수 · 게스트가 쓰는 큐 수 · IRQ 분포를 한 표로 적으면 닫는다. 큐가 하나로 나오면 §117.5 가 든 single queue bottleneck 이 이 환경에서도 일어날 수 있는 구성이라고 적는다. 그것이 실제 병목인지는 부하를 건 뒤 vCPU 별 CPU 사용량이 답하기 때문에 「부하 중 network 가상화가 지연을 바꿀 만큼 호스트 CPU 를 쓰는가」로 넘긴다. 큐가 여럿이고 IRQ 도 흩어져 있으면 이 항목은 지금 실험에서 우선순위를 낮추고 닫는다.
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
---
|
||||
id: 38716d7f-1aa1-48d8-ab4c-3fc505332128
|
||||
kind: QUESTION
|
||||
slug: vm-network-mode-bridge-nat-or-routed
|
||||
title: 이 호스트의 가상 머신 네트워크는 Bridge 인가 NAT 인가 Routing 인가
|
||||
topic: network-virtualization
|
||||
topicName: 네트워크 가상화
|
||||
project: virtualization
|
||||
status: 초안
|
||||
questionStatus: OPEN
|
||||
studio: "https://hyeonworks.com/studio/documents/38716d7f-1aa1-48d8-ab4c-3fc505332128/edit"
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#122-open-question-oq-1
|
||||
- final/document.md#96-linux-bridge의-역할
|
||||
- final/document.md#97-routing의-역할
|
||||
- final/document.md#98-nat의-역할
|
||||
- final/document.md#114-linux-bridge가-항상-host-tcp-ip-stack을-거치는-것은-아니다
|
||||
- final/document.md#94-전체-네트워크-계층
|
||||
---
|
||||
|
||||
# 이 호스트의 가상 머신 네트워크는 Bridge 인가 NAT 인가 Routing 인가
|
||||
|
||||
§98 은 가상 머신 네트워크를 분석하기 전에 Bridge 기반인가 · Routing 기반인가 · NAT(Network Address Translation, 네트워크 주소 변환) 기반인가를 구분하라고 적었다. 셋은 프레임이 지나는 계층이 다르고, 그에 따라 호스트의 L3 경로와 Netfilter 가 끼어드는지도 갈린다. 이 물음은 성능을 재지 않는다. 제3부가 서술한 경로 가운데 어느 절이 이 호스트에 그대로 적용되는지를 먼저 확정한다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **Guest 의 packet 이 Host Physical NIC 에 닿기까지 — virtio-net · virtqueue · vhost-net · TAP · Bridge**
|
||||
그 경로 그림에서 TAP 과 Physical NIC 사이에 놓인 계층이 이 물음이 확정하려는 부분이다.
|
||||
- **packet 이 어디서 끊겼는지는 계층마다 capture 해서 가른다**
|
||||
구성이 무엇이냐에 따라 캡처를 걸 계층의 이름이 달라진다.
|
||||
- **VM1 과 VM2 의 TAP/vnet interface 는 무엇이고 어디에 붙어 있는가**
|
||||
ip link 와 bridge link 를 두 물음이 같이 쓰므로 한 번 찍어 둘을 함께 읽는다.
|
||||
- **Host Nginx 에서 Keycloak 까지 packet 은 실제로 어디를 지나는가**
|
||||
이 물음이 닫혀야 그 추적에서 tcpdump 를 걸 대상이 정해진다.
|
||||
|
||||
## 사실
|
||||
|
||||
- §96 은 Linux Bridge 를 호스트 커널 안의 L2 소프트웨어 스위치로 적었다. 이더넷 프레임의 Destination MAC 을 보고 어느 포트로 보낼지 정하고, MAC learning 을 하며, 여러 가상 포트와 물리 포트를 잇는다.
|
||||
- §97 은 Routing 을 L3 에서 IP 를 보고 내리는 결정으로 놓았다. Bridge 가 같은 이더넷 네트워크를 잇는 것과 달리 Routing 은 서로 다른 IP 네트워크를 잇고, destination IP 를 보고 어느 인터페이스나 next-hop 으로 보낼지 정한다.
|
||||
- §98 은 NAT 을 패킷의 IP/Port 정보를 바꾸는 것으로 놓고, 가상 머신이 private subnet 을 쓰면 호스트가 NAT gateway 처럼 동작할 수 있다는 예를 들었다.
|
||||
VM : 192.168.122.10
|
||||
Host NAT 를 지난 뒤 : 203.0.113.10
|
||||
- §96 과 §97 은 절 끝에 확인 명령을 달았다. §96 은 bridge link · bridge fdb show · ip link show type bridge 를, §97 은 ip route 를 든다. 셋을 구분하라고 적은 §98 에는 확인 명령이 없다.
|
||||
- §114 는 Bridge 가 단순 L2 forwarding 만 하는 구성이면 프레임이 호스트의 일반적인 L3 TCP/IP 스택을 지나지 않고 다른 TAP 으로 나갈 수 있다고 적었다. 호스트가 Routing · NAT · Host-local termination · Firewall 을 맡으면 그때는 L3/Netfilter 경로가 끼어든다.
|
||||
- 그래서 §114 는 Physical NIC → Host TCP/IP Stack → Bridge 를 고정된 패킷 경로로 보면 안 되고, 실제 경로는 bridge/routing/NAT 구성에 따라 달라진다고 못 박았다.
|
||||
- §94 는 이 문서가 기준으로 삼은 구조를 virtio-net + vhost-net + TAP + Linux Bridge 로 적고, 실제 환경은 Bridge · NAT · Routed Network · macvtap · SR-IOV · VFIO passthrough · Open vSwitch · Kubernetes CNI 에 따라 달라질 수 있다고 덧붙였다.
|
||||
- 확인할 명령은 §122 OQ-1 이 다섯 줄로 적어 두었다.
|
||||
정의된 가상 네트워크 열거 : virsh net-list --all
|
||||
그 가상 네트워크의 정의 읽기 : virsh net-dumpxml 에 이름을 넣는다
|
||||
호스트 인터페이스 목록 : ip link
|
||||
어느 인터페이스가 어느 브리지의 포트인지 : bridge link
|
||||
라우팅 테이블 : ip route
|
||||
- §118 이 같은 계층에 든 명령 가운데 virsh net-info 와 ip rule 은 OQ-1 의 다섯 줄에 없다.
|
||||
- 이 호스트에서 그 명령을 돌린 출력은 SSOT 에 없다. 셋 중 무엇인지도 적혀 있지 않다.
|
||||
|
||||
## 가정
|
||||
|
||||
- 호스트에 붙어 virsh 와 ip 계열 명령을 실행할 수 있다고 본다.
|
||||
- libvirt 가상 네트워크로 정의된 구성이면 virsh net-list --all 에 이름이 나온다고 본다. 호스트가 미리 만들어 둔 브리지에 가상 머신을 직접 붙인 구성이면 그 목록에 아무 이름도 나오지 않을 수 있다.
|
||||
- 확인하는 동안 네트워크 구성이 바뀌지 않는다고 전제한다.
|
||||
- 셋 가운데 하나로 갈린다고 보고 물음을 세웠다. 다만 §114 가 Bridge 구성에도 Routing 과 NAT 과 Firewall 이 함께 걸릴 수 있다고 적었으므로, 하나로 갈리지 않으면 걸린 것을 모두 적는다.
|
||||
|
||||
## 미지수
|
||||
|
||||
- 이 호스트의 가상 머신 네트워크가 Bridge 기반인지 NAT 기반인지 Routing 기반인지.
|
||||
- libvirt 가상 네트워크로 정의되어 있는지, 아니면 호스트의 브리지에 가상 머신이 직접 붙어 있는지.
|
||||
- 정의되어 있다면 그 가상 네트워크의 forward mode 와 bridge 이름이 무엇인지.
|
||||
- 가상 머신을 떠난 프레임이 호스트의 L3/Netfilter 경로를 지나는지.
|
||||
|
||||
## 제약
|
||||
|
||||
- 구성이 셋 중 무엇인지를 확정하는 데서 끊는다. 그 구성이 지연에 얼마나 영향을 주는지는 부하 중 호스트 CPU 사용을 보는 물음이 받는다.
|
||||
- 이 호스트에서 읽은 출력이 없어 다른 장비의 구성을 근거로 삼지 않는다.
|
||||
- 돌릴 명령은 §122 OQ-1 이 정해 두었다. 실행한 명령과 출력을 함께 남겨야 다음 사람이 같은 값을 다시 읽는다.
|
||||
|
||||
## 선택지
|
||||
|
||||
### 1. libvirt 쪽부터 읽는다
|
||||
|
||||
virsh net-list --all 로 정의된 가상 네트워크를 열거하고, 나온 이름마다 virsh net-dumpxml 로 forward mode 와 bridge 이름을 읽는다. forward mode 하나로 Bridge 인지 NAT 인지 Routed 인지가 갈리므로 확인이 짧다.
|
||||
|
||||
libvirt 로 정의하지 않고 호스트 브리지에 직접 붙인 구성이면 목록이 비어 나오고, 그때는 호스트 인터페이스 쪽을 다시 읽어야 한다.
|
||||
|
||||
### 2. 호스트 인터페이스 쪽부터 읽는다
|
||||
|
||||
ip link 로 실재하는 인터페이스를 세우고, bridge link 로 어느 인터페이스가 어느 브리지의 포트인지를 잡고, ip route 로 L3 결정을 본다. libvirt 로 정의했든 안 했든 호스트에 실재하는 것을 읽으므로 구성 방식과 무관하게 답이 나온다.
|
||||
|
||||
forward mode 라는 이름으로 적힌 의도는 나오지 않으므로, NAT 이 걸려 있는지는 routing 과 방화벽 규칙을 따로 봐야 한다. §117.3 이 NAT/Firewall 오류에 든 것도 nftables · iptables · NAT rules · IP forwarding 이라는 확인 대상 이름이고 돌릴 명령이 아니다.
|
||||
|
||||
### 3. tcpdump 로 경로부터 잡는다 — 제외
|
||||
|
||||
§119 는 Host NIC · Bridge · TAP · Guest NIC 에서 tcpdump 로 패킷을 추적하는 순서를 적어 두었다. 다만 그 추적은 캡처를 걸 인터페이스 이름을 이미 알고 있을 때 성립한다. 지금은 브리지 이름도 TAP 이름도 모르므로 명령의 대상을 채울 수 없다. §126 의 실습 순서에서도 Bridge/NAT/Route 확인이 셋째이고 Host Nginx → VM packet path tcpdump 는 여덟째다. 이 방법은 이 물음이 닫힌 뒤 실제 패킷 경로를 묻는 물음이 받는다.
|
||||
|
||||
## 다음 검증
|
||||
|
||||
1. virsh net-list --all 로 정의된 가상 네트워크를 열거한다.
|
||||
2. 나온 이름마다 virsh net-dumpxml 에 그 이름을 넣어 forward mode 와 bridge 이름을 읽는다.
|
||||
3. ip link 로 호스트의 인터페이스 목록을 적는다.
|
||||
4. bridge link 로 어느 인터페이스가 어느 브리지에 붙어 있는지 적는다.
|
||||
5. ip route 로 라우팅 테이블을 적는다.
|
||||
6. 다섯 출력을 실행한 명령과 함께 증거로 남긴다.
|
||||
|
||||
닫는 조건 : 세 구성 가운데 무엇인지가 출력으로 확정되면 닫는다. Bridge 로 나오면 §96 과 §114 의 L2 forwarding 서술이 이 호스트에 적용된다고 적는다. NAT 으로 나오면 §98 의 주소 변환이 경로에 들어가고, Routing 으로 나오면 §97 의 L3 결정이 들어간다. 어느 쪽이든 그 결과로 실제 패킷 경로를 묻는 물음의 캡처 지점이 정해진다. §94 가 기준으로 삼은 구조와 다르게 나오면 제3부의 서술 가운데 이 호스트에 적용되지 않는 절을 함께 적는다.
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
---
|
||||
id: 68a848c7-eaff-4421-ad25-de21673ca40c
|
||||
kind: REFERENCE
|
||||
slug: bisect-the-packet-path-with-capture-points
|
||||
title: packet 이 어디서 끊겼는지는 계층마다 capture 해서 가른다
|
||||
topic: network-virtualization
|
||||
topicName: 네트워크 가상화
|
||||
project: virtualization
|
||||
status: 초안
|
||||
studio: "https://hyeonworks.com/studio/documents/68a848c7-eaff-4421-ad25-de21673ca40c/edit"
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#119-실제-packet-path-추적
|
||||
- final/document.md#118-실제-linux에서-확인할-명령어
|
||||
- final/document.md#117-이-구조에서-발생할-수-있는-문제-117-1
|
||||
- final/document.md#117-이-구조에서-발생할-수-있는-문제-117-2
|
||||
- final/document.md#117-이-구조에서-발생할-수-있는-문제-117-3
|
||||
- final/document.md#117-이-구조에서-발생할-수-있는-문제-117-6
|
||||
- final/document.md#113-offload-최적화
|
||||
- final/document.md#114-linux-bridge가-항상-host-tcp-ip-stack을-거치는-것은-아니다
|
||||
- final/document.md#99-tap의-역할
|
||||
---
|
||||
|
||||
# packet 이 어디서 끊겼는지는 계층마다 capture 해서 가른다
|
||||
|
||||
가상 머신이 밖과 통신하지 못할 때 게스트 안에서만 원인을 찾으면 호스트의 브리지와 TAP 연결은 마지막에야 보게 된다. 그 사이의 계층은 애플리케이션 로그에 아무것도 남기지 않는다.
|
||||
|
||||
그래서 패킷이 지나야 할 지점마다 캡처를 걸고 어디까지 보였는지로 구간을 좁힌다. 호스트의 물리 NIC(Network Interface Card) 와 브리지, 가상 머신에 붙은 TAP, 게스트 안의 인터페이스 넷이 그 지점이고, 보이지 않은 첫 지점의 앞 구간이 의심 구간이 된다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **Guest 의 packet 이 Host Physical NIC 에 닿기까지 — virtio-net · virtqueue · vhost-net · TAP · Bridge**
|
||||
이 절차가 관측 지점으로 쓰는 계층을 세운 글이다. 어느 계층이 무엇을 하는지는 그쪽에 있다.
|
||||
- **Keycloak 실험 결과를 애플리케이션 원인으로 읽기 전에 network 계층을 따로 검증한다**
|
||||
그 기준은 문제가 네트워크인지 아닌지를 가르고, 이 절차는 네트워크 안에서 어느 구간인지를 가른다.
|
||||
- **이 호스트의 가상 머신 네트워크는 Bridge 인가 NAT 인가 Routing 인가**
|
||||
캡처 지점 이름을 확정하는 첫 규칙이 그대로 이 물음의 확인 절차다.
|
||||
- **VM1 과 VM2 의 TAP/vnet interface 는 무엇이고 어디에 붙어 있는가**
|
||||
TAP 이름을 모르면 이 절차의 세 번째 지점에 캡처를 걸 수 없다.
|
||||
- **Host Nginx 에서 Keycloak 까지 packet 은 실제로 어디를 지나는가**
|
||||
그 경로를 처음 확인할 때 쓰는 절차다.
|
||||
|
||||
## 목적
|
||||
|
||||
이 기준은 두 가지를 막는다.
|
||||
|
||||
가상 머신이 통신하지 못할 때 게스트 안쪽과 애플리케이션부터 뒤지느라 호스트의 브리지와 TAP 연결을 늦게 보는 일이 하나다.
|
||||
|
||||
오프로드 때문에 실제 wire 와 다르게 보이는 정상 패킷을 결함으로 판정하는 일이 다른 하나다.
|
||||
|
||||
이 순서는 물음 셋이 함께 쓴다. 이 호스트의 가상 머신 네트워크가 무엇으로 구성돼 있는지, TAP 인터페이스가 무엇인지, 호스트 Nginx 에서 Keycloak 까지 패킷이 어디를 지나는지 셋이 모두 이 순서 위에서 돈다. 그래서 세 곳에 같은 절차를 되풀이하지 않고 한 편에 두었다.
|
||||
|
||||
## 규칙
|
||||
|
||||
### 1. 캡처할 지점의 이름을 먼저 확정한다
|
||||
|
||||
관측 지점은 넷이다. 호스트의 물리 NIC, 호스트의 브리지, 가상 머신에 붙은 tap 또는 vnet, 그리고 게스트 안의 인터페이스.
|
||||
|
||||
이름을 모른 채 시작하면 없는 인터페이스에 캡처를 걸어 놓고 패킷이 안 보인다고 판정하게 된다.
|
||||
|
||||
인터페이스 목록은 ip link 로 본다.
|
||||
브리지에 무엇이 붙어 있는지는 bridge link 와 bridge fdb show 가 알려 준다.
|
||||
tap 목록은 ip tuntap show 로 본다.
|
||||
어느 가상 머신이 어느 tap 에 붙어 있는지는 virsh domiflist 에 도메인 이름을 붙여 확인한다.
|
||||
libvirt 가 만든 가상 네트워크가 무엇인지는 virsh net-list --all 과 virsh net-dumpxml 로 확인한다.
|
||||
라우팅이 개입하는 구성이면 ip route 와 ip rule 도 함께 본다.
|
||||
|
||||
### 2. 네 지점에 같은 요청을 흘리면서 캡처를 건다
|
||||
|
||||
호스트에서는 물리 NIC 와 브리지, tap 또는 vnet 에 각각 tcpdump -ni 로 캡처를 건다.
|
||||
게스트에서는 게스트 인터페이스에 같은 방식으로 건다.
|
||||
|
||||
### 3. 보이지 않은 첫 지점의 앞 구간을 의심 구간으로 삼는다
|
||||
|
||||
판독은 어느 지점까지 보였는가로만 한다.
|
||||
|
||||
물리 NIC 에 보이고 브리지에도 보이는데 tap 에 안 보이면, 게스트 내부보다 먼저 호스트의 브리지와 tap 연결을 의심한다.
|
||||
|
||||
tap 에는 보이는데 게스트 NIC 에 안 보이면 virtio 와 vhost, 게스트 NIC 계층을 의심한다.
|
||||
|
||||
게스트 NIC 에는 보이는데 소켓까지 오지 않으면 게스트의 라우팅과 방화벽, listen 상태를 의심한다.
|
||||
|
||||
### 4. 증상마다 다음에 볼 명령을 미리 정해 둔다
|
||||
|
||||
의심 구간이 나오면 그 구간의 증상에 맞는 확인으로 넘어간다.
|
||||
|
||||
가상 머신이 외부와 통신하지 못하거나 호스트와 통신하지 못하거나 특정 가상 머신만 통신하지 못하면 tap 과 브리지 연결을 본다. ip link, bridge link, bridge fdb show, virsh domiflist 가 그 확인이다.
|
||||
|
||||
같은 서브넷은 통신되는데 다른 서브넷이 안 되거나 게이트웨이까지는 가는데 외부 통신이 실패하면 라우팅을 본다. ip route 와 ip rule 이다.
|
||||
|
||||
가상 머신에서 인터넷으로 나가지 못하거나 외부에서 가상 머신에 접근하지 못하거나 특정 포트만 실패하면 nftables 와 iptables, NAT(Network Address Translation) 규칙, IP(Internet Protocol) forwarding 설정을 확인 대상으로 둔다.
|
||||
|
||||
증상 셋을 각각 다른 기준으로 나누지 않은 것은 셋이 모두 어느 지점에서 끊겼는가로 환원되기 때문이다. 따로 적으면 같은 규칙의 부분 증상이 셋으로 늘어난다.
|
||||
|
||||
### 5. 패킷 크기와 체크섬이 예상과 다르다는 이유만으로 결함이라고 읽지 않는다
|
||||
|
||||
오프로드가 켜져 있으면 캡처에서 보이는 패킷 크기나 체크섬이 실제 wire 에서 보이는 것과 다르게 보일 수 있다. 원인 후보는 GSO(Generic Segmentation Offload), GRO(Generic Receive Offload), TSO(TCP Segmentation Offload), checksum offload 다.
|
||||
|
||||
크기와 체크섬이 예상과 다르면 오프로드 설정을 먼저 확인하고, 그 값만으로 판정하지 않는다.
|
||||
|
||||
### 6. 호스트의 L3 쪽에서 안 보이는 것을 곧바로 실패로 읽지 않는다
|
||||
|
||||
브리지가 L2 전달만 하는 경우 프레임이 호스트의 일반적인 L3 TCP/IP 스택을 반드시 거치지는 않는다. 한 가상 머신의 tap 에서 브리지를 지나 다른 가상 머신의 tap 으로 가는 경로가 그렇다.
|
||||
|
||||
호스트가 라우팅이나 NAT, host-local termination, 방화벽 역할을 할 때 L3 와 Netfilter 경로가 개입한다. 그래서 구성에 따라 호스트의 L3 관측에서 안 보이는 것이 정상이다.
|
||||
|
||||
### 7. 네 지점이 모두 보이는데 느리기만 한 경우는 이 절차가 답하지 않는다
|
||||
|
||||
이 절차가 가르는 것은 패킷이 어디서 끊겼는가다. 끊기지 않고 느리기만 하면 다른 기준으로 넘긴다.
|
||||
|
||||
## 적용 조건
|
||||
|
||||
KVM(Kernel-based Virtual Machine)/QEMU/libvirt 로 만든 가상 머신이 외부와 통신하지 못할 때.
|
||||
|
||||
특정 구간에서만 통신이 실패할 때.
|
||||
|
||||
실제 패킷 경로를 처음 확인할 때.
|
||||
|
||||
## 예외
|
||||
|
||||
오프로드가 켜진 구성에서는 패킷 크기와 체크섬이 그대로 증거가 되지 않는데, 다섯 번째 규칙이 그 경우다.
|
||||
|
||||
브리지가 L2 전달만 하는 구성에서는 호스트의 L3 관측에 안 보이는 것이 정상인데, 여섯 번째 규칙이 그 경우다.
|
||||
|
||||
네 지점이 모두 보이는데 느린 경우는 이 기준이 다루지 않는다. Keycloak 실험 결과를 애플리케이션 원인으로 읽기 전에 network 계층을 따로 검증한다 쪽으로 넘긴다.
|
||||
|
||||
이 저장소에는 아직 이 절차를 실제로 돌린 출력이 없다. 여기 적은 판독은 원문 문서가 서술한 것이고 이 호스트의 캡처로 확인한 것이 아니다.
|
||||
|
||||
## 예시
|
||||
|
||||
- 물리 NIC 에 보이고 브리지에도 보이는데 tap 에 안 보임 : 호스트의 브리지와 tap 연결을 먼저 확인한다
|
||||
- tap 에 보이는데 게스트 NIC 에 안 보임 : virtio 와 vhost, 게스트 NIC 계층을 확인한다
|
||||
- 게스트 NIC 에 보이는데 소켓까지 안 옴 : 게스트의 라우팅과 방화벽, listen 상태를 확인한다
|
||||
- 특정 가상 머신만 통신 불가 : ip link 와 bridge link, virsh domiflist 로 그 가상 머신의 tap 이 브리지에 붙어 있는지 본다
|
||||
- 같은 서브넷은 되고 다른 서브넷만 실패 : ip route 와 ip rule 을 본다
|
||||
- 캡처에 보이는 패킷이 예상보다 크고 체크섬이 어긋남 : 오프로드 설정을 먼저 확인한다
|
||||
- 네 지점이 모두 보이는데 응답만 느림 : 이 절차를 여기서 멈춘다
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
---
|
||||
id: ca36f0db-9028-4761-91a8-afb7eb30f2b9
|
||||
kind: REFERENCE
|
||||
slug: verify-the-network-path-before-blaming-the-application
|
||||
title: Keycloak 실험 결과를 애플리케이션 원인으로 읽기 전에 network 계층을 따로 검증한다
|
||||
topic: network-virtualization
|
||||
topicName: 네트워크 가상화
|
||||
project: virtualization
|
||||
status: 초안
|
||||
studio: "https://hyeonworks.com/studio/documents/ca36f0db-9028-4761-91a8-afb7eb30f2b9/edit"
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#120-keycloak-refresh-token-실험과의-관계
|
||||
- final/document.md#117-이-구조에서-발생할-수-있는-문제-117-7
|
||||
- final/document.md#124-핵심-claim
|
||||
- final/document.md#89-문서-목적
|
||||
- final/document.md#116-현재-keycloak-k3s-테스트-환경과-연결
|
||||
- final/document.md#102-packet이-keycloak까지-올라오는-과정
|
||||
---
|
||||
|
||||
# Keycloak 실험 결과를 애플리케이션 원인으로 읽기 전에 network 계층을 따로 검증한다
|
||||
|
||||
Refresh Token 경쟁 자체는 virtio-net 문제가 아니다. 다만 그 실험은 클라이언트에서 Nginx 와 가상 머신, K3s, Keycloak 을 거쳐 PostgreSQL 또는 Redis 까지 가는 경로를 여러 노드가 함께 쓴다. 그 경로 위의 네트워크 가상화 문제는 애플리케이션 동시성 문제와 비슷한 증상으로 나타난다.
|
||||
|
||||
노드 하나만 느리거나 특정 가상 머신에서만 요청이 실패하는 것을 Refresh Token 경쟁이나 데이터베이스 락으로 결론내지 않으려면, 실험 결과를 원인에 귀속하기 전에 네트워크 경로를 따로 검증한다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **Guest 의 packet 이 Host Physical NIC 에 닿기까지 — virtio-net · virtqueue · vhost-net · TAP · Bridge**
|
||||
이 기준이 「따로 검증한다」고 말하는 계층을 세운 글이다.
|
||||
- **packet 이 어디서 끊겼는지는 계층마다 capture 해서 가른다**
|
||||
이 기준이 문제가 네트워크인지 아닌지를 가른다면, 그 절차는 네트워크 안에서 어느 구간인지를 가른다.
|
||||
- **Host Nginx 에서 Keycloak 까지 packet 은 실제로 어디를 지나는가**
|
||||
검증 순서의 첫 항목이다. 경로를 모르면 무엇을 관측할지도 정해지지 않는다.
|
||||
- **부하 중 network 가상화가 지연을 바꿀 만큼 호스트 CPU 를 쓰는가**
|
||||
검증 순서의 마지막 항목이고, 네트워크 처리와 CPU 경쟁을 가르는 관측이다.
|
||||
- **Keycloak 동시 refresh 실험 중 CPU 가상화 계층이 결과를 흔들 만큼 포화되는가**
|
||||
같은 실험을 CPU 쪽에서 본 물음이다. 두 계층이 같은 지연으로 보이므로 함께 기록한다.
|
||||
|
||||
## 목적
|
||||
|
||||
이 기준은 실험 결과를 잘못된 원인에 붙이는 일을 막는다.
|
||||
|
||||
Keycloak 멀티 노드 실험에서 관측되는 지연과 실패에는 네트워크 가상화 쪽 원인이 섞일 수 있다. 노드 하나만 지연되는 경우, 가상 머신 하나에서만 패킷이 유실되는 경우, 호스트 브리지 구성이 잘못된 경우, NAT(Network Address Translation) 나 conntrack 문제, 호스트 CPU 경쟁 때문에 vhost 처리가 밀리는 경우가 여기 들어간다.
|
||||
|
||||
이 원인들을 확인하지 않은 채 결과를 Refresh Token 경쟁이나 데이터베이스 락으로 적으면, 고칠 곳이 아닌 곳을 고치게 된다.
|
||||
|
||||
이 분리는 실험 결과를 보고 나서 세운 규칙이 아니다. 원문 문서는 이 실험 기반으로 검증하려는 항목을 일곱 개 적어 두었고, 그 마지막이 「네트워크 계층 문제와 애플리케이션/저장소 문제의 분리」였다.
|
||||
|
||||
## 규칙
|
||||
|
||||
### 1. 실험이 지나는 경로를 먼저 적고 시작한다
|
||||
|
||||
요청은 클라이언트에서 Nginx 로 가고, 거기서 가상 머신 두 대 중 하나로, 그 안의 K3s 를 지나 Keycloak 으로, 다시 PostgreSQL 또는 Redis 로 간다. 이 경로를 노드들이 공유한다.
|
||||
|
||||
경로를 적지 않으면 어느 관측이 어느 구간을 덮는지 정해지지 않는다.
|
||||
|
||||
### 2. 노드 하나에서만 나는 증상을 애플리케이션 동시성으로 먼저 읽지 않는다
|
||||
|
||||
노드 하나만 지연되거나 가상 머신 하나에서만 실패하는 증상은 그 노드로 가는 경로 쪽에서도 나올 수 있다. 호스트 브리지 구성 오류, NAT 와 conntrack 문제, 그 가상 머신 쪽 패킷 유실이 같은 모양으로 관측된다.
|
||||
|
||||
증상이 노드별로 갈리면 그 노드까지 가는 경로를 먼저 확인한다.
|
||||
|
||||
### 3. 애플리케이션 로그만으로 네트워크 계층을 배제하지 않는다
|
||||
|
||||
Keycloak 은 virtqueue 와 vhost-net, TAP, Bridge, 물리 NIC(Network Interface Card) 를 직접 알지 못하고, 게스트가 주는 소켓 위에서만 동작한다. 아래 계층이 어긋나도 애플리케이션 로그에는 느렸다는 것과 실패했다는 것만 남는다.
|
||||
|
||||
그래서 애플리케이션 쪽 관측을 아무리 늘려도 이 계층이 원인인지 아닌지는 갈리지 않는다.
|
||||
|
||||
### 4. 네트워크 처리가 쓰는 호스트 CPU 를 실험과 같은 시간축에 기록한다
|
||||
|
||||
vhost-net 과 QEMU 스레드, softirq 도 호스트 CPU 를 쓴다. 그래서 네트워크 문제처럼 보이는 지연이 CPU 스케줄링 문제일 수도 있다. 앞의 세 규칙이 애플리케이션 증상을 네트워크 쪽으로 되돌린다면 이 규칙은 네트워크 증상을 CPU 쪽으로 되돌린다. 방향이 반대라서 둘을 한 기준에 둔다.
|
||||
|
||||
부하 실험을 돌릴 때 QEMU 와 vhost 의 CPU 사용량, 호스트와 게스트의 CPU, 네트워크 지연을 같은 시간축에 남긴다. 나중에 따로 재면 그때의 부하를 다시 만들어야 한다.
|
||||
|
||||
### 5. 세 가지 검증을 마친 뒤에 원인을 적는다
|
||||
|
||||
경로가 무엇인지, 그 경로가 끊기지 않았는지, 네트워크 처리가 호스트 CPU 를 얼마나 쓰는지 셋을 확인한다.
|
||||
|
||||
첫째는 호스트 Nginx 에서 Keycloak 까지의 실제 패킷 경로를 추적해서, 둘째는 계층마다 캡처해서, 셋째는 부하 중 CPU 사용량을 재서 답한다.
|
||||
|
||||
## 적용 조건
|
||||
|
||||
여러 노드가 같은 네트워크 경로를 공유하는 실험의 결과를 원인에 귀속할 때.
|
||||
|
||||
노드별로만 나타나는 지연을 볼 때.
|
||||
|
||||
특정 가상 머신에서만 나는 실패를 볼 때.
|
||||
|
||||
부하 구간에서만 커지는 지연을 애플리케이션 동시성이나 저장소 lock 으로 결론내려 할 때.
|
||||
|
||||
## 예외
|
||||
|
||||
이 기준은 애플리케이션 쪽 관측을 늘려서는 적용되지 않는다. Keycloak 이 게스트 소켓 위에서만 동작하므로 애플리케이션 로그로는 이 계층이 원인인지 가릴 수 없다.
|
||||
|
||||
네트워크 계층이 깨끗하다고 해서 이 기준이 애플리케이션 결함을 배제해 주지는 않는다. 여기서 나오는 것은 네트워크가 원인이 아니라는 것까지다.
|
||||
|
||||
가상 머신 위 K3s 안쪽은 이 기준의 범위 밖이다. CNI(Container Network Interface) 와 Service, Pod 네트워크는 별도 계층으로 두고 따로 분석한다.
|
||||
|
||||
원문 문서가 이 테스트 환경에 얹어 그린 경로는 확인된 것이 아니라 기준 구조를 그대로 옮겨 놓은 그림이다. 이 저장소에는 이 기준으로 원인을 실제로 가른 실험이 아직 없다.
|
||||
|
||||
## 예시
|
||||
|
||||
- Keycloak 노드 하나만 응답이 느림 : 그 노드가 있는 가상 머신까지의 경로부터 확인한다
|
||||
- 가상 머신 하나에서만 요청이 실패 : 호스트 브리지 구성과 그 가상 머신의 tap 연결을 본다
|
||||
- 부하를 올릴수록 지연이 커짐 : QEMU 와 vhost 의 CPU 사용량을 같은 시간축에서 함께 본다
|
||||
- 동시 갱신 실험에서 두 번째 요청이 거부됨 : 네트워크 검증 셋을 마친 뒤에 Refresh Token 경쟁으로 적는다
|
||||
- 네트워크 검증이 전부 깨끗함 : 네트워크가 원인이 아니라는 것까지만 적고 애플리케이션 쪽을 계속 본다
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
---
|
||||
id: 3b05c1f0-13e9-414a-97a6-2078fb4bab26
|
||||
kind: CONCEPT
|
||||
slug: guest-block-io-path-to-virtqueue
|
||||
title: Guest 의 write() 가 virtqueue 에 실리기까지 — VFS · Filesystem · Page Cache · Block Layer · virtio-blk
|
||||
topic: storage-virtualization
|
||||
topicName: 스토리지 가상화
|
||||
project: virtualization
|
||||
status: 초안
|
||||
basisVersion: QEMU/KVM 의 virtio-blk frontend 와 virtqueue · 게스트가 ext4 나 XFS 에 buffered I/O 로 쓰는 경우 · SSOT 가 커널과 QEMU 버전을 적지 않았다
|
||||
studio: "https://hyeonworks.com/studio/documents/3b05c1f0-13e9-414a-97a6-2078fb4bab26/edit"
|
||||
assets:
|
||||
- key: guest-block-io-to-virtqueue
|
||||
file: ../../../final/assets/diagrams/guest-block-io-to-virtqueue/guest-block-io-to-virtqueue.svg
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#128-전체-구조
|
||||
- final/document.md#129-guest-application-read-write-에서-시작
|
||||
- final/document.md#130-vfs-공통-파일-인터페이스-계층
|
||||
- final/document.md#131-filesystem-ext4-xfs-파일-세계를-block-공간에-배치
|
||||
- final/document.md#132-inode
|
||||
- final/document.md#133-page-cache-write-가-바로-ssd-write는-아니다
|
||||
- final/document.md#134-guest-block-i-o-layer
|
||||
- final/document.md#135-dev-vda-guest가-보는-가상-block-device
|
||||
- final/document.md#136-dev-vda-와-filesystem-관계
|
||||
- final/document.md#137-virtio-blk-guest의-가상-block-device-driver
|
||||
- final/document.md#138-virtio-blk와-virtqueue
|
||||
- final/document.md#139-virtqueue의-실제-의미
|
||||
- final/document.md#166-storage-i-o-completion
|
||||
- final/document.md#127-문서-목적
|
||||
- final/document.md#172-storage-virtualization-canonical-flow
|
||||
- final/document.md#173-network-virtualization과-비교
|
||||
- final/document.md#177-최종-요약
|
||||
---
|
||||
|
||||
# Guest 의 write() 가 virtqueue 에 실리기까지 — VFS · Filesystem · Page Cache · Block Layer · virtio-blk
|
||||
|
||||
가상 머신 안의 PostgreSQL 이 파일에 데이터를 기록하면, 그 요청은 게스트 Linux 안에서만 여러 계층을 지난 뒤에야 가상 머신 경계에 닿는다. VFS 가 열린 파일을 어느 파일시스템 구현으로 보낼지 정하고, ext4 나 XFS 가 파일을 블록 공간에 배치하고, 페이지 캐시가 dirty page 로 받아 두었다가 나중에 writeback 한다. 이어서 블록 I/O 계층이 그 내용을 READ 와 WRITE, FLUSH 요청으로 바꾸고, 마지막에 virtio-blk 드라이버가 Virtio 블록 요청을 만들어 virtqueue 에 게시한다. 계층 이름과 게스트·호스트 구분을 아는 사람을 독자로 둔다. Keycloak 멀티 노드 실험을 가상 머신 두 대 위에서 돌리고 있다 보니, 이 경로를 알아 두면 게스트에서 본 저장소 지연을 애플리케이션 문제와 가상 머신 경계 아래의 문제로 갈라 볼 수 있다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **/dev/vda 뒤에 있는 것 — qcow2 · RAW · Host block device**
|
||||
이 글은 virtqueue 까지만 따라간다. 그 요청을 받은 QEMU 가 무엇에 연결돼 있는지는 그 글이 이어받는다.
|
||||
- **완료라는 말의 네 가지 뜻 — write · writeback · flush · 전원 장애 생존**
|
||||
여기서는 write() 성공이 SSD 영속화가 아니라는 데까지만 적었다. 그 응답이 어디까지 보장하는지는 그 글이 나눈다.
|
||||
- **VM 아래에 한 번 더 있는 Host block stack — blk-mq · I/O Scheduler · NVMe**
|
||||
게스트 블록 계층과 같은 이름의 계층이 호스트에도 한 번 더 있고, 그쪽에서는 다른 가상 머신의 요청과 섞인다.
|
||||
- **Guest 안에서 본 disk 로 backend 를 단정하지 않는다**
|
||||
게스트에 /dev/vda 가 보인다는 관측이 그 규칙의 근거가 된다.
|
||||
- **이 호스트의 /dev/vda 는 어떤 backend 에 붙어 있는가**
|
||||
이 글은 게스트가 보는 이름까지만 말한다. 이 장비에서 그 이름이 무엇에 붙어 있는지는 아직 확인하지 않았다.
|
||||
- **Guest 의 fsync() 지연과 Host storage 지연은 같이 오르는가**
|
||||
경로는 적었지만 각 구간의 지연을 이 호스트에서 재지 않았다.
|
||||
- **Guest 의 packet 이 Host Physical NIC 에 닿기까지 — virtio-net · virtqueue · vhost-net · TAP · Bridge**
|
||||
virtqueue 라는 같은 구조를 패킷 쪽에서 설명한 글이다.
|
||||
|
||||
## 본문
|
||||
|
||||
<!-- body:start -->
|
||||
|
||||
## 애플리케이션은 저장장치를 직접 다루지 않는다
|
||||
|
||||
가상 머신 안의 PostgreSQL 이나 Keycloak 같은 프로세스는 SSD 나 `virtio-blk` 를 직접 다루지 않는다. 파일에 데이터를 기록할 때 개념적으로 부르는 것은 시스템 콜 하나다.
|
||||
|
||||
```c label="게스트 애플리케이션이 파일에 기록할 때 부르는 system call"
|
||||
write(fd, buffer, size);
|
||||
```
|
||||
|
||||
파일과 관련된 시스템 콜은 이것 말고도 여럿이다.
|
||||
|
||||
```text label="대표적인 파일 관련 system call"
|
||||
open()
|
||||
read()
|
||||
write()
|
||||
close()
|
||||
fsync()
|
||||
```
|
||||
|
||||
이 시점에는 QEMU 도 `qcow2` 도 호스트의 NVMe 도 등장하지 않는다. 애플리케이션은 저장장치를 조작하는 것이 아니라 게스트 Linux 커널에 파일 연산을 요청하고, 그 뒤의 계층들이 그 요청을 하나씩 옮긴다.
|
||||
|
||||
## VFS 가 어느 파일시스템으로 보낼지 정한다
|
||||
|
||||
VFS(Virtual File System, 가상 파일 시스템)는 Linux 커널 안에서 여러 파일시스템을 같은 방식으로 쓸 수 있게 묶어 주는 공통 계층이다. 게스트가 `ext4` 를 쓰든 `XFS` 를 쓰든 애플리케이션이 부르는 `write()` 는 같고, 그 뒤를 VFS 가 갈라 준다. VFS 는 이 `fd` 가 어떤 파일인지 확인하고, 그 파일이 어떤 파일시스템에 속하는지 확인한 뒤 해당 파일시스템 구현으로 연산을 넘긴다.
|
||||
|
||||
## ext4 와 XFS 가 파일을 블록 공간에 배치한다
|
||||
|
||||
SSD 는 `/var/lib/postgresql/data` 같은 디렉터리 구조를 모른다. 저장장치가 보는 것은 번호가 붙은 블록 단위 공간인데 사람과 프로그램이 보는 것은 파일과 디렉터리 계층이다. 그 둘을 잇는 계층이 `ext4` 나 `XFS` 같은 파일시스템이다. 파일시스템이 관리하는 것은 파일 이름과 디렉터리 구조, 파일 크기, 소유자와 권한, 시각 정보, `inode` 와 메타데이터, 파일 데이터가 저장될 블록, 남은 공간, 파일시스템 일관성이다.
|
||||
|
||||
`inode` 는 파일 메타데이터와 저장 위치 정보를 관리하는 자료구조다. 디렉터리 항목이 파일 이름을 `inode` 번호로 옮기고, 그 `inode` 가 소유자와 권한, 크기, 시각 정보, 파일 데이터가 저장된 블록 정보를 들고 있다. 그래서 파일 이름과 `inode` 는 같은 것이 아니다. 어디까지 들어갈지는 SSOT 가 직접 그어 두었다.
|
||||
|
||||
> Storage 가상화를 이해하기 위해 inode 내부 구현까지 파고들 필요는 없지만, filesystem이 파일과 block을 연결한다는 점은 알아야 한다.
|
||||
|
||||
이 글도 거기서 멈춘다.
|
||||
|
||||
## write() 가 끝나도 SSD 에는 아직 없다
|
||||
|
||||
일반적인 buffered I/O 에서는 `write()` 가 호출될 때마다 물리 SSD 까지 즉시 내려갈 필요가 없다. 애플리케이션이 넘긴 데이터는 메모리의 페이지 캐시에 먼저 닿는다. 저장장치에 `ABC` 가 있는데 애플리케이션이 `DEF` 를 추가하면 페이지 캐시에는 `ABCDEF` 가 들어가고 SSD 에는 아직 `ABC` 만 있다. 이렇게 저장장치보다 최신인 페이지를 dirty page 라고 한다. 이후 커널 writeback 이 그 내용을 파일시스템과 블록 계층을 거쳐 저장장치 쪽으로 내려보낸다.
|
||||
|
||||
데이터가 페이지 캐시에 머무는 동안에도 호출이 성공으로 돌아오기 때문에, `write()` 성공과 물리 SSD 영속화 완료는 같은 사건이 아니다. 게스트 애플리케이션이 성공을 돌려받은 시점에 데이터가 어디에 있는지는 게스트 메모리까지만 확정된다.
|
||||
|
||||
## 블록 I/O 계층이 파일 세계를 블록 장치 세계로 옮긴다
|
||||
|
||||
파일시스템이 파일과 블록 할당을 관리하면, Linux 의 블록 I/O 서브시스템이 그 요청을 아래의 블록 장치 드라이버가 처리할 수 있는 I/O 요청으로 바꿔 전달한다. 파일시스템 세계에서 `/users/data.db` 의 오프셋 8192 에 4KB 를 쓰라고 내려온 것이, 블록 장치 세계에서는 `/dev/vda` 의 특정 위치에 `READ` 나 `WRITE`, `FLUSH` 를 거는 요청이 된다. 대표 요청은 넷이다 — `READ`, `WRITE`, `FLUSH`, `DISCARD`.
|
||||
|
||||
이 계층 안에는 `bio` 와 `request`, `queue`, `blk-mq` 가 실제로 있다. SSOT 는 `blk-mq` 의 tag allocator 같은 내부 구현을 별도 문서로 미뤘고 이 글도 이름까지만 적는다.
|
||||
|
||||
## /dev/vda 는 게스트가 보는 이름이다
|
||||
|
||||
물리 머신에서는 `/dev/sda` 나 `/dev/nvme0n1` 같은 블록 장치가 보인다. `virtio-blk` 를 쓰는 가상 머신에서는 흔히 `/dev/vda` 와 `/dev/vdb` 처럼 보이고, 게스트 Linux 는 그것을 하나의 블록 장치로 인식한다. 게스트 안에서 `lsblk` 를 실행하면 그 장치와 파티션이 나온다.
|
||||
|
||||
```bash label="게스트가 보는 block device"
|
||||
lsblk
|
||||
```
|
||||
|
||||
```text label="SSOT 가 든 출력 예시. 이 호스트에서 실행한 결과가 아니다"
|
||||
NAME SIZE TYPE MOUNTPOINT
|
||||
vda 100G disk
|
||||
├─vda1 1G part /boot
|
||||
└─vda2 99G part /
|
||||
```
|
||||
|
||||
게스트에 이렇게 보여도 그 장치가 호스트의 실제 SSD 인지는 게스트 안에서 알 수 없다. 이 장비의 `/dev/vda` 가 무엇에 붙어 있는지도 아직 확인하지 않았다. 게스트가 보는 것은 `/dev/vda` 에서 파티션으로, 파티션에서 파일시스템으로, 파일시스템에서 마운트 지점으로 이어지는 연결까지다. `cd /var/lib/postgresql` 로 디렉터리를 옮기는 동작은 파일시스템 세계를 지나고, `lsblk` 의 `vda` 는 블록 장치 세계를 보여 준다.
|
||||
|
||||
이름 둘이 가리키는 대상도 다르다. `/dev/vda` 가 게스트 Linux 에 보이는 블록 장치를 가리키고, `virtio-blk` 는 그 가상 블록 장치를 제어하는 게스트 커널 드라이버를 가리킨다. 네트워크 쪽의 `ens3` 와 `virtio-net` 이 같은 짝이다.
|
||||
|
||||
## virtio-blk 가 요청을 virtqueue 에 게시한다
|
||||
|
||||
게스트 블록 계층에서 `/dev/vda` 의 특정 위치에 데이터를 `WRITE` 하라는 요청이 내려오면, `virtio-blk` 드라이버가 그것을 Virtio 블록 요청으로 구성해 `virtqueue` 에 게시한다. 네트워크에서 TCP/IP 스택이 `virtio-net` 을 거쳐 `virtqueue` 로 내려가던 구조가 저장소 쪽에서도 반복된다.
|
||||
|
||||
게스트 메모리 안에 I/O 버퍼가 있고 `virtqueue` 의 디스크립터가 그 버퍼를 가리키기 때문에, `virtqueue` 를 데이터가 흘러가는 관으로 보면 부정확하다. 게시되는 요청에는 `Operation` 이 `WRITE` 인지, `Sector` 가 어디인지, `Data Buffer` 가 게스트 메모리의 어디인지가 들어간다. 뜻을 풀면 `/dev/vda` 의 이 위치에 게스트 메모리의 이 버퍼를 기록하라는 요청이다.
|
||||
|
||||

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

|
||||
|
||||
## `blk-mq` 는 CPU 마다 큐를 둔다
|
||||
|
||||
현대 Linux 의 블록 계층은 `blk-mq` 로 되어 있다. CPU0 부터 CPU3 까지 각 CPU 가 자기 큐를 가지고 그 큐들이 NVMe 로 이어진다. NVMe 가 높은 병렬성과 큐 깊이를 지원하기 때문에 여러 CPU 가 병렬로 블록 I/O 를 처리할 수 있는 구조가 중요해진다. 그래서 스토리지 처리 성능은 CPU 스케줄링과 완전히 분리되지 않는다.
|
||||
|
||||
## I/O 스케줄러는 들어온 순서 그대로 내보내지 않는다
|
||||
|
||||
여러 I/O 요청이 있어도 항상 들어온 순서 그대로 장치에 전달되지는 않는다. 요청은 I/O 스케줄러를 지나 장치 드라이버로 나간다. 스케줄러마다 목적과 내보내는 정책이 다르다. 대표적으로 볼 수 있는 값은 `none` 과 `mq-deadline` 과 `bfq` 다.
|
||||
|
||||
`none` 을 고르면 복잡한 스케줄링 정책을 최소화해서 비교적 직접 장치 쪽으로 내보낸다. NVMe 처럼 장치 자체가 강한 병렬성과 큐 기능을 가진 경우에는 이런 단순한 정책이 적합할 수 있다. 그렇게 골랐다고 블록 계층이 아무 일도 하지 않지는 않는다.
|
||||
|
||||
지금 선택된 스케줄러는 `sysfs` 에서 읽는다.
|
||||
|
||||
```bash label="호스트의 현재 I/O Scheduler"
|
||||
cat /sys/block/nvme0n1/queue/scheduler
|
||||
```
|
||||
|
||||
출력이 `[none] mq-deadline` 이면 대괄호 안의 `none` 이 지금 선택된 스케줄러다. SATA 나 SCSI 장치라면 `cat /sys/block/sda/queue/scheduler` 처럼 장치 이름을 바꿔 읽는다.
|
||||
|
||||
여기 든 `[none] mq-deadline` 은 SSOT 가 읽는 법을 보이려고 든 출력이다. 이 호스트에서 그 명령을 아직 돌리지 않아서 이 장비의 스케줄러가 무엇인지는 이 글이 말하지 않는다.
|
||||
|
||||
## NVMe 드라이버는 호스트 커널의 장치 드라이버다
|
||||
|
||||
호스트 블록 계층에서 I/O 스케줄러를 지난 요청은 NVMe 드라이버로 가고, NVMe 컨트롤러를 거쳐 물리 저장장치에 닿는다. NVMe 드라이버는 호스트 Linux 커널의 장치 드라이버다. 네트워크에서 물리 NIC 드라이버가 하드웨어를 제어하는 것과 같은 계층에 놓인다.
|
||||
|
||||
이름이 비슷해 섞이는 두 낱말도 여기서 갈린다. SSD 는 저장장치의 넓은 종류를 가리키고, NVMe 는 PCIe 기반 비휘발성 저장장치를 위한 프로토콜이자 인터페이스다. SSD 아래에서 SATA SSD 는 SATA 와 AHCI 를 쓴다. NVMe SSD 는 PCIe 와 NVMe 를 쓴다. NVMe SSD 에서는 Linux NVMe 드라이버가 PCIe 를 통해 NVMe 컨트롤러에 요청을 보낸다. 컨트롤러는 플래시를 다룬다.
|
||||
|
||||
## 스토리지 경쟁과 CPU 경쟁은 다투는 자원이 다르다
|
||||
|
||||
여러 가상 머신이 같은 물리 NVMe 를 쓰면 스토리지 자원 경쟁이 생길 수 있다. VM1 의 PostgreSQL 과 VM2 의 Keycloak 이 요청한 I/O 가 같은 호스트 블록 계층과 같은 I/O 큐를 지나 하나의 NVMe 로 가기 때문에, VM1 에서 대량 I/O 가 발생하면 VM2 의 스토리지 지연이 올라갈 수 있다. 이렇게 벌어지는 경쟁을 스토리지 경쟁(Storage Contention)이라고 한다.
|
||||
|
||||
```text label="두 경쟁이 다투는 자원"
|
||||
CPU Contention
|
||||
→ Host logical CPU 실행 시간 경쟁
|
||||
|
||||
Storage Contention
|
||||
→ IOPS / bandwidth / queue / device 처리시간 경쟁
|
||||
```
|
||||
|
||||
CPU 경쟁은 호스트 논리 CPU 의 실행 시간을 두고 벌어지고, 스토리지 경쟁은 IOPS 와 대역폭, 큐, 장치 처리시간을 두고 벌어진다. 애플리케이션에서는 둘 다 「느리다」로 관찰되지만 다투는 자원이 달라서 따로 잰다.
|
||||
|
||||
## 이 호스트에서 확인하지 않은 것
|
||||
|
||||
이 글에도 이 테스트 호스트에서 잰 값은 없다. 이 호스트의 I/O 스케줄러가 무엇으로 설정돼 있는지 SSOT 에 없고, 가상 머신들의 디스크 이미지가 최종적으로 어느 호스트 블록 장치 위에 있는지도 없다. 가상 머신 두 대와 호스트 프로세스의 I/O 가 이 환경에서 실제로 같은 시간에 겹치는지도 재지 않았다. VM1 의 대량 I/O 가 VM2 의 스토리지 지연을 올린다는 것은 이 계층 구성에서 나오는 가능성이고 이 서버에서 관측한 값이 아니다. 이 글은 요청이 호스트에서 어떤 순서로 어느 계층을 지나는지까지 설명하고, 이 서버가 그 계층에서 실제로 막히는지는 재지 않았다.
|
||||
|
||||
<!-- body:end -->
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
---
|
||||
id: 892b6087-8961-486c-b4d9-d3dd46bc2605
|
||||
kind: CONCEPT
|
||||
slug: qemu-block-backend-forms
|
||||
title: /dev/vda 뒤에 있는 것 — qcow2 · RAW · Host block device
|
||||
topic: storage-virtualization
|
||||
topicName: 스토리지 가상화
|
||||
project: virtualization
|
||||
status: 초안
|
||||
basisVersion: QEMU block backend 세 형태 — qcow2 파일 · RAW 파일 · Host block device. libvirt 로 정의한 VM 기준이고 SSOT 가 QEMU 와 libvirt 버전을 적지 않았다
|
||||
studio: "https://hyeonworks.com/studio/documents/892b6087-8961-486c-b4d9-d3dd46bc2605/edit"
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#140-vm-boundary를-넘으면-qemu가-등장
|
||||
- final/document.md#141-qemu가-물리-ssd를-직접-제어하는-것은-아니다
|
||||
- final/document.md#142-qcow2-host에서는-파일-guest에서는-디스크
|
||||
- final/document.md#143-qcow2-virtual-size와-실제-host-사용량
|
||||
- final/document.md#144-raw-image
|
||||
- final/document.md#145-host-block-device를-직접-backend로-사용-가능
|
||||
- final/document.md#146-실제-연결-확인
|
||||
- final/document.md#128-전체-구조
|
||||
- final/document.md#172-storage-virtualization-canonical-flow
|
||||
- final/document.md#174-핵심-claim
|
||||
---
|
||||
|
||||
# /dev/vda 뒤에 있는 것 — qcow2 · RAW · Host block device
|
||||
|
||||
게스트 안에서 /dev/vda 를 보는 것만으로는 호스트에 무엇이 있는지 알 수 없다. 가상 머신 경계를 넘으면 QEMU 의 virtio-blk 장치 모델과 블록 백엔드가 그 요청을 받는데, 그 백엔드는 qcow2 파일일 수도, RAW 파일일 수도, 호스트의 실제 블록 장치일 수도 있다. 갈래마다 바뀌는 것이 다르다. 파일 백엔드면 게스트 스토리지 계층 아래에 호스트 파일시스템과 블록 계층이 한 겹 더 놓이고, qcow2 면 게스트가 보는 크기와 호스트가 실제로 쓰는 크기가 갈린다. 무엇에 붙어 있는지는 게스트가 아니라 호스트에서 virsh domblklist 로 확인한다. 가상 머신에 디스크를 붙여 본 사람을 독자로 둔다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **Guest 의 write() 가 virtqueue 에 실리기까지 — VFS · Filesystem · Page Cache · Block Layer · virtio-blk**
|
||||
게스트 안에서 virtqueue 까지 내려온 요청을 이 글이 이어받는다.
|
||||
- **완료라는 말의 네 가지 뜻 — write · writeback · flush · 전원 장애 생존**
|
||||
파일 백엔드면 호스트 페이지 캐시가 한 겹 더 생기고, 그 위에서 완료가 무엇을 보장하는지가 갈린다.
|
||||
- **VM 아래에 한 번 더 있는 Host block stack — blk-mq · I/O Scheduler · NVMe**
|
||||
이 글은 백엔드 형태까지만 본다. 그 아래에서 요청이 어떻게 줄을 서는지는 그 글이 본다.
|
||||
- **Guest 안에서 본 disk 로 backend 를 단정하지 않는다**
|
||||
이 글이 적은 세 갈래가 그 규칙의 근거다.
|
||||
- **이 호스트의 /dev/vda 는 어떤 backend 에 붙어 있는가**
|
||||
세 갈래 가운데 어느 것인지 이 장비에서 확인하지 않았다.
|
||||
- **이 disk image 는 qcow2 인가 RAW 인가, 그리고 Guest 가 보는 크기와 Host 가 실제로 쓰는 크기는 얼마나 다른가**
|
||||
이 글이 든 크기 차이는 SSOT 의 예시 값이고, 이 장비의 값은 그 질문이 잰다.
|
||||
- **disk image 는 최종적으로 어느 Host block device 위에 있는가**
|
||||
파일 백엔드라면 그 파일이 놓인 호스트 장치까지 따라가야 경로가 끝난다.
|
||||
|
||||
## 본문
|
||||
|
||||
<!-- body:start -->
|
||||
|
||||
## 가상 머신 경계를 넘으면 QEMU 가 받는다
|
||||
|
||||
게스트의 `virtio-blk` 가 `virtqueue` 에 게시한 요청은 가상 머신 경계를 넘어 호스트 사용자 공간의 QEMU 로 간다. QEMU 안에서는 `virtio-blk` 장치 모델과 블록 백엔드가 그 요청을 받는다. QEMU 는 게스트에게 가상 블록 장치를 노출하고, 게스트의 가상 I/O 를 호스트 백엔드에 연결한다.
|
||||
|
||||
SSOT 는 제4부의 전체 구조를 그린 뒤 이 갈림을 핵심 문장 하나로 따로 뽑아 두었다.
|
||||
|
||||
> Guest는 `/dev/vda`를 실제 block device처럼 보지만, Host에서는 그 disk가 qcow2 파일, RAW 파일, 또는 실제 block device에 연결되어 있을 수 있다.
|
||||
|
||||
이 글이 다루는 것이 그 문장이 남긴 세 갈래다.
|
||||
|
||||
## QEMU 가 물리 SSD 를 직접 제어하지는 않는다
|
||||
|
||||
백엔드가 `qcow2` 파일이면 QEMU 는 결국 호스트 Linux 에 파일 I/O 를 요청한다. `pread` 나 `pwrite` 같은 호출이 호스트 커널로 들어가고, 그 뒤로 호스트 파일시스템과 호스트 블록 계층, NVMe 드라이버를 지나 물리 NVMe 에 닿는다. 그 경로가 호스트 커널을 한 번 더 지나기 때문에, 게스트 스토리지 계층 아래에 호스트 스토리지 계층이 한 번 더 존재할 수 있다.
|
||||
|
||||
## 같은 대상을 호스트는 파일로, 게스트는 디스크로 본다
|
||||
|
||||
호스트에 `/var/lib/libvirt/images/keycloak-node1.qcow2` 라는 파일이 있다고 하자. 호스트 관점에서 그것은 파일 하나인데, 같은 대상을 게스트는 `/dev/vda` 라는 디스크로 보고 그 안을 `/dev/vda1` 과 `/dev/vda2` 로 나눠 쓴다. 두 관점 모두 맞다.
|
||||
|
||||
## qcow2 는 게스트가 보는 크기와 호스트가 쓰는 크기가 다르다
|
||||
|
||||
`qcow2` 는 가상 디스크 크기와 실제 호스트 할당량이 다를 수 있다. SSOT 가 든 예시에서는 게스트가 `/dev/vda` 를 100 GB 로 보는 동안 호스트의 `vm1.qcow2` 가 3GB 를 쓴다. 게스트가 데이터를 기록하면서 가상 크기 100GB 는 그대로인 채 실제 할당량이 1GB 에서 10GB 로, 다시 40GB 로 늘 수 있다. 이 숫자들은 SSOT 가 설명하려고 든 값이고 이 호스트에서 잰 값이 아니다.
|
||||
|
||||
```bash label="qcow2 의 virtual size 와 실제 할당량"
|
||||
qemu-img info vm1.qcow2
|
||||
```
|
||||
|
||||
출력의 `virtual size` 와 실제 할당량은 서로 다른 값이다.
|
||||
|
||||
## RAW 와 qcow2 를 성능으로 먼저 가르지 않는다
|
||||
|
||||
RAW 는 `qcow2` 보다 구조가 단순하다. 게스트의 블록 요청이 `qcow2` 에서는 QEMU 의 매핑과 메타데이터 처리를 지나 `qcow2` 파일 I/O 가 되고, RAW 에서는 상대적으로 직접적인 오프셋 대응을 지나 RAW 파일 I/O 가 된다. `qcow2` 는 Copy-on-Write 와 sparse allocation, 스냅샷에 유리하지만 그 대신 매핑과 메타데이터 처리가 붙는다.
|
||||
|
||||
그렇다고 RAW 가 무조건 빠르고 `qcow2` 가 무조건 느리다고 일반화하면 안 된다. SSOT 는 실제 성능이 캐시 모드와 스토리지 백엔드, 작업 부하 패턴, 큐 깊이, 스냅샷 체인, 그 아래 파일시스템, 물리 장치에 영향을 받는다고 적었다. 형식 이름만으로는 어느 쪽이 빠른지 정해지지 않는다.
|
||||
|
||||
## 백엔드가 파일이 아니라 호스트 블록 장치일 수도 있다
|
||||
|
||||
백엔드는 파일이 아니어도 된다. 게스트의 `/dev/vda` 가 `virtio-blk` 와 QEMU 를 지나 호스트의 `/dev/nvme0n1p3` 같은 블록 장치에 바로 연결될 수 있고, SSOT 가 그린 이 경로에는 호스트 파일시스템이 없다. 같은 이름 아래에서 경로가 세 갈래로 갈리기 때문에, 게스트에 `/dev/vda` 가 있다는 정보만으로는 백엔드 구조를 알 수 없다.
|
||||
|
||||
## 세 갈래가 각각 바꾸는 것
|
||||
|
||||
| 호스트에서는 무엇인가 | 그래서 무엇이 달라지나 |
|
||||
|---|---|
|
||||
| `qcow2` 파일 | 게스트 파일시스템 아래에 호스트 파일시스템과 블록 계층이 한 겹 더 놓인다. Copy-on-Write 와 sparse allocation, 스냅샷을 쓸 수 있고 매핑과 메타데이터 처리가 붙는다. 게스트가 보는 가상 크기와 호스트 실제 할당량이 갈린다 |
|
||||
| RAW 파일 | 게스트 파일시스템 아래에 호스트 파일시스템과 블록 계층이 한 겹 더 놓인다. 블록 위치가 파일 오프셋에 상대적으로 직접 대응한다 |
|
||||
| 호스트 블록 장치 | SSOT 가 그린 경로에 호스트 파일시스템이 없다 |
|
||||
|
||||
## 무엇에 붙어 있는지는 호스트에서 확인한다
|
||||
|
||||
게스트에서 `lsblk` 로 장치를 보고, 호스트에서 `virsh domblklist` 로 그 장치의 `Source` 를 본다.
|
||||
|
||||
```bash label="게스트가 보는 block device"
|
||||
lsblk
|
||||
```
|
||||
|
||||
```bash label="호스트에서 본 VM 의 disk 연결"
|
||||
virsh domblklist <VM_NAME>
|
||||
```
|
||||
|
||||
```text label="SSOT 가 든 출력 예시. 이 호스트에서 실행한 결과가 아니다"
|
||||
Target Source
|
||||
-----------------------------------------------
|
||||
vda /var/lib/libvirt/images/vm1.qcow2
|
||||
```
|
||||
|
||||
이 출력이 나오면 게스트의 `/dev/vda` 가 `virtio-blk` 와 QEMU 를 지나 `/var/lib/libvirt/images/vm1.qcow2` 에 닿는 연결이 확인된다.
|
||||
|
||||
## 확인하지 못한 것
|
||||
|
||||
이 호스트의 백엔드가 셋 중 무엇인지는 SSOT 에 없다. SSOT 가 열린 질문 넷을 적어 두었는데, 그 확인을 아직 돌리지 않았다. `/dev/vda` 가 어떤 백엔드에 붙어 있는지, 백엔드가 `qcow2` 인지 RAW 인지, `qcow2` 의 가상 크기와 실제 호스트 사용량이 얼마나 다른지, 그 이미지가 최종적으로 어느 호스트 블록 장치 위에 있는지 넷이다. 앞에서 든 100 GB 와 3GB, 1GB 에서 40GB 까지의 증가는 SSOT 가 설명하려고 든 예시 값이다.
|
||||
|
||||
`qcow2` 와 RAW 의 성능 비교도 이 저장소에 없다. SSOT 는 실제 성능을 가르는 조건만 열거했고 어느 쪽이 이 환경에서 빠른지는 재지 않았다.
|
||||
|
||||
<!-- body:end -->
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
---
|
||||
id: 8fafaeea-8746-4537-87a4-679a3a91aa4c
|
||||
kind: CONCEPT
|
||||
slug: write-completion-is-not-durability
|
||||
title: 완료라는 말의 네 가지 뜻 — write · writeback · flush · 전원 장애 생존
|
||||
topic: storage-virtualization
|
||||
topicName: 스토리지 가상화
|
||||
project: virtualization
|
||||
status: 초안
|
||||
basisVersion: Linux O_DIRECT 와 QEMU/libvirt disk 의 cache=none · cache=writeback 두 설정 기준
|
||||
studio: "https://hyeonworks.com/studio/documents/8fafaeea-8746-4537-87a4-679a3a91aa4c/edit"
|
||||
assets:
|
||||
- key: write-completion-boundaries
|
||||
file: ../../../final/assets/diagrams/write-completion-boundaries/write-completion-boundaries.svg
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#147-vm에서는-page-cache가-두-번-나타날-수-있다
|
||||
- final/document.md#148-write-완료와-영속화는-다르다
|
||||
- final/document.md#149-direct-i-o
|
||||
- final/document.md#150-fsync-가-필요한-이유
|
||||
- final/document.md#151-flush
|
||||
- final/document.md#152-가장-위험한-상황-거짓-완료
|
||||
- final/document.md#153-qemu-cache-mode
|
||||
- final/document.md#154-cache=none
|
||||
- final/document.md#155-cache=writeback
|
||||
- final/document.md#156-writeback-=-위험-이라고-단정하면-안-되는-이유
|
||||
- final/document.md#157-device-side-cache
|
||||
- final/document.md#170-postgresql-예시-wal과-durability
|
||||
- final/document.md#133-page-cache-write-가-바로-ssd-write는-아니다
|
||||
- final/document.md#174-핵심-claim
|
||||
---
|
||||
# 완료라는 말의 네 가지 뜻 — write · writeback · flush · 전원 장애 생존
|
||||
|
||||
buffered I/O 에서 write() 는 페이지 캐시까지만 내려간다. 가상 머신에서는 그 페이지 캐시가 게스트와 호스트 양쪽에 두 번 나타날 수 있다. 게스트가 성공을 돌려받은 데이터가 호스트 메모리에 머무는 동안 호스트 전원이 나가면 물리 SSD 에는 이전 상태가 남는다. 그래서 완료라는 말이 네 단계로 갈린다 — write() 완료, writeback 완료, fsync/flush 완료, 전원 장애에도 안전한 영속성. Direct I/O 와 QEMU 캐시 모드는 이 가운데 어디까지 갔는지를 바꾼다. 게스트에게 FLUSH 완료라고 응답했는데 데이터가 실제로는 호스트 메모리에만 있으면, 그것은 느린 구성이 아니라 영속성 계약이 깨진 상태다. 페이지 캐시와 fsync 를 아는 사람을 독자로 둔다. Keycloak 과 PostgreSQL 을 가상 머신 위에서 돌리는 실험에서 이 구분을 먼저 세워 두면, 지연을 재는 것과 데이터가 남는지를 재는 것을 한 실험에 섞지 않게 된다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **Guest 의 write() 가 virtqueue 에 실리기까지 — VFS · Filesystem · Page Cache · Block Layer · virtio-blk**
|
||||
게스트 쪽 경로를 VFS 부터 virtqueue 까지 이어 그린 글이다. 이 글은 그 경로 위에서 완료 응답이 무엇을 보장하는지만 본다.
|
||||
- **/dev/vda 뒤에 있는 것 — qcow2 · RAW · Host block device**
|
||||
백엔드가 파일이면 게스트 파일시스템 아래에 호스트 파일시스템과 호스트 페이지 캐시가 한 겹 더 놓인다. 두 번째 페이지 캐시가 왜 생기는지를 그 글이 설명한다.
|
||||
- **VM 아래에 한 번 더 있는 Host block stack — blk-mq · I/O Scheduler · NVMe**
|
||||
fsync 요구가 호스트로 내려간 뒤 어느 계층을 지나는지는 그 글이 맡는다.
|
||||
- **빨라진 구성이 durability contract 를 지운 것은 아닌지 먼저 가른다**
|
||||
이 개념이 세운 네 경계를 설정 선택과 성능 개선의 판정에 적용하는 규칙이다.
|
||||
- **이 VM 들의 QEMU disk cache mode 는 무엇인가**
|
||||
이 호스트가 cache=none 구성인지 cache=writeback 구성인지 SSOT 에 적혀 있지 않다.
|
||||
- **Guest 의 fsync() 지연과 Host storage 지연은 같이 오르는가**
|
||||
fsync() 지연 값은 그 질문이 게스트와 호스트를 같은 시간축으로 잰 뒤에 이 글에 들어온다.
|
||||
|
||||
## 본문
|
||||
|
||||
<!-- body:start -->
|
||||
|
||||
## 게스트와 호스트에 페이지 캐시가 두 번 생긴다
|
||||
|
||||
일반적인 buffered I/O 에서는 `write()` 가 호출될 때마다 물리 SSD 까지 즉시 내려갈 필요가 없다. 커널은 그 내용을 메모리의 페이지 캐시에 받아 두고, 나중에 writeback 으로 파일시스템과 블록 계층을 지나 저장장치 쪽에 내려보낸다. 저장장치보다 최신인 페이지를 dirty page 라고 한다. 저장장치에 `ABC` 가 있는 상태에서 애플리케이션이 `DEF` 를 추가하면 페이지 캐시는 `ABCDEF` 를 dirty 상태로 들고 있고 SSD 에는 아직 `ABC` 만 있다.
|
||||
|
||||
가상 머신에서는 이 페이지 캐시가 한 번 더 나타날 수 있다. 게스트가 buffered I/O 를 쓰고 디스크 백엔드가 호스트의 파일이며 호스트도 페이지 캐시를 쓰는 구성이라고 하자. 게스트 PostgreSQL 의 쓰기는 게스트 `ext4` 와 게스트 페이지 캐시를 지나 게스트 블록 계층과 `virtio-blk` 를 거쳐 `virtqueue` 로 나간다. 가상 머신 경계를 넘은 다음에는 QEMU 와 디스크 이미지 파일을 지나 호스트 페이지 캐시에 다시 담긴다. 같은 데이터가 게스트 메모리와 호스트 메모리 양쪽에 캐시될 수 있다.
|
||||
|
||||
## write() 성공이 어디까지를 뜻하는가
|
||||
|
||||
`write()` 가 성공을 돌려준 시점에 데이터는 게스트 페이지 캐시에 있고, virtio 를 지나 호스트 페이지 캐시까지 갔을 수도 있다. 그 상태에서 호스트 전원이 나가면 물리 SSD 에는 그 데이터가 없기 때문에, 완료라는 말이 네 단계로 갈린다.
|
||||
|
||||
```text label="완료의 네 단계"
|
||||
write() 완료
|
||||
≠
|
||||
writeback 완료
|
||||
≠
|
||||
fsync/flush 완료
|
||||
≠
|
||||
전원 장애에도 안전한 durability
|
||||
```
|
||||
|
||||
## fsync() 는 요구를 계층 전체로 내려보낸다
|
||||
|
||||
애플리케이션이 부르는 쓰기 호출은 이렇게 생겼다.
|
||||
|
||||
```c label="이 호출이 성공해도 정전 이후 생존은 보장되지 않는다"
|
||||
write(fd, data, size);
|
||||
```
|
||||
|
||||
필요한 시점에 `fsync()` 를 불러 변경 내용을 필요한 영속성 경계까지 반영하도록 요청한다.
|
||||
|
||||
```c label="영속성 경계까지 반영을 요청한다"
|
||||
fsync(fd);
|
||||
```
|
||||
|
||||
가상 머신에서는 이 요청이 한 계층에서 끝나지 않는다. 게스트 PostgreSQL 의 `fsync()` 가 게스트 파일시스템으로 가고, 게스트 블록 계층에서 `FLUSH` 등으로 바뀐다. 그다음 `virtio-blk` 를 통해 QEMU 와 백엔드로 내려가고, 호스트 스토리지 계층을 지나 물리 저장장치까지 그 의미가 전달되어야 한다.
|
||||
|
||||

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