Kubernetes 이야기

Pod 스케줄링 본문

Kubernetes/일반

Pod 스케줄링

kmaster 2022. 4. 2. 21:38
반응형

Kubernetes 에는 Pod가 어느 노드에 생성되어야 하는지 결정할 수 있는 다양한 방법이 있다. 이 중 대표적으로 아래와 같은 방법이 있다.

 

  • Assingning Pods to Nodes ( nodeSelector / Affinity and anti-affinity )
  • Tains와 Tolerations
  • Kuber-scheduler

1. Assingning Pods to Nodes

- nodeSelector

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  nodeSelector:
    disktype: ssd

- affinity/anti-affinity

affinity 는 node와 pod affinity로 구분하여 설정할 수 있다.

https://opsani.com/resources/kubernetes-node-affinities-best-practices/
https://opsani.com/resources/kubernetes-node-affinities-best-practices/

 

nodeSelector 는 파드를 특정 레이블이 있는 노드로 제한하는 매우 간단한 방법을 제공한다. affinity/anti-affinity 기능은 표현할 수 있는 제약 종류를 크게 확장한다. 스케쥴링은 아래와같이 구분된다.

 

  • requiredDuringSchedulingIgnoredDuringExecution
  • preferredDuringSchedulingIgnoredDuringExecution
  • requiredDuringSchedulingRequiredDuringExecution
  • preferredDuringSchedulingRequiredDuringExecution

required(hard affinity) & preferred(soft affinity)는 반드시 포함해야 하는지 또는 우선시하되 필수는 아닌지를 결정하는 조건이고, Ignored & Required는 운영 중(Runtime) Node의 Label이 변경되면 무시할 것인지(Ignore) 또는 즉시 Eviction 처리(Required)하여 재기동을 수행할 것인지를 결정한다.

 

affinity는 또한 nodeAffinity와 podAffinity 가 있다.

apiVersion: v1
kind: Pod
metadata:
  name: with-node-affinity
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/e2e-az-name
            operator: In
            values:
            - e2e-az1
            - e2e-az2
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: another-node-label-key
            operator: In
            values:
            - another-node-label-value
  containers:
  - name: with-node-affinity
    image: k8s.gcr.io/pause:2.0

이 노드 어피니티 규칙은 키가 kubernetes.io/e2e-az-name 이고 값이 e2e-az1 또는 e2e-az2 인 레이블이 있는 노드에만 파드를 배치할 수 있다고 말한다. 또한, 이 기준을 충족하는 노드들 중에서 키가 another-node-label-key 이고 값이 another-node-label-value 인 레이블이 있는 노드를 선호하도록 한다.

예시에서 연산자 In 이 사용되고 있는 것을 볼 수 있다. 새로운 노드 어피니티 구문은 다음의 연산자들을 지원한다. In, NotIn, Exists, DoesNotExist, Gt, Lt. NotIn 과 DoesNotExist 를 사용해서 안티-어피니티를 수행하거나, 특정 노드에서 파드를 쫓아내는 노드 테인트(taint)를 설정할 수 있다.

nodeSelector 와 nodeAffinity 를 모두 지정한다면 파드가 후보 노드에 스케줄되기 위해서는 둘 다 반드시 만족해야 한다.

apiVersion: v1
kind: Pod
metadata:
  name: with-pod-affinity
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: security
            operator: In
            values:
            - S1
        topologyKey: topology.kubernetes.io/zone
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: security
              operator: In
              values:
              - S2
          topologyKey: topology.kubernetes.io/zone
  containers:
  - name: with-pod-affinity
    image: k8s.gcr.io/pause:2.0

