특정 노드에 파드 할당하기

특정 노드에 파드 할당하기 (Assigning Pods to Nodes)

특정 노드에서만 실행되도록 파드를 제한 하거나 특정 노드에서 실행되는 것을 선호 하도록 파드를 제약할 수 있어요. 이를 위한 방법은 여러 가지가 있으며, 권장되는 접근 방식은 모두 라벨 선택자를 사용해 선택을 용이하게 합니다. 보통은 그러한 제약을 전혀 설정할 필요가 없습니다. 스케줄러가 자동으로 합리적인 배치(예: 충분한 여유 리소스가 없는 노드에 파드를 배치하지 않도록 파드를 노드에 분산)를 하기 때문입니다. 그러나 파드가 SSD가 장착된 노드에 배치되도록 보장하거나, 많이 통신하는 두 서로 다른 서비스의 파드를 같은 가용성 영역에 공동 배치하는 것 같은 특정 상황에서는 파드가 배포될 노드를 제어하고 싶을 수 있습니다.

출처: 문서

본문

다음 방법 중 하나를 사용해 쿠버네티스가 특정 파드를 스케줄링할 위치를 선택하도록 할 수 있습니다:

노드 라벨 {#built-in-node-labels}

많은 다른 쿠버네티스 오브젝트처럼 노드에는 라벨이 있습니다. 라벨을 수동으로 부착할 수 있습니다. 쿠버네티스는 또한 클러스터의 모든 노드에 표준 라벨 집합을 채웁니다.

이 라벨의 값은 클라우드 제공자별이며 신뢰할 수 있다고 보장되지 않습니다. 예를 들어 kubernetes.io/hostname의 값은 일부 환경에서는 노드 이름과 같고 다른 환경에서는 다른 값일 수 있습니다.

노드 격리/제한

노드에 라벨을 추가하면 특정 노드나 노드 그룹에 파드를 스케줄링하도록 지정할 수 있습니다. 이 기능을 사용해 특정 파드가 특정 격리, 보안, 규제 속성을 가진 노드에서만 실행되도록 보장할 수 있습니다.

노드 격리에 라벨을 사용한다면 kubelet이 수정할 수 없는 라벨 키를 선택하세요. 이는 손상된 노드가 그 라벨을 자신에게 설정해 스케줄러가 워크로드를 손상된 노드에 스케줄링하지 않도록 방지합니다.

NodeRestriction admission 플러그인은 kubelet이 node-restriction.kubernetes.io/ 접두사를 가진 라벨을 설정하거나 수정하는 것을 방지합니다.

노드 격리에 그 라벨 접두사를 사용하려면:

  1. Node authorizer를 사용하고 NodeRestriction admission 플러그인을 활성화 했는지 확인한다.
  2. node-restriction.kubernetes.io/ 접두사로 노드에 라벨을 추가하고, node selectors에서 그 라벨을 사용한다. 예: example.com.node-restriction.kubernetes.io/fips=true 또는 example.com.node-restriction.kubernetes.io/pci-dss=true.

nodeSelector

nodeSelector는 노드 선택 제약의 가장 단순한 권장 형태입니다. 파드 명세에 nodeSelector 필드를 추가하고 대상 노드가 가져야 하는 노드 라벨을 지정할 수 있습니다. 쿠버네티스는 지정한 각 라벨을 모두 가진 노드에만 파드를 스케줄링합니다.

자세한 내용은 노드에 파드 할당을 참조하세요.

Affinity와 anti-affinity

nodeSelector는 특정 라벨을 가진 노드로 파드를 제약하는 가장 단순한 방법입니다. Affinity와 anti-affinity는 정의할 수 있는 제약 유형을 확장합니다. affinity와 anti-affinity의 몇 가지 이점:

  • affinity/anti-affinity 언어는 더 표현력이 풍부합니다. nodeSelector는 지정된 모든 라벨을 가진 노드만 선택합니다. Affinity/anti-affinity는 선택 로직에 대한 더 많은 제어를 제공합니다.
  • 규칙이 소프트(soft) 또는 선호(preferred) 이라고 나타낼 수 있어, 일치하는 노드를 찾지 못해도 스케줄러가 여전히 파드를 스케줄링합니다.
  • 노드 라벨만이 아니라 노드(또는 다른 토폴로지 도메인)에서 실행 중인 다른 파드의 라벨을 사용해 파드를 제약할 수 있습니다. 이는 어떤 파드가 노드에 공동 배치될 수 있는지에 대한 규칙을 정의할 수 있게 해줍니다.

affinity 기능은 두 가지 유형의 affinity로 구성됩니다:

  • 노드 affinity(node affinity)nodeSelector 필드처럼 동작하지만 더 표현력이 풍부하며 소프트 규칙을 지정할 수 있게 합니다.
  • 파드 간 affinity/anti-affinity(inter-pod affinity/anti-affinity) 는 다른 파드의 라벨에 대해 파드를 제약할 수 있게 합니다.

노드 affinity

노드 affinity는 개념적으로 nodeSelector와 유사하며, 노드 라벨을 기반으로 파드가 스케줄링될 수 있는 노드를 제약할 수 있게 합니다. 두 가지 유형의 노드 affinity가 있습니다:

  • requiredDuringSchedulingIgnoredDuringExecution: 규칙이 충족되지 않으면 스케줄러가 파드를 스케줄링할 수 없습니다. 이는 nodeSelector처럼 동작하지만 더 표현력 있는 구문입니다.
  • preferredDuringSchedulingIgnoredDuringExecution: 스케줄러가 규칙을 충족하는 노드를 찾으려 시도합니다. 일치하는 노드를 사용할 수 없으면 스케줄러가 여전히 파드를 스케줄링합니다.

앞선 유형에서 IgnoredDuringExecution은 쿠버네티스가 파드를 스케줄링한 후 노드 라벨이 바뀌어도 파드가 계속 실행됨을 의미합니다.

파드 스펙의 .spec.affinity.nodeAffinity 필드를 사용해 노드 affinity를 지정할 수 있습니다.

예를 들어 다음 파드 스펙을 고려해 보겠습니다:

이 예시에서 다음 규칙이 적용됩니다:

  • 노드는 반드시 topology.kubernetes.io/zone 키의 라벨을 가져야 하며, 그 라벨의 값은 반드시 antarctica-east1 또는 antarctica-west1 중 하나여야 합니다.
  • 노드는 바람직하게 another-node-label-key 키와 another-node-label-value 값을 가진 라벨이 있어야 합니다.

operator 필드를 사용해 규칙을 해석할 때 쿠버네티스가 사용할 논리 연산자를 지정할 수 있습니다. In, NotIn, Exists, DoesNotExist, Gt, Lt를 사용할 수 있습니다.

이것들이 어떻게 동작하는지 더 배우려면 Operators를 읽어보세요.

NotInDoesNotExist를 사용해 노드 anti-affinity 동작을 정의할 수 있습니다. 또는 노드 테인트를 사용해 특정 노드에서 파드를 밀어낼 수 있습니다.

nodeSelectornodeAffinity를 모두 지정하면 파드가 노드에 스케줄링되려면 둘 다 충족되어야 합니다.

nodeAffinity 유형과 관련된 nodeSelectorTerms에 여러 용어(terms)를 지정하면, 지정된 용어 중 하나라도 충족될 수 있으면 파드가 노드에 스케줄링될 수 있습니다(용어는 OR로 결합).

nodeSelectorTerms의 한 용어와 관련된 단일 matchExpressions 필드에 여러 표현식을 지정하면, 모든 표현식이 충족될 때만 파드가 노드에 스케줄링될 수 있습니다(표현식은 AND로 결합).

자세한 내용은 노드 affinity를 사용해 노드에 파드 할당을 참조하세요.

노드 affinity 가중치

preferredDuringSchedulingIgnoredDuringExecution affinity 유형의 각 인스턴스에 대해 1에서 100 사이의 weight를 지정할 수 있습니다. 스케줄러가 파드의 다른 모든 스케줄링 요구 사항을 충족하는 노드를 찾으면, 스케줄러는 노드가 충족하는 모든 선호 규칙을 순회하며 그 표현식에 대한 weight 값을 합계에 더합니다.

최종 합계는 노드에 대한 다른 우선순위 함수의 점수에 더해집니다. 스케줄러가 파드에 대한 스케줄링 결정을 만들 때 가장 높은 총 점수를 가진 노드가 우선됩니다.

예를 들어 다음 파드 스펙을 고려해 보겠습니다:

preferredDuringSchedulingIgnoredDuringExecution 규칙과 일치하는 두 개의 가능한 노드가 있는데, 하나는 label-1:key-1 라벨을, 다른 하나는 label-2:key-2 라벨을 가진다면, 스케줄러가 각 노드의 weight를 고려해 그 노드의 다른 점수에 가중치를 더하고, 최종 점수가 가장 높은 노드에 파드를 스케줄링합니다.

이 예시에서 쿠버네티스가 파드를 성공적으로 스케줄링하길 원한다면 kubernetes.io/os=linux 라벨이 있는 기존 노드가 있어야 합니다.

스케줄링 프로필별 노드 affinity

여러 스케줄링 프로필을 구성할 때 프로필을 노드 affinity와 연결할 수 있으며, 프로필이 특정 노드 집합에만 적용될 때 유용합니다. 그러려면 스케줄러 구성NodeAffinity 플러그인 args 필드에 addedAffinity를 추가하세요. 예:

apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration

profiles:
  - schedulerName: default-scheduler
  - schedulerName: foo-scheduler
    pluginConfig:
      - name: NodeAffinity
        args:
          addedAffinity:
            requiredDuringSchedulingIgnoredDuringExecution:
              nodeSelectorTerms:
              - matchExpressions:
                - key: scheduler-profile
                  operator: In
                  values:
                  - foo

addedAffinity는 PodSpec에 지정된 NodeAffinity에 더해 .spec.schedulerNamefoo-scheduler로 설정하는 모든 파드에 적용됩니다. 즉, 파드와 일치하기 위해 노드는 addedAffinity와 파드의 .spec.NodeAffinity를 모두 충족해야 합니다.

addedAffinity는 최종 사용자에게 보이지 않으므로 그 동작이 예상 밖일 수 있습니다. 스케줄러 프로필 이름과 명확한 상관관계가 있는 노드 라벨을 사용하세요.

DaemonSet의 파드를 만드는 DaemonSet 컨트롤러는 스케줄링 프로필을 지원하지 않습니다. DaemonSet 컨트롤러가 파드를 만들 때 기본 쿠버네티스 스케줄러가 그 파드를 배치하고 DaemonSet 컨트롤러의 nodeAffinity 규칙을 준수합니다.

파드 간 affinity와 anti-affinity

파드 간 affinity와 anti-affinity는 노드 라벨이 아니라 그 노드에서 이미 실행 중인 파드의 라벨을 기반으로 파드가 스케줄링될 수 있는 노드를 제약할 수 있게 합니다.

파드 간 Affinity와 Anti-affinity의 유형

파드 간 affinity와 anti-affinity는 "이 파드는 그 X가 이미 규칙 Y를 충족하는 파드 하나 이상을 실행 중이라면 X에서 실행되어야(또는 anti-affinity의 경우 실행되지 말아야) 한다"는 형태를 취합니다. 여기서 X는 노드, 랙, 클라우드 제공자 존/리전 같은 토폴로지 도메인이고 Y는 쿠버네티스가 충족하려 시도하는 규칙입니다.

이 규칙(Y)을 선택적 연관 네임스페이스 목록과 함께 라벨 선택자로 표현합니다. 파드는 쿠버네티스에서 네임스페이스가 있는 객체이므로 파드 라벨도 암시적으로 네임스페이스를 가집니다. 파드 라벨에 대한 모든 라벨 선택자는 쿠버네티스가 그 라벨을 찾아야 할 네임스페이스를 지정해야 합니다.

토폴로지 도메인(X)은 시스템이 도메인을 나타내는 데 사용하는 노드 라벨의 키인 topologyKey를 사용해 표현합니다. 예시는 잘 알려진 라벨, 어노테이션, 테인트를 참조하세요.

파드 간 affinity와 anti-affinity는 상당한 양의 처리가 필요하며, 이는 대규모 클러스터에서 스케줄링을 크게 느리게 할 수 있습니다. 수백 개가 넘는 노드가 있는 클러스터에서는 사용하지 않는 것을 권장합니다.

파드 anti-affinity는 노드가 일관되게 라벨이 지정되어야 합니다. 다시 말해 클러스터의 모든 노드는 topologyKey와 일치하는 적절한 라벨을 가져야 합니다. 일부 또는 모든 노드에 지정된 topologyKey 라벨이 없으면 의도하지 않은 동작이 발생할 수 있습니다.

노드 affinity와 유사하게 두 가지 유형의 파드 affinity와 anti-affinity가 있습니다:

  • requiredDuringSchedulingIgnoredDuringExecution
  • preferredDuringSchedulingIgnoredDuringExecution

예를 들어 requiredDuringSchedulingIgnoredDuringExecution affinity를 사용해 서로 많이 통신하는 두 서비스의 파드를 같은 클라우드 제공자 존에 공동 배치하라고 스케줄러에 지시할 수 있습니다. 마찬가지로 preferredDuringSchedulingIgnoredDuringExecution anti-affinity를 사용해 서비스의 파드를 여러 클라우드 제공자 존에 분산할 수 있습니다.

파드 간 affinity를 사용하려면 파드 스펙의 affinity.podAffinity 필드를 사용하세요. 파드 간 anti-affinity에는 파드 스펙의 affinity.podAntiAffinity 필드를 사용합니다.

스케줄링 동작

새 파드를 스케줄링할 때 쿠버네티스 스케줄러는 현재 클러스터 상태의 맥락에서 파드의 affinity/anti-affinity 규칙을 평가합니다:

  1. 하드 제약 (노드 필터링):

    • podAffinity.requiredDuringSchedulingIgnoredDuringExecutionpodAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution:
      • 스케줄러는 기존 파드를 기반으로 이 필수 affinity와 anti-affinity 규칙을 충족하는 노드에 새 파드가 할당되도록 보장합니다.
  2. 소프트 제약 (점수 매기기):

    • podAffinity.preferredDuringSchedulingIgnoredDuringExecutionpodAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution:
      • 스케줄러는 이 선호 affinity/anti-affinity 규칙을 얼마나 잘 충족하는지에 따라 노드에 점수를 매겨 파드 배치를 최적화합니다.
  3. 무시된 필드:

    • 기존 파드의 podAffinity.preferredDuringSchedulingIgnoredDuringExecution:
      • 새 파드에 대한 스케줄링 결정 중에는 이 선호 affinity 규칙이 고려되지 않습니다.
    • 기존 파드의 podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution:
      • 마찬가지로 스케줄링 중 기존 파드의 선호 anti-affinity 규칙은 무시됩니다.

스스로에 대한 파드 간 Affinity를 가진 파드 그룹 스케줄링

스케줄링되는 현재 파드가 스스로에 대해 affinity를 가진 연속 중 첫 번째라면, 다른 모든 affinity 검사를 통과하면 스케줄링이 허용됩니다. 이는 클러스터의 다른 어떤 파드도 이 파드의 네임스페이스와 선택자와 일치하지 않고, 파드가 자신의 용어와 일치하며, 선택된 노드가 요청된 모든 토폴로지를 충족함을 확인해 결정됩니다. 이는 모든 파드가 파드 간 affinity를 지정해도 교착 상태(deadlock)가 없도록 보장합니다.

파드 Affinity 예시 {#an-example-of-a-pod-that-uses-pod-affinity}

다음 파드 스펙을 고려해 보겠습니다:

이 예시는 파드 affinity 규칙 하나와 파드 anti-affinity 규칙 하나를 정의합니다. 파드 affinity 규칙은 "하드" requiredDuringSchedulingIgnoredDuringExecution을 사용하고, anti-affinity 규칙은 "소프트" preferredDuringSchedulingIgnoredDuringExecution을 사용합니다.

affinity 규칙은 다른 파드가 security=S1으로 라벨이 지정된 특정 zone에 예시 파드를 배치하는 것만 허용한다고 지정합니다. 예를 들어 "Zone V"라고 부르는 지정된 존이 있는 클러스터가 있고, 그 존은 topology.kubernetes.io/zone=V로 라벨이 지정된 노드로 구성되어 있다면, 스케줄러는 Zone V 안에 이미 security=S1으로 라벨이 지정된 파드가 하나 이상 있는 한 예시 파드를 Zone V의 어떤 노드에도 할당할 수 있습니다. 반대로 Zone V에 security=S1 라벨의 파드가 없다면 스케줄러는 예시 파드를 그 존의 어떤 노드에도 할당하지 않습니다.

anti-affinity 규칙은 다른 파드가 security=S2으로 라벨이 지정된 특정 zone에 파드를 스케줄링하는 것을 피하려 시도해야 한다고 지정합니다. 예를 들어 "Zone R"이라고 부르는 지정된 존이 있고 그 존은 topology.kubernetes.io/zone=R로 라벨이 지정된 노드로 구성되어 있다면, Zone R 안에 이미 security=S2으로 라벨이 지정된 파드가 하나 이상 있는 한 스케줄러는 예시 파드를 Zone R의 어떤 노드에도 할당하는 것을 피해야 합니다. 반대로 security=S2 라벨의 파드가 없으면 anti-affinity 규칙은 Zone R로의 스케줄링에 영향을 주지 않습니다.

파드 affinity와 anti-affinity의 예시에 더 익숙해지려면 design proposal을 참조하세요.

파드 affinity와 anti-affinity의 operator 필드에서 In, NotIn, Exists, DoesNotExist 값을 사용할 수 있습니다.

이것들이 어떻게 동작하는지 더 배우려면 Operators를 읽어보세요.

원칙적으로 topologyKey는 성능과 보안 이유로 다음과 같은 예외를 제외하고 허용된 라벨 키가 될 수 있습니다:

  • 파드 affinity와 anti-affinity에서는 requiredDuringSchedulingIgnoredDuringExecutionpreferredDuringSchedulingIgnoredDuringExecution 모두에서 빈 topologyKey 필드가 허용되지 않습니다.
  • requiredDuringSchedulingIgnoredDuringExecution 파드 anti-affinity 규칙의 경우 admission controller LimitPodHardAntiAffinityTopologytopologyKeykubernetes.io/hostname으로 제한합니다. 사용자 정의 토폴로지를 허용하려면 admission controller를 수정하거나 비활성화할 수 있습니다.

labelSelectortopologyKey 외에도, labelSelectortopologyKey와 같은 수준의 namespaces 필드를 사용해 labelSelector가 매칭해야 할 네임스페이스 목록을 선택적으로 지정할 수 있습니다. 생략하거나 비워 두면 namespaces는 affinity/anti-affinity 정의가 나타나는 파드의 네임스페이스로 기본 설정됩니다.

네임스페이스 선택자

namespaceSelector를 사용해 일치 네임스페이스를 선택할 수도 있습니다. 이는 네임스페이스 집합에 대한 라벨 질의입니다. affinity 용어는 namespaceSelectornamespaces 필드가 모두 선택한 네임스페이스에 적용됩니다. 빈 namespaceSelector({})는 모든 네임스페이스와 일치하는 반면, null 또는 빈 namespaces 목록과 null namespaceSelector는 규칙이 정의된 파드의 네임스페이스와 일치한다는 점에 주의하세요.

matchLabelKeys

matchLabelKeys 필드는 beta 수준 필드이며 쿠버네티스 v1.37에서 기본 활성화됩니다. 비활성화하려면 MatchLabelKeysInPodAffinity 기능 게이트를 통해 명시적으로 비활성화해야 합니다.

쿠버네티스는 파드 affinity 또는 anti-affinity에 선택적 matchLabelKeys 필드를 포함합니다. 이 필드는 파드 (anti)affinity를 충족할 때 들어오는 파드의 라벨과 일치해야 하는 라벨의 키를 지정합니다.

키는 파드 라벨에서 값을 조회하는 데 사용됩니다. 그 키-값 라벨은 labelSelector 필드를 사용해 정의된 매칭 제한과 결합됩니다(structuring AND). 결합된 필터링은 파드 (anti)affinity 계산에 포함될 기존 파드 집합을 선택합니다.

파드에서 직접 업데이트될 수 있는 라벨과 matchLabelKeys를 사용하는 것은 권장되지 않습니다. matchLabelKeys에 지정된 파드 라벨을 직접 편집해도(즉 deployment가 아닌), kube-apiserver는 병합된 labelSelector에 라벨 업데이트를 반영하지 않습니다.

일반적인 사용 사례는 matchLabelKeyspod-template-hash(Deployment의 일부로 관리되는 파드에 설정되며, 각 리비전마다 고유한 값)와 함께 사용하는 것입니다. matchLabelKeyspod-template-hash를 사용하면 들어오는 파드와 같은 리비전에 속하는 파드를 대상으로 지정할 수 있어, 롤링 업그레이드가 affinity를 깨뜨리지 않습니다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: application-server
...
spec:
  template:
    spec:
      affinity:
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - database
            topologyKey: topology.kubernetes.io/zone
            # Only Pods from a given rollout are taken into consideration when calculating pod affinity.
            # If you update the Deployment, the replacement Pods follow their own affinity rules
            # (if there are any defined in the new Pod template)
            matchLabelKeys:
            - pod-template-hash

mismatchLabelKeys

mismatchLabelKeys 필드는 beta 수준 필드이며 쿠버네티스 v1.37에서 기본 활성화됩니다. 비활성화하려면 MatchLabelKeysInPodAffinity 기능 게이트를 통해 명시적으로 비활성화해야 합니다.

쿠버네티스는 파드 affinity 또는 anti-affinity에 선택적 mismatchLabelKeys 필드를 포함합니다. 이 필드는 파드 (anti)affinity를 충족할 때 들어오는 파드의 라벨과 일치하지 말아야 하는 라벨의 키를 지정합니다.

파드에서 직접 업데이트될 수 있는 라벨과 mismatchLabelKeys를 사용하는 것은 권장되지 않습니다. mismatchLabelKeys에 지정된 파드 라벨을 직접 편집해도(즉 deployment가 아닌), kube-apiserver는 병합된 labelSelector에 라벨 업데이트를 반영하지 않습니다.

한 가지 사용 사례는 파드가 같은 테넌트나 팀의 파드만 스케줄링되는 토폴로지 도메인(노드, 존 등)으로 가도록 보장하는 것입니다. 다시 말해 두 다른 테넌트의 파드가 같은 시간에 같은 토폴로지 도메인에서 실행되는 것을 피하고 싶습니다.

apiVersion: v1
kind: Pod
metadata:
  labels:
    # Assume that all relevant Pods have a "tenant" label set
    tenant: tenant-a
...
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      # ensure that Pods associated with this tenant land on the correct node pool
      - matchLabelKeys:
          - tenant
        labelSelector: {}
        topologyKey: node-pool
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      # ensure that Pods associated with this tenant can't schedule to nodes used for another tenant
      - mismatchLabelKeys:
        - tenant # whatever the value of the "tenant" label for this Pod, prevent
                 # scheduling to nodes in any pool where any Pod from a different
                 # tenant is running.
        labelSelector:
          # We have to have the labelSelector which selects only Pods with the tenant label,
          # otherwise this Pod would have anti-affinity against Pods from daemonsets as well, for example,
          # which aren't supposed to have the tenant label.
          matchExpressions:
          - key: tenant
            operator: Exists
        topologyKey: node-pool

더 실용적인 사용 사례

파드 간 affinity와 anti-affinity는 ReplicaSet, StatefulSet, Deployment 같은 더 높은 수준의 컬렉션과 함께 사용될 때 훨씬 더 유용할 수 있습니다. 이러한 규칙을 사용하면 워크로드 집합이 같은 정의된 토폴로지에 공동 배치되도록 구성할 수 있습니다. 예를 들어 두 관련 파드를 같은 노드에 배치하는 것을 선호하는 경우입니다.

예를 들어: 3노드 클러스터를 상상해 보세요. 그 클러스터를 사용해 웹 애플리케이션과 인메모리 캐시(예: Redis)를 실행합니다. 이 예시에서 웹 애플리케이션과 메모리 캐시 사이의 지연 시간이 실용적으로 가능한 한 낮아야 한다고 가정합니다. 파드 간 affinity와 anti-affinity를 사용해 웹 서버를 캐시와 최대한 공동 배치할 수 있습니다.

Redis 캐시의 다음 예시 Deployment에서 replica는 app=store 라벨을 얻습니다. podAntiAffinity 규칙은 스케줄러에 app=store 라벨이 있는 여러 replica를 단일 노드에 배치하는 것을 피하라고 지시합니다. 이는 각 캐시를 별도의 노드에 만듭니다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis-cache
spec:
  selector:
    matchLabels:
      app: store
  replicas: 3
  template:
    metadata:
      labels:
        app: store
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - store
            topologyKey: "kubernetes.io/hostname"
      containers:
      - name: redis-server
        image: redis:3.2-alpine

웹 서버의 다음 예시 Deployment는 app=web-store 라벨로 replica를 만듭니다. 파드 affinity 규칙은 스케줄러에 app=store 라벨이 있는 파드를 가진 노드에 각 replica를 배치하라고 지시합니다. 파드 anti-affinity 규칙은 여러 app=web-store 서버를 단일 노드에 절대 배치하지 말라고 스케줄러에 지시합니다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-server
spec:
  selector:
    matchLabels:
      app: web-store
  replicas: 3
  template:
    metadata:
      labels:
        app: web-store
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - web-store
            topologyKey: "kubernetes.io/hostname"
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - store
            topologyKey: "kubernetes.io/hostname"
      containers:
      - name: web-app
        image: nginx:1.16-alpine

앞선 두 Deployment를 만들면 다음 클러스터 레이아웃이 생기며, 각 웹 서버가 캐시와 함께 세 개의 별도 노드에 공동 배치됩니다.

node-1 node-2 node-3
webserver-1 webserver-2 webserver-3
cache-1 cache-2 cache-3

전반적인 효과는 각 캐시 인스턴스가 같은 노드에서 실행되는 단일 클라이언트에 의해 접근될 가능성이 높다는 것입니다. 이 접근 방식은 스큐(불균형 부하)와 지연 시간을 모두 최소화하는 것을 목표로 합니다.

파드 anti-affinity를 사용할 다른 이유가 있을 수 있습니다. 이 예시와 같은 기법을 사용해 고가용성을 위해 anti-affinity로 구성된 StatefulSet의 예시는 ZooKeeper 튜토리얼을 참조하세요.

nodeName

nodeName은 affinity나 nodeSelector보다 더 직접적인 노드 선택 형태입니다. nodeName은 파드 스펙의 필드입니다. nodeName 필드가 비어 있지 않으면 스케줄러가 파드를 무시하고, 이름이 지정된 노드의 kubelet이 그 노드에 파드를 배치하려 시도합니다. nodeName을 사용하면 nodeSelector나 affinity/anti-affinity 규칙을 사용하는 것을 덮어씁니다.

nodeName으로 노드를 선택할 때의 몇 가지 제한:

  • 이름이 지정된 노드가 존재하지 않으면 파드가 실행되지 않으며, 어떤 경우 자동으로 삭제될 수 있습니다.
  • 이름이 지정된 노드에 파드를 수용할 리소스가 없으면 파드가 실패하고 그 이유(예: OutOfmemory 또는 OutOfcpu)가 표시됩니다.
  • 클라우드 환경의 노드 이름은 항상 예측 가능하거나 안정적이지 않습니다.

nodeName은 사용자 정의 스케줄러 또는 구성된 스케줄러를 우회해야 하는 고급 사용 사례를 위해 의도되었습니다. 스케줄러를 우회하면 할당된 노드가 초과 구독되면 실패한 파드가 생길 수 있습니다. 노드 affinitynodeSelector 필드를 사용해 스케줄러를 우회하지 않고 특정 노드에 파드를 할당할 수 있습니다.

nodeName 필드를 사용하는 파드 스펙의 예시:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx
  nodeName: kube-01

위 파드는 kube-01 노드에서만 실행됩니다.

nominatedNodeName

nominatedNodeName은 외부 구성 요소가 대기 중인 파드에 노드를 지명하는 데 사용할 수 있습니다. 이 지명은 최선의 노력(best effort)입니다. 스케줄러가 파드가 지명된 노드에 갈 수 없다고 결정하면 무시될 수 있습니다.

또한 이 필드는 스케줄러가 (덮어)쓸 수 있습니다:

  • 스케줄러가 선점(preemption)을 통해 지명할 노드를 찾는 경우.
  • 스케줄러가 파드가 갈 위치를 결정하고 바인딩 주기로 옮기는 경우.
    • 이 경우, nominatedNodeName은 파드가 WaitOnPermit 또는 PreBind 확장 지점을 거쳐야 할 때만 설정됩니다.

nominatedNodeName 필드를 사용하는 파드 상태의 예시:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
...
status:
  nominatedNodeName: kube-01

Pod 토폴로지 분산 제약

토폴로지 분산 제약 을 사용해 지역, 존, 노드 같은 장애 도메인 또는 정의한 다른 토폴로지 도메인 사이에 파드가 클러스터 전체에 어떻게 분산되는지 제어할 수 있습니다. 이는 성능, 예상 가용성, 전반적인 활용도를 개선하기 위해 할 수 있습니다.

이것들이 어떻게 동작하는지 더 배우려면 Pod 토폴로지 분산 제약을 읽어보세요.

Pod 토폴로지 라벨

파드는 할당된 노드에 그 라벨이 있으면 그 노드에서 토폴로지 라벨(topology.kubernetes.io/zonetopology.kubernetes.io/region)을 상속받습니다. 이 라벨은 Downward API를 통해 활용해 워크로드에 노드 토폴로지 인식을 제공할 수 있습니다.

downward API를 사용해 존과 지역을 얻는 파드의 예시:

apiVersion: v1
kind: Pod
metadata:
  name: pod-with-topology-labels
spec:
  containers:
    - name: app
      image: alpine
      command: ["sh", "-c", "env"]
      env:
        - name: MY_ZONE
          valueFrom:
            fieldRef:
              fieldPath: metadata.labels['topology.kubernetes.io/zone']
        - name: MY_REGION
          valueFrom:
            fieldRef:
              fieldPath: metadata.labels['topology.kubernetes.io/region']

Operators

다음은 위에서 언급한 nodeAffinitypodAffinityoperator 필드에서 사용할 수 있는 모든 논리 연산자입니다.

Operator 동작
In 라벨 값이 제공된 문자열 집합에 존재한다
NotIn 라벨 값이 제공된 문자열 집합에 포함되지 않는다
Exists 이 키를 가진 라벨이 객체에 존재한다
DoesNotExist 이 키를 가진 라벨이 객체에 존재하지 않는다

다음 연산자는 nodeAffinity에서만 사용할 수 있습니다.

Operator 동작
Gt 필드 값을 정수로 파싱하고, 이 선택자가 이름을 지은 라벨의 값을 파싱한 결과 정수가 이 정수보다 크다
Lt 필드 값을 정수로 파싱하고, 이 선택자가 이름을 지은 라벨의 값을 파싱한 결과 정수가 이 정수보다 작다

GtLt 연산자는 정수가 아닌 값에서는 동작하지 않습니다. 주어진 값이 정수로 파싱되지 않으면 파드는 스케줄링되지 않습니다. 또한 GtLtpodAffinity에서 사용할 수 없습니다.

더 알아보기 (Learn more)