책&스터디

[하둡 완벽 가이드] - PART1 04장 YARN

jyu_seo_ 2026. 2. 3. 21:46

아파치 YARN(Yet Another Resource Negotiator)은 하둡의 클러스터 자원 관리 시스템이다. YARN은 맵리듀스의 성능을 높이기 위해 하둡 2에서 처음 도입되었다.하지만 YARN은 맵리듀스 뿐만 아니라 다른 분산 컴퓨팅 도구도 지원한다.

 

YARN은 클러스터의 자원을 요청하고 사용하기 위한 API를 제공한다. 하지만 사용자 코드에서 직접 이러한 API를 사용할 수는 없다. 대신 사용자는 YARN이 내장된 분산 컴퓨팅 프레임워크에서 고수준 API를 작성해야 하며, 따라서 사용자는 자원 관리의 자세한 내용은 알 수 없다. 전체적인 구조는 [그림 4-1]에서 볼 수 있다. 맵리듀스, 스파크 등과 같은 분산 컴퓨팅 프레임워크는 클러스터 계산 계층(YARN)과 클러스터 저장 계층(HDFS와 HBase) 위에서 YARN 애플리케이션을 실행한다.

 

그림을 보면 프레임워크 기반의 애플리케이션 계층이 존재하는 것을 알 수 있다. 피그,하이브,크런치와 같은 애플리케이션은 맵리듀스, 스파크,테즈에서 구동되는 처리 프레임워크의 예이며, YARN에 직접 접근하지는 않는다.

 

이 장에서는 YARN의 특징을 전반적으로 살펴보겠다. 이는 4부에서 소개하는 다양한 하둡 분산 처리 프레임워크를 이해하는데 도움이 될것이다.

4-1 YARN 애플리케이션 수행 해부해보기

YARN은 리소스 매니저와 노드 매니저 등 두가지 유형의 장기 실행 데몬을 통해 핵심 서비스를 제공한다. 클러스터에서 유일한 리소스 매니저는 클러스터 전체 자원의 사용량을 관리하고,모든 머신에서 실행되는 노드매니저는 컨테이너를 구동하고 모니터링 하는 역할을 맡는다.

 

*장기실행 데몬(long-running daemon) : 서버에서 백그라운드로 계속 떠 있으면서 요청을 받아 처리하는 YARN의 상시 실행 프로세스들을 뜻한다.그럼 반대로 맵리듀스 작업 같은건 "필요할때 떴다가 끝나면 내려가는" 애플리케이션 프로세스다.

  • ResourceManager (RM)
    클러스터 전체 관점에서 자원(CPU/메모리) 스케줄링하고, 어떤 노드에 컨테이너를 줄지 결정하는 “중앙 관리자” 역할.
  • NodeManager (NM)
    각 워커 노드(머신)마다 하나씩 떠서 컨테이너 실행/모니터링, 로그/리소스 상태 보고 등을 하는 “노드 관리자” 역할.

자원(메모리, CPU등)의 사용 한도를 가진 특정 애플리케이션 프로세스는 컨테이너에서 실행된다. YARN의 설정방법(10.3.3절의 'YARN' 참조)에 따라 다르지만, 컨테이너는 Unix 프로세스 또는 리눅스 cgroup이 된다. [그림 4-2]는 YARN이 애플리케이션을 실행하는 방법을 보여준다.

 

클라이언트는 YARN에서 애플리케이션을 구동하기 위해 리소스 매니저에 접속하여 애플리케이션 마스터 프로세스의 구동을 요청한다 리소스 매니저는 컨테이너에서 애플리케이션 마스터를 시작할 수 있는 노드 매니저를 하나 찾는다(2a와 2b 단계). 애플리케이션 마스터가 딱 한번만 실행될지는 애플리케이션에 따라 다르다.애플리케이션 마스터가 단순한 계산을 단일 컨테이너에서 수행하고 그 결과를 클라이언트에 반환한 후 종료되거나, 리소스 매니저에 더 많은 컨테이너를 요청(3단계)한 후 분산 처리 수행(4a와 4b단계)하는 경우도 있다. 후자는 맵리듀스 YARN 애플리케이션이 수행하는 방법이다.

단계별 해석

클라이언트 → 리소스 매니저

“클라이언트는 YARN에서 애플리케이션을 구동하기 위해 리소스 매니저에 접속하여 애플리케이션 마스터 프로세스의 구동을 요청한다”

 

뭔뜻이냐면 spark-submit이나 hadoop jar를 치면 RM한테 말하는거다 " 이 애플리케이션 하나 실행하고 싶은데, 전담 관리자 하나 만들어줘"

