본문으로 건너뛰기
목록으로 돌아가기
스터디
8

Apache Ambari Hadoop YARN Heap 메모리 부족 장애 회고

13-node Hadoop 환경에서 반복된 YARN container 메모리 초과를 재구성하고, 원인 가설과 설정 변경·검증 절차를 정리합니다.

박효열 (Hyoyoul Park)마지막 수정
#Hadoop#Spark#Hive

보안 안내: 실제 게임명·상세 아키텍처·원본 로그는 비공개했습니다. 아래 내용은 당시 운영 기록을 바탕으로 재구성한 회고이며, 관측한 사실과 사후 추정을 구분해 설명합니다.

환경과 관측 사실

지난 인프라 구조는 다음과 같았습니다.

  • Bastion Host 구조
  • Namenode 1대
  • 12대의 datanode

해당 인프라 구조에서 주로 두 개의 게임 서비스를 운영하였고 평균 쿼리 사이즈가 1GB를 상회했었습니다.

실제 Hadoop 아키텍처 이미지는 보안상 공개하지 않습니다.

그러나 해당 인프라를 운영하면서 유독 Heap 메모리 부족으로 많은 인프라 장애를 앓고 있었습니다. 관련 유력 원인은 다음과 같습니다.

  1. YARN NodeManager/ResourceManager 메모리 부족
  • 컨테이너 할당이 물리 메모리를 초과할 때
  • yarn.nodemanager.resource.memory-mb 설정이 실제 가용 메모리보다 클 때
  • Java heap이 작은데 많은 애플리케이션이 동시 실행될 때
  1. MapReduce Job의 메모리 설정 문제
  • Map/Reduce task의 heap size가 너무 작게 설정 (mapreduce.map.java.opts, mapreduce.reduce.java.opts)
  • 대용량 데이터 처리 시 메모리 버퍼 부족
  • Shuffle 단계에서 메모리 초과
  1. HBase RegionServer OOM
  • MemStore 크기 설정이 부적절할 때
  • BlockCache와 MemStore 합이 heap의 80%를 초과할 때
  • 대량의 동시 요청 처리 시
  1. Hive/Tez 실행 엔진
  • Tez AM (Application Master) heap 부족
  • 복잡한 쿼리의 실행 계획 생성 시
  • 조인이나 집계 연산의 중간 결과가 메모리에 쌓일 때

그러나 당시 상황 로그 분석 및 자료들 분석 결과, 1GB 정도 쿼리를 돌리는 환경에서 YARN 메모리 부족이 자주 발생했다면, 전형적인 메모리 오버커밋(over-commit) 문제였을 가능성이 높습니다.

당시 상황에 대한 사후 분석

xlarge 인스턴스 (보통 4 vCPU, 16GB RAM 기준)

  • 실제 가용 메모리: ~14GB (OS, 데몬 제외)
  • DataNode가 사용: ~2GB
  • NodeManager가 컨테이너에 할당 가능: 이론상 ~12GB

문제 발생 시나리오:

yarn.nodemanager.resource.memory-mb = 12288 (12GB)
mapreduce.map.memory.mb = 2048 (2GB)
mapreduce.reduce.memory.mb = 4096 (4GB)

→ Map task 6개 or Reduce task 3개까지만 가능
→ 하지만 Java heap + off-heap 메모리가 실제로는 더 사용됨

1GB 쿼리에서 왜 문제가?

Multiple Map tasks 동시 실행

1GB 데이터 → HDFS block 단위(128MB)로 나뉨 → 약 8개 Map task 각 Map task가 2GB 메모리 요청 노드당 6개만 가능한데 8개가 스케줄링되면 대기/재시도

Reduce 단계의 메모리 압박

Shuffle/Sort 단계에서 메모리 버퍼 사용 mapreduce.reduce.shuffle.memory.limit.percent (기본 0.25) 4GB reduce task의 경우 실제 heap 3GB + shuffle buffer 1GB = 실제 4GB+ 사용

Container 메모리 vs Java Heap 불일치

mapreduce.map.memory.mb = 2048
mapreduce.map.java.opts = -Xmx1638m  (80% 권장)

→ 나머지 400MB는 off-heap, overhead
→ 실제로는 2GB 이상 사용하는 경우 발생

전형적인 에러 로그

Container killed by YARN for exceeding memory limits. 
2.1 GB of 2 GB physical memory used.

