컨테이너는 기본적으로 호스트의 모든 CPU와 메모리를 제한 없이 쓸 수 있습니다. 그래서 한 컨테이너에서 메모리 누수가 나면 호스트 전체 메모리를 먹어버리고, 커널 OOM Killer가 다른 멀쩡한 컨테이너까지 죽입니다. 한 컨테이너의 문제가 호스트 전체로 번지는 것을 막는 것이 리소스 제한입니다.
1단계 — 지금 얼마나 쓰는지 본다
제한값을 정하려면 먼저 실제 사용량을 알아야 합니다.
# 컨테이너별 실시간 CPU·메모리 사용량
docker stats
# 컨테이너가 OOM으로 죽었는지 확인
docker inspect -f '{{.State.OOMKilled}}' <컨테이너>
# 종료 코드 137이면 메모리 초과 강제 종료
docker inspect -f '{{.State.ExitCode}}' <컨테이너>
docker stats의 MEM USAGE / LIMIT에서 LIMIT이 호스트 전체 메모리로 잡혀 있다면 제한이 없는 상태입니다.
OOMKilled 신호 읽기
| 신호 | 의미 |
|---|---|
ExitCode 137 | 메모리 limit 초과로 SIGKILL |
OOMKilled: true | 커널이 컨테이너를 강제 종료 |
| 호스트 전체 응답 없음 | 제한 없는 컨테이너가 호스트 메모리 고갈 |
ExitCode 137은 거의 항상 메모리 부족 신호입니다. 제한이 없는데 죽었다면 호스트 메모리가 고갈된 것이고, 제한이 있는데 죽었다면 limit이 너무 낮은 것입니다.
메모리 제한 설정
가장 중요한 건 메모리입니다. --memory로 상한을 둡니다.
docker run -d \
--memory 512m \
--memory-swap 512m \
myapp
--memory는 물리 메모리 상한, --memory-swap은 메모리+스왑 합계입니다. 둘을 같은 값으로 두면 스왑을 막아, 느려진 채 살아 있는 것보다 빨리 OOM으로 드러나게 할 수 있습니다.
CPU 제한 설정
CPU는 여러 컨테이너가 나눠 쓰므로 보통 비율로 제한합니다.
docker run -d \
--cpus 1.5 \
myapp
--cpus 1.5는 코어 1.5개 분량만 쓰게 합니다. 메모리와 달리 CPU 초과는 컨테이너를 죽이지 않고 **스로틀링(속도 제한)**만 걸립니다.
docker-compose에서
services:
app:
image: myapp
deploy:
resources:
limits:
memory: 512m
cpus: "1.5"
Compose v2 standalone에서는 deploy.resources.limits가 적용되며, Swarm 없이도 동작합니다.
체크리스트
docker stats # 실제 사용량으로 limit 산정
docker inspect -f '{{.State.OOMKilled}}' <컨테이너> # OOM 여부
docker inspect -f '{{.HostConfig.Memory}}' <컨테이너> # 제한 적용 확인(0이면 무제한)
제한값은 docker stats로 본 평소 사용량에 여유를 더해 잡는 것이 안전합니다. 너무 빡빡하면 정상 부하에도 OOM이 납니다.
메모리 제한을 걸고 일부러 초과시켜 OOMKilled를 직접 재현해 보는 실습은 도커 트랙에서 무료로 할 수 있습니다.