이 전담 관리자가 바로 ApplicationMaster(AM) 위에 책에 나온 애플리케이션 마스터이다.

 

RM → NodeManager 찾기 (2a, 2b)

“리소스 매니저는 컨테이너에서 애플리케이션 마스터를 시작할 수 있는 노드 매니저를 하나 찾는다”

 

RM이 클러스터를 훓어보고 "어디서버가 지금 한가하냐?" 그리고 서버 하나 고른다음에

2a = RM -> 해당서버의 NM에게 "야 여기 컨테이너 하나 만들어라"

2b = NM -> 그 컨테이너 안에서: ApplicationMaster 실행

 

여기까지 구조를 살펴보면 클라이언트 -> ResourceManager -> NodeManager -> [컨테이너] -> ApplicationMaster 실행

 

자 여기서부터는 AM이 주인공이다.

AM의 선택

“애플리케이션 마스터가 딱 한번만 실행될지는 애플리케이션에 따라 다르다”

 

AM이 할 수 있는 일은 두 가지다.

1. 혼자 끝내버리는 앱

"단일 컨테이너에서 수행하고 결과 반환후 종료"

ex :) 간단한 계산, 작은 Spark job, 테스트용 작업

 

2. 분산 처리(MapReduce, Spark, Flink 등)

"리소스 매니저에 더 많은 컨테이너를 요청(3단계)" 

 

AM이 RM에게 말한다.

“나 일꾼 10명 더 필요해. CPU랑 메모리 좀 줘”

실제 분산 작업 시작

“분산 처리 수행(4a와 4b단계)”

4a

RM이 여러 서버의 NM들에게

“여기 컨테이너 만들어라”

4b

각 NM이

컨테이너 안에서 작업(Task) 실행

전체 흐름 한 장으로 정리

1. 클라이언트
   → RM: "앱 실행해줘"
2. RM
   → NM: "AM 실행할 컨테이너 만들어"
3. NM
   → 컨테이너에서 AM 실행
4. AM
   → RM: "일꾼 컨테이너 더 줘"
5. RM
   → 여러 NM에 컨테이너 할당
6. NM
   → 실제 작업 실행 (Map, Reduce, Spark Task 등)
7. AM
   → 결과 정리 → 클라이언트 전달 → 종료

 

이건 책에 나온내용은 아니지만 전체의 흐름을 이해하기위해서 한번 예시로 적어봤다.


YARN자체는 클라이언트,마스터,프로세스와 같은 애플리케이션이 서로 통신하는 기능은 제공하지 않는다. 대부분의 주요 YARN 애플리케이션은 하둡의 RPC와 같은 원격 호출 방식을 이용하여 상태 변경을 전달하고 클라이언트로부터 결과를 받는데, 구체적인 방법은 애플리케이션에 따라 다르다.

 

4-2 자원 요청

YARN은 유연한 자원 요청 모델을 갖고 있다. 다수의 컨테이너를 요청할때는 각 컨테이너에 필요한 컴퓨터 자원(메모리,CPU)의 용량뿐만 아니라 해당 요청에 대한 컨테이너의 지역성 제약도 표현할 수 있다.

지역성의 3단계

YARN은 요청할 때 우선순위 지역을 정할 수 있다

Node-local 데이터 있는 같은 서버
Rack-local 같은 랙(서버 묶음)
Off-rack / Any 클러스터 아무 서버

분산 데이터 처리 알고리즘에서 클러스터의 네트워크 대역폭을 효율적으로 활용하기 위해서는 지역성을 보장하는 것이 가장 중요하다. 따라서 YARN은 특정 애플리케이션이 호출한 컨테이너에 대해 지역성 제약을 규정하는 것을 허용한다.지역성 제약은 특정 노드나 랙 또는 클러스터의 다른 곳(외부 랙)에서 컨테이너를 요청할 때 사용된다.

 

가끔은 지역성 제약이 불가능할때가 있는데, 이때는 할당이 실패하거나 또는 선택적으로 제약을 조금 느슨하게 적용할 수 있다. 예를들어 특정 노드를 요청했는데 그 노드에서 컨테이너를 시작할 수 없으면(현재 다른 컨테이너가 실행되고 있기 때문에) YARN은 동일한 랙의 다른 노드에서 컨테이너를 시작하려 시도한다. 또 다시 실패하면 클러스터의 임의 노드에서 다시 시도할 것이다.

 

