LabHub

블로그

Hadoop 에코시스템 실전 가이드: HDFS, MapReduce, YARN 핵심 정리

한국어English日本語

들어가며

Hadoop은 2006년 등장한 이래 빅데이터 처리의 표준으로 자리잡았습니다. Spark, Flink 등 차세대 프레임워크가 등장했지만, HDFS와 YARN은 여전히 데이터 인프라의 핵심 기반입니다. 2026년 현재에도 많은 기업이 Hadoop 기반 데이터 레이크를 운영하고 있으며, 클라우드 환경에서도 HDFS 호환 스토리지(S3, ADLS)를 활용합니다.

이 글에서는 Hadoop의 핵심 구성 요소를 아키텍처 수준에서 이해하고, 실전 운영에 필요한 설정과 튜닝 포인트를 정리합니다.

1. Hadoop 에코시스템 개요

핵심 구성 요소

┌─────────────────────────────────────────────────────────────┐
Hadoop Ecosystem├─────────────────────────────────────────────────────────────┤
[Hive]  [Pig]  [Spark]  [HBase]  [Presto]  [Flink]Application / Processing Layer├─────────────────────────────────────────────────────────────┤
YARNResource Management Layer├─────────────────────────────────────────────────────────────┤
HDFSDistributed Storage Layer└─────────────────────────────────────────────────────────────┘
구성 요소역할버전 (2026 기준)
HDFS분산 파일 시스템Hadoop 3.4.x
YARN리소스 관리 및 스케줄링Hadoop 3.4.x
MapReduce배치 처리 프레임워크Hadoop 3.4.x
HiveSQL-on-HadoopHive 4.x
HBaseNoSQL 데이터베이스HBase 2.6.x
Spark통합 분석 엔진Spark 3.5.x / 4.x
ZooKeeper분산 코디네이션ZooKeeper 3.9.x

2026년 Hadoop의 위상

2. HDFS 아키텍처

핵심 구성 요소

                    ┌──────────────┐
NameNode                      (Master)- 메타데이터  │
- 블록 위치   │
                    └──────┬───────┘
              ┌────────────┼────────────┐
              │            │            │
      ┌───────▼──────┐ ┌──▼──────────┐ ┌▼─────────────┐
DataNode 1  │ │  DataNode 2 │ │  DataNode 3Block A     │ │  Block A    │ │  Block BBlock C     │ │  Block B    │ │  Block A      └──────────────┘ └─────────────┘ └──────────────┘
                    Replication Factor = 3

NameNode

NameNode는 HDFS의 메타데이터를 관리하는 마스터 노드입니다.

# NameNode가 관리하는 정보
# 1. 파일/디렉토리 트리 구조 (Namespace)
# 2. 각 파일의 블록 목록
# 3. 각 블록이 어느 DataNode에 있는지 (Block Mapping)
# 4. 파일 권한, 수정 시간 등

# NameNode 메모리 사용량 추정
# 파일 1개 ≈ 150 bytes
# 블록 1개 ≈ 150 bytes
# 1억 개 파일 → 약 30GB 힙 메모리 필요

DataNode

# DataNode 핵심 동작
# 1. 블록 저장 (기본 128MB)
# 2. 클라이언트에게 블록 읽기/쓰기 서비스
# 3. NameNode에 하트비트 전송 (3초 간격)
# 4. 블록 리포트 전송 (6시간 간격)

HDFS HA (High Availability)

<!-- hdfs-site.xml - HA 설정 -->
<configuration>
    <property>
        <name>dfs.nameservices</name>
        <value>mycluster</value>
    </property>
    <property>
        <name>dfs.ha.namenodes.mycluster</name>
        <value>nn1,nn2</value>
    </property>
    <property>
        <name>dfs.namenode.rpc-address.mycluster.nn1</name>
        <value>namenode1:8020</value>
    </property>
    <property>
        <name>dfs.namenode.rpc-address.mycluster.nn2</name>
        <value>namenode2:8020</value>
    </property>

    <!-- JournalNode 설정 (최소 3개) -->
    <property>
        <name>dfs.namenode.shared.edits.dir</name>
        <value>qjournal://jn1:8485;jn2:8485;jn3:8485/mycluster</value>
    </property>

    <!-- 자동 failover -->
    <property>
        <name>dfs.ha.automatic-failover.enabled</name>
        <value>true</value>
    </property>
    <property>
        <name>dfs.ha.fencing.methods</name>
        <value>sshfence</value>
    </property>
