이 글의 목차

Docker Exit Code 137만 보고 메모리 제한부터 늘리지 마세요. 137은 일반적인 Unix 셸에서 SIGKILL 종료와 관련된 값이지만, 프로세스가 해당 숫자를 직접 반환하는 경우도 있어 코드 하나로 원인을 확정할 수 없습니다. 실제 OOM 여부, 종료 시각, 배포·관리 명령을 대조해야 합니다.

Kubernetes에서 발생했다면 OOMKilled 파드 점검도 함께 보세요. 이 글은 Docker 컨테이너가 갑자기 종료되거나 반복 재시작할 때 남아 있는 증거로 원인을 구분하는 절차입니다.

Exit Code 137과 SIGKILL의 관계

Bash는 치명적 신호 번호가 N이면 종료 상태에 128 + N을 사용합니다. 일반적인 Linux 환경에서 SIGKILL은 9이므로 128 + 9 = 137입니다. GNU Bash 종료 상태 설명

그렇다고 137의 원인이 하나인 것은 아닙니다. 메모리 부족에 따른 OOM killer, 명시적 강제 종료, 정상 종료 대기 시간 초과 등이 SIGKILL로 이어질 수 있습니다. Docker도 일부 특별한 실행 실패 코드를 제외하면 컨테이너 명령의 종료 상태를 전달합니다. Docker 실행과 종료 코드

재시작하기 전 기록할 값

가능하면 컨테이너를 삭제·재생성하기 전에 상태와 로그를 저장하세요. 다음은 app이라는 실제 컨테이너 이름을 알고 있다는 가정입니다. Compose 서비스명과 컨테이너 이름이 다르면 docker compose ps에서 확인하세요.

Terminal window
docker inspect --format \
'ExitCode={{.State.ExitCode}} OOMKilled={{.State.OOMKilled}} FinishedAt={{.State.FinishedAt}} RestartCount={{.RestartCount}}' app
docker inspect --format \
'Memory={{.HostConfig.Memory}} MemorySwap={{.HostConfig.MemorySwap}}' app
docker logs --timestamps --tail 200 app
docker stats --no-stream app

Memory 값은 바이트 단위이며 0이면 그 설정에서 명시적 hard limit이 없는 뜻입니다. 호스트 메모리가 무제한이라는 뜻은 아닙니다. docker stats는 현재 또는 관측 가능한 상태의 값이므로, 죽기 직전 최고 사용량을 복원하는 기록으로 볼 수는 없습니다.

자동 재시작 후 inspect가 현재 실행 상태를 보여주는 경우에는 이전 실행의 원인을 놓칠 수 있습니다. 장기적으로 컨테이너 이벤트와 메모리 지표를 수집해야 하는 이유입니다.

OOMKilled=true와 false의 해석

증거무엇을 시사하나추가 확인
137 + OOMKilled=true런타임이 OOM 종료를 기록컨테이너 제한·호스트 압박·메모리 추이
137 + OOMKilled=false137만으로 OOM 확정 불가관리 명령·stop 시간 초과·호스트 로그
배포 시각마다 종료종료 절차 문제 가능SIGTERM 처리와 정리 작업 시간
부하가 커질수록 종료메모리·동시 작업 증가 가능RSS, 큐, 캐시, 작업 크기 추이
낮은 현재 사용량종료 전 피크를 반증하지 않음과거 모니터링과 커널 기록

OOMKilled=false를 ‘메모리와 무관’으로 확정해서도 안 됩니다. 컨테이너 재생성, 런타임 상태, 호스트 전체 OOM 상황 등에 따라 추가 증거가 필요합니다.

Linux 호스트 로그를 읽을 권한이 있다면 종료 시각 주변의 커널 로그를 확인합니다. 시간 범위는 실제 사고 시점으로 바꾸세요.

Terminal window
journalctl -k --since "2026-10-08 10:00:00" --until "2026-10-08 10:10:00"