맵리듀스의 맵 태스크를 실행하기 위해 HDFS 블록에 접근할 컨테이너를 찾는 사례를 한번 살펴보자. 애플리케이션은 먼저 복제본을 저장하고 있는 세 개의 노드 중 하나에 컨테이너를 요청한다. 아니면 복제본을 포함한 랙의 다른 노드에 요청하거나 그래도 실패하면 클러스터 전체 노드에서 찾을 것이다.

 

YARN 애플리케이션은 실행 중에는 아무 때나 자원 요청을 할 수 있다. 예를 들어 애플리케이션은 처음에 모든 요청을 하거나 유동적인 접근이 필요한 경우에는 애플리케이션의 요구에 따라 동적으로 자원을 추가로 요청할 수 있다.

 

전자의 방식을 따르는 스파크는 클러스터에서 고정 개수의 수행자(executor)를 시작한다. 이와 달리 맵리듀스는 두단계로 되어있다. 처음에 필요한 맵 태스크 컨테이너를 요청한다. 하지만 리듀스 태스크 컨테이너는 맵 태스크가 어느 정도 실행된 후에야 시작될 수 있다. 또한 특정 태스크가 실패하면 실패한 태스크를 다시 실행하기 위해 컨테이너를 추가로 요청한다.

위에 글은 "YARN에서 자원을 어떻게, 어디에, 언제 요청하느냐”를 설명하는 핵심 파트 부분이다.

4-2-1 애플리케이션의 수명

YARN 애플리케이션의 수명은 몇초 만에 끝나는 짧은 수명의 애플리케이션부터 며칠 또는 몇달이 걸리는 긴 수명의 애플리케이션까지 그 차이가 매우 클 수 있다. 실행 시간보다는 사용자가 실행하는 잡의 방식에 따라 애플리케이션을 분류하는 것이 더 좋다. 가장 단순한 첫 번째 유형은 사용자의 잡 당 하나의 애플리케이션이 실행되는 방식으로, 맵리듀스 잡이 여기에 속한다.

 

두번째 유형은 워크플로나 사용자의 잡 세션(잡은 서로 관련이 없을 수도 있다)당 하나의 애플리케이션이 실행되는 방식이다. 이 유형은 첫 번째 유형보다 훨씬 더 효율적이다. 순차적으로 실행되는 잡이 동일한 컨테이너를 재사용할 수 있기 때문이다. 또한 잡 사이에 공유 데이터를 캐싱할 수 있는 큰 장점도 있다. 두번째 유형의 대표적인 사례는 스파크다.

 

세번째 유형은 서로 다른 사용자들이 공유할 수 있는 장기 실행 애플리케이션이다. 이러한 유형의 애플리케이션은 일종의 코디네이션 역할을 수행하기도 한다. 예를 들어 아파치 슬라이더는 클러스터에서 다양한 애플리케이션을 구동시키는 장기 실행 애플리케이션 마스터를 가지고 있다.임팔라(Impala)가 바로 이러한 방식을 사용한다. 임팔라는 여러 임팔라 데몬이 클러스터 자원을 요청할 수 있도록 프록시 애플리케이션을 제공하고 있다. 항상 켜져 있는 애플리케이션 마스터는 사용자가 뭔가를 요청하면 매우 빠른 시간 내에 응답할 수 있는데, 그 이유는 새로운 애플리케이션 마스터를 구동할 때 필요한 오버헤드를 피할 수 있기 때문이다.

4-2-2 YARN 애플리케이션 만들기

아무런 준비 없이 YARN 애플리케이션을 작성하는 것은 매우 어렵기 때문에 가능하면 기존 애플리케이션을 활용하는 편이 더 좋다.

예를 들어 잡의 방향성 비순환 그래프(directed acyclic graph) DAG 를 실행하고 싶으면 스파크나 테즈가 더 적합하고 스트리밍 처리는 스파크,쌈자(samza) 또는 스톰(storm)을 사용하는 것이 좋다.

 

YARN 애플리케이션을 쉽게 만들수 있도록 도와주는 프로젝트가 있다. 앞서 언급한 아파치 슬라이더는 기존의 분산 애플리케이션을 YARN 위에서 실행하도록 해준다. 사용자는 HBase와 같은 자신의 애플리케이션 인스턴스를 다른 사용자와 상관없이 클러스터에서 실행할 수 있다.이렇게 되면 여러 사용자가 동일한 애플리케이션의 서로 다른 버전을 실행할 수도 있다. 슬라이더는 애플리케이션이 실행되는 노드의 수를 변경하거나 실행 애플리케이션을 중지하고 다시 시작하게 제어할 수 있다.

 