</configuration>

블록 복제 전략

# 기본 Rack-Aware 복제 전략 (Replication Factor = 3)
#
# 1번째 복제본: 클라이언트와 같은 노드 (또는 같은 랙)
# 2번째 복제본: 다른 랙의 노드
# 3번째 복제본: 2번째와 같은 랙의 다른 노드
#
# ┌──────── Rack 1 ────────┐  ┌──────── Rack 2 ────────┐
# │ [DataNode1: Block A]   │  │ [DataNode3: Block A]# │ [DataNode2]            │  │ [DataNode4: Block A]# └────────────────────────┘  └────────────────────────┘

3. MapReduce 원리와 실행 흐름

MapReduce 프로그래밍 모델

InputSplitMapShuffle & SortReduceOutput

[파일 분할]
  Split 1Mapper 1  (key, value) pairs ─┐
  Split 2Mapper 2  (key, value) pairs ──┤── Shuffle & Sort
  Split 3Mapper 3  (key, value) pairs ─┘     │
                                                   ├→ Reducer 1Output Part 1
                                                   └→ Reducer 2Output Part 2

WordCount 예제 (Java)

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

import java.io.IOException;
import java.util.StringTokenizer;

public class WordCount {

    // Mapper: 각 라인을 단어로 분리하여 (단어, 1) 출력
    public static class TokenizerMapper
            extends Mapper<Object, Text, Text, IntWritable> {

        private final static IntWritable one = new IntWritable(1);
        private Text word = new Text();

        public void map(Object key, Text value, Context context)
                throws IOException, InterruptedException {
            StringTokenizer itr = new StringTokenizer(value.toString());
            while (itr.hasMoreTokens()) {
                word.set(itr.nextToken().toLowerCase());
                context.write(word, one);
            }
        }
    }

    // Reducer: 같은 키의 값들을 합산
    public static class IntSumReducer
            extends Reducer<Text, IntWritable, Text, IntWritable> {

        private IntWritable result = new IntWritable();

        public void reduce(Text key, Iterable<IntWritable> values, Context context)
                throws IOException, InterruptedException {
            int sum = 0;
            for (IntWritable val : values) {
                sum += val.get();
            }
            result.set(sum);
            context.write(key, result);
        }
    }

