Apache Hadoop — HDFS 와 YARN 을 한 파드에 세우고 운영한다 · 블록과 복제 · 이론
파일은 블록으로 쪼개져 DataNode 디스크에 놓인다
한 줄 요약
HDFS 파일은 같은 크기의 블록으로 잘려 DataNode 디스크에 평범한 파일로 놓이고, 블록마다 복제 계수만큼 사본이 생긴다. 블록 크기는 파일마다 정할 수 있지만 한 번 정하면 블록 수, NameNode 부담, 심지어 파일 체크섬 값까지 따라 바뀐다.
왜 블록이 이렇게 큰가
로컬 파일 시스템의 블록은 보통 몇 KB 다. HDFS 의 블록은 [hdfs-default.xml](https://hadoop.apache.org/docs/r3.5.0/hadoop-project-dist/hadoop-hdfs/hdfs-default.xml) 의 dfs.blocksize 로 기본 134217728 바이트, 곧 128MB 다. 수만 배 차이가 나는 데는 이유가 있다.
첫째, HDFS 는 큰 파일을 처음부터 끝까지 읽는 일에 맞춰 설계됐다. [설계 문서](https://hadoop.apache.org/docs/r3.5.0/hadoop-project-dist/hadoop-hdfs/HdfsDesign.html)는 지연 시간보다 처리량을 중시한다고 적는다. 블록이 크면 한 번 자리를 잡은 뒤 오래 순차로 읽는다. 둘째, 블록 하나하나가 NameNode 메모리의 객체다. 1TB 파일을 4KB 블록으로 자르면 블록이 2억 개를 훌쩍 넘고, NameNode 는 그 전부의 위치를 메모리에 들고 있어야 한다. 셋째, 블록은 계산을 나누는 단위로도 쓰인다. 설계 문서의 표현대로 데이터를 옮기는 것보다 계산을 옮기는 편이 싸기 때문에, 맵리듀스 같은 엔진은 블록이 있는 곳에서 일을 돌린다.
그렇다고 무한정 작게 만들 수도 없다. dfs.namenode.fs-limits.min-block-size 는 기본 1048576 바이트(1MB)이고, 설명에는 실수로 아주 작은 블록 크기를 잡아 블록이 폭증하는 것을 막기 위한 하한이라고 적혀 있다.
어떻게 동작하나
블록 크기는 파일마다 다르다. 설계 문서는 블록 크기와 복제 계수를 파일 단위로 정할 수 있다고 적는다. 클러스터 기본값은 만들 때 따로 지정하지 않은 파일에 쓰일 뿐이다. 또 마지막 블록을 뺀 모든 블록은 같은 크기다. 그래서 12MiB 파일을 블록 4MiB 로 올리면 블록 3개, 기본 128MB 로 올리면 블록 1개가 된다. 1KB 짜리 파일도 블록 하나를 차지한다. 작은 파일 50개는 블록 50개다 — 블록은 파일끼리 나눠 쓰지 않는다.
# 이 파일만 블록 4MiB 로 올린다hdfs dfs -D dfs.blocksize=4194304 -put big.dat /data/big.dat# 블록 목록과 위치를 본다hdfs fsck /data/big.dat -files -blocks -locations[HDFS 명령 문서](https://hadoop.apache.org/docs/r3.5.0/hadoop-project-dist/hadoop-hdfs/HDFSCommands.html)의 fsck 는 -files -blocks -locations 로 파일마다 블록 ID 와 그 블록을 가진 DataNode 를 보여 준다. 여기서 블록 ID 를 얻으면 다음 단계로 내려갈 수 있다.
디스크 위의 블록. DataNode 가 블록을 두는 곳은 dfs.datanode.data.dir 이고 기본은 file://${hadoop.tmp.dir}/dfs/data 다(실습 이미지는 /var/lib/hadoop/data 로 옮겨 두었다). 그 아래를 따라가면 blk_ 로 시작하는 평범한 파일이 있고, 옆에 같은 이름에 .meta 가 붙은 짝 파일이 있다. 앞의 것이 블록의 바이트 그대로이고, 뒤의 것이 그 바이트의 체크섬이다. 설계 문서는 DataNode 가 한 디렉터리에 파일을 몰아 두지 않고 하위 디렉터리를 알아서 나눈다고 설명한다. 로컬 파일 시스템이 한 디렉터리의 수많은 파일을 잘 다루지 못하기 때문이다. 텍스트 파일을 올렸다면 blk_ 파일을 head 로 열어 원래 내용을 그대로 읽을 수 있다. HDFS 가 특별한 저장 형식을 쓰지 않는다는 뜻이다.
복제. 복제 계수의 기본은 dfs.replication 3 이다. 복제 계수가 3 일 때의 배치 정책을 설계 문서는 이렇게 설명한다. 하나는 쓰는 쪽의 기계(또는 같은 랙의 임의 노드)에, 하나는 다른 랙의 노드에, 마지막은 그 다른 랙의 또 다른 노드에 둔다. 랙 하나가 통째로 나가도 살아남고, 랙 사이 쓰기 트래픽은 줄인다.
중요한 제약이 하나 있다. NameNode 는 한 DataNode 에 같은 블록의 복제본을 둘 이상 두지 않으므로, 만들 수 있는 복제본의 최대 수는 그때의 DataNode 수다. DataNode 가 하나뿐인 실습 환경에서 [파일 시스템 셸](https://hadoop.apache.org/docs/r3.5.0/hadoop-project-dist/hadoop-common/FileSystemShell.html)의 setrep 3 을 걸면 명령은 성공하지만 사본은 영영 하나다. fsck 는 이런 블록을 복제 부족(under-replicated)으로 보고한다. 명령이 성공했다고 복제가 된 것은 아니다.
체크섬이 블록 크기에 묶이는 이유
HDFS 는 바이트를 작은 조각마다 검사한다. dfs.bytes-per-checksum 은 512, dfs.checksum.type 은 CRC32C 가 기본이다. 512바이트마다 CRC 하나를 계산해 .meta 파일에 둔다.
문제는 hadoop fs -checksum 이 돌려주는 파일 수준 체크섬이다. 기본 조합 방식 dfs.checksum.combine.mode 는 MD5MD5CRC 다. 이름 그대로 512바이트마다의 CRC 들을 블록마다 MD5 로 묶고, 그 블록 MD5 들을 다시 MD5 로 묶는다. 블록 경계가 계산 과정에 들어가 있으므로 내용이 같아도 블록 크기가 다르면 값이 달라진다. hdfs-default.xml 의 설명도 원래 방식은 블록 배치가 다른 파일끼리 견줄 수 없고, COMPOSITE_CRC 같은 방식은 블록 배치와 상관없이 견줄 수 있다고 적는다.
hadoop fs -checksum /a/4m.dat /a/128m.dat # 값이 다르다hadoop fs -D dfs.checksum.combine.mode=COMPOSITE_CRC \ -checksum /a/4m.dat /a/128m.dat # 값이 같다이것이 현장에서 사고가 되는 곳이 복사 검증이다. [DistCp 문서](https://hadoop.apache.org/docs/r3.5.0/hadoop-distcp/DistCp.html)는 -update 가 원본과 대상의 크기, 블록 크기, 체크섬을 견준다고 적는다. 블록 크기를 보존하지 않고 옮긴 사본은 기본 방식에서 체크섬이 어긋나 보일 수 있다.
현장에서 만나는 모습
첫째, "복제 3 으로 바꿨는데 fsck 가 계속 경고한다." DataNode 가 셋보다 적거나, 랙 배치를 만족할 곳이 없을 때 생긴다. setrep 은 목표를 바꿀 뿐이고, 실제 사본을 만드는 것은 NameNode 가 DataNode 에게 시키는 뒤쪽 일이다.
둘째, 블록 크기를 줄이면 NameNode 가 먼저 아프다. 블록 수는 파일 크기를 블록 크기로 나눈 만큼이라 블록 크기를 반으로 줄이면 블록 객체가 두 배가 된다. 작은 파일이 많은 것과 같은 방향의 부담이다.
셋째, 복사한 파일의 체크섬이 다르다고 데이터가 깨진 것은 아니다. 블록 크기가 달라서일 수 있다. 조합 방식을 COMPOSITE_CRC 로 바꿔 다시 견주면 가릴 수 있다.
넷째, DataNode 디스크의 blk_ 파일을 손으로 건드리지 않는다. 그 파일은 NameNode 의 블록 지도와 짝을 이룬다. 옮기거나 지우면 NameNode 는 블록 리포트로 뒤늦게 알게 되고 그 사이 읽기가 실패한다.
실무에서 진짜 중요한 것
- 블록 크기와 복제 계수는 파일마다의 속성이다. 클러스터 기본값은 지정하지 않은 파일에만 쓰인다.
- 블록 수가 NameNode 부담이다. 작은 파일도, 작은 블록도 같은 방향으로 객체를 늘린다.
- 복제본 수의 상한은 DataNode 수다.
setrep이 성공해도fsck로 실제 사본 수를 확인한다. - 블록은 DataNode 디스크의 평범한 파일이다.
blk_와.meta짝으로 놓인다. - 파일 체크섬의 기본값은 블록 배치에 묶여 있다. 블록 크기가 다른 사본끼리는 COMPOSITE_CRC 로 견준다.
다음 실습에서 할 것
12MiB 파일을 블록 4MiB 로 올려 블록 셋으로 쪼개지는 것을 fsck 로 보고, 그 블록 ID 를 따라 DataNode 의 로컬 디스크에서 실제 블록 파일을 찾아 연다. 같은 파일을 기본 블록 크기로 올리면 블록이 하나가 되는 것도 견준다. 복제 계수를 3 으로 올려 DataNode 하나뿐인 환경에서 복제 부족이 어떻게 보고되는지 보고, 작은 파일 여러 개가 그만큼의 블록이 되는 것을 센다. 마지막으로 블록 크기만 다른 두 파일의 체크섬이 기본 방식에서는 다르고 COMPOSITE_CRC 에서는 같아지는 것을 확인한다.