아파치 트월(apache Twill)은 슬라이더와 비슷하지만 YARN에서 실행되는 분산 애플리케이션을 개발할 수 있는 간단한 프로그래밍 모델을 추가로 제공하고 있다. 트윌은 자바 Runnable 객체를 확장한 클러스터 프로세스를 정의한 후 클러스터의 YARN 컨테이너에서 이를 실행하는 기능을 제공한다. 트윌은 또한 실시간 로깅(runnables의 로그 이벤트를 클라이언트에 스트리밍으로 돌려줌)과 명령 메세지(클라이언트에서 runnables로 전송)기능 등을 제공한다.

 

복잡한 스케줄링 요구사항이 있는 애플리케이션은 앞서 설명한 도구가 충분하지 않을 수 있다. 이러한 경우에는 YARN 프로젝트의 일부로 제공되는 분산 쉘(distributed shell) 을 사용하면 YARN 애플리케이션을 작성하는 방법에 대한 좋은 예제를 얻을 수 있다. 분산 쉘은 클라이언트 또는 애플리케이션 마스터가 YARN 데몬과 통신하기 위해 YARN의 클라이언트 API를 어떻게 사용하는지 잘 보여주고 있다.

 

4-2-3 YARN과 맵리듀스 1의 차이점

하둡 구버전(하둡 1과 이전 버전)의 맵리듀스 분산 구현은 '맵리듀스 1'로, YARN(하둡 2와 이후 버전)을 이용한 구현은 '맵리듀스 2'로 구분해서 언급한다. 맵리듀스 1에는 잡의 실행 과정을 제어하는 하나의 잡트래커(jobtracker)와 하나 이상의 태스크트래커(tasktracker)등 두 종류의 데몬이 있다. 잡트래커는 여러 태스크트래커에서 실행되는 태스크를 스케줄링 함으로써 시스템에서 실행되는 모든 잡을 조율한다. 태스크트래커는 태스크를 실행하고 진행 상황을 잡트래커에 전송하기 때문에 잡트래커는 각 잡의 전체적인 진행상황을 파악할 수 있다. 태스크가 실패하면 잡트래커는 다른 태스크트래커에 그 태스크를 다시 스케줄링 할 수 있다.

 

맵리듀스 1에서 잡트래커는 잡 스케줄링(태스크와 태스크트래커를 연결)과 태스크 진행 모니터링(태스크를 추적하고, 실패하거나 느린 태스크를 다시 시작하고, 전체 카운터를 유지하는 방법으로 태스크 장부를 기록한다)을 맡고있다. 반면 YARN은 이러한 역할을 분리된 객체인 리소스 매니저와 애플리케이션 마스터(맵리듀스 잡당 하나)를 통해 처리한다. 또한 잡트래커는 완료된 잡에 대한 잡 이력을 저장하는 역할도 맡고 있는데 이 기능은 잡트래커의 부하를 줄이기 위해 별도의 데몬인 히스토리 서버를 통해 수행될 수 도 있다. YARN에서 이와 동일한 역할은 애플리케이션의 이력을 저장하는 타임라인 서버가 맡고 있다.

태스크트래커는 YARN의 노드 매니저와 같다. 둘의 관계를 요약해두었다.

MapReduce 1 YARN
잡트래커 리소스 매니저, 애플리케이션 마스터,타임라인 서버
태스크트래커 노드 매니저
슬롯 컨테이너

 