권한 때문에 읽지 못했다면 ‘OOM 로그 없음’이 아니라 ‘확인하지 못함’으로 기록합니다. 로그에서 killed process와 memory cgroup 관련 메시지를 컨테이너·프로세스 정보와 대조하세요. 공유할 때는 민감한 경로와 환경 정보를 제거합니다.

docker stop도 137로 끝날 수 있습니다

Docker는 정상 종료 신호를 보낸 후 정해진 시간 안에 종료되지 않으면 SIGKILL을 보낼 수 있습니다. 기본 신호와 대기 시간은 이미지·옵션·환경에 따라 달라집니다. Docker stop 문서

배포할 때마다 137이 발생한다면 앱이 종료 신호를 받는지 확인하세요. 셸이 PID 1인 실행 구조, 새 요청을 받는 서버, 긴 작업을 정리하는 과정이 영향을 줄 수 있습니다. 충분한 종료 시간을 주는 것과 앱이 신호를 받아 새 작업을 멈추는 것은 함께 검토해야 합니다. Graceful Shutdown 가이드를 참고하세요.

기존 docker stop 대기 시간을 늘려 종료가 달라지는지 확인하는 실험은 운영 트래픽에 영향을 줍니다. 재현 환경에서 먼저 검증하고 실제 작업의 최대 소요 시간을 근거로 조정하세요.

메모리가 원인이면 무엇을 바꿀까요?

Docker 공식 문서는 호스트 전체 메모리 부족 위험, 컨테이너 hard·soft limit, swap의 성능 영향을 설명합니다. OOM 보호를 끄는 방식으로 문제를 숨기지 말고 필요한 자원과 사용량을 측정해야 합니다. Docker 메모리 제약

조치 선택은 사용 패턴에 따라 달라집니다.

  • 시작할 때만 급증: 이미지·인덱스 로딩, 런타임 초기화와 동시 시작 수를 확인합니다.
  • 요청 증가에 비례: 동시 처리 수, 큐 길이, 큰 응답·파일 버퍼링을 줄입니다.
  • 시간에 따라 계속 증가: 캐시 상한, 해제되지 않는 객체, 메모리 누수를 조사합니다.
  • 명백히 작은 제한: 정상 피크와 호스트 여유를 근거로 제한을 조정합니다.

Node.js의 힙 제한은 컨테이너 전체 메모리와 같지 않습니다. 버퍼·네이티브 메모리·스레드 등도 RSS에 포함됩니다. Java 힙, Python 객체 등 런타임별 수치도 전체 사용량과 구분하세요. 메모리 제한만 계속 키우면 문제 발생 시점만 뒤로 밀릴 수 있습니다.

수정 후 확인할 성공 조건

한 번 실행에 성공한 것보다, 이전에 죽던 작업과 동시성에서 일정 시간 관측하는 것이 중요합니다. 종료 횟수, 최고 메모리 사용량, 처리 지연, 호스트 여유를 함께 기록하세요. 종료 시간을 고쳤다면 실제 배포에서 진행 중 작업이 어떻게 마무리되는지도 확인합니다.

자주 묻는 질문

Exit Code 137이면 항상 OOMKilled인가요?

아닙니다. 강제 종료와 종료 대기 시간 초과도 원인일 수 있고, 코드만으로 신호 종료를 확정할 수도 없습니다. 런타임 상태·로그·시간을 함께 보세요.

컨테이너 메모리 제한이 없으면 OOM이 안 나나요?

호스트 전체의 메모리는 유한합니다. 명시적 제한이 없어도 시스템 메모리가 부족하면 프로세스가 종료될 수 있습니다.

swap을 늘리면 해결되나요?

일부 피크를 완충할 수 있지만 성능 비용이 있고 원인을 제거하지는 않습니다. 사용량 패턴, 호스트 상황과 지연 요구를 먼저 확인하세요.

참고 자료

2026년 10월 8일 문서 확인. 호스트·런타임 권한과 로그 보존 상태에 따라 확인 가능한 증거가 달라집니다.

← 전체 글로 돌아가기