기록 84편을 계약 에이전트로 다시 썼다. 기존 71편(kss 25 · virt 46)과, 계약에만 있고 안 쓰여 있던 새 글감 13편이다. 원장 84개를 열어 단계마다 스킬 영수증과 관문 종료 코드를 적었고 verify-pipeline-run.py 가 error 0 으로 닫는다. SSOT 결함 둘을 고쳤다. - kss 의 `약 58일` 이 반입 중 `약 59일` 로 바뀌어 있었다. 원 증거 파일이 「남은 일수: 88일 … 실제 갱신까지 약 58일」로 산수를 직접 적는다. D-4a 쪽 `약 59일` 은 강제 갱신 뒤(`VALID: 89 days`)라 맞는 값이라 그대로 뒀다. - virt §198 의 `11.6GB` 는 §178 의 원 측정 `Mem: 11648`(MiB)과 어긋나는데 원 가이드의 표기 그대로라 고치지 않고 쓰이는 자리에 대조를 적었다. 기록의 수치 오류 셋을 고쳤다 — CASE 요약의 「게스트 셋에 8240MB」(5120+3120 은 둘이다), k3s 편이 같은 것을 여섯·일곱·여덟로 세던 것, no-docker 편의 「셋을 더 든다」(§281 의 표는 네 행이고 디스크 행이 빠져 있었다). 계약을 셋 고쳤다. - kss 의 sourceRepository 리비전이 cdac9b8 이었는데 그 커밋에는 docs/guides/** 28개가 아예 없다. 9465582b 로 바꾸고, 반입한 바이트가 어느 커밋과도 같지 않다는 것을 측정값과 함께 적었다 — 반입은 커밋이 아니라 그 시점의 작업 트리에서 떠 온 것이다(kss 297/306 · virt 12/14 가 작업 트리와 같고, 200 커밋을 거슬러 전수 대조했을 때 가장 가까운 커밋도 28개가 어긋났다). - virt 계약이 「2026-09-11 재배분」이라고 적는데 SSOT 는 재배분 날짜를 적지 않고 재배분 뒤 값은 이미 2026-09-10 측정에 찍혀 있다. - kss 후보 대장이 지나친 절 아홉에 처분을 적었다(warn 9 → 0). 새 글감은 0건이고 넷은 앵커가 h3 슬러그의 접두가 아니라 중간 토막이라 검사기가 못 본 것이었다. style_profile.mjs 의 결함 둘을 고쳤다 — frontmatter 가 문장으로 세어져 (실측 398자짜리 「문장」 하나) 평균 길이를 기준 안으로 밀어 올리고 있었고, engPerSent 의 분자는 목록을 포함한 글에서, 분모는 목록을 걷어낸 글에서 세고 있었다(Question 기록에서 11.94 → 3.86). verify-pipeline.py 전 항목 PASS · error 0 · unittest 334건 OK. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
9.3 KiB
id, kind, slug, title, topic, topicName, project, status, studio, sourceRevision, source
| id | kind | slug | title | topic | topicName | project | status | studio | sourceRevision | source | ||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 63fa33e5-9957-4d28-8d3c-c75735a73bd6 | REFERENCE | memory-symptom-needs-layer-separation | 메모리 증상 하나로 계층을 단정하지 않는다 | memory-virtualization | 메모리 가상화 | virtualization | 초안 | https://hyeonworks.com/studio/documents/63fa33e5-9957-4d28-8d3c-c75735a73bd6/edit | no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다 |
|
메모리 증상 하나로 계층을 단정하지 않는다
메모리 지연이나 OOM 이 보이면 「메모리 부족」으로 닫지 말고 §86 의 다섯 갈래로 먼저 가른다. Guest Virtual Memory · Virtualization Translation · Host Memory · Dynamic Memory · NUMA 다섯이다. §63 · §78 · §88 이 따로 요구한 판독도 이 한 절차로 묶었다.
관계
- 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(Out Of Memory, 커널이 필요한 메모리를 확보하지 못해 프로세스를 종료할 수 있는 상태)인데, 둘 다 다섯 계층 어디서든 나올 수 있다.
다섯 갈래를 개념 한 편 안에 넣지 않고 따로 세운 것은 갈래마다 받는 개념이 다르기 때문이다. 어느 한 개념 안에 두면 그 글이 나머지 넷을 가리키지 못한다.
§63 과 §78, §88 을 따로 기준으로 세우지 않은 것은 보는 대상만 다를 뿐 §86 과 같은 말을 하기 때문이다. 규칙이 세 벌이면 판독하는 사람이 어느 것을 따르는지가 갈린다. Swap Used 값 하나로 판단하지 않는 것(§63), 토폴로지를 재기 전에 NUMA 최적화를 정하지 않는 것(§78), 게스트 하나의 free -h 로 닫지 않는 것(§88)은 각각 2 번과 5 번, 6 번 규칙이다.
좁히는 것과 확정하는 것을 갈라 둔 이유도 여기 있다. 이 기준은 어느 갈래인지까지만 좁히고, 원인 확정은 §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
마지막 갈래의 NUMA(Non-Uniform Memory Access, 비균일 메모리 접근)는 어느 CPU 가 어느 RAM 에 접근하느냐에 따라 비용이 달라질 수 있는 구조다.
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 영향을 같은 시간축에서 관측해야 한다고 했다.
이 실험대에는 아직 그 시간축의 한쪽만 있다. Prometheus 가 긁는 node-exporter 는 게스트 안에서 돌아 게스트 커널이 내놓는 값을 읽고, 호스트 쪽 지표는 긁지 않는다(§192 · §193). 거기서 나오는 메모리와 스왑 값은 전부 게스트가 본 것이라, 여기서 이 규칙을 지키려면 호스트에서 재는 값을 따로 들고 와야 한다.
적용 조건
메모리 증상을 원인으로 옮기려는 판독 전부에 걸린다. 지연 증가, 스왑 관측, OOM, fault 증가가 여기 들어간다.
가르는 축은 다섯이고, 갈래마다 무엇을 볼지는 1 번 규칙에 적혀 있다.
예외
cgroup memory limit 이 걸린 환경에서는 호스트 전체 RAM 에 여유가 있어도 그 경계에서 OOM 이 나기 때문에, Host Memory 갈래를 곧바로 지우면 안 된다(§72).
이 실험대의 Keycloak 은 K3s 파드로 돈다. 파드가 죽었을 때 describe 의 Exit Code 가 137 이면 OOM 이나 강제 종료이고 그때 보는 것은 파드의 메모리 한도라고 §191 이 적었는데, 그 한도가 얼마로 걸려 있는지는 매니페스트가 source/ 에 없어 대조할 방법이 지금은 없다(§194).
NUMA node 가 하나인 호스트에서는 NUMA 갈래의 우선순위를 낮춰도 된다고 §78 이 적었다. 그것도 토폴로지를 잰 뒤의 이야기다.
이 기준은 갈래를 좁힐 뿐 원인을 확정하지 않는다. 확정은 §85 의 조건을 남긴 측정이 한다.
예시
- 게스트에서 Swap Used 만 보고 「메모리 부족」으로 닫은 판독 : swap-in/out 이 지속되는지를 재지 않았다
- 게스트 free -h 에 여유가 보여 호스트를 보지 않은 판독 : 호스트 reclaim 과 호스트 스왑을 확인하지 않았다
- Keycloak 프로세스가 죽었는데 호스트 커널 로그를 보지 않은 판독 : 게스트 OOM 인지 호스트 OOM 인지 갈리지 않는다
- 호스트 RAM 에 여유가 있다는 이유로 OOM 을 다른 갈래로 넘긴 판독 : cgroup 경계를 확인하지 않았다
- 토폴로지를 재지 않고 vCPU pinning 부터 손댄 조치 : NUMA 갈래가 이 호스트에 걸리는지 모른다
- 게스트와 호스트를 다른 시각에 찍어 한 표로 놓은 기록 : 같은 시간축이 아니다