YARN은 맵리듀스 1의 여러 한계를 극복하기 위해 설계되었다. YARN을 사용하여 얻을수 있는 이익은 다음과 같다.

  • 확장성
    • YARN은 맵리듀스 1보다 큰 클러스터에서 실행될 수 있다. 맵리듀스 1은 4,000 노드나 40,000 태스크를 넘어서면 병목현상이 발생한다. 잡트래커가 잡과 태스크를 모두 관리하기 때문이다. YARN은 리소스 매니저와 애플리케이션 마스터를 분리하는 구조이므로 이러한 한계를 극복할 수 있다. YARN은 10,000 노드와 100,000 태스크까지 확장할 수 있도록 설계되었다.
    • 잡 트래커와 달리 애플리케이션(여기서는 맵리듀스 잡)의 각 인스턴스는 별도의 전용 애플리케이션 마스터를 가진다. 애플리케이션 마스터는 해당 애플리케이션이 실행될 때만 존재한다. 사실 이 모델은 구글의 맵리듀스 논문과 더 유사한데, 이 논문에서 마스터는 워커 집합에서 실행되는 맵과 리듀스 태스크를 코디네이션(조정)할때 시작된다고 설명하고 있다.
  • 가용성
    • 고가용성(high availability: HA)은 서비스 데몬에 문제가 발생했을 때 서비스에 필요한 작업을 다른 데몬이 이어 받을수 잇도록 상태 정보를 항상 복사해두는 방법으로 구현된다. 하지만 잡트래커의 메모리에 있는 복잡한 상태정보가 매우 빠르게 변경되는 상황에서 잡트래커 서비스에 HA를 적용하는 것은 매우 어려운 일이다. 각 태스크의 상태는 수 초마다 변경되기 때문이다. 
    • 잡트래커의 역할이 YARN에서는 리소스 매니저와 애플리케이션 마스터로 분리되었기 때문에 HA 서비스가 '분할 후 정복' 문제로 바뀌었다. 먼저 리소스 매니저의 HA를 제공한 후 YARN 애플리케이션 (각 애플리케이션 기준)을 지원하면 된다. 실제로 하둡 2는 리소스 매니저와 맵리듀스 잡을 위한 애플리케이션 마스터 모두 HA를 제공한다.
  • 효율성
    • 맵리듀스 1에서 각 태스크트래커는 맵 슬롯과 리듀스 슬롯으로 구분된 고정 크기 '슬롯'의 정적 할당 설정을 가지고 있다. 맵 슬롯은 맵 태스크 실행에만 사용할 수 있고 리듀스 슬롯은 리듀스 태스크에만 사용할 수 있다.

      YARN에서 노드 매니저는 정해진 개수의 슬롯 대신 일종의 리소스 풀을 관리한다. YARN에서 실행되는 맵리듀스는 클러스터의 맵 슬롯은 남아 있지만 리듀스 슬롯이 없어서 리듀스 태스크가 마냥 대기하고 있는 상황(맵리듀스 1에서 발생할 수 있는)은 절대 발생하지 않는다. 태스크를 실행할 수 있는 자원이 있으면 애플리케이션은 그 자원을 받을 자격이 있다.

      게다가 YARN의 자원은 잘게 쪼개져 있기 때문에 애플리케이션은 필요한 만큼만 자원을 요청할 수 있다. 기존에는 개별 슬롯을 사용했기 때문에 특정 태스크를 위해 너무 많거나(자원의 낭비) 너무 적게(실패의 원인)자원을 할당했다
  • 멀티테넌시(다중 사용자)
    • 특정 측면에서 YARN의 가장 큰 장점은 하둡이 맵리듀스를 뛰어넘어 다양한 분산 애플리케이션을 수용할 수 있다는 것이다. 맵리듀스는 YARN의 애플리케이션 중 하나일 뿐이다.

      뿐만 아니라 사용자는 서로 다른 버전의 맵리듀스를 동일한 YARN 클러스터에서 수행하는 것도 가능하다.이는 맵리듀스 업그레이드 과정을 관리하기 쉽게 만든다.그러나 잡 히스토리 서버나 셔플 핸들러 같은 일부 맵리듀스나 YARN 자신은 업그레이드를 위해 별도의 이중화된 클러스터가 여전히 필요하다.

하둡 2가 널리 사용되고 있고 가장 최근의 안정화된 버전이기 때문에 이 장 이후의 맵리듀스는 별도의 언급이 없는 한 맵리듀스2를 지칭하는것으로 하겠다.

4-3 YARN 스케줄링

이상 세계에서는 YARN 애플리케이션의 요청이 즉시 처리될 것이다. 그러나 현실 세계에서는 자원이 제한되어 있고 클러스터는 매우 바쁘고 어떤 애플리케이션은 요청이 처리될 때까지 기다려야 한다.YARN 스케줄러의 역할은 정해진 정책에 따라 애플리케이션에 자원을 할당하는 것이다. 일반적으로 스케줄링은 난해한 문제고 유일한 '최선'의 정책은 있을 수 없다. 이러한 이유로 YARN은 스케줄러와 설정 정책을 사용자가 직접 선택하도록 기능을 제공하고 있다. 이러한 내용을 좀 더 자세히 살펴보도록 하자.

4-3-1 스케줄러 옵션

YARN은 FIFO, 캐퍼시티(Capacity 가용량),페어(Fair 균등) 스케줄러를 제공한다. FIFO 스케줄러는 애플리케이션을 큐에 하나씩 넣고 제출된 순서에 따라 순차적으로 실행한다.(first in first out 방식) 즉, 큐에 처음으로 들어온 애플리케이션 요청을 먼저 할당하고, 이 요청을 처리 한 후 큐에 있는 다음 애플리케이션 요청을 처리하는 방식으로 순차적으로 실행한다.

 

