사용자가 물어본 상황을 정리하면:
free -h의 Used ≈ Total − Free − Buffers/Cache 인데, 이 Used가 비정상적으로 큼slabtop으로 본 커널 슬랩(SUnreclaimable 포함 캐시들) 사용량도 크지 않음이건 실제로 대규모 k8s/DirectPV/Cilium 환경에서 꽤 흔히 겪는 미스터리라, 정확한 근거를 좀 더 확인하고 답변드리겠습니다.좋은 질문입니다. "RSS 합 + Cache/Buffer + Slab 합 ≈ Used의 절반"이라는 건 실제로 상당히 전형적인 패턴이고, 원인 후보가 꽤 정해져 있습니다. 순서대로 짚어보겠습니다.
free의 Used 계산식부터 재확인free의 Used는 단순히 Total - Free - Buffers - Cache가 아니라 버전/커널에 따라 계산 방식이 다릅니다. procps 최신 버전 기준:
buff/cache = Buffers + Cached + SReclaimable - Shmem
used = MemTotal - MemFree - buff/cache
여기서 중요한 건, Shmem(tmpfs, /dev/shm, 공유 메모리 세그먼트)이 Cached에 포함되어 있다가 다시 빠져서 실제로는 "Used" 쪽으로 넘어간다는 점입니다. 즉 tmpfs를 많이 쓰는 컴포넌트(예: 컨테이너 런타임, 일부 CSI 드라이버, 로그 버퍼)가 있으면 free -h의 buff/cache에는 안 잡히고 Used에 잡힙니다.
grep -Ei 'Shmem|MemTotal|MemFree|Cached|Buffers|SReclaimable|SUnreclaim' /proc/meminfo
Shmem 값이 큰데 RSS로는 안 잡힌다면 → tmpfs/shm에 데이터가 떠 있는 것.
/proc/meminfo에서 놓치기 쉬운 필드들slabtop과 RSS 합만으로는 안 보이는 커널 메모리 영역이 꽤 많습니다. 아래를 전부 확인해보세요.
cat /proc/meminfo
특히 주목할 항목:
| 필드 | 의미 | 놓치는 이유 |
|---|---|---|
PageTables | 프로세스 페이지 테이블 | RSS와 별개로 집계, 프로세스 수/매핑 많으면 증가 |
KernelStack | 커널 스택 (스레드당) | 스레드 수 많은 프로세스(예: MinIO, cilium-agent)에서 커짐 |
VmallocUsed | vmalloc 영역 실사용량 | slabtop에 안 잡힘 (아래 3번 참고) |
Percpu | per-CPU 할당 메모리 | 코어 수 많으면 누적 |
HugePages_Rsvd / HugePages_Total | 예약만 되고 미사용인 hugepage | RSS에도 slab에도 안 잡히고 Used만 깎아먹음 |
Mlocked / Unevictable | mlock된 페이지 | 특정 앱이 잠가둔 메모리 |
Bounce | 구형 DMA bounce buffer | 특정 HBA/RAID 카드 드라이버에서 발생 가능 |
Committed_AS vs 실사용 | 오버커밋 여부 참고용 | - |
HugePages_Total이 0이 아닌데 AnonHugePages나 실제 사용처가 없다면, 누군가 vm.nr_hugepages를 설정해놨고 아무도 안 쓰는 상태일 수 있습니다 — 이건 RSS에도 slab에도 안 잡히지만 Used를 그대로 깎아먹는 대표적인 "숨은 메모리"입니다.
말씀하신 증상(slab 정상, RSS 정상, 그런데 Used만 큼)은 eBPF map이 vmalloc으로 할당되는 경우와 정확히 맞아떨어집니다.
핵심 포인트: Cilium의 BPF map(conntrack, NAT, LB service, policy, ipcache, endpoint별 policy map 등)은 대부분 생성 시점에 max_entries × value_size만큼 커널 메모리를 즉시 확보합니다. 실제 엔트리 수와 무관하게 최대 용량 기준으로 잡힙니다. 그리고 이 할당은 크기에 따라 kmalloc이 아니라 vmalloc 경로를 타는 경우가 많아서 — slabtop에는 전혀 안 보이고, /proc/meminfo의 VmallocUsed에만 잡힙니다.
확인 방법:
# vmalloc 총 사용량
grep Vmalloc /proc/meminfo
# 상세 내역 (bpf 관련 항목 grep)
sudo cat /proc/vmallocinfo | grep -i bpf | awk '{print $2}' | \
sed 's/[^0-9]//g' | awk '{sum+=$1} END {print sum/1024/1024 " MB"}'
# 현재 로드된 bpf map들과 각각의 크기 확인
sudo bpftool map show
sudo bpftool map show -j | jq -r '.[] | "\(.name) max_entries=\(.max_entries) value_size=\(.value_size) key_size=\(.key_size)"'
이걸로 max_entries × (key_size+value_size)를 map별로 합산해보면 실제로 수백 MB~수 GB가 나오는 경우가 흔합니다. 특히:
cilium_ct4_global / cilium_ct6_global (conntrack, 기본 100만 엔트리급)cilium_lb4_services, cilium_lb4_backends/sys/fs/bpf 밑에 pin된 채 남는 버그가 실제로 보고된 적 있습니다.# pin된 orphan map/link 확인 (실제 살아있는 엔드포인트 수와 비교)
sudo find /sys/fs/bpf -type f | wc -l
cilium-dbg endpoint list # 또는 cilium endpoint list
pin된 개수가 실제 엔드포인트/서비스 수보다 훨씬 많다면 정리가 안 되고 누적되는 것입니다.
알려진 이슈 사례 (실제 GitHub 이슈 기준):
l2NeighDiscovery 비활성화가 임시 우회책으로 쓰인 사례가 있습니다.두 사례 다 cilium-agent 자체(유저스페이스 프로세스) 문제이므로, 이건 RSS로 잡혀야 정상입니다. 사용자분이 "RSS는 낮다"고 하셨으니 이 경로보다는 커널 쪽 BPF map(vmalloc)이 더 유력한 후보입니다. 혹시 cilium-agent 버전을 알려주시면 해당 버전에 알려진 이슈가 있는지 더 좁혀볼 수 있습니다.
파드/컨테이너 churn이 잦은 클러스터(특히 DirectPV처럼 볼륨 마운트/언마운트가 잦고, MinIO 오토스케일링/재시작이 있는 환경)에서 자주 나오는 패턴입니다.
확인:
# cgroup v2 기준, dying(죽었지만 회수 안 된) memcg 개수/메모리
find /sys/fs/cgroup -name memory.stat 2>/dev/null | wc -l # 참고용 총 개수
cat /sys/fs/cgroup/memory.stat 2>/dev/null | grep -i slab
# 커널 로그에 memcg 관련 경고가 있는지
dmesg | grep -i -E 'memcg|oom|cgroup'
# 커널 버전 확인 (이 문제는 커널/cgroup 버전에 따라 다름)
uname -r
xfs_inode, xfs_ili, xfs_buf)은 slabtop에 보통 잡히지만, XFS의 buffer cache(xfs_buf)가 page cache와 별도로 pin되는 경우가 있어 이것도 확인해보세요:slabtop -o | grep -i xfs
# 1) 전체 그림 재확인
cat /proc/meminfo
# 2) vmalloc/hugepage/shmem/kernelstack/pagetables 합산해서 gap과 비교
# 3) bpf map 총량 계산
sudo bpftool map show -j | jq -r '[.[] | (.max_entries * (.value_size + .key_size))] | add'
# 4) pin된 bpf map/link 개수 vs 실제 리소스 개수 비교
sudo find /sys/fs/bpf -type f | wc -l
# 5) cilium 자체 메모리 압박 지표 확인
cilium status --verbose | grep -i -A2 'BPF Maps'
가능성 높은 순서로 보면:
1. Cilium BPF map이 vmalloc으로 커널 메모리를 max_entries 기준으로 선점 — slabtop/RSS 어디에도 안 보이고 VmallocUsed에만 잡힘 (가장 유력)
2. HugePages 예약 후 미사용 — RSS/slab 모두에 안 잡힘
3. Shmem/tmpfs 사용량 — free의 buff/cache 계산에서 다시 빠져나가 Used로 들어감
4. 죽은 cgroup(memcg)이 회수 안 된 캐시를 물고 있음 — 파드 churn 많은 클러스터에서 흔함
5. cilium-agent 자체의 유저스페이스 메모리 누수(1.16.0-1.16.1 L2 neighbor discovery, 1.19.0 보고 사례) — 이건 RSS로 잡혀야 하므로 후순위
/proc/meminfo 전체 값과 bpftool map show -j 결과를 공유해주시면 실제로 gap이 어디서 나는지 계산해서 짚어드릴 수 있습니다.