Kubernetes

Kubenets 기초 - Scheduling(4)

사실 나도 모름 2024. 1. 6. 03:14
  1. NodeSelector
  2. Affinity and Anti-affinity
  3. Pod Overhead
  4. 파드 스케줄링 준비성(Readiness)
  5. 파드 토폴로지 분배 제약 조건            ←  오늘 볼 내용
  6. Taints and Tolerations
  7. cordon and drain

5. 파드 토폴로지 분배 제약 조건

https://kubernetes.io/ko/docs/concepts/scheduling-eviction/topology-spread-constraints/

 

파드 토폴로지 분배 제약 조건

사용자는 토폴로지 분배 제약 조건 을 사용하여 지역(region), 존(zone), 노드 및 기타 사용자 정의 토폴로지 도메인과 같이 장애 도메인으로 설정된 클러스터에 걸쳐 파드가 분배되는 방식을 제어

kubernetes.io

 

 

파드는 생성이 되면 스케줄러에 의해 리소스 사용량이나 사용자 정의에 의해 설정된 여러 조건으로 인해 스케줄 될 위치를 결정한다.

쿠버네티스 운영환경이 하나의 리전이나 데이터센터(인프라스트럭처 존)에 국한된 것이 아닌 여러 리전이나 존, 혹은 사용자 정의 도메인 등 파드가 스케줄 될 수 있는 위치가 다양할 수도 있다.

이와 같은 환경에서 파드가 생성될 때 어느 토폴로지에서 스케줄 될 것인지 분배하는 조건을 설정함으로 고가용성과 리소스 효율성을 달성할 수 있다.

만약 여러 워커노드를 운영하는 클러스터 환경의 경우 여러 대의 워커노드가 있음에도 한 노드에 여러 파드가 스케줄되는 것을 원치 않을 것이다.

그렇게 되면 하나의 노드 장애만으로 전체 시스템에 문제가 발생하는 불상사가 생길 수 있기에 적절한 파드 배치가 필요하다.

혹은 여러 리전에 각각 노드가 위치하고 더 많은 파드가 골고루 배포된 상황을 고려해 보자.

이렇게 된다면 단일 노드 장애로 인한 문제는 해소되지만 당연히 네트워크가 다르기 때문에 지연시간이 생기고 네트워크 트래픽으로 인한 전송 비용이 발생할 것이다.

 

정상적인 동작 중에는 각 인프라스트럭처 존에 비슷한 수의 레플리카가 스케줄되고, 클러스터에 문제가 있는 경우 스스로 치유하도록 설정할 수 있다.
파드 토폴로지 분배 제약 조건은 이러한 설정을 할 수 있도록 하는 선언적인 방법을 제공한다.

 

그래서 우리가 알아볼 내용은 이러한 클러스터 환경에서 파드가 스케줄 될 때 균형있고 적절하게 파드가 배치되도록 하는 고급 사용 예시에 대한 내용이다.

 

 

 

topologySpreadConstraints 필드

파드 API에 spec.topologySpreadConstraints 필드가 있다.

이 필드는 다음과 같이 쓰인다.

---
apiVersion: v1
kind: Pod
metadata:
  name: example-pod
spec:
  # 토폴로지 분배 제약 조건을 구성한다.
  topologySpreadConstraints:
    - maxSkew: <integer>
      minDomains: <integer> # 선택 사항이며, v1.25에서 베타 기능으로 도입되었다.
      topologyKey: <string>
      whenUnsatisfiable: <string>
      labelSelector: <object>
      matchLabelKeys: <list> # 선택 사항이며, v1.27에서 베타 기능으로 도입되었다.
      nodeAffinityPolicy: [Honor|Ignore] # 선택 사항이며, v1.26에서 베타 기능으로 도입되었다.
      nodeTaintsPolicy: [Honor|Ignore] # 선택 사항이며, v1.26에서 베타 기능으로 도입되었다.
  ### 파드의 다른 필드가 이 아래에 오게 된다.

 

 

다음 명령으로 해당 필드에 대한 더 자세한 내용을 볼 수 있다.