FIFO 스케줄러는 이해하기 쉽고 설정이 필요 없다는 장점이 있지만 공유 클러스터 환경에서는 적합하지 않다. 대형 애플리케이션이 수행될 때는 클러스터의 모든 자원을 점유해 버릴수 있기 때문에 다른 애플리케이션은 자기 차례가 올 때까지 계속 대기해야 한다.

공유 클러스터 환경에서는 캐퍼시티 스케줄러나 페어 스케줄러를 사용하는 것이 더 좋다.이 두 스케줄러는 장시간 수행되는 잡을 계속 처리하는 동시에 작은 비정형 질의도 중간에 실행하여 적당한 시간 내에 사용자가 결과를 얻을 수 있도록 허용한다.

 

그림 4-3은 스케줄러의 차이점을 도식화한 것이다.

(i) 번 그림의 FIFO 스케줄러를 보면 대형 잡이 완료될 때까지 작은 잡은 계속 대기해야 한다.

(ii) 번 그림의 캐퍼시티 스케줄러는 작은 잡을 제출되는 즉시 분리된 전용 큐에서 처리해준다. 물론 해당 큐는 잡을 위한 자원을 미리 예약해두기 때문에 전체 클러스터의 효율성은 떨어진다. 또한 대형 잡은 FIFO 스케줄러보다 늦게 끝나게 된다.

(iii) 번 그림의 페어 스케줄러는 실행중인 모든 잡의 자원을 동적으로 분배하기 때문에 미리 자원의 가용량을 예약할 필요가 없다.

대형 잡이 먼저 시작되면 이때는 실행중인 잡이 하나밖에 없기 때문에 클러스터의 모든 자원을 얻을 수 있다. 대형 잡이 실행되는 도중에 작은 잡이 추가로 시작되면 페어 스케줄러는 클러스터 자원의 절반을 이 잡에 할당한다. 따라서 각잡은 클러스터의 자원을 공평하게 사용할 수 있게 된다.

 

두번째 잡이 시작된 후 공평하게 자원을 받을 때까지 약간의 시간차가 있을수 있다는 점을 유의해야 한다.첫번째 잡이 사용하고 있는 컨테이너의 자원이 완전히 해제될때 까지 기다려야 하기 때문이다. 작은 잡이 완료된 후에는 자원 요청이 더이상 없기 때문에 대형 잡은 클러스터의 전체 가용량을 다시 확보할 수 있게 된다. 전체적으로 보면 클러스터의 효율성도 높고 작은 잡도 빨리 처리되는 효과가 있다.

 

그림 4-3은 세 가지 스케줄러의 기본 동작 방식을 비교해서 보여준다 다음 두 절에서는 캐퍼시티 스케줄러와 페어 스케줄러의 고급 설정 옵션을 다룬다.

4-3-2 캐퍼시티 스케줄러 설정

캐퍼시티 스케줄러를 이용하면 회사의 조직 체계에 맞게 하둡 클러스터를 공유할 수 있다. 각 조직은 전체 클러스터의 지정된 가용량을 미리 할당받는다. 각 조직은 분리된 전용 큐를 가지며 클러스터 가용량의 지정된 부분을 사용하도록 설정할 수 있다. 큐는 1단계 이상의 계층 구조로 분리될 수 있으므로 각 조직은 조직에 속한 서로 다른 사용자 그룹 사이에도 클러스터의 가용량을 공유하도록 할 수 있다. 단일 큐 내부에 있는 애플리케이션들은 FIFO 방식으로 스케줄링 된다.

 

[그림 4-3]에서 보았듯이, 하나의 단일 잡은 해당 큐의 가용량을 넘는 자원은 사용할 수 없다.

그러나 큐 안에 다수의 잡이 존재하고 현재 가용할 수 있는 자원이 클러스터에 남아 있다면 캐퍼시티 스케줄러는 해당 큐에 있는 잡을 위해 여분의 자원을 할당할 수 있다. 물론 이렇게 하면 큐의 가용량을 초과하게 된다. 이러한 방식을 큐 탄력성(queue elasticity)이라고 한다.

 

일반적인 운용에서 캐퍼시티 스케줄러는 컨테이너를 선점하기 위해 강제로 죽이는 방법을 사용하지는 않는다. 그러므로 요청한 가용량에 미달한 큐가 있으면 필요한 요청은 늘어난다. 이때 다른 큐의 컨테이너가 완료되어 자원이 해제된 경우에만 해당 큐에 가용량을 돌려준다.다른 큐의 가용량을 너무 많이 잡아먹지 않도록 큐에 최대 가용량을 설정하는 방법으로 이러한 문제를 해결할 수 있다. 이것이 바로 큐 탄력성의 단점이다. 물론 시행착오를 통해 적절한 조정을 할 수 있다.

 