이 파드의 어피니티는 하나의 파드 어피니티 규칙과 하나의 파드 안티-어피니티 규칙을 정의한다. 이 예시에서 podAffinity 는 requiredDuringSchedulingIgnoredDuringExecution 이고 podAntiAffinity 는 preferredDuringSchedulingIgnoredDuringExecution 이다. 파드 어피니티 규칙에 의하면 키 "security" 와 값 "S1"인 레이블이 있는 하나 이상의 이미 실행 중인 파드와 동일한 영역에 있는 경우에만 파드를 노드에 스케줄할 수 있다. (보다 정확하게는, 클러스터에 키 "security"와 값 "S1"인 레이블을 가지고 있는 실행 중인 파드가 있는 키 topology.kubernetes.io/zone 와 값 V인 노드가 최소 하나 이상 있고, 노드 N이 키 topology.kubernetes.io/zone 와 일부 값이 V인 레이블을 가진다면 파드는 노드 N에서 실행할 수 있다.) 파드 안티-어피니티 규칙에 의하면 파드는 키 "security"와 값 "S2"인 레이블을 가진 파드와 동일한 영역의 노드에 스케줄되지 않는다.

 

예시1) 2개의 deployment의 replicas들을 항상 같은 노드에 위치시키기

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
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

위와 같이 배포하면 아래와 같이 생성된다.

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

 

예시2) worker 노드에 배포하며, 같은 노드에 중복되지 않도록 위치시키기

...
nodeAffinity:
  preferredDuringSchedulingIgnoredDuringExecution:
    nodeSelectorTerms:
      - matchExpressions:
        - key: "node-role.kubernetes.io/worker"
          operator: In
          values: 
          - ""
podAntiAffinity:
  preeferredDuringSchedulingIgnoredDuringExecution:
    - labelSelector:
        matchExpressions:
        - key: role
          operator: In
          values:
          - "monitoring"
      topologyKey: "kubernetes.io/hostname"

nodeAffinity를 이용해 worker노드에 배포하도록 구성 후 podAntiAffinity를 이용해 role=monitoring라는 label이 들어간 pod를 topologyKey를 기준으로 같은노드에 중복배포 되지않도록 구현한다.

 

위의 예는 hostname으로 제외를 하기 때문에 worker노드가 2개일 경우 replicas가 1일때 worker1에 배포되었다면 replicas가 2일땐 worker2에는 잘배포되지만, replicas가 3일때는 조건에 맞는 hostname이 없기때문에 pod배포가 실패한다.

 

- topologySpreadConstraints 

topologySpreadConstraints  지역, 영역, 노드 그리고 기타 사용자-정의 토폴로지 도메인과 같이 장애-도메인으로 설정된 클러스터에 걸쳐 파드가 분산되는 방식을 제어할 수 있다. 이를 통해 고가용성뿐만 아니라, 효율적인 리소스 활용의 목적을 이루는 데 도움이 된다. ( kubernetes 1.19에서 stable 상태로 승격되었다. )

 

예시) worker 노드에 배포하며, 같은 노드에 중복되지 않도록 위치시키기

...
  topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: "kubernetes.io/hostname"
    whenUnsatisfiable: ScheduleAnyway
  • maxSkew: 파드가 균등하지않게 분산될수 있는정도
  • topologyKey: 어떤 항목을 기준으로 구분할것인지
  • whenUnsatisfiable: 분산제약조건을 만족하지 않을경우 처리방법
위의 예제에서는 pod가 균등하지않게 분산될수 있는정도는 1로 설정되었기때문에, topologyKey 기준으로 1개이상 차이가 나면 안된다.

예를들어, 노드개수를 총 3대이고 replicas가 2라면 worker1: 1, worker2: 1, worker3: 0으로 배치될 수 있고, replicas가 3일때는 모든 서버에 1개씩 pod가 생성된다.

topologySpreadConstraints 는 균등배포가 가능하지만 리소스가 없는 노드에도 필수배포가 될 수 있다.

2. Tains와 Tolerations

노드 어피니티는 노드 셋을 (기본 설정 또는 어려운 요구 사항으로) 끌어들이는 파드의 속성이다. 테인트 는 그 반대로, 노드가 파드 셋을 제외할 수 있다.

 

노드에 taint 설정

kubectl taint nodes node1 key1=value1:NoSchedule

설정된 taint 제거

kubectl taint nodes node1 key1=value1:NoSchedule-