    public static void main(String[] args) throws Exception {
        Configuration conf = new Configuration();
        Job job = Job.getInstance(conf, "word count");
        job.setJarByClass(WordCount.class);
        job.setMapperClass(TokenizerMapper.class);
        job.setCombinerClass(IntSumReducer.class);  // 로컬 집계로 네트워크 트래픽 감소
        job.setReducerClass(IntSumReducer.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(IntWritable.class);
        FileInputFormat.addInputPath(job, new Path(args[0]));
        FileOutputFormat.setOutputPath(job, new Path(args[1]));
        System.exit(job.waitForCompletion(true) ? 0 : 1);
    }
}

Job 실행 흐름

1. 클라이언트가 Job 제출
   └→ JAR, 설정, InputSplit 정보를 HDFS에 업로드

2. ResourceManager가 ApplicationMaster 컨테이너 할당
   └→ NodeManager에서 AM 시작

3. ApplicationMaster가 InputSplit 수에 따라 Map 태스크 요청
   └→ ResourceManager가 컨테이너 할당

4. Map 태스크 실행
   └→ InputSplit 읽기 → map() 호출 → 중간 결과를 로컬 디스크에 저장

5. Shuffle & Sort
   └→ Map 출력을 파티셔닝 → Reducer로 전송 → 키별 정렬

6. Reduce 태스크 실행
   └→ reduce() 호출 → 최종 결과를 HDFS에 저장

7. ApplicationMaster가 Job 완료를 ResourceManager에 알림

4. YARN 리소스 관리

YARN 아키텍처

┌─────────────────────────────────────────────────────┐
ResourceManager│  ┌──────────────┐  ┌────────────────────────┐       │
│  │  Scheduler   │  │  ApplicationManager    │       │
  (자원 할당)  (앱 관리)              │       │
│  └──────────────┘  └────────────────────────┘       │
└──────────────────────┬──────────────────────────────┘
        ┌──────────────┼──────────────┐
        │              │              │
┌───────▼──────┐ ┌─────▼────────┐ ┌──▼────────────┐
NodeManager 1│ │ NodeManager 2│ │ NodeManager 3│ ┌──────────┐ │ │ ┌──────────┐ │ │ ┌──────────┐  │
│ │Container │ │ │ │Container │ │ │ │Container │  │
(AM)      │ │ │ (Map Task)│ │ │ (Reduce)  │  │
│ └──────────┘ │ │ └──────────┘ │ │ └──────────┘  │
│ ┌──────────┐ │ │ ┌──────────┐ │ │               │
│ │Container │ │ │ │Container │ │ │               │
(Map Task)│ │ │ (Map Task)│ │ │               │
│ └──────────┘ │ │ └──────────┘ │ │               │
└──────────────┘ └──────────────┘ └───────────────┘

YARN 스케줄러 설정

<!-- yarn-site.xml -->
<configuration>
    <!-- 스케줄러 종류: Capacity, Fair, FIFO -->
    <property>
        <name>yarn.resourcemanager.scheduler.class</name>
        <value>org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacityScheduler</value>
    </property>

    <!-- NodeManager 리소스 설정 -->
    <property>
        <name>yarn.nodemanager.resource.memory-mb</name>
        <value>65536</value>  <!-- 64GB -->
    </property>
    <property>
        <name>yarn.nodemanager.resource.cpu-vcores</name>
        <value>16</value>
    </property>

    <!-- 컨테이너 최소/최대 메모리 -->
    <property>
        <name>yarn.scheduler.minimum-allocation-mb</name>
        <value>1024</value>
    </property>
    <property>
        <name>yarn.scheduler.maximum-allocation-mb</name>
        <value>32768</value>
    </property>

    <!-- 컨테이너 최소/최대 vCores -->
    <property>
        <name>yarn.scheduler.minimum-allocation-vcores</name>
        <value>1</value>
    </property>
    <property>
        <name>yarn.scheduler.maximum-allocation-vcores</name>
        <value>8</value>
    </property>
</configuration>

Capacity Scheduler 큐 설정

<!-- capacity-scheduler.xml -->
<configuration>
    <property>
        <name>yarn.scheduler.capacity.root.queues</name>
        <value>default,production,development</value>
    </property>

    <!-- 큐별 용량 (합계 100%) -->
    <property>
        <name>yarn.scheduler.capacity.root.default.capacity</name>
        <value>20</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.production.capacity</name>
        <value>60</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.development.capacity</name>
        <value>20</value>
    </property>

    <!-- 최대 용량 (elasticity) -->
    <property>
        <name>yarn.scheduler.capacity.root.production.maximum-capacity</name>
        <value>80</value>
    </property>