캐퍼시티 스케줄러란?

여러팀(또는 서비스)이 하나의 YARN 클러스터를 "정해진 비율만큼 공정하게" 나눠 쓰도록 해주는 자원 배분 관리자이다.

왜 필요하냐! ex :) 회사에 YARN 클러스터가 하나 있는데 -데이터팀, -AI팀, -로그팀 이 다같이 쓰면 어느 팀이 대형 Spark 잡을 돌리면 CPU, 메모리 다 먹어버려서 다른 팀 작업이 멈춤 그래서 나온 게 “각 팀한테 최소한 이만큼은 보장해주자”

 

4-3-3 큐 배치

애플리케이션을 큐에 배치하는 방법은 애플리케이션의 종류에 따라 달라진다. 예를 들어 맵리듀스 mapreduce.job.queuename 속성에 원하는 큐의 이름을 지정할 수 있다. 지정한 이름의 큐가 없다면 제출 시점에 에러가 발생한다. 큐를 지정하지 않으면 애플리케이션은 기본 큐인 default에 배치된다.

4-3-4 페어 스케줄러 설정

페어 스케줄러는 실행 중인 모든 애플리케이션에 동일하게 자원을 할당한다.[그림 4-3]은 동일한 큐에 있는 애플리케이션에 공평하게 자원을 할당하는 방법을 보여주고 있다. 하지만 균등공유는 큐 사이에만 실제로 적용된다. 자세한 내용은 다음 절에서 다룬다.

 

큐 사이에 자원을 공유하는 방법을 쉽게 설명하기 위해 사용자 A와 B가 있다고 가정하자. 두 사용자는 자신의 큐를 가지고있다.

사용자 A가 잡을 하나 시작하면 아직 B의 요청이 없기 때문에 모든 자원을 점유할 수 있다. A의 잡이 끝나기 전에 사용자 B가 잡을 하나 시작하면 앞에서 본 것과 같이 한동안 각 잡은 전체 자원의 절반씩을 사용하게 된다. 사용자 A와 B의 잡이 여전히 실행되는 중에 사용자 B가 두번째 잡을 시작하면 이 잡은 B의 다른 잡과 자원을 공유하게 된다. 따라서 사용자 B의 각 잡은 전체 자원의 4분의 1씩을 사용할수 있고, 사용자 A의 잡은 계속 절반을 사용하게 된다. 결국 자원은 사용자 사이에만 균등하게 공유된다고 할 수 있다.

4-3-5 큐 설정

페어 스케줄러는 클래스경로에 있는 fair-scheduler.xml이라는 할당 파일에 원하는 속성을 설정한다. 할당 파일의 이름은 yarn.scheduler.fair.allocation.file 속성을 지정하여 변경할 수 있다. 할당 파일이 없으면 페어 스케줄러는 앞에서 설명한 것처럼 작동한다. 즉, 각 애플리케이션은 해당 사용자 이름의 큐에 배치된다. 사용자 큐는 해당 사용자가 처음 애플리케이션을 제출할 때 동적으로 생성된다.

 

또한 각 큐별 설정도 할당 파일에서 정의한다. 페어 스케줄러는 캐퍼시티 스케줄러에서 지원했던 것과 비슷한 계층적인 큐 설정도 가능하다.예를들어 캐퍼시티 스케줄러의 할당 파일에서 설정한 것처럼 prod와 dev 큐를 정의할 수 있다.

 

큐의 계층 구조는 중첩된 큐 항목으로 정의된다. 실제로 큐는 root 큐 항목에 중첩된 것은 아니지만 모든 큐는 root 큐의 자식이다. 여기서 dev 큐는 그 하위에 eng 큐와 science 큐로 분리된다.

 

각 큐에 균등 공유의 비율로 적용되는 가중치를 설정할 수 있다. 이 예제에서 prod와 dev 큐는 40:60의 비율로 클러스터의 자원을 할당받는다. 그리고 eng와 science 큐는 가중치를 지정하지 않았기 때문에 자원을 동등하게 할당받는다. 가중치는 정확하게 100% 로 설정하지 않아도 된다. 예제에서는 쉽게 설명하기 위해 가중치의 합이 100이 되도록 설정했다. 예제의 prod와 dev 큐의 가중치를 각각 2와 3으로 변경해도 그 비율은 동일하다.

 