당시 해결 방법들

즉시 조치:

  • yarn.nodemanager.resource.memory-mb를 10GB로 축소
  • yarn.nodemanager.vmem-check-enabled=false (임시 우회)

vmem-check-enabled=false는 가상 메모리 제한 검사를 끄는 임시 우회입니다. 물리 메모리 고갈과 노드 불안정을 숨길 수 있으므로 원인 확인과 rollback 조건 없이 상시 적용하면 안 됩니다.

근본적 해결:

  • Map/Reduce task 메모리를 더 여유있게 (2GB → 3GB)
  • 동시 실행 task 수 제한
  • DataNode heap 메모리 최적화 (불필요하게 크면 줄임)

위 근본 해결 방법 중 Map/Reduce task 메모리를 더 여유 있게 조정하는 절차를 순서대로 살펴보겠습니다.

Ambari UI에서 변경 (가장 일반적)

  1. MapReduce2 설정

Ambari Web UI → Services → MapReduce2 → Configs → Advanced

[Memory 관련 설정]

mapreduce.map.memory.mb = 3072 (2048 → 3072) mapreduce.reduce.memory.mb = 6144 (4096 → 6144)

[Java Heap 설정 - 80% 규칙]

mapreduce.map.java.opts = -Xmx2457m (80% of 3072) mapreduce.reduce.java.opts = -Xmx4915m (80% of 6144)

  1. YARN 설정도 함께 확인

Services → YARN → Configs

yarn.scheduler.minimum-allocation-mb = 1024 yarn.scheduler.maximum-allocation-mb = 8192 (task가 요청 가능한 최대치) yarn.nodemanager.resource.memory-mb = 10240 (노드당 할당 가능 총량)

설정 파일로 직접 변경 (모든 노드)

mapred-site.xml (보통 /etc/hadoop/conf/)

<property>
  <name>mapreduce.map.memory.mb</name>
  <value>3072</value>
</property>

<property>
  <name>mapreduce.reduce.memory.mb</name>
  <value>6144</value>
</property>

<property>
  <name>mapreduce.map.java.opts</name>
  <value>-Xmx2457m</value>
</property>

<property>
  <name>mapreduce.reduce.java.opts</name>
  <value>-Xmx4915m</value>
</property>

Job 실행 시 동적으로 변경 (임시 테스트용)

Hive 쿼리 실행 전:

SET mapreduce.map.memory.mb=3072;
SET mapreduce.reduce.memory.mb=6144;
SET mapreduce.map.java.opts=-Xmx2457m;
SET mapreduce.reduce.java.opts=-Xmx4915m;

SELECT ... -- 실제 쿼리

Spark submit 시:

spark-submit \
  --conf spark.executor.memory=3g \
  --conf spark.driver.memory=2g \
  your_job.py

변경 후 필수 작업

# Ambari에서 변경 시:
1. "Save" 클릭
2. "Restart All Required Services" (NodeManager 재시작 필요)

# 직접 변경 시:
ambari-server restart
# 또는
sudo systemctl restart hadoop-yarn-nodemanager

실전 팁

메모리 계산 공식:

Java Heap = Container Memory × 0.8

예시:
Container 3GB (3072MB) → Heap 2457MB (-Xmx2457m)
Container 6GB (6144MB) → Heap 4915MB (-Xmx4915m)

왜 80%인가?

  • 나머지 20%는 JVM overhead, off-heap, native memory 사용
  • 100%로 설정하면 Container killed 발생

변경 후 검증 체크리스트

  1. 동일한 대표 쿼리와 동시 작업 수로 변경 전후를 비교합니다.
  2. YARN container 종료 횟수, NodeManager RSS, JVM GC 시간, 작업 재시도 횟수를 함께 기록합니다.
  3. 처리 시간이 개선돼도 다른 tenant의 대기 시간이 악화되지 않았는지 확인합니다.
  4. 한 번에 하나의 설정군만 변경하고, 원복할 이전 설정값을 보관합니다.
  5. 최소 한 번의 peak 시간대를 통과한 뒤 임시 우회를 제거할 수 있는지 재평가합니다.

이 회고의 수치는 공개 가능한 범위에서 재구성한 예시입니다. 실제 환경에서는 OS 예약 메모리, daemon 사용량, scheduler 정책, JVM 버전에 맞춰 다시 측정해야 합니다.