    <!-- 큐별 사용자 제한 -->
    <property>
        <name>yarn.scheduler.capacity.root.production.user-limit-factor</name>
        <value>2</value>
    </property>
</configuration>

5. 클러스터 설치

Pseudo-Distributed 모드 (개발/테스트용)

# 1. Java 설치
sudo apt install -y openjdk-11-jdk
export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64

# 2. Hadoop 다운로드
wget https://dlcdn.apache.org/hadoop/common/hadoop-3.4.1/hadoop-3.4.1.tar.gz
tar -xzf hadoop-3.4.1.tar.gz
sudo mv hadoop-3.4.1 /opt/hadoop
export HADOOP_HOME=/opt/hadoop
export PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin

# 3. SSH 키 설정 (localhost)
ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
<!-- core-site.xml -->
<configuration>
    <property>
        <name>fs.defaultFS</name>
        <value>hdfs://localhost:9000</value>
    </property>
    <property>
        <name>hadoop.tmp.dir</name>
        <value>/opt/hadoop/data/tmp</value>
    </property>
</configuration>
<!-- hdfs-site.xml -->
<configuration>
    <property>
        <name>dfs.replication</name>
        <value>1</value>  <!-- Pseudo-distributed에서는 1 -->
    </property>
    <property>
        <name>dfs.namenode.name.dir</name>
        <value>/opt/hadoop/data/namenode</value>
    </property>
    <property>
        <name>dfs.datanode.data.dir</name>
        <value>/opt/hadoop/data/datanode</value>
    </property>
</configuration>
# 4. NameNode 포맷 및 시작
hdfs namenode -format
start-dfs.sh
start-yarn.sh

# 5. 확인
hdfs dfs -mkdir /user
hdfs dfs -mkdir /user/$USER
jps  # NameNode, DataNode, ResourceManager, NodeManager 확인

Fully Distributed 모드 (프로덕션)

# workers 파일 설정 (/opt/hadoop/etc/hadoop/workers)
datanode1
datanode2
datanode3
datanode4
datanode5
<!-- core-site.xml (HA 모드) -->
<configuration>
    <property>
        <name>fs.defaultFS</name>
        <value>hdfs://mycluster</value>
    </property>
    <property>
        <name>ha.zookeeper.quorum</name>
        <value>zk1:2181,zk2:2181,zk3:2181</value>
    </property>
</configuration>

6. HDFS 핵심 명령어

파일 시스템 조작

# 파일/디렉토리 목록
hdfs dfs -ls /user/hadoop/
hdfs dfs -ls -R /user/hadoop/   # 재귀적 목록

# 파일 업로드
hdfs dfs -put localfile.txt /user/hadoop/
hdfs dfs -copyFromLocal data.csv /user/hadoop/input/
hdfs dfs -moveFromLocal temp.log /user/hadoop/logs/

# 파일 다운로드
hdfs dfs -get /user/hadoop/output/part-r-00000 ./result.txt
hdfs dfs -copyToLocal /user/hadoop/data.csv ./

# 파일 내용 확인
hdfs dfs -cat /user/hadoop/input/data.csv
hdfs dfs -head /user/hadoop/input/data.csv   # 첫 1KB
hdfs dfs -tail /user/hadoop/input/data.csv   # 마지막 1KB
hdfs dfs -text /user/hadoop/output/part-r-00000  # 압축 파일도 읽기

# 디렉토리 생성/삭제
hdfs dfs -mkdir -p /user/hadoop/input/2026/03
hdfs dfs -rm /user/hadoop/temp.txt
hdfs dfs -rm -r /user/hadoop/output/      # 디렉토리 삭제
hdfs dfs -rm -skipTrash /user/hadoop/old/  # 휴지통 건너뛰기

# 권한 관리
hdfs dfs -chmod 755 /user/hadoop/scripts/
hdfs dfs -chown hadoop:hadoop /user/hadoop/
hdfs dfs -chgrp analytics /data/shared/

# 디스크 사용량
hdfs dfs -du -h /user/hadoop/        # 디렉토리별 사용량
hdfs dfs -df -h                       # 전체 용량
hdfs dfs -count -h /user/hadoop/      # 파일/디렉토리 수

관리 명령어

# 클러스터 상태
hdfs dfsadmin -report                    # 전체 클러스터 리포트
hdfs dfsadmin -printTopology             # 랙 구성 확인

# Safe Mode
hdfs dfsadmin -safemode get              # Safe Mode 상태 확인
hdfs dfsadmin -safemode leave            # Safe Mode 해제

# 블록 관리
hdfs fsck / -files -blocks -locations    # 파일 시스템 무결성 검사
hdfs fsck /user/hadoop/ -files           # 특정 경로 검사

# 스냅샷
hdfs dfsadmin -allowSnapshot /user/hadoop/important
hdfs dfs -createSnapshot /user/hadoop/important snap_20260308
hdfs dfs -deleteSnapshot /user/hadoop/important snap_20260308

# 쿼터 관리
hdfs dfsadmin -setSpaceQuota 100G /user/hadoop/project
hdfs dfsadmin -clrSpaceQuota /user/hadoop/project

7. 성능 튜닝

HDFS 튜닝

<!-- hdfs-site.xml -->
<configuration>
    <!-- 블록 크기 (대용량 파일이 많으면 256MB로 증가) -->
    <property>
        <name>dfs.blocksize</name>
        <value>268435456</value>  <!-- 256MB -->
    </property>