각 큐에 서로 다른 스케줄링 정책을 설정할 수도 있다. 기본정책은 최상단의 defaultQueueSchedulingPolicy 항목에 설정한다. 이를 생략하면 페어 스케줄링이 적용된다. 이름과 상관없이 페어 스케줄러는 큐에 FIFO 정책도 지원한다. 이장의 뒤에서 언급하겠지만 우성 자원 공평성(Dominant Resource Fairness DRF) 정책도 사용할 수 있다.

 

특정 큐의 정책은 schedulingPolicy 항목으로 재정의할 수 있다. 이 예제에서 prod 큐는 각 잡이 순차적으로 실행되고 최대한 빨리 끝나야 하기 때문에 FIFO 스케줄링 정책을 적용했다. 여기서 prod와 dev 큐는 자원을 공유할 때 균등 공유 정책이 적용되고, dev 큐의 하위에 있는 eng 와 science 큐에도 동일하게 적용된다는 점을 주의해야 한다.

 

예제의 할당 파일에는 없지만 각 큐에 최소와 최대 자원 사용량과 최대 실행 애플리케이션의 개수도 지정할 수 있다. 최소 자원 사용량은 엄격한 제한이 아니라 자원 할당의 우선순위로 보면 된다. 균등 공유 정책의 적용을 받는 큐가 두개 있다면 그중 최소 자원 사용량이 더 큰 큐가 우선적으로 자원을 할당 받을 수 있다. 잠시 뒤에 다루겠지만 최소 자원 설정은 선점에도 활용될 수 있다.

4-3-6 큐 배치

페어 스케줄러는 애플리케이션을 큐에 할당할 때 규칙 기반 시스템을 이용한다.[예제 4-2]에 있는 queuePlacementPolicy 항목은 규칙 목록을 포함하고 있는데, 맞는 규칙이 나올때 까지 순서대로 시도한다. 첫 번째 규칙인 specified는 지정된 큐에 애플리케이션을 배치한다. 큐를 지정하지 않았거나 지정된 큐가 존재하지 않으면 이 규칙은 적용되지 않고 그 다음에 나오는 규칙으로 다시 시도한다. primaryGroup 규칙은 사용자의 유닉스 그룹의 이름을 가진 큐에 애플리케이션을 배치한다. 큐가 존재하지 않으면 큐를 자동으로 생성하는 대신 다음 규칙으로 넘어간다. default 규칙은 catch-all로, 항상 dev.eng 큐에 애플리케이션을 배치한다.

 

queuePlacementPolicy 항목은 완전히 생략할 수 있으며, 이때 다음과 같이 specified 규칙이 기본으로 적용된다.

<queuePlacementPolicy>
	<rule name="specified" />
    <rule name="user" />
</queuePlacementPolicy>

 

다시 말해, 큐를 명시적으로 지정하지 않으면 사용자 이름의 큐를 사용한다(없으면 자동으로 생성된다.)

 

모든 애플리케이션을 동일한 큐(default)에 저장하는 단순한 큐 배치 정책도 있다. 이 정책을 사용하면 사용자가 아닌 모든 애플리케이션에 균등한 자원 공유가 가능하다.다음과 같이 정의하면 된다.

<queuePlacementPolicy>
	<rule name="default" />
</queuePlacementPolicy>

 

또한 yarn.scheduler.fair.user-as-default-queue 속성을 false로 설정하면 할당 파일을 사용하지 않고도 정책을 지정할 수 있다.

이때 사용자별 큐가 아닌 default 큐에 모든 애플리케이션이 배치된다. 추가로 yarn.scheduler.fair.allow-undeclared-pools 속성을 false로 설정하면 사용자는 동적으로 큐를 생성할 수 없게 된다.

4-3-7 선점

바쁘게 돌아가는 클러스터에서는 빈 큐에 잡이 제출되더라도 클러스터에서 이미 실행되고 있는 다른 잡이 자원을 해제해주기 전까지 잡을 시작할수 없다. 잡의 시작 시간을 어느정도 예측 가능하게 만들기 위해 페어 스케줄러는 선점이라는 기능을 제공한다.

 

선점은 스케줄러가 자원의 균등 공유에 위배되는 큐에서 실행되는 컨테이너를 죽일수 있도록 허용하는 기능으로, 큐에 할당된 자원은 균등 공유 기준을 반드시 따라야 한다. 중단된 컨테이너는 반드시 다시 수행되어야 하므로 클러스터의 전체 효율은 떨어지게 된다는 점을 유의해야 한다.