- Published on
EKS에서 Pod /dev/shm 부족으로 OOM 해결하기
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버리스가 아닌 이상, EKS에서 발생하는 OOM은 단순히 memory limit만의 문제가 아닙니다. 특히 Chromium(Playwright/Puppeteer), PyTorch DataLoader, PostgreSQL 클라이언트, 일부 IPC 기반 워커 모델처럼 POSIX shared memory를 적극적으로 쓰는 프로세스는 컨테이너의 /dev/shm 크기에 의해 먼저 죽을 수 있습니다. 이 글은 EKS에서 /dev/shm 부족으로 인한 OOM/크래시를 어떻게 식별하고, 쿠버네티스 매니페스트로 안전하게 해결하는지에 초점을 맞춥니다.
/dev/shm이 왜 문제인가: 컨테이너의 기본값 함정
리눅스에서 /dev/shm은 tmpfs로 마운트되는 공유 메모리 영역입니다. Docker/Containerd 환경에서 컨테이너의 기본 /dev/shm 크기는 종종 64MiB로 제한됩니다(런타임/설정에 따라 다르지만 “작다”는 게 핵심).
문제는 다음과 같은 패턴에서 터집니다.
- Chromium 기반(Puppeteer/Playwright): 렌더러/IPC가
/dev/shm에 의존 → 부족하면 크래시 또는 성능 급락 - 멀티프로세스/멀티워커: 워커 간 버퍼 공유, mmap, sem/shm 사용
- ML/데이터 파이프라인: shared memory로 배치 프리패치
이때 컨테이너 메모리 limit을 아무리 늘려도 /dev/shm은 그대로 64MiB라서 동일 증상이 반복됩니다.
증상: “OOMKilled”처럼 보이지만 진짜 원인은 shm
1) Pod 이벤트/종료 사유 확인
먼저 Pod 종료 이유가 진짜 OOMKilled인지 확인합니다.
kubectl describe pod <pod-name> -n <ns>
kubectl get pod <pod-name> -n <ns> -o jsonpath='{.status.containerStatuses[0].lastState.terminated.reason}'
kubectl get pod <pod-name> -n <ns> -o jsonpath='{.status.containerStatuses[0].lastState.terminated.exitCode}'
reason=OOMKilled면 cgroup 메모리 제한으로 커널이 프로세스를 죽였을 가능성이 큽니다.- 하지만 애플리케이션 로그에
/dev/shm관련 메시지가 있으면 shm이 트리거였을 수 있습니다.
2) 컨테이너 내부에서 /dev/shm 크기 확인
크래시 직전/재현 가능한 환경이라면 직접 확인하는 게 가장 빠릅니다.
kubectl exec -it <pod-name> -n <ns> -- df -h /dev/shm
kubectl exec -it <pod-name> -n <ns> -- mount | grep shm
대개 다음처럼 나옵니다.
tmpfs 64M ... /dev/shm
3) 애플리케이션 로그에서 자주 보이는 단서
- Chromium 계열:
Failed to create shared memory/No space left on device/Crashpad관련 에러 - Python/torch: DataLoader worker가 갑자기 죽거나
Bus error류 - Node/IPC:
ENOSPC(No space left on device)
> 참고: ENOSPC는 디스크가 꽉 찼을 때만이 아니라, tmpfs(/dev/shm) 용량이 꽉 찼을 때도 흔히 등장합니다.
해결 전략 1: emptyDir(memory)로 /dev/shm을 명시적으로 키우기(권장)
쿠버네티스에서 정석은 emptyDir를 medium: Memory로 만들고 /dev/shm에 마운트하는 방식입니다. 이렇게 하면 /dev/shm이 노드의 메모리를 사용하는 tmpfs로 잡히며, sizeLimit로 상한도 줄 수 있습니다.
Deployment 예시
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
spec:
replicas: 2
selector:
matchLabels:
app: app
template:
metadata:
labels:
app: app
spec:
containers:
- name: app
image: your-image:tag
resources:
requests:
cpu: "500m"
memory: "1Gi"
limits:
cpu: "1"
memory: "2Gi"
volumeMounts:
- name: dshm
mountPath: /dev/shm
volumes:
- name: dshm
emptyDir:
medium: Memory
sizeLimit: "1Gi"
포인트
mountPath: /dev/shm로 기존 shm 마운트를 덮어씌웁니다.sizeLimit은 “이 컨테이너가 shm을 최대 얼마까지 쓰게 할지”를 명시합니다.sizeLimit을 너무 크게 잡으면 노드 메모리 압박이 커질 수 있으니, 실제 사용량을 관측해서 조정합니다.
적용 후 검증
kubectl rollout restart deploy/app
kubectl exec -it deploy/app -- df -h /dev/shm
/dev/shm이 1Gi로 보이면 성공입니다.
해결 전략 2: Chromium 계열은 shm 외 추가 옵션도 같이 점검
Playwright/Puppeteer/Chromium은 shm이 작을 때 흔히 크래시합니다. 위의 /dev/shm 확장이 근본 해결에 가깝지만, 운영 환경에서는 아래도 같이 점검하면 안정성이 올라갑니다.
Puppeteer 예시(옵션)
const browser = await puppeteer.launch({
args: [
'--no-sandbox',
'--disable-setuid-sandbox'
]
});
--disable-dev-shm-usage옵션은/dev/shm대신/tmp를 사용하도록 유도합니다. 다만/tmp가 느리거나 용량/IO 이슈를 만들 수 있어, 가능하면 shm 자체를 늘리는 방식이 더 예측 가능합니다.
“메모리 limit 올렸는데도 죽음”의 이유를 구조적으로 이해하기
쿠버네티스에서 메모리는 크게 두 층으로 보시면 트러블슈팅이 쉬워집니다.
- cgroup 메모리 제한(request/limit): 컨테이너가 쓸 수 있는 RAM 상한
- tmpfs(/dev/shm) 용량: 공유 메모리 파일시스템의 별도 상한(작게 잡히는 경우가 많음)
즉, 컨테이너 메모리 limit이 2Gi여도 /dev/shm이 64Mi면, shm을 많이 쓰는 워크로드는 64Mi에서 먼저 실패합니다.
운영 팁: 관측과 가드레일
1) shm 사용량을 주기적으로 확인
컨테이너에서 다음을 주기적으로 보거나, 문제 재현 시 스냅샷으로 남기면 좋습니다.
kubectl exec -it <pod> -- df -h /dev/shm
kubectl exec -it <pod> -- ls -alh /dev/shm | head
2) 노드 메모리 압박과 연쇄 장애 주의
emptyDir.medium=Memory는 노드 메모리를 사용합니다. 여러 Pod가 동시에 큰 shm을 쓰면 노드가 메모리 압박을 받아 다른 Pod까지 영향을 줄 수 있습니다. 이때는 다음을 같이 점검하세요.
- Pod의
requests/limits가 실제 사용량과 맞는지 - HPA/Replica가 늘 때 shm 총량이 노드 메모리를 초과하지 않는지
- 노드 타입(메모리)과 노드 수가 충분한지
노드 리소스 압박으로 Pod가 축출(Evicted)되는 케이스도 함께 발생할 수 있는데, 디스크(nodefs) 쪽 이슈와 헷갈리기 쉽습니다. 관련 패턴은 EKS에서 nodefs ImageGC로 Pod가 Evicted될 때도 같이 참고하면 원인 분리가 빨라집니다.
3) exec/logs 자체가 안 될 때
shm 문제를 확인하려고 kubectl exec를 치는데, 하필 그 상황에서 exec/logs가 안 되면 진단이 막힙니다. 네트워크/10250/WebSocket/RBAC/보안그룹 등 다른 원인일 수 있으니, 그 경우에는 EKS에서 kubectl exec·logs가 안 될 때 진단법을 먼저 체크하는 게 좋습니다.
실전 체크리스트(요약)
- Pod 종료 사유가
OOMKilled인지, 앱 크래시인지 구분 -
df -h /dev/shm로 shm 크기 확인(대개 64Mi면 의심) - 로그에
No space left on device,Failed to create shared memory단서 확인 -
emptyDir: { medium: Memory, sizeLimit: ... }를/dev/shm에 마운트 - shm을 키운 만큼 노드 메모리 여유/스케일 전략 재점검
결론
EKS에서 “OOM”처럼 보이는 장애 중 꽤 많은 비율이 실제로는 컨테이너 /dev/shm 부족에서 시작합니다. 특히 브라우저 자동화나 멀티프로세스 워커가 섞이면 재현이 어렵고, 단순히 memory limit만 올리면 비용만 증가한 채 문제는 지속되기 쉽습니다.
가장 예측 가능하고 운영 친화적인 해결책은 emptyDir(memory)로 /dev/shm을 명시적으로 확장하는 것입니다. 적용 후에는 shm 사용량과 노드 메모리 압박을 함께 관측하면서 sizeLimit을 워크로드에 맞게 튜닝하면, 크래시와 성능 저하를 동시에 잡을 수 있습니다.