「하둡 완벽 가이드」1 : 하둡 기초
https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=103031150
무려 2017년 책이다. 원서는 2015년 책... 그러다보니 하둡 2.X 을 기준으로 책이 작성되어있다. 2.0 -> 3.0 의 변화가 엄청 큰 건 아니고, 애초에 이 책을 읽는 목적이 가볍게 아키텍처만 훑자는 것이었으니 나는 괜찮았다.
OOO 완벽 가이드 시리즈의 명성답게 자세하고, 디테일한 부분까지 설명해준다. 다만, 과거 내용이다보니 유심히 보진 않았다.
"Part 1, 하둡 기초" 에선 하둡의 기초적인 컴포넌트와 아키텍처에 대해 말해준다. 맵리듀스와 HDFS(하둡 분산파일 시스템), Yarn 을 주로 다루게 되고 Part2 에선 맵리듀스 프로그래밍에 대해 자세히 다룬다.
1.5.1 관계형 데이터베이스 관리 시스템
맵 리듀스는 비정형 분석과 같이 일괄 처리 방식으로 전체 데이터셋을 분석할 필요가 있는 문제에 적합하다. RDBMS 는 상대적으로 작은 양의 데이터를 낮은 지연 시간에 추출하고 변경하기 위해 데이터셋을 색인하기 때문에 특정 쿼리와 데이터 변경에 적합하다. 맵 리듀스는 데이터를 한 번 쓰고 여러 번 읽는 어플리케이션에 적합하지만, 관계형 데이터베이스는 지속적으로 변경되는 데이터셋에 적합하다고 할 수 있다.
1.5.2 그리드 컴퓨팅
데이터 지역성으로 알려진 이러한 특성이 바로 하둡에서 데이터 처리의 핵심이고
2.4.1 데이터 흐름
맵리듀스 잡(Job)은 클라이언트가 수행하는 작업의 기본 단위다. 이 잡은 입력 데이터, 맵리듀스 프로그램, 설정 정보로 구성된다. 하둡은 잡을 맵 태스크와 리듀스 태스크로 나누어 실행한다.
맵 태스크의 결과는 HDFS 가 아닌 로컬 디스크에 저장된다. 맵의 결과는 리듀스가 최종 결과를 생성하기 위한 중간 결과물이고, 잡이 완료된 후 맵의 결과는 그냥 버려지기 때문이다.
리듀스 태스크는 일반적으로 모든 매퍼의 출력 결과를 입력으로 받기 때문에 데이터 지역성의 장점이 없다.
정렬된 모든 맵의 결과는 네트워크를 통해 일단 리듀스 태스크가 실행 중인 노드로 전송되고, 맵의 모든 결과를 병합한 후 사용자 정의 리듀스 함수로 전달된다. 일반적으로 리듀스 결과는 안정성을 위해 HDFS 에 저장된다.
3.2.5 HDFS 고가용성
일상적인 유지 관리에서 재가동시간이 오래 걸리는 것은 당연히 문제가 된다 ... 이 문제를 해결하기 위해 하둡 2.x 릴리즈부터 HDFS 고가용성(HA)을 지원한다. 고가용성은 활성대기(active-standby) 상태로 설정된 한 쌍의 네임노드로 구현된다. 활성 네임노드에 장애가 발생하면 대기 네임노드가 그 역할을 이어받아 큰 중단 없이 클라이언트의 요청을 처리한다.
네임노드는 에디트 로그를 공유하기 위해 고가용성 공유 스토리지를 반드시 사용해야한다.
데이터 노트는 블록 리포트를 두 개의 네임노드에 보내야 한다. 블록 매핑 정보는 디스크가 아닌 네임노드의 메모리에 보관되기 때문이다.
대기 네임노드는 보조 네임노드의 역할을 포함하고 있으며, 활성 네임노드 네임스페이스의 체크포인트 작업을 주기적으로 수행한다.
고가용성 공유 스토리지를 위해 NFS 필러나 QJM 중 하나를 선택할 수 있다.(보통은 QJM을 사용한다)
QJM은 저널 노드 그룹에서 동작하며 각 에디트 로그는 전체 저널 로그에 동시에 쓰여진다. 일반적으로 저널 노드는 세 개이기 때문에 하나가 손상되어도 문제가 없다.
QJM은 한 번에 하나의 네임노드만 에디트 로그에 쓸 수 있도록 보장한다.
3.6.1 파일 읽기 상세
1) FileSystem 객체의 open() 메소드를 활용해서 원하는 파일을 연다.
2) 파일의 첫 번째 블록 위치를 파악하기 위해 RPC 를 사용해서 네임노드를 호출한다.
- 네임 노드는 블록별로 해당 블록의 복제본을 가진 데이터노드의 주소를 반환한다.
- FileSystem 은 클라이언트가 데이터를 읽을 수 있도록 DFSInputStream 을 반환한다.
3) 클라이언트는 스트림을 읽기 위해 read() 메소드를 호출한다.
- 파일의 첫번째 블록의 데이터 노드 주소를 저장하는 DFSInputStream 은 가장 가까운 첫 번째 데이터 노드와 연결한다.
4) 위를 반복해서 모든 데이터를 읽는다.
5) 블록의 끝에 도달하면 DFSInputStream 은 데이터 노드의 연결을 닫고 다음 블록의 데이터노드를 찾는다.
- 클라이언트는 스트림을 통해 블록을 순서대로 하나씩 읽는다.(연결도 계속 새로 맺음)
- 클라이언트는 다음 블록의 데이터 노드 위치를 얻기 위해 네임 노드를 호출한다.
6) 모든 블록에 대한 읽기가 끝나면 클라이언트는 close() 메서를 호출한다.
3.6.2 파일 쓰기 상세
1) FileSystem의 create() 를 호출해서 파일을 생성한다.
2) FileSystem 은 파일시스템의 네임스페이스에 새로운 파일을 생성하기 위해 네임노드에 RPC 요청을 보낸다.
- 블록에 대한 정보는 보내지 않는다.
- 네임 노드는 이미 그 파일이 존재하는지, 클라이언트가 권한은 있는지 등등 여러가지 유효성 체크를 한다.
- DFSOutputStream 을 반환한다.
3) DFSOutputStream 은 데이터를 패킷으로 분리하고 데이터 큐라 불리는 내부 큐로 패킷을 보낸다.
- DataStreamer 는 데이터 큐에 있는 패킷을 처리한다.
- 네임노드로부터 데이터 노드 목록을 받아오고, 그 데이터 노드 목록에 포함된 노드는 파이프라인을 형성한다.
4) DataStreamer 는 파이프라인의 첫번째 데이터노드로 패킷을 전송한다. 첫번째 데이터노드는 각 패킷을 저장하고 이를 두번째 데이터노드로 전송한다.
- 세번째도 마찬가지.
5) DFSOutputStream은 데이터노드의 승인 여부를 기다리는 ack 큐라 불리는 내부 패킷 큐를 유지한다.
- ack 큐에 있는 패킷은 파이프라인의 모든 데이터노드로부터 ack 응답을 받아야 제거된다.
6) 데이터 쓰기를 완료할 때 클라이언트는 스트림에 close() 메서드를 호출한다.
- 이 메서드는 데이터노드 파이프라인에 남아있는 모든 패킷을 플러시하고 승인이 나기를 기다린다.
- 모든 패킷이 완전히 전송되면 네임노드에 '파일 완료' 신호를 보낸다.
4.1 YARN 애플리케이션 수행 해부해보기
클러스터에서 유일한 리소스 매니저는 클러스터 전체 자원의 사용량을 관리하고, 모든 머신에서 실행되는 노드 매니저는 컨테이너를 구동하고 모니터링하는 역할을 맡는다.
4.3.4 지연 스케줄링
YARN의 모든 스케줄러는 지역성 요청을 가장 우선시한다.
어떤 어플리케이션이 노드를 요청했을 때 해당 노드에 다른 어플리케이션이 실행중일 확률이 높다. 지역성 수준을 낮출 수도 있지만, 대게의 경우 조금만 기다리면 리소스 여유가 생긴다. 또한 이렇게되면 클러스터의 효율성도 높아지게 된다. 이러한 기능을 지연 스케줄링이라고 부르며, 캐퍼시티 스케줄러와 페어 스케줄러는 모두 이러한 기능을 제공하고 있다.
YARN의 모든 노드 매니저는 주기적(기본은 1초)으로 리소스 매니저에 하트비트 요청을 보낸다. 각 하트비트는 애플리케이션이 실행할 컨테이너를 얻을 수 있는 중요한 스케줄링 기회가 된다.
4.3.5 우성 자원 공평성
각 사용자의 우세한 자원을 확인한 후 이를 클러스터 사용량의 측정 기준으로 삼는것이다.