    <!-- DataNode 동시 전송 스레드 (기본 4096) -->
    <property>
        <name>dfs.datanode.max.transfer.threads</name>
        <value>8192</value>
    </property>

    <!-- NameNode 핸들러 스레드 (기본 10) -->
    <property>
        <name>dfs.namenode.handler.count</name>
        <value>100</value>  <!-- ln(클러스터 노드 수) * 20 -->
    </property>

    <!-- Short-circuit 로컬 읽기 -->
    <property>
        <name>dfs.client.read.shortcircuit</name>
        <value>true</value>
    </property>
    <property>
        <name>dfs.domain.socket.path</name>
        <value>/var/lib/hadoop-hdfs/dn_socket</value>
    </property>
</configuration>

MapReduce 튜닝

<!-- mapred-site.xml -->
<configuration>
    <!-- Map 태스크 메모리 -->
    <property>
        <name>mapreduce.map.memory.mb</name>
        <value>2048</value>
    </property>
    <property>
        <name>mapreduce.map.java.opts</name>
        <value>-Xmx1638m</value>  <!-- memory.mb의 80% -->
    </property>

    <!-- Reduce 태스크 메모리 -->
    <property>
        <name>mapreduce.reduce.memory.mb</name>
        <value>4096</value>
    </property>
    <property>
        <name>mapreduce.reduce.java.opts</name>
        <value>-Xmx3276m</value>
    </property>

    <!-- Shuffle 버퍼 -->
    <property>
        <name>mapreduce.task.io.sort.mb</name>
        <value>512</value>  <!-- Map 출력 정렬 버퍼 -->
    </property>
    <property>
        <name>mapreduce.reduce.shuffle.input.buffer.percent</name>
        <value>0.70</value>
    </property>

    <!-- 압축 -->
    <property>
        <name>mapreduce.map.output.compress</name>
        <value>true</value>
    </property>
    <property>
        <name>mapreduce.map.output.compress.codec</name>
        <value>org.apache.hadoop.io.compress.SnappyCodec</value>
    </property>
</configuration>

튜닝 요약표

항목기본값권장값영향
dfs.blocksize128MB128~256MB대용량 파일 → 256MB
dfs.replication32~3저장 공간 vs 안정성
dfs.namenode.handler.count1020~200NameNode RPC 처리량
mapreduce.map.memory.mb10241024~4096Map 태스크 메모리
mapreduce.reduce.memory.mb10242048~8192Reduce 태스크 메모리
mapreduce.task.io.sort.mb100256~512Map 정렬 성능
yarn.nodemanager.resource.memory-mb8192물리 메모리의 80%노드 총 할당 메모리

8. Spark vs MapReduce 비교

항목MapReduceSpark
처리 방식디스크 기반 배치인메모리 배치/스트리밍
속도기준 (1x)10~100x 빠름
반복 처리매번 디스크 I/O메모리에 캐싱
프로그래밍 모델Map → ReduceRDD, DataFrame, SQL
언어JavaScala, Python, Java, R, SQL
실시간 처리불가Structured Streaming
메모리 요구낮음높음
장애 복구디스크에서 재계산Lineage로 재계산
적합한 워크로드단순 ETL, 로그 처리ML, 그래프, 반복 처리, 대화형 분석
# Spark WordCount (비교용)
from pyspark.sql import SparkSession

spark = SparkSession.builder.appName("WordCount").getOrCreate()
text_file = spark.sparkContext.textFile("hdfs:///user/hadoop/input/")
counts = text_file.flatMap(lambda line: line.split(" ")) \
                  .map(lambda word: (word, 1)) \
                  .reduceByKey(lambda a, b: a + b)
counts.saveAsTextFile("hdfs:///user/hadoop/output/spark-wordcount")

9. 모니터링

핵심 모니터링 메트릭

메트릭설명경고 기준
NameNode Heap 사용률NN 메모리 사용량> 80%
Under-Replicated Blocks복제 부족 블록 수> 0 (지속)
Dead DataNodes장애 DN 수> 0
HDFS Capacity Used디스크 사용률> 80%
YARN Memory Used메모리 사용률> 85%
Pending Containers대기 중 컨테이너> 100 (지속)

웹 UI 포트

# NameNode UI: http://namenode:9870
# ResourceManager UI: http://resourcemanager:8088
# DataNode UI: http://datanode:9864
# NodeManager UI: http://nodemanager:8042
# MapReduce History: http://historyserver:19888

Prometheus + Grafana 연동

# prometheus.yml
scrape_configs:
  - job_name: 'hadoop-namenode'
    metrics_path: '/jmx'
    params:
      format: ['prometheus']
    static_configs:
      - targets: ['namenode:9870']

