파드 토폴로지 분배 제약
파드 토폴로지 분배 제약 (Pod Topology Spread Constraints)
토폴로지 분배 제약(topology spread constraints)을 사용해 파드가 리전, 존, 노드, 그리고 다른 사용자 정의 토폴로지 도메인 같은 장애 도메인(failure-domains) 전반에 걸쳐 클러스터에 어떻게 분산되는지 제어할 수 있어요. 이것은 높은 가용성과 효율적인 리소스 활용을 달성하는 데 도움이 될 수 있어요.
클러스터 레벨 제약을 기본값으로 설정하거나, 개별 워크로드에 대한 토폴로지 분배 제약을 구성할 수 있어요.
출처: 문서
본문
동기 (Motivation)
최대 스무 개의 노드가 있는 클러스터가 있고, 사용하는 레플리카 수를 자동으로 스케일링할 워크로드를 실행하려 한다고 상상해 보세요. 파드는 두 개만 있을 수도 있고 최대 열다섯 개일 수도 있어요. 파드가 두 개뿐일 때는 두 파드 모두 같은 노드에서 실행되는 것을 선호하지 않을 거예요: 단일 노드 장애가 워크로드를 오프라인으로 만들 위험을 감수하게 되기 때문이에요.
이 기본적인 사용 외에도 워크로드가 높은 가용성과 클러스터 활용에서 이점을 얻을 수 있게 하는 몇 가지 고급 사용 예시가 있어요.
확장하면서 더 많은 파드를 실행하면 다른 우려가 중요해져요. 각각 다섯 개의 파드를 실행하는 세 개의 노드가 있다고 상상해 보세요. 노드는 그만큼의 레플리카를 실행하기에 충분한 용량이 있어요; 그러나 이 워크로드와 상호작용하는 클라이언트는 세 개의 다른 데이터센터(또는 인프라 영역)에 걸쳐 나뉘어 있어요. 이제 단일 노드 장애에 대한 우려는 줄었지만, 지연 시간이 원하는 것보다 높고 서로 다른 존 사이에 네트워크 트래픽을 보내는 것과 관련된 네트워크 비용을 지불하고 있다는 것을 알게 돼요.
정상 작동 중에는 각 인프라 영역에 비슷한 수의 레플리카가 스케줄링되는 것을 선호하고, 문제가 있을 때 클러스터가 자가 치유(self-heal)되기를 원한다고 결정해요.
파드 토폴로지 분배 제약은 그것을 구성하는 선언적인 방법을 제공해요.
topologySpreadConstraints 필드
Pod API에는 spec.topologySpreadConstraints라는 필드가 포함돼요. 이 필드의 사용은 다음과 같아요:
---
apiVersion: v1
kind: Pod
metadata:
name: example-pod
spec:
# Configure a topology spread constraint
topologySpreadConstraints:
- maxSkew: <integer>
minDomains: <integer> # optional
topologyKey: <string>
whenUnsatisfiable: <string>
labelSelector: <object>
matchLabelKeys: <list> # optional; beta since v1.27
nodeAffinityPolicy: [Honor|Ignore] # optional; beta since v1.26
nodeTaintsPolicy: [Honor|Ignore] # optional; beta since v1.26
### other Pod fields go here
참고:
이 필드에 대해 더 읽으려면 kubectl explain Pod.spec.topologySpreadConstraints를 실행하거나 Pod에 대한 API 참조의 스케줄링 섹션을 참조하세요.
분배 제약 정의
하나 또는 여러 topologySpreadConstraints 항목을 정의해 클러스터 전반의 기존 파드와 관련해 들어오는 각 파드를 kube-scheduler가 어떻게 배치할지 지시할 수 있어요. 그 필드들은 다음과 같아요:
- maxSkew는 파드가 얼마나 고르지 않게 분산될 수 있는지를 설명해요. 이 필드를 반드시 지정해야 하며 숫자는 0보다 커야 해요. 그 의미는
whenUnsatisfiable값에 따라 달라져요:whenUnsatisfiable: DoNotSchedule을 선택하면,maxSkew는 대상 토폴로지의 일치하는 파드 수와 글로벌 최소값(적격 도메인에서 일치하는 파드의 최소 수, 또는 적격 도메인 수가 MinDomains보다 적으면 0) 사이의 최대 허용 차이를 정의해요. 예를 들어 3개의 존에 각각 2, 2, 1개의 일치하는 파드가 있고 MaxSkew가 1로 설정되면 글로벌 최소값은 1이에요.whenUnsatisfiable: ScheduleAnyway를 선택하면, 스케줄러는 스큐를 줄이는 데 도움이 되는 토폴로지에 더 높은 우선순위를 부여해요. - minDomains는 적격 도메인의 최소 수를 나타내요. 이 필드는 선택적이에요. 도메인(domain)은 토폴로지의 특정 인스턴스예요. 적격 도메인(eligible domain)은 노드가 노드 셀렉터와 일치하는 도메인이에요.
참고:
-
지정된 경우
minDomains값은 0보다 커야 해요.minDomains은whenUnsatisfiable: DoNotSchedule과 함께만 지정할 수 있어요. -
일치하는 토폴로지 키를 가진 적격 도메인 수가
minDomains보다 적으면, 파드 토폴로지 분배는 글로벌 최소값을 0으로 취급하고 스큐 계산이 수행돼요. 글로벌 최소값은 적격 도메인에서 일치하는 파드의 최소 수, 또는 적격 도메인 수가minDomains보다 적으면 0이에요. -
일치하는 토폴로지 키를 가진 적격 도메인 수가
minDomains이상이면, 이 값은 스케줄링에 효과가 없어요. -
minDomains을 지정하지 않으면 제약은minDomains이 1인 것처럼 동작해요. -
topologyKey는 노드 레이블의 키예요. 이 키와 동일한 값을 가진 레이블이 있는 노드는 같은 토폴로지에 있는 것으로 간주돼요. 우리는 토폴로지의 각 인스턴스(즉
<key, value>쌍)를 도메인이라고 불러요. 스케줄러는 각 도메인에 균형 잡힌 수의 파드를 두려고 할 거예요. 또한 노드가nodeAffinityPolicy와nodeTaintsPolicy의 요구 사항을 충족하는 도메인을 적격 도메인으로 정의해요. -
whenUnsatisfiable은 파드가 분배 제약을 충족하지 않으면 어떻게 처리할지를 나타내요:
DoNotSchedule(기본값)은 스케줄러가 그것을 스케줄링하지 말라고 알려줘요.ScheduleAnyway는 스큐를 최소화하는 노드에 우선순위를 두면서 여전히 스케줄링하라고 알려줘요. -
labelSelector는 일치하는 파드를 찾는 데 사용돼요. 이 레이블 셀렉터와 일치하는 파드는 해당 토폴로지 도메인의 파드 수를 결정하기 위해 계산돼요. 자세한 내용은 레이블 셀렉터를 참조하세요.
-
matchLabelKeys는 분배 스큐가 계산될 파드 그룹을 선택하기 위한 파드 레이블 키 목록이에요. 파드 생성 시 kube-apiserver는 이러한 키를 사용해 들어오는 파드 레이블에서 값을 조회하고, 그 키-값 레이블은 기존
labelSelector와 병합돼요. 같은 키가matchLabelKeys와labelSelector둘 다에 존재하는 것은 금지돼요.matchLabelKeys는labelSelector가 설정되지 않으면 설정할 수 없어요. 파드 레이블에 존재하지 않는 키는 무시돼요. null이나 빈 목록은labelSelector에 대해서만 일치함을 의미해요.
주의:
matchLabelKeys가 있는 경우, 서로 다른 리비전 간에 pod.spec을 업데이트할 필요가 없어요. 컨트롤러/연산자는 다른 리비전에 대해 같은 레이블 키에 다른 값을 설정하면 돼요. 예를 들어 Deployment를 구성한다면, Deployment 컨트롤러가 자동으로 추가하는 pod-template-hash로 키 지정된 레이블을 사용해 단일 Deployment 내에서 서로 다른 리비전을 구분할 수 있어요.
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: foo
matchLabelKeys:
- pod-template-hash
참고:
matchLabelKeys 필드는 베타 레벨 필드이며 1.27에서 기본적으로 활성화돼 있어요. MatchLabelKeysInPodTopologySpread 기능 게이트를 비활성화해 끌 수 있어요.
v1.34 이전에는 matchLabelKeys가 암묵적으로 처리됐어요. v1.34부터 matchLabelKeys에 해당하는 키-값 레이블이 labelSelector에 명시적으로 병합돼요. 이를 비활성화하고 이전 동작으로 되돌리려면 kube-apiserver의 MatchLabelKeysInPodTopologySpreadSelectorMerge 기능 게이트를 비활성화할 수 있어요.
- nodeAffinityPolicy는 파드 토폴로지 분배 스큐를 계산할 때 파드의
nodeAffinity/nodeSelector를 어떻게 처리할지를 나타내요. 옵션은:Honor:nodeAffinity/nodeSelector와 일치하는 노드만 계산에 포함.Ignore:nodeAffinity/nodeSelector는 무시. 모든 노드가 계산에 포함. 이 값이 null이면 동작은Honor정책과 동등해요.
참고:
nodeAffinityPolicy는 1.26에서 베타가 되었고 1.33에서 GA로 승격됐어요. 베타에서 기본적으로 활성화되어 있으며, NodeInclusionPolicyInPodTopologySpread 기능 게이트를 비활성화해 끌 수 있어요.
- nodeTaintsPolicy는 파드 토폴로지 분배 스큐를 계산할 때 노드 테인트를 어떻게 처리할지를 나타내요. 옵션은:
Honor: 테인트가 없는 노드와 들어오는 파드에 대한 톨러레이션이 있는 테인트된 노드가 포함.Ignore: 노드 테인트는 무시. 모든 노드가 포함. 이 값이 null이면 동작은Ignore정책과 동등해요.
참고:
nodeTaintsPolicy는 1.26에서 베타가 되었고 1.33에서 GA로 승격됐어요. 베타에서 기본적으로 활성화되어 있으며, NodeInclusionPolicyInPodTopologySpread 기능 게이트를 비활성화해 끌 수 있어요.
파드가 둘 이상의 topologySpreadConstraint를 정의하면, 그 제약들은 논리적 AND 연산으로 결합돼요: kube-scheduler는 들어오는 파드에 대해 모든 구성된 제약을 충족하는 노드를 찾아요.
노드 레이블
토폴로지 분배 제약은 각 노드가 있는 토폴로지 도메인을 식별하기 위해 노드 레이블에 의존해요. 예를 들어 노드에 다음 레이블이 있을 수 있어요:
region: us-east-1
zone: us-east-1a
참고:
간결함을 위해 이 예시는 잘 알려진 레이블 키 topology.kubernetes.io/zone과 topology.kubernetes.io/region을 사용하지 않아요. 그러나 이 등록된 레이블 키는 여기서 사용된 사적(비정규화) 레이블 키 region과 zone보다 여전히 권장돼요.
서로 다른 컨텍스트 사이에서 사적 레이블 키의 의미에 대해 신뢰할 만한 가정을 할 수 없어요.
다음 레이블이 있는 4노드 클러스터가 있다고 가정해 보세요: