HDFS: The Hadoop Distributed File System
세션대비
- Haram Lee
- 2026-03-12
- projects / YBIGTA-DE / DE
1. Introduction
Hadoop은 아주 큰 데이터셋을 분산 저장하고 분산 처리하기 위한 생태계이고,
그중 HDFS는 분산 파일 시스템 역할을 한다. Hadoop의 중요한 특징은 데이터와 계산을 많은 노드에 분산하고, 가능하면 데이터가 있는 곳 가까이에서 계산하도록 만드는 것이다. 이렇게 하면 서버를 추가하는 것만으로 계산 능력, 저장 용량, I/O 대역폭을 함께 키울 수 있다.
Hadoop 구성요소
HDFS: 분산 파일 시스템
MapReduce: 분산 계산 프레임워크
HBase: 열 지향(column-oriented) 테이블 서비스
Pig: 데이터플로우 언어 + 병렬 실행 프레임워크
Hive: 데이터 웨어하우스 인프라
ZooKeeper: 분산 코디네이션 서비스
Chukwa: 관리 데이터 수집 시스템
Avro: 데이터 직렬화 시스템
HDFS 기본 아이디어
NameNode는 파일 시스템 메타데이터를 관리한다.
DataNode는 실제 애플리케이션 데이터(블록) 를 저장한다.
모든 서버는 TCP 기반 프로토콜로 통신한다.
HDFS는 RAID 같은 방식 대신 여러 DataNode에 블록을 복제해서 내구성을 확보한다.
복제는 단순히 안전성만 높이는 게 아니라,
데이터 내구성 보장
읽기 대역폭 증가
계산을 데이터 가까이에 둘 기회 증가
라는 장점도 있다.
2. Architecture
볼 포인트
각 컴포넌트에 대해
왜 존재하는지
뭐가 좋은지
뭐가 문제인지
어떻게 동작하는지
를 보면 된다.
A. NameNode
역할
HDFS 네임스페이스는 파일/디렉터리 계층 구조로 이루어진다.
파일과 디렉터리는 inode로 표현된다.
권한
수정 시간 / 접근 시간
namespace quota
disk space quota
같은 속성을 기록한다.
NameNode는
네임스페이스 트리
각 파일 블록이 어느 DataNode에 있는지에 대한 매핑
을 유지한다.
읽기
클라이언트는 먼저 NameNode에 파일을 구성하는 블록들과 그 복제본 위치를 요청한다.
그 다음 가장 가까운 DataNode로부터 블록을 직접 읽는다.
즉, NameNode는 위치를 알려주고, 실제 데이터 전송은 DataNode와 직접 한다.
쓰기
클라이언트는 NameNode에 새 블록의 복제본을 저장할 DataNode들을 골라 달라고 요청한다.
NameNode가 후보를 정하면, 클라이언트는 DataNode들 사이에 파이프라인을 구성해서 데이터를 보낸다.
예를 들어 3중 복제라면:클라이언트 → DN1 → DN2 → DN3
형태로 흘러가고,ACK는 반대로 올라온다.
메타데이터 저장
NameNode는 전체 네임스페이스를 RAM에 유지한다.
inode 정보 + 각 파일이 어떤 블록들로 구성되는지에 대한 목록 = image(네임시스템 메타데이터)
이 image의 디스크 상 영구 저장본을 checkpoint라고 한다.
image의 변경 로그는 journal이다.
NameNode는 재시작 시
checkpoint를 읽고
journal을 replay해서
최신 네임스페이스를 복원한다.
블록 복제본의 위치는 계속 바뀔 수 있기 때문에 checkpoint에 포함되지 않는다.
장점 / 한계
장점: 메타데이터를 메모리에 두므로 빠르다.
한계: 당시 설계에서는 클러스터당 NameNode가 하나라서, NameNode가 병목이자 SPOF가 된다. 이게 뒤의 future work로 이어진다.
B. DataNode
역할
각 블록 복제본은 DataNode의 로컬 파일 시스템에 두 개의 파일로 저장된다.
실제 데이터 파일
블록 메타데이터 파일
체크섬
generation stamp 포함
또한 블록 파일 크기는 실제 블록 길이만큼만 차지하고, 명목상 블록 크기만큼 패딩하지 않는다.
시작 과정
DataNode는 시작 시 NameNode와 handshake를 한다.
여기서 확인하는 것:
namespace ID
software version
둘 중 하나라도 NameNode와 다르면 DataNode는 자동 종료한다.
namespace ID는 파일 시스템 인스턴스를 포맷할 때 부여되고, 클러스터 전체 노드에 영구 저장된다.
namespace ID가 다른 노드는 그 클러스터에 합류할 수 없다.
→ 파일 시스템 무결성을 지키기 위함.
등록과 식별
handshake 후 DataNode는 NameNode에 등록한다.
각 DataNode는 storage ID라는 내부 식별자를 영구 저장한다.
이 값 덕분에 IP나 포트가 바뀌어 재시작해도 동일 노드로 인식된다.
block report / heartbeat
DataNode는 자신이 가진 블록 복제본 정보를 block report로 NameNode에 알린다.
block id
generation stamp
length
첫 block report는 등록 직후 즉시,
이후에는 매시간 전송된다.
또 평소에는 heartbeat를 보낸다.
기본 간격: 3초
NameNode가 10분 동안 heartbeat를 못 받으면, 그 DataNode를 out-of-service로 간주한다.
그러면 그 노드의 블록 복제본들을 unavailable로 보고, 다른 DataNode에 새 복제본 생성을 예약한다.
heartbeat에는 다음 정보도 담긴다.
총 저장 용량
사용 중인 비율
현재 진행 중인 데이터 전송 수
NameNode는 이를 공간 할당과 부하 분산에 활용한다.
NameNode와의 상호작용
NameNode는 DataNode를 직접 호출하지 않는다.
대신 heartbeat 응답에 명령을 실어 보낸다.
다른 노드로 블록 복제
로컬 복제본 삭제
재등록 또는 종료
즉시 block report 전송
C. HDFS Client
역할
HDFS client는 HDFS 파일 시스템 인터페이스를 제공하는 라이브러리다.
파일 읽기 / 쓰기 / 삭제
디렉터리 생성 / 삭제
를 지원한다.
사용자는 보통
메타데이터가 NameNode에 있고
실제 데이터가 DataNode들에 흩어져 있으며
블록이 여러 복제본을 가진다는 사실을
직접 신경 쓸 필요가 없다.
읽기
NameNode에 각 블록의 복제본을 가진 DataNode 목록을 요청
이후 DataNode와 직접 통신해 블록을 전송받음
쓰기
NameNode에 블록 복제본을 저장할 DataNode를 고르라고 요청
클라이언트가 노드 간 파이프라인을 구성해 데이터 전송
한 블록이 차면, 다음 블록에 대해 다시 DataNode 집합을 받아 새 파이프라인을 구성
추가 특징
HDFS는 블록 위치를 드러내는 API를 제공한다.
그래서 MapReduce는 데이터가 있는 위치에 태스크를 배치할 수 있다.
파일별로 replication factor를 설정할 수 있고, 기본값은 3이다.
자주 읽히거나 중요한 파일은 replication factor를 높이면
장애 허용성 증가
읽기 대역폭 증가
효과가 있다.
D. Image and Journal
Image: 디렉터리/파일 구조를 포함한 파일 시스템 메타데이터
Journal: 파일 시스템 변경 사항에 대한 write-ahead commit log
클라이언트가 만든 각 트랜잭션에 대해:
변경 내용을 먼저 journal에 기록
journal을 flush + sync
그 다음에야 클라이언트에게 commit 완료로 보임
checkpoint 파일은 NameNode가 계속 수정하는 파일이 아니다.
새 checkpoint가 만들어질 때 통째로 교체된다.
시작 시 NameNode는
checkpoint로 image 초기화
journal replay
최신 상태 복원
을 수행한다.
내구성
checkpoint나 journal이 누락/손상되면 namespace 정보가 일부 또는 전부 유실될 수 있다.
그래서 여러 storage directory에 저장할 수 있다.
권장:
서로 다른 볼륨에 저장
그중 하나는 원격 NFS 서버
journal 쓰기 실패가 나면 해당 storage directory는 제외된다.
사용 가능한 storage directory가 하나도 없으면 NameNode는 스스로 종료한다.
성능
NameNode는 멀티스레드로 여러 클라이언트 요청을 처리한다.
journal flush/sync는 병목이 될 수 있으므로,
여러 클라이언트의 트랜잭션을 batch로 묶어 한 번에 커밋한다.
E. CheckpointNode
NameNode는 주 역할 외에 CheckpointNode 또는 BackupNode 역할로 실행될 수 있다.
CheckpointNode는 기존 checkpoint와 journal을 합쳐
새로운 checkpoint
빈 journal
을 만든다.
동작
보통 NameNode와 다른 호스트에서 실행한다.
현재 checkpoint와 journal을 NameNode에서 내려받음
로컬에서 병합
새 checkpoint를 NameNode로 다시 업로드함
이 과정이 끝나면 NameNode는 journal 앞부분을 잘라낼 수 있다.
왜 필요한가?
journal이 너무 길어지면
손상될 확률 증가
NameNode 재시작 시간 증가
논문에서는 하루 한 번 checkpoint 생성을 좋은 관행으로 제시한다.
F. BackupNode
BackupNode는 CheckpointNode처럼 주기적으로 checkpoint를 만들 수 있다.
추가로, 항상 최신 상태로 동기화된 in-memory namespace image를 유지한다.
동작
활성 NameNode로부터 namespace transaction journal stream을 받는다.
이를 자기 storage에 저장하고,
메모리 속 namespace image에도 반영한다.
의미
NameNode가 실패해도 BackupNode 쪽에는
최신 namespace 상태를 반영한 메모리 이미지
체크포인트 디스크 사본
이 남아 있다.
주의
BackupNode = 바로 자동 failover 되는 active NameNode는 아님.
논문 기준으로는 read-only NameNode처럼 볼 수 있고,
block location 정보는 없다.즉, 완전한 HA master라고 이해하면 조금 과하다.
G. Upgrade and File System Snapshots
목적
업그레이드 중 소프트웨어 버그나 실수로 인한 데이터 손상을 줄이고, 필요하면 롤백하기 위해 snapshot을 둔다.
snapshot
논문에서는 한 번에 하나의 snapshot만 존재한다고 설명한다.
시스템 시작 시 관리자 선택으로 생성할 수 있다.
생성 과정
NameNode:
checkpoint와 journal을 읽어 메모리에서 병합
새 위치에 새로운 checkpoint와 빈 journal 기록
기존 것은 그대로 보존
DataNode:
저장 디렉터리 복사본 생성
기존 블록 파일에는 hard link
append 시에는 copy-on-write 사용
→ 저장 공간을 2배로 다 복제하지 않아도 됨
rollback
재시작 시 snapshot 상태로 롤백 가능
NameNode는 snapshot 시점 checkpoint를 복원
DataNode는 이전 디렉터리로 되돌리고
snapshot 이후 생긴 블록 복제본은 백그라운드에서 삭제한다.
layout version
checkpoint/journal 형식이나 block replica 표현 형식이 바뀔 수 있는데,
이를 구분하는 값이 layout version
NameNode와 DataNode storage directory에 영구 저장된다.
시작 시 현재 소프트웨어 버전과 저장된 버전을 비교하고, 필요하면 자동 변환한다.
3. File I/O operations and replica management
A. File Read and Write
기본 모델
single-writer, multiple-reader
한 파일은 한 번에 한 writer만 쓸 수 있다.
reader는 여러 개 가능하다.
lease
파일을 쓰는 클라이언트는 그 파일에 대한 lease를 받는다.
다른 클라이언트는 그 파일에 쓸 수 없다.
writer는 주기적으로 NameNode에 heartbeat를 보내 lease를 갱신한다.
파일을 닫으면 lease는 회수된다.
soft limit
writer가 배타적 접근을 보장받는 기간
soft limit가 지났는데도 파일을 닫지 않거나 lease를 갱신하지 않으면 다른 클라이언트가 lease를 선점 가능
hard limit
1시간
이 시간이 지나도 갱신이 없으면 HDFS는 writer가 종료된 것으로 보고 파일을 닫고 lease를 회수함
블록 쓰기
새 블록이 필요해지면 NameNode가
고유한 block ID를 할당하고
해당 블록 복제본을 둘 DataNode 목록을 정한다.
DataNode들은 클라이언트에서 마지막 DataNode까지의 총 네트워크 거리가 최소가 되도록 파이프라인 순서를 잡는다.
데이터 전송
애플리케이션이 쓴 바이트는 우선 클라이언트 쪽에서 버퍼링된다.
패킷 버퍼가 차면(보통 64KB) 파이프라인으로 밀어 넣는다.
이전 패킷 ACK를 다 받기 전에도 다음 패킷을 보낼 수 있다.
outstanding packet window 크기로 동시에 떠 있을 수 있는 패킷 수를 제한한다.
hflush
여기 네 정리에서 아주 중요한 포인트가 하나 있어.
기본적으로 HDFS는 파일이 닫히기 전까지 새 reader에게 데이터 가시성을 보장하지 않는다.
가시성 보장이 필요하면 hflush를 호출해야 한다.
그러면 현재 패킷이 즉시 파이프라인으로 전송되고, 모든 DataNode ACK를 받을 때까지 기다린다.
그 이전에 기록된 데이터는 reader에게 보이게 된다.
checksum
클라이언트가 각 블록에 대한 checksum sequence를 계산해서 데이터와 함께 DataNode로 보낸다.
DataNode는 checksum을 block data와 별도의 metadata file에 저장한다.
읽을 때는 블록 데이터와 checksum이 함께 클라이언트로 전달된다.
클라이언트는 checksum을 다시 계산해 비교한다.
안 맞으면 NameNode에 corrupt replica임을 알리고, 다른 DataNode에서 다른 복제본을 가져온다.
읽기
클라이언트가 파일을 열면 NameNode에서 블록 목록과 각 블록 복제본 위치를 받는다.
위치는 reader와의 거리 순서대로 정렬된다.
가장 가까운 복제본부터 읽기를 시도한다.
실패하면 다음 복제본을 시도한다.
실패 원인 예시:
DataNode가 unavailable
더 이상 그 블록을 갖고 있지 않음
checksum 검증 실패로 손상 판정
open for write 상태의 파일 읽기
HDFS는 현재 쓰는 중인 파일도 읽을 수 있게 허용한다.
단, 마지막 블록 길이는 NameNode가 정확히 모를 수 있으므로, 클라이언트가 복제본 중 하나에 최신 길이를 물어본다.
Scribe / HBase
Scribe: 실시간 로그/스트리밍 데이터를 HDFS로 보내는 류의 애플리케이션 예시
HBase: 큰 테이블에 대한 랜덤, 실시간 접근이 필요한 시스템 예시
논문은 HDFS가 원래는 batch sequential I/O에 최적화됐지만, 이런 요구를 위해 응답시간 개선도 시도해 왔다고 말한다.
B. Block placement
rack-aware 배치
대규모 클러스터는 보통 여러 rack으로 나뉜다.
같은 rack 내 노드들은 같은 스위치를 공유
rack 스위치들은 코어 스위치에 연결
다른 rack 간 통신은 더 많은 스위치를 거침
→ 보통 같은 rack 내 통신이 더 빠르다.
거리 개념
HDFS는 두 노드 간 네트워크 대역폭을 “거리”로 추정한다.
노드에서 부모까지 거리 = 1
두 노드 간 거리는 가장 가까운 공통 조상까지 거리의 합
거리가 짧을수록 더 높은 대역폭을 활용 가능하다고 본다.
rack 인식
관리자는 노드 주소를 넣으면 rack ID를 반환하는 스크립트를 설정할 수 있다.
NameNode가 DataNode 등록 시 이 스크립트를 이용해 rack 위치를 결정한다.
스크립트가 없으면 모든 노드를 하나의 기본 rack으로 본다.
기본 배치 정책
새 블록 생성 시:
첫 번째 복제본: writer가 있는 노드
두 번째, 세 번째 복제본: 다른 rack의 서로 다른 두 노드
그 이후 복제본: 제약을 만족하는 랜덤 노드 배치
제약:
한 DataNode에는 같은 블록 복제본을 두 개 이상 두지 않음
rack 수가 충분하면 같은 rack에 세 개 이상 두지 않도록 함
(보통 복제본 수가 rack 수의 두 배보다 작을 때 rack당 최대 2개)
왜 이렇게 두나?
inter-rack / inter-node write traffic 감소
write 성능 향상
rack failure 확률은 node failure보다 낮으므로 신뢰성도 크게 해치지 않음
다만 보통 3중 복제일 때는 블록이 2개 rack에만 놓여서, 읽기 aggregate bandwidth는 3 rack 분산보다 약간 덜할 수 있다.
C. Replication management
과복제
NameNode는 block report를 보고 어떤 블록이 over-replicated인지 감지한다.
제거할 복제본 선택 기준:
블록이 걸쳐 있는 rack 수를 줄이지 않는 방향 선호
남은 디스크 공간이 가장 적은 DataNode의 복제본 제거 선호
→ 저장 공간 균형도 고려한다.
복제 부족
under-replicated block은 replication priority queue에 들어간다.
복제본이 1개뿐인 블록이 최고 우선순위
현재 복제본 수가 목표의 2/3를 넘는 블록은 낮은 우선순위
백그라운드 스레드가 주기적으로 큐를 훑으면서 새 복제본 위치를 정한다.
추가 정책:
복제본이 1개뿐이면 다음 복제본은 다른 rack에 둠
복제본이 2개인데 둘 다 같은 rack에 있으면, 세 번째는 다른 rack
이미 서로 다른 rack에 있으면, 세 번째는 기존 복제본이 있는 rack의 다른 노드에 둘 수 있음
→ 새 복제 비용을 줄이기 위한 절충이다.
D. Balancer
기본 block placement는 DataNode의 디스크 사용률을 직접 보지 않는다.
그래서 데이터가 항상 균등하게 분산되는 것은 아니다.
새 노드가 추가될 때도 imbalance가 생길 수 있다.
Balancer
클러스터의 디스크 사용량을 균형 맞추는 도구
threshold 값을 입력받음
각 DataNode 사용률이 클러스터 평균 사용률과 threshold 이내면 balanced 상태로 본다.
동작
사용률이 높은 DataNode에서 낮은 DataNode로 복제본을 이동
이때
복제본 수 감소 X
rack 수 감소 X
를 보장함
inter-rack copying을 최소화하도록 최적화
rebalancing이 쓸 수 있는 bandwidth도 제한 가능
E. Block Scanner
각 DataNode는 block scanner를 실행한다.
주기적으로 자신이 가진 블록 복제본을 읽고, 저장된 checksum과 실제 데이터가 일치하는지 검사한다.
scan period 안에 검증을 마치도록 읽기 bandwidth를 조절한다.
손상 발견 시
client 또는 block scanner가 corrupt block을 발견하면 NameNode에 알림
NameNode는 그 복제본을 corrupt로 표시하지만 즉시 지우지 않는다.
먼저 정상 복제본을 추가로 만든다.
정상 복제본 수가 replication factor에 도달한 뒤 corrupt replica를 삭제한다.
→ 최대한 데이터를 살리기 위한 정책이다.
F. Decommissioning
클러스터 관리자는 include/exclude 목록으로 어떤 노드가 클러스터에 참여 가능한지 관리한다.
기존 노드가 exclude 목록에 들어가면 decommissioning 상태가 된다.
이 노드는
새 복제본 배치 대상에서는 제외되지만
기존 읽기 요청은 계속 처리한다.
NameNode는 이 노드의 블록들을 다른 DataNode들로 복제한다.
모든 블록이 안전하게 복제되면 decommissioned 상태가 되고, 안전하게 제거 가능하다.
G. Inter-Cluster Data Copy
HDFS는 클러스터 안팎으로 데이터를 크게 옮길 때 DistCp를 제공한다.
DistCp는 MapReduce job으로 구현되어 있다.
각 map task가 원본 데이터의 일부를 목적지 파일 시스템으로 복사한다.
병렬 태스크 스케줄링, 오류 탐지, 복구는 MapReduce가 처리한다.
4. Practice at Yahoo
이 부분은 “구현 원리”보다는 운영 경험과 규모 감각을 주는 섹션이다.
네가 중요도를 낮게 본 건 맞고, 다만 왜 replication / checkpoint / scanner / quota 같은 기능이 필요한지 감을 주는 부분이라 한 번쯤 보는 가치는 있다. Yahoo의 예시 클러스터는 수천 노드 규모, 각 노드에 여러 SATA 디스크와 16GB RAM, 1Gbps Ethernet을 사용했고, NameNode/BackupNode는 더 큰 메모리를 가진 별도 호스트에 두었다.
A. Durability of Data
3중 복제는 서로 독립적인 노드 장애에 대해서는 매우 강력하다.
논문은 대규모 클러스터에서 블록 손실 확률이 매우 낮다고 본다.
노드 장애는 흔하지만, re-replication이 병렬로 빨라서 수 분 안에 복구된다.
하지만 correlated failure는 위험하다.
rack/core switch 장애
전원 상실
대규모 재부팅 후 일부 노드가 복귀 실패
데이터 손상
같은 경우다. 이 때문에 block scanner가 중요하다. Yahoo에서는 대형 클러스터의 전체 블록을 약 2주에 한 번씩 검사한다고 했다.
B. Caring for the Commons
HDFS는 Unix 비슷한 permission 체계를 둔다.
차이:
일반 파일에 execute 권한 없음
sticky bit 없음
당시에는 사용자 인증이 약했고, 더 강한 인증 모델로 Kerberos 도입을 계획 중이라고 했다.
또,
저장 용량뿐 아니라
namespace 크기(파일/디렉터리 수) 도 RAM 자원이므로 중요하다.
그래서 디렉터리 subtree 단위로
공간 quota
파일/디렉터리 수 quota
를 둘 수 있다.
MapReduce는 reduce task마다 작은 파일 하나씩 만드는 경향이 있어 small files 문제를 만든다. 이를 완화하기 위해 HAR(Hadoop Archive) 를 쓸 수 있다.
tar/jar/zip 비슷하지만
내부 개별 파일 접근 가능
MapReduce input으로도 투명하게 사용 가능
C. Benchmarks
설계 목표는 큰 데이터셋에 대한 높은 I/O bandwidth 제공
DFSIO 벤치마크
Read: 66 MB/s per node
Write: 40 MB/s per node
Busy cluster 평균
Read: 1.02 MB/s per node
Write: 1.09 MB/s per node
5. Future Work
NameNode 문제
당시 HDFS는 NameNode가 내려가면 사실상 클러스터 전체가 unavailable
Hadoop이 batch 중심이라 재시작으로 버티는 전략이 어느 정도 가능했지만,
자동 failover 필요성이 분명했다.
failover 방향
BackupNode가 primary NameNode의 transaction을 받고 있으므로,
block report까지 둘 다 받게 만들면 warm/hot backup으로 failover하는 방향을 생각하고 있었다.
자동 failover는 ZooKeeper 기반으로 구축할 계획이라고 말한다.
scalability 문제
NameNode는 namespace와 block location을 메모리에 들고 있으므로,
heap 크기가 파일 수와 블록 수 확장의 한계가 된다.
특히 메모리 사용량이 최대치에 가까워지면 Java GC 때문에 NameNode가 멈칫하거나 재시작이 필요해질 수 있다.
해결 방향
여러 namespace / NameNode가 하나의 물리 저장소를 공유하게 만들기
이를 위해 block ID 앞에 block pool identifier를 붙이는 확장을 고려함
또, 더 큰 단일 클러스터 하나보다 여러 클러스터를 두는 것이 availability와 isolation 측면에서 낫다고 본다.