Files
document-haness/docs/virtualization/tech-log-studio/memory-virtualization/concept/concept-memory-pressure-reclaim-swap-oom.md
T

12 KiB

id, kind, slug, title, topic, topicName, project, status, basisVersion, studio, sourceRevision, source
id kind slug title topic topicName project status basisVersion studio sourceRevision source
647d6d11-5bb2-4028-a530-b10c8925aa11 CONCEPT memory-pressure-reclaim-swap-oom Host 메모리 압박이 Guest 와 스토리지까지 내려가는 경로 memory-virtualization 메모리 가상화 virtualization 초안 Linux memory reclaim · swap · OOM Killer · cgroup memory limit · QEMU 의 Guest RAM backing https://hyeonworks.com/studio/documents/647d6d11-5bb2-4028-a530-b10c8925aa11/edit no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
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 갈래를 설명한다.

본문

CPU 가 모자랄 때와 RAM 이 모자랄 때

가상 머신들에 설정한 메모리 총량이 호스트의 물리 RAM 보다 큰 구성을 메모리 초과 할당(memory overcommit)이라고 한다. 근거 문서는 이 구성을 예시 숫자로 설명한다.

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 도 같이 기록해야 원인을 메모리 쪽으로 좁힐 수 있다. 세 확인은 각각 열린 질문으로 두었고 관계에 걸어 두었다.