tolerations 를 사용하여 taint설정된 노드에도 배포 가능하도록 설정

tolerations:
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoSchedule"

3. Kuber-scheduler

Kube-scheduler 는 Kubernetes의 기본 스케쥴러이며 컨트롤 플레인의 일부로 실행된다.

 

이 scheduler는 2단계 작업에서 파드에 대한 노드를 선택한다.

  1. 필터링
  2. 스코어링(scoring)

필터링 단계는 파드를 스케줄링 할 수 있는 노드 셋을 찾는다. 여기에서는 파드의 특정 리소스 요청을 충족시키기에 충분한 가용 리소스가 있는지 확인한다. 

 

스코어링 단계에서 스케줄러는 목록에 남아있는 노드의 순위를 지정하여 가장 적합한 파드 배치를 선택한다. 스케줄러는 사용 중인 스코어링 규칙에 따라 이 점수를 기준으로 필터링에서 통과된 각 노드에 대해 점수를 지정한다.

 

마지막으로 kube-scheduler는 파드를 순위가 가장 높은 노드에 할당한다. 점수가 같은 노드가 두 개 이상인 경우 kube-scheduler는 이들 중 하나를 임의로 선택한다.

 

kubenetes scheduler에는 기본적으로 default-scheduler가 설정된다. 이 기본 스케쥴러를 다른 profile을 이용하여 방식을 변경할 수 있다.

 

# /etc/kubernetes/scheduler-config.yaml (생성)

apiVersion: kubescheduler.config.k8s.io/v1beta1
kind: KubeSchedulerConfiguration
leaderElection:
  leaderElect: true
clientConnection:
  kubeconfig: /etc/kubernetes/scheduler.conf
profiles:
- schedulerName: default-scheduler
- schedulerName: worker-scheduler
  pluginConfig:
    - name: NodeAffinity
      args:
        addedAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: "node-role.kubernetes.io/worker"
                operator: In
                values:
                - ""
    - name: PodTopologySpread
      args:
        defaultConstraints:
          - maxSkew: 1
            topologyKey: "kubernetes.io/hostname"
            whenUnsatisfiable: ScheduleAnyway
스케쥴러 네임을 2개로 아래와 같이 구분했는데
  - schedulerName: default-scheduler
  - schedulerName: worker-scheduler
worker-scheduler를 추가하기전에 default-scheduler는 정책없이 생성해두는것이 권장사항이다.

 

적용은 아래와 같이 한다.

vi /etc/kubernetes/manifests/kube-scheduler.yaml

command에 아래항목추가.
 - --config=/etc/kubernetes/scheduler-config.yaml

volumeMounts 추가
    volumeMounts:
    - mountPath: /etc/kubernetes/scheduler-config.yaml
      name: scheduler-config
      readOnly: true

volumes 추가
  volumes:
  - hostPath:
      path: /etc/kubernetes/scheduler-config.yaml
      type: FileOrCreate
마스터가 3중화일 경우에는 3대 모두 적용해야 한다.

 

위와 같이 설정하고 아래와 같이 deployment를 설정하면 모든 worker노드에 균형있게 배포되는 것을 확인할 수 있다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  replicas: 6
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      schedulerName: worker-scheduler
      containers:
      - name: nginx
        image: nginx

여기서 schedulerName을 새로 만든 profile 이름으로 적용해야 한다.

 

 

 

 

 

참고자료

https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/

https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/

https://kubernetes.io/docs/concepts/scheduling-eviction/kube-scheduler/

https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/https://kubernetes.io/ko/docs/concepts/scheduling-eviction/taint-and-toleration/

https://kubernetes.io/docs/reference/scheduling/config/

반응형

'Kubernetes > 일반' 카테고리의 다른 글

Service Account 권한 설정 및 kube config  (0) 2022.04.04
PodNodeSelector  (0) 2022.04.02
Kubernetes Pod Eviction  (0) 2022.04.02
Pod에서 imagePullSecrets 설정하기  (0) 2022.03.24
kubernetes yaml 을 helm chart로 변환하기  (0) 2022.03.15
Comments