배치 제약
배치 제약 (Placement Constraints)
YARN은 애플리케이션이 데이터 지역성(data locality)(특정 노드나 랙에 대한 선호) 또는 (중첩되지 않는) 노드 레이블 형태로 배치 제약(placement constraints)을 지정할 수 있게 해줍니다. 이 문서는 YARN에서 더 표현력 있는 배치 제약에 초점을 맞춥니다. 이러한 제약은 애플리케이션의 성능과 복원력에 중요할 수 있으며, 특히 서비스, 머신러닝, 스트리밍 워크로드 같은 장기 실행 컨테이너를 포함하는 경우에 그렇습니다.
출처: 문서
본문
개요 (Overview)
YARN은 애플리케이션이 데이터 지역성 또는 (중첩되지 않는) 노드 레이블 형태로 배치 제약을 지정할 수 있게 해줍니다. 본 문서는 YARN에서 더 표현력 있는 배치 제약을 다룹니다.
예를 들어 네트워크 비용을 줄이기 위해 작업의 할당을 같은 랙에 배치하고(affinity 제약), 리소스 간섭을 최소화하기 위해 머신들에 할당을 분산시키거나(anti-affinity 제약), 둘 사이의 균형을 맞추기 위해 노드 그룹에 특정 수까지의 할당을 허용하는(cardinality 제약) 것이 유리할 수 있습니다. 배치 결정은 복원력에도 영향을 줍니다. 예를 들어 같은 클러스터 업그레이드 도메인 안에 배치된 할당은 동시에 오프라인이 될 것입니다.
애플리케이션은 클러스터의 기본 토폴로지(예: 컨테이너를 어디에 배치해야 하는지 특정 노드나 랙을 지정할 필요 없음)나 배포된 다른 애플리케이션에 대한 지식 없이도 제약을 지정할 수 있습니다. 현재 모든 제약은 하드(hard)입니다. 즉 현재 클러스터 상태나 충돌하는 제약 때문에 컨테이너의 제약이 충족될 수 없으면 컨테이너 요청은 대기(pending) 상태로 남거나 거부됩니다.
이 문서에서 "할당(allocation)"은 노드에 할당되는 리소스(예: CPU와 메모리)의 단위를 의미합니다. YARN의 현재 구현에서 할당은 단일 컨테이너에 해당합니다. 그러나 애플리케이션이 할당 하나로 둘 이상의 컨테이너를 생성하는 경우에는 할당이 여러 컨테이너에 해당할 수 있습니다.
빠른 가이드 (Quick Guide)
먼저 배치 제약을 통한 스케줄링을 활성화하는 방법을 설명하고, 주어진 셸 명령을 컨테이너 집합에서 실행할 수 있는 애플리케이션인 distributed shell을 사용해 이 기능을 실험하는 예를 제공합니다.
배치 제약 활성화 (Enabling placement constraints)
배치 제약을 활성화하려면 conf/yarn-site.xml에서 다음 속성을 placement-processor 또는 scheduler로 설정해야 합니다:
| Property | Description | Default value | | yarn.resourcemanager.placement-constraints.handler | PlacementConstraints를 처리하는 데 사용할 핸들러를 지정합니다. 허용되는 값: placement-processor, scheduler, disabled. | disabled |
세 가지 배치 제약 핸들러 각각에 대해 좀 더 자세히 설명합니다:
- placement-processor: 이 핸들러를 사용하면 제약이 있는 컨테이너의 배치는 capacity 또는 fair 스케줄러가 호출되기 전에 전처리 단계로 결정됩니다. 배치가 결정되면 capacity/fair 스케줄러가 실제 할당을 수행하도록 호출됩니다. 이 핸들러의 장점은 모든 제약 유형(affinity, anti-affinity, cardinality)을 지원한다는 것입니다. 게다가 한 번에 여러 컨테이너를 고려하므로 컨테이너를 하나씩 처리하는 방식보다 더 많은 제약을 충족할 수 있습니다. 메인 스케줄러 밖에 있으므로 capacity와 fair 스케줄러 양쪽에서 사용할 수 있습니다. 현재 애플리케이션 내 태스크 우선순위는 배치 제약과 충돌할 수 있으므로 고려하지 않는다는 점에 유의하세요.
- scheduler: 이 핸들러를 사용하면 제약이 있는 컨테이너는 메인 스케줄러가 배치합니다(현재는 capacity 스케줄러만 SchedulingRequests를 지원합니다). 현재 anti-affinity 제약만 지원합니다(affinity나 cardinality 없음). 이 핸들러의 장점은 placement-processor와 비교해, 기존 메인 스케줄러가 강제하는 것과 동일한 큐(이용률, 우선순위로 정렬), 앱(FIFO/fairness/우선순위로 정렬), 그리고 같은 앱 내 태스크(우선순위) 정렬 규칙을 따른다는 것입니다.
- disabled: 이 핸들러를 사용하면 애플리케이션이 SchedulingRequest를 요청하면 해당 allocate 호출은 거부됩니다.
placement-processor 핸들러는 더 넓은 범위의 제약을 지원하고, 특히 애플리케이션이 까다로운 제약을 가지거나 클러스터가 고이용률일 때(한 번에 여러 컨테이너를 고려하므로) 더 많은 컨테이너를 배치할 수 있습니다. 그러나 애플리케이션 내 태스크 우선순위를 지키는 것이 사용자에게 중요하고 capacity 스케줄러를 사용한다면, scheduler 핸들러를 대신 사용해야 합니다.
distributed shell로 배치 제약 실험하기
사용자는 다음 명령으로 distributed shell 애플리케이션을 사용해 배치 제약을 실험할 수 있습니다:
$ yarn org.apache.hadoop.yarn.applications.distributedshell.Client -jar share/hadoop/yarn/hadoop-yarn-applications-distributedshell-3.5.0.jar -shell_command sleep -shell_args 10 -placement_spec PlacementSpec
여기서 PlacementSpec은 다음 형태입니다:
PlacementSpec => "" | PlacementExpr;PlacementSpec
PlacementExpr => SourceTag,ConstraintExpr
SourceTag => String(NumContainers)
ConstraintExpr => SingleConstraint | CompositeConstraint
SingleConstraint => "IN",Scope,TargetTag | "NOTIN",Scope,TargetTag | "CARDINALITY",Scope,TargetTag,MinCard,MaxCard | NodeAttributeConstraintExpr
NodeAttributeConstraintExpr => NodeAttributeName=Value, NodeAttributeName!=Value
CompositeConstraint => AND(ConstraintList) | OR(ConstraintList)
ConstraintList => Constraint | Constraint:ConstraintList
NumContainers => int
Scope => "NODE" | "RACK"
TargetTag => String
MinCard => int
MaxCard => int
주의:
- distributed shell 명령에서
-placement_spec인자가 지정되면(NodeAttributeConstraintExpr 제외)-num-containers인자는 사용해서는 안 됩니다.-num-containers인자를-placement-spec과 함께 사용하면 전자는 무시됩니다. 이는 PlacementSpec에서 태그당 컨테이너 수를 결정하므로-num-containers가 중복되고 충돌할 수 있기 때문입니다. 또한-placement_spec을 사용하면 모든 컨테이너가GUARANTEED실행 유형으로 요청됩니다. - NodeAttributeConstraintExpr이 지정되면 SourceTag(NumContainers)는 선택사항이며
-num-containers값이 요청할 컨테이너 수로 고려됩니다.
PlacementSpec의 예는 다음과 같습니다:
zk(3),NOTIN,NODE,zk:hbase(5),IN,RACK,zk:spark(7),CARDINALITY,NODE,hbase,1,3
위는 세 가지 제약을 인코딩합니다:
- 태그 "zk"(ZooKeeper를 나타냄)가 있는 3개 컨테이너를 서로 노드 anti-affinity로 배치합니다. 즉 노드당 컨테이너를 하나 이상 두지 않습니다(첫 번째 제약에서 SourceTag와 TargetTag가 일치함을 주목하세요).
- 태그 "hbase"가 있는 5개 컨테이너를 태그 "zk"가 있는 컨테이너가 실행 중인 랙에 affinity로 배치합니다(즉 "zk" 컨테이너가 실행 중인 랙에는 "hbase" 컨테이너를 배치하지 않아야 합니다. "zk"가 두 번째 제약의 TargetTag이기 때문입니다).
- 태그 "spark"가 있는 7개 컨테이너를 태그 "hbase"가 있는 컨테이너가 최소 하나, 최대 세 개 있는 노드에 배치합니다.
복합 형태의 제약을 보여주는 또 다른 예는 다음과 같습니다:
zk(5),AND(IN,RACK,hbase:NOTIN,NODE,zk)
위 제약은 AND 연산자를 사용해 두 제약을 결합합니다. AND 제약은 두 자식 제약이 모두 충족될 때 충족됩니다. 특정 PlacementSpec은 최소 하나의 "hbase" 컨테이너가 실행 중인 랙에, 그리고 "zk" 컨테이너가 실행되지 않는 노드에 5개의 "zk" 컨테이너를 배치하도록 요청합니다. 마찬가지로 OR 연산자를 사용해 자식 제약 중 하나 이상이 충족될 때 충족되는 제약을 정의할 수 있습니다. "zk"와 "hbase"가 다른 애플리케이션에 속한 컨테이너인 경우(실제 사용 사례에서 대부분 그럴 것), PlacementSpec의 할당 태그에는 아래에서 설명하는 것처럼 네임스페이스가 포함되어야 합니다(Allocation tags namespace 참조).
배치 제약 정의 (Defining Placement Constraints)
할당 태그 (Allocation tags)
할당 태그는 애플리케이션이 (그룹의) 컨테이너와 연관시킬 수 있는 문자열 태그입니다. 태그는 애플리케이션의 컴포넌트를 식별하는 데 사용됩니다. 예를 들어 HBase Master 할당은 "hbase-m"으로, Region Server는 "hbase-rs"로 태그할 수 있습니다. 다른 예로 할당의 더 일반적인 요구를 나타내는 "latency-critical" 또는 작업 ID를 나타내는 "app_0041"이 있습니다. 할당 태그는 제약에서 핵심적인 역할을 하는데, 공통 태그를 공유하는 여러 할당을 참조할 수 있게 해주기 때문입니다.
할당 태그를 정의하기 위해 ResourceRequest 객체 대신 새로운 SchedulingRequest 객체를 사용합니다. 이는 ResourceRequest와 많은 유사점이 있지만, 요청된 할당의 크기(할당의 수와 크기, 우선순위, 실행 유형 등)와 이러한 할당이 어떻게 배치되어야 하는지를 규정하는 제약(리소스 이름, relaxed locality)을 더 잘 분리합니다. 애플리케이션은 여전히 ResourceRequest 객체를 사용할 수 있지만, 할당 태그와 제약을 정의하려면 SchedulingRequest 객체를 사용해야 합니다. 단일 AllocateRequest 내에서 애플리케이션은 ResourceRequest 또는 SchedulingRequest 객체 중 하나만 사용해야 하며 둘 다 사용해서는 안 됩니다.
할당 태그 네임스페이스 (Allocation tags namespace)
할당 태그는 같은 애플리케이션 또는 다른 애플리케이션의 컨테이너를 참조할 수 있으며, 각각 애플리케이션 내(intra) 또는 애플리케이션 간(inter) 제약을 표현하는 데 사용됩니다. 할당 태그가 참조할 수 있는 애플리케이션의 범위를 지정하기 위해 할당 태그 네임스페이스를 사용합니다. 할당 태그를 네임스페이스와 결합하여, 태그가 같은 애플리케이션에 속한 컨테이너, 특정 애플리케이션 그룹, 또는 클러스터의 어떤 애플리케이션을 대상으로 하는지 제한할 수 있습니다.
현재 지원되는 네임스페이스는 다음과 같습니다:
| Namespace | Syntax | Description | | SELF | self/${allocationTag} | 할당 태그는 현재 애플리케이션(제약이 적용될 애플리케이션)의 컨테이너를 참조합니다. 기본 네임스페이스입니다. | NOT_SELF | not-self/${allocationTag} | 할당 태그는 현재 애플리케이션에 속하지 않는 컨테이너만 참조합니다. | ALL | all/${allocationTag} | 할당 태그는 어떤 애플리케이션의 컨테이너든 참조합니다. | APP_ID | app-id/${applicationID}/${allocationTag} | 할당 태그는 지정된 애플리케이션 ID의 애플리케이션 컨테이너를 참조합니다. | APP_TAG | app-tag/application_tag_name/${allocationTag} | 할당 태그는 지정된 애플리케이션 태그로 태그된 애플리케이션의 컨테이너를 참조합니다.
대상 태그 targetTag에 할당 태그 네임스페이스 ns를 붙이려면 PlacementSpec에서 ns/allocationTag 구문을 사용합니다. 기본 네임스페이스는 SELF이며, 이는 애플리케이션 내(intra-app) 제약에 사용됩니다. 나머지 네임스페이스 태그는 애플리케이션 간(inter-app) 제약을 지정하는 데 사용됩니다. 태그 옆에 네임스페이스가 지정되지 않으면 SELF로 간주됩니다.
위에서 사용한 예제 제약은 네임스페이스로 다음과 같이 확장할 수 있습니다:
zk(3),NOTIN,NODE,not-self/zk:hbase(5),IN,RACK,all/zk:spark(7),CARDINALITY,NODE,app-id/appID_0023/hbase,1,3
이 제약들의 의미는 다음과 같습니다:
- 태그 "zk"(ZooKeeper를 나타냄)가 있는 3개 컨테이너를, 다른 애플리케이션의 "zk" 컨테이너가 실행되지 않는 노드에 배치합니다.
- 태그 "hbase"가 있는 5개 컨테이너를, 태그 "zk"(같은 애플리케이션이든 다른 것이든 어떤 애플리케이션의)가 있는 컨테이너가 실행 중인 랙에 affinity로 배치합니다.
- 태그 "spark"가 있는 7개 컨테이너를, ID가 appID_0023인 애플리케이션에 속한 태그 "hbase" 컨테이너가 최소 하나, 최대 세 개 있는 노드에 배치합니다.
노드 레이블, 노드 속성, 할당 태그의 차이점
할당 태그와 노드 레이블 또는 노드 속성의 차이는, 할당 태그는 할당에 붙고 노드에는 붙지 않는다는 점입니다. 스케줄러가 할당을 노드에 할당하면 그 할당의 태그 집합은 할당 기간 동안 자동으로 노드에 추가됩니다. 따라서 노드는 현재 그 노드에 할당된 할당들의 태그를 상속합니다. 마찬가지로 랙은 그 노드들의 태그를 상속합니다. 또한 노드 레이블과 유사하게 그리고 노드 속성과는 다르게, 할당 태그에는 값이 붙지 않습니다. 아래에서 보여주듯 우리의 제약은 할당 태그뿐 아니라 노드 레이블과 노드 속성도 참조할 수 있습니다.
배치 제약 API
애플리케이션은 PlacementConstraints의 공개 API를 사용해 배치 제약을 구성할 수 있습니다. 제약을 만드는 메서드를 설명하기 전에, 제약에서 사용될 대상 표현식(target expression)을 구성하는 데 사용되는 PlacementTargets 클래스의 메서드를 설명합니다:
| Method | Description | | allocationTag(String... allocationTags) | 할당 태그에 대한 대상 표현식을 구성합니다. 주어진 태그 중 하나를 가진 할당이 있으면 충족됩니다. | allocationTagWithNamespace(String namespace, String... allocationTags) | allocationTag(String...)와 유사하지만, 주어진 할당 태그에 대한 네임스페이스를 지정할 수 있습니다. | nodePartition(String... nodePartitions) | 노드 파티션에 대한 대상 표현식을 구성합니다. nodePartitions 중 하나에 속한 노드에 대해 충족됩니다. | nodeAttribute(String attributeKey, String... attributeValues) | 노드 속성에 대한 대상 표현식을 구성합니다. 지정된 노드 속성이 주어진 값 중 하나를 가지면 충족됩니다.
위 nodeAttribute 메서드는 진행 중인 노드 속성 기능이 필요하므로 아직 기능하지 않는다는 점에 유의하세요.
제약을 만드는 PlacementConstraints 클래스의 메서드는 다음과 같습니다:
| Method | Description | | targetIn(String scope, TargetExpression... targetExpressions) | 주어진 범위(예: node 또는 rack) 내에서 모든 대상 표현식을 충족하는 노드에 할당이 배치되도록 요구하는 제약을 생성합니다. 예: targetIn(RACK, allocationTag("hbase-m"))은 태그 "hbase-m"을 가진 할당이 하나 이상 있는 랙에 속한 노드에 할당을 허용합니다. | targetNotIn(String scope, TargetExpression... targetExpressions) | 어떤 대상 표현식도 충족하지 않는 범위(예: node 또는 rack)에 속한 노드에 할당이 배치되도록 요구하는 제약을 생성합니다. | cardinality(String scope, int minCardinality, int maxCardinality, String... allocationTags) | 주어진 범위(예: node 또는 rack) 내에서 할당 수를 제한하는 제약을 생성합니다. 예: cardinality(NODE, 3, 10, "zk")는 태그 "zk" 할당이 3개 이상 10개 이하인 노드에서 충족됩니다. | minCardinality(String scope, int minCardinality, String... allocationTags) | cardinality(String, int, int, String...)와 유사하지만 최소 카디널리티만 결정합니다(최대 카디널리티는 무제한). | maxCardinality(String scope, int maxCardinality, String... allocationTags) | cardinality(String, int, int, String...)와 유사하지만 최대 카디널리티만 결정합니다(최소 카디널리티는 0). | targetCardinality(String scope, int minCardinality, int maxCardinality, String... allocationTags) | 이 제약은 cardinality와 target 제약을 일반화합니다. 제약에서 지정된 범위에 속하는 노드 집합 N을 고려합니다. 대상 표현식이 노드 집합 N에서 최소 minCardinality번, 최대 maxCardinality번 충족되면 제약이 충족됩니다. 예: targetCardinality(RACK, 2, 10, allocationTag("zk"))는 태그 "zk"가 있는 다른 할당이 2개 이상 10개 이하인 랙 내에 할당이 배치되도록 요구합니다.
PlacementConstraints 클래스에는 복합 제약(여러 제약을 가진 AND/OR 표현식)을 만드는 메서드도 포함됩니다. 복합 제약 지원을 추가하는 것은 진행 중인 작업입니다.
애플리케이션에서 제약 지정하기
애플리케이션은 각 제약이 활성화될 컨테이너를 지정해야 합니다. 이를 위해 애플리케이션은 할당 태그 집합(source tags)에서 배치 제약으로의 매핑을 제공할 수 있습니다. 예를 들어 이 매핑의 항목이 "hbase"->constraint1이면, 태그 "hbase"가 있는 각 할당을 스케줄링할 때 constraint1이 적용된다는 뜻입니다.
placement-processor 핸들러(Enabling placement constraints 참조)를 사용할 때 이 제약 매핑은 RegisterApplicationMasterRequest 내에서 지정됩니다.
scheduler 핸들러를 사용할 때 제약은 각 SchedulingRequest 객체에도 추가될 수 있습니다. 각 제약은 그 스케줄링 요청의 태그에 유효합니다. RegisterApplicationMasterRequest와 스케줄링 요청 양쪽에서 제약이 지정되면 후자가 전자를 덮어씁니다.
더 알아보기 (Learn more)
- 원문: 문서