  - job_name: 'hadoop-datanode'
    metrics_path: '/jmx'
    params:
      format: ['prometheus']
    static_configs:
      - targets: ['datanode1:9864', 'datanode2:9864', 'datanode3:9864']

  - job_name: 'hadoop-resourcemanager'
    metrics_path: '/jmx'
    params:
      format: ['prometheus']
    static_configs:
      - targets: ['resourcemanager:8088']

10. 트러블슈팅

NameNode 장애

# NameNode가 Safe Mode에서 빠져나오지 않을 때
hdfs dfsadmin -safemode get
hdfs dfsadmin -safemode leave

# NameNode EditLog 손상 시
hdfs namenode -recover

# NameNode OOM
# → hadoop-env.sh에서 힙 사이즈 증가
# export HDFS_NAMENODE_OPTS="-Xmx32g -Xms32g"

DataNode 장애

# DataNode 상태 확인
hdfs dfsadmin -report | grep -A5 "Dead datanodes"

# 특정 DataNode 블록 검증
hdfs fsck / -files -blocks -locations | grep "datanode1"

# DataNode 디스크 풀
# → dfs.datanode.data.dir에 새 디스크 추가
# → DataNode 재시작

YARN 애플리케이션 장애

# 실행 중인 애플리케이션 목록
yarn application -list

# 애플리케이션 로그 확인
yarn logs -applicationId application_1709875200_0001

# 애플리케이션 강제 종료
yarn application -kill application_1709875200_0001

# 컨테이너 로그 확인
yarn logs -applicationId application_1709875200_0001 -containerId container_1709875200_0001_01_000001

11. 운영 체크리스트

초기 구축 체크리스트

일상 운영 체크리스트

정기 점검 체크리스트

마무리

Hadoop은 빅데이터 생태계의 기반 인프라로서 여전히 중요한 위치를 차지하고 있습니다. HDFS는 대규모 데이터 저장소로, YARN은 다양한 처리 프레임워크의 리소스 관리자로 계속 활용되고 있습니다.

핵심 정리:

  1. HDFS는 블록 단위 분산 저장 + 복제로 안정성 확보
  2. MapReduce는 단순하지만 디스크 I/O가 많아 Spark 대비 느림
  3. YARN은 다양한 프레임워크를 위한 범용 리소스 관리자
  4. 성능 튜닝은 블록 크기, 메모리, 스레드 수 조정이 핵심
  5. 모니터링은 NameNode 힙, Under-replicated 블록, YARN 큐에 집중

댓글

아직 댓글이 없습니다.

로그인하면 댓글을 쓸 수 있습니다