AWS ECS Fargate 'Exit Code 137' OOM 에러 완벽 해결: 컨테이너 메모리 최적화 아키텍처
"트래픽이 약간만 몰려도 AWS ECS Fargate에 띄워둔 스프링 부트(또는 Node.js) 컨테이너가 아무런 에러 로그도 없이 갑자기 죽어버립니다. 중지된 작업의 세부 정보를 보면 Exit Code 137이라는 문구만 덩그러니 남아있습니다."
서버리스 컨테이너 서비스인 ECS Fargate를 실무에 도입한 백엔드 엔지니어들이 가장 빈번하게 마주하는 악명 높은 셧다운 현상이다. 코드는 정상 작동하고 있었기에 애플리케이션 로그(CloudWatch)에는 어떤 예외(Exception) 처리 기록도 남지 않는다. 마치 누군가 서버의 전원 플러그를 갑자기 뽑아버린 것과 같다.
초보자들은 이 에러를 마주하면 Fargate의 Task 메모리 용량을 2GB에서 4GB, 8GB로 무작정 스케일업(Scale-up)하며 클라우드 비용을 낭비한다. 하지만 이는 임시방편일 뿐, 근본적인 원인은 리눅스 커널의 OOM(Out Of Memory) Killer 메커니즘과 애플리케이션 런타임 간의 '소통 부재'에 있다. 본 가이드에서는 컨테이너와 애플리케이션 간의 메모리 계층을 해체하고, 무결점 자원 할당 아키텍처를 단호하게 제시한다.
1단계: 핵심 원인 분석 (OOMKilled와 런타임의 착각)
에러 코드 137은 리눅스 시스템에서 128 + 9(SIGKILL)을 의미한다. 즉, OS 커널이 메모리 부족 상태를 감지하고 가장 자원을 많이 쓰는 프로세스를 자비 없이 처형(Kill)했다는 뜻이다.
왜 이런 일이 발생할까? 예를 들어 Fargate 컨테이너에 2GB의 메모리를 할당했다고 가정하자. 내부에서 도는 Java(JVM)나 Node.js 런타임은 자신이 2GB짜리 좁은 방에 갇혀 있다는 사실을 모를 때가 많다. 이들은 호스트 머신의 전체 메모리(예: AWS 내부의 거대한 물리 서버)를 자신의 것으로 착각하고, 2GB를 넘어서 3GB, 4GB의 힙(Heap) 메모리를 사용하려 시도한다. 그 선을 넘는 순간 커널의 OOM Killer가 즉각 개입하여 프로세스를 죽여버리는 것이다.
2단계: 완벽한 해결책 - 런타임 메모리 명시적 제한 (Heap Sizing)
가장 안전한 해결책은 애플리케이션 런타임에게 "너는 컨테이너 전체 메모리(2GB) 중 70~80%만 사용해야 해"라고 명시적인 한계선을 그어주는 것이다. 나머지 20~30%는 OS 커널 및 기타 시스템 프로세스가 숨을 쉴 수 있는 여유 공간(Overhead)으로 남겨두어야 한다.
Docker 이미지 빌드 시점이나 ECS Task Definition의 환경 변수(Environment Variables) 영역에 애플리케이션별 메모리 제한 플래그를 강제로 주입하라.
# Node.js 환경의 경우 (Fargate 메모리가 2GB일 때 V8 엔진 힙을 1.5GB로 제한)
environment:
- name: NODE_OPTIONS
value: "--max-old-space-size=1536"
# Spring Boot (Java) 환경의 경우 (Fargate 메모리가 2GB일 때 힙을 1.5GB로 제한)
environment:
- name: JAVA_OPTS
value: "-Xmx1536m -Xms1536m"
이 코드를 적용하면, 애플리케이션은 지정된 1.5GB 한계에 도달할 무렵 스스로 가비지 컬렉터(Garbage Collector)를 적극적으로 가동하여 메모리를 청소한다. 리눅스 커널의 분노를 사서 강제 처형당하는 일(Exit 137)은 영원히 사라진다.
3단계: 추가 트러블슈팅 - 메모리 누수(Memory Leak) 추적
제한을 올바르게 걸어두었는데도 애플리케이션 자체에서 OutOfMemoryError 로그를 뱉으며 죽는다면, 이는 인프라 설정 문제가 아니라 애플리케이션 코드 내부의 '메모리 누수'다. 닫히지 않은 데이터베이스 커넥션, 계속 쌓이기만 하는 전역(Global) 캐시 리스트 등이 주범이다. 이럴 때는 AWS X-Ray나 Datadog 같은 APM 도구를 연결하여 힙 덤프(Heap Dump)를 뜨고, 어떤 객체가 메모리를 반환하지 않고 쥐고 있는지 코드 레벨에서 적출해 내야 한다.
아키텍트의 시선 (Insight)
클라우드 환경에서 컨테이너 기술은 애플리케이션을 외부 환경으로부터 완벽하게 격리해 주는 마법처럼 보이지만, 결국 그 밑바탕은 제한된 물리적 컴퓨팅 자원이다. 인프라 엔지니어는 런타임 언어의 특성을 이해해야 하고, 개발자는 인프라의 제약 사항을 알아야 한다. 무작정 서버 스펙을 올리는 안일함을 버리고, 컨테이너와 프로세스 간의 자원 동기화 아키텍처를 적용하여 극강의 비용 효율과 시스템 안정성을 동시에 달성하라.
댓글
댓글 쓰기