docs: correct the memory analysis to distinguish host and guest headroom

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
DongHyeonka
2026-09-03 17:02:11 +09:00
co-authored by Claude Opus 5
parent d6f8b9f8b3
commit 001efd624a
+104 -29
View File
@@ -108,46 +108,121 @@ kubectl -n header-lab top pods
**2026-09-03 기준, Keycloak 배포 전**
```
lab host 총 7628MB · 사용 7227MB · 여유 400MB (+ swap 8GB)
├ qemu #1 RSS 3765MB kc-lab-1
└ qemu #2 RSS 2670MB kc-lab-2
### 호스트 여유와 게스트 여유는 다르다
kc-lab-1 CPU 4% 메모리 1774Mi / 3.4GB (51%)
kc-lab-2 CPU 1% 메모리 1032Mi / 2.4GB (41%)
echo 파드 각 150Mi
```
**호스트 여유가 400MB뿐이다.** 다만 QEMU RSS는 이미 게스트 할당 상한에
근접했으므로 **앞으로 크게 늘지 않는다.** 압박은 호스트가 아니라
**게스트 안에서** 생긴다.
**다음 배포의 예상 소요**
| 워크로드 | 예상 |
|---|---|
| Keycloak × 2 | 각 ~800Mi |
| PostgreSQL | ~300Mi |
| Redis | ~100Mi |
| 합계 | 약 2000Mi, 게스트당 ~1000Mi |
가장 오해하기 쉬운 지점이다. 호스트만 보면 절망적으로 보인다.
```
kc-lab-1 1774 + 1000 ≈ 2774Mi / 3.4GB (80%)
kc-lab-2 1032 + 1000 ≈ 2032Mi / 2.4GB (84%)
lab host 총 7628MB · 사용 7189MB · 여유 439MB
├ qemu #1 RSS 3765MB kc-lab-1 (할당 3584MB) → 상한 도달
└ qemu #2 RSS 2633MB kc-lab-2 (할당 2560MB) → 상한 도달
```
**빠듯하지만 가능하다.** 여유가 필요하면 헤더 실험이 끝난 `header-lab`
네임스페이스를 지운다 — 파드 2개 × 150Mi가 회수된다.
그런데 게스트 안을 보면 여유가 있다.
```
kc-lab-1 총 3423MB · used 1464 · buff/cache 2020 · available 1959MB
kc-lab-2 총 2480MB · used 580 · buff/cache 1714 · available 1899MB
─────────────────
게스트 여유 합계 약 3.8GB
```
**왜 이런가** — QEMU의 RSS는 게스트가 **터치한 페이지**만큼이다. 게스트가
메모리를 페이지 캐시로 다 채우면 QEMU RSS도 할당 상한까지 올라간다.
지금이 그 상태다.
**그래서 앞으로 워크로드를 올려도 호스트 압박은 늘지 않는다.** 게스트 안의
페이지 캐시가 밀려날 뿐이다. **QEMU RSS는 이미 천장이다.**
```
확인 방법:
ps -eo rss,args --sort=-rss | grep '[q]emu-system' # 호스트에서 본 VM
ssh kc-lab-1 free -m # 게스트 안 실제
kubectl top nodes # working set
```
세 값이 다른 것을 보는 것이 이 실험대의 메모리 감각이다.
### 배포 예산
| 워크로드 | 예상 | 배치 |
|---|---|---|
| Keycloak × 2 | 각 700Mi | 노드당 1개 |
| PostgreSQL | 300Mi | kc-lab-1 |
| Redis | 100Mi | kc-lab-2 |
| BFF × 2 | 각 400Mi | 노드당 1개 |
| **합계** | **약 2600Mi** | |
**게스트 여유 3.8GB 중 2.6GB → 가능하다.** 다만 여기에
Prometheus/Grafana(로드맵 10번 관측성)를 얹을 여유는 없다.
### 대응 — 비용이 없는 것부터
**1. 끝난 실험은 지운다**
```bash
kubectl delete ns header-lab # 실험 종료 후
kubectl delete ns header-lab # 파드 2개 × 150Mi 회수
```
증거는 `docs/evidence/`에 남아 있으므로 워크로드를 유지할 이유가 없다.
**2. Keycloak 힙을 명시적으로 제한한다**
Keycloak은 기본값이 넉넉해 그냥 두면 1GB를 넘긴다.
```yaml
env:
- name: JAVA_OPTS_KC_HEAP
value: "-Xms256m -Xmx512m"
resources:
limits:
memory: 768Mi
```
**모든 워크로드에 `resources.limits`를 반드시 건다.** 안 걸면 한 파드가
게스트 메모리를 다 먹고 다른 파드까지 OOMKilled된다. 3.4GB / 2.4GB짜리
게스트에서는 현실적인 위험이다.
게스트 메모리를 다 먹고 다른 파드까지 OOMKilled된다.
---
**3. 실험을 순차로 돌린다 — 동시에 다 띄우지 않는다**
```
A층(Keycloak + PostgreSQL) → 결과 기록 → 정리
B층(BFF + Redis) → 결과 기록 → 정리
관측성(Prometheus) → 필요할 때만
```
절약책이 아니라 **정상적인 실험 운영 방식**이다. 동시에 띄우면 변수가
섞여서 원인 분리가 어려워진다.
### swap은 쓰지 않는다
호스트에는 8GB swap이 있지만 **게스트에는 0MB이며, 그것이 맞다.**
| 이유 | |
|---|---|
| k3s/kubelet | 기본적으로 swap 을 거부한다 |
| 성능 | 호스트 swap 으로 QEMU 페이지가 밀리면 급락한다 |
| **측정 오염** | 이 실험대는 **타이밍**(refresh 경쟁, Infinispan 복제 지연)을 잰다. swap 이 끼면 측정이 통째로 무의미해진다 |
### 근본 해결 — 메모리 증설
남은 실험이 10개이고 관측성까지 하려면 증설이 가장 확실하다.
```bash
sudo pacman -S dmidecode
sudo dmidecode -t memory | grep -E "Maximum Capacity|Number Of Devices|Size:|Locator:|Type:|Speed:"
```
| 슬롯 상태 | 조치 |
|---|---|
| 2슬롯 중 1개만 사용 | 동일 규격 8GB 추가 → 16GB |
| 온보드 8GB + 슬롯 1개 | 16GB 추가 → 24GB |
| 2슬롯 모두 사용 | 8GB × 2 를 16GB × 2 로 교체 |
i5-1135G7(Tiger Lake)은 DDR4-3200 SO-DIMM을 쓰며 최대 용량은 보드마다
다르므로 `Maximum Capacity` 값을 확인한다. **비용 대비 효과가 가장 크다**
증설하면 Prometheus·Grafana·BFF 2 replica를 동시에 띄우고도 남는다.
## 3. 자주 쓰는 명령