kubectl explain Pod.spec.topologySpreadConstraints

 

 

  • maxSkew (필수 항목)
    • 파드가 배포되는 노드 간의 최대 편차
    • 0보다 반드시 커야 한다.
    • whenUnsatisfiable의 값에 따라 결과가 달라진다.
  • minDomains
    • 허용되는 최소 서로 다른 토폴로지 도메인 수
    • 예를 들어, minDomains: 3으로 설정하면 적어도 세 가지 서로 다른 토폴로지 도메인에 Pod이 퍼져야 한다.
    • 0보다 반드시 커야 한다.
    • whenUnsatisfiable: DoNotSchedule일 때에만 지정할 수 있다.
  • topologyKey  (필수 항목)
    • 분산을 제어할 토폴로지 도메인의 키
    • 스케줄러는 각 도메인에 균형잡힌 수의 파드를 배치하려고 시도한다. 
  • whenUnsatisfiable  (필수 항목)
    • 분산 제약 조건을 만족하지 않을 경우에 파드를 처리하는 방법
    • 두 가지 옵션이 있다.
      • DoNotSchedule(기본값) : 스케줄러에 스케줄링을 하지 말라고 알려준다.
      • ScheduleAnyway : 스케줄러에게 차이(skew)를 최소화하는 노드에 높은 우선 순위를 부여하면서, 스케줄링을 계속하도록 지시한다.
  • labelSelector  (필수 항목)
    • 토폴로지 제약 조건을 적용할 노드를 선택
    • 레이블 셀렉터와 일치하는 파드의 수를 계산하여 해당 토폴로지 도메인에 속할 파드의 수를 결정한다.
  • matchLabelKeys
    • 분배도(spreading)가 계산될 파드를 선택하기 위한 파드 레이블 키 목록
    • 예를 들어 Deployment를 구성하는 경우 Deployment 컨트롤러에 의해 자동으로 추가되는 pod-template-hash 키가 지정된 레이블을 사용하여 단일 배포의 여러 파드들을 구별할 수 있다.
  • nodeAffinityPolicy
    • 파드 토폴로지의 스프레드 스큐(spread skew)를 계산할 때 파드의 nodeAffinity/nodeSelector를 다루는 방법을 나타낸다.
    • 두 가지 옵션이 있다.
      • Honor : nodeAffinity/nodeSelector와 일치하는 노드만 계산에 포함된다.
      • Ignore : nodeAffinity/nodeSelector는 무시된다. 모든 노드가 계산에 포함된다.
    • 옵션이 null일 때는 Honor가 적용된다.
  • nodeTaintsPolicy 
    • 파드 토폴로지의 스프레드 스큐(spread skew)를 계산할 때 노드 테인트(taint)를 다루는 방법을 나타낸다.
    • 두 가지 옵션이 있다.
      • Honor : 테인트가 없는 노드, 그리고 노드가 톨러레이션이 있는 들어오는 파드(incoming pod)를 위한 테인트가 설정된 노드가 포함된다.
      • Ignore : 노드 테인트는 무시된다. 모든 노드가 포함된다.
    • 옵션이 null일 때는 Ignore가 적용된다.

 

 

 

실습

현재 클러스터 환경은 워커 노드가 두 개만 존재한다.

먼저 아래 디플로이먼트를 실행하여 확인해보자.

# topology-spread-constraints-pod.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: test-dep
spec:
  replicas: 10
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      topologySpreadConstraints:
        - maxSkew: 3
          topologyKey: kubernetes.io/hostname
          whenUnsatisfiable: DoNotSchedule
          labelSelector:
            matchLabels:
              app: web
      containers:
      - name: nginx-container
        image: nginx:1.25

6개 Running, 4개 Pending

 

위 토폴로지 제약 조건을 보면 다음과 같다.

maxSkew: 3 으로 인해 각 노드간 파드의 최대 편차는 3개로 제한한다.

또한 토폴로지 키를 호스트 네임으로 정하였기 때문에 같은 호스트 네임에 위치한 파드는 같은 곳에 있는 것으로 간주한다.

하지만 호스트 네임은 각 노드마다 각각 다르기 때문에 평소와 큰 차이없다.

레이블 셀렉터로 인해 레이블은 app: web인 파드들만 한정해서 계산한다.

그로 인해 파드가 노드 당 3개만 스케줄되어 있는 것을 볼 수 있다.

Q :
워커 노드가 2개 밖에 없는데 왜 파드가 전부 스케줄 되지 않는가?

A :
파드 토폴로지 분배 제약 조건은 노드가 몇개 있는지 상관하지 않는다.
단지, 파드의 개수가 각 토폴로지마다 얼마나 차이가 있는 것만 생각한다.
다시 말해, 파드 템플릿에 설정되어 있는 제약 조건은 마치 다른 노드가 있는 것처럼 판단하여 존재하는 노드 외에 다른 노드에는 0개의 파드가 스케줄되어 있다고 생각하는 것이다.
(마스터 노드도 스케줄 대상으로 보는 것인지는 모르겠다)
그래서 제약 조건으로 인해 10개의 모든 파드가 스케줄되지 않고 각 노드 당 3개씩만 스케줄된 것이다.

 

 

만약 app: web이라는 레이블을 가진 파드가 node1에만 3개 스케줄되어 있다고 하면 node1에 스케줄하려는 app: web 레이블을 가진 파드는 스케줄러가 스케줄하려고 하지 않을 것이다.

위 yaml파일에서 whenUnsatisfiable의 옵션을 ScheduleAnyway로 바꾸면 다음과 같이 된다.

모든 파드가 스케줄된다.

 

 

뭔가 아무것도 설정안했을 때와 차이가 없다고 생각이 든다면 눈치가 빠른 것이다.

파드 토폴로지 분배에 대해 클러스터 수준의 기본 제약을 설정하지 않으면, kube-scheduler는 다음과 같은 기본 토폴로지 제약이 설정되어 있는 것처럼 동작한다.

defaultConstraints:
  - maxSkew: 3
    topologyKey: "kubernetes.io/hostname"
    whenUnsatisfiable: ScheduleAnyway
  - maxSkew: 5
    topologyKey: "topology.kubernetes.io/zone"
    whenUnsatisfiable: ScheduleAnyway

 

 

 

만약 whenUnsatisfiable: DoNotSchedule로 설정하고도 파드는 모두 실행되길 원한다면 Affinity 혹은 topologySpreadConstraint를 하나 더 정의하여 제약조건을 한정하면 가능하다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: test-dep
spec:
  replicas: 10
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      topologySpreadConstraints:
        - maxSkew: 3
          topologyKey: kubernetes.io/hostname
          whenUnsatisfiable: DoNotSchedule
          labelSelector:
            matchLabels:
              app: web
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: kubernetes.io/hostname
                operator: In
                values:
                - node1    # 본인 노드의 호스트 네임
                - node2    # 본인 노드의 호스트 네임
                - node3    # 본인 노드의 호스트 네임
      containers:
      - name: nginx-container
        image: nginx:1.25

모든 파드 Running

 

VM을 바꿔서 워커노드가 3개 있는 것으로 바꾸긴 했지만 Pending상태에 남아있는 파드없이 모두 실행되었다.

위 yaml코드로는 모든 워커노드를 선택했지만 특정 노드에는 스케줄하지 않고 나머지 노드에 모든 파드를 스케줄 하겠다는 경우에 values의 값을 수정함으로 적용가능하다.

 

위 yaml코드에서 어피니티의 대상 레이블에서 node3을 제외하면 다음과 같이 된다.

 

 

다음과 같이 노드를 한정하여 실행하도록 만들 수도 있다.

podAffinity :
파드를 끌어당긴다.
조건이 충족되는 토폴로지 도메인에는 원하는 수의 파드를 얼마든지 채울 수 있다.

podAntiAffinity :
파드를 밀어낸다.
이를 requiredDuringSchedulingIgnoredDuringExecution 모드로 설정하면 각 토폴로지 도메인에는 하나의 파드만 스케줄링될 수 있다.
반면 preferredDuringSchedulingIgnoredDuringExecution로 설정하면 제약 조건이 강제성을 잃게 된다.

 

 

만일 파드에 2개 이상의 topologySpreadConstraint가 정의되어 있으면, 각 제약 조건은 논리 AND 연산으로 조합되며, kube-scheduler는 새로운 파드의 모든 제약 조건을 만족하는 노드를 찾는다.

 

 

이와 같이 파드 토폴로지 분배 제약 조건을 통해 스케줄될 위치를 균형있게 맞출 수 있으며 적절히 활용 시 고가용성, 저비용 토폴로지 환경을 구성하는데 도움이 될 수 있다.

 

 


자세한 내용은 쿠버네티스 공식 문서와 아래 내용을 읽어보면 이해가 잘 될 것입니다.

상단에 기재된 공식 문서 내에는 그림으로 자세한 설명이 나와있으니 참고하면 좋습니다.

https://kubernetes.io/blog/2020/05/introducing-podtopologyspread/

 

Introducing PodTopologySpread

Author: Wei Huang (IBM), Aldo Culquicondor (Google) Managing Pods distribution across a cluster is hard. The well-known Kubernetes features for Pod affinity and anti-affinity, allow some control of Pod placement in different topologies. However, these feat

kubernetes.io

 

'Kubernetes' 카테고리의 다른 글

Kubernetes 기초 - Scheduling(6)  (0) 2024.01.10
Kubernetes 기초 - Scheduling(5)  (0) 2024.01.09
Kubernetes 기초 - Scheduling(3)  (0) 2024.01.05
Kubernetes 기초 - Scheduling(2)  (0) 2024.01.04
Pod Affinity - topologyKey 상세 동작  (0) 2024.01.03