공부하고 기록하는, 경제학과 출신 개발자의 노트

학습일지/kubernetes

CKA 대비 kubernetes 스터디 - 2. Scheduler

inspirit941 2022. 2. 27. 11:48
반응형

Manual Scheduling

만약 k8s에서 해주는 scheduling 대신 직접 스케줄링을 진행하고 싶다면?

 

스크린샷 2022-02-23 오후 10 21 34

  • 모든 pod에는 선택옵션으로 nodeName 필드를 추가할 수 있다.
    • 보통은 사용자가 입력하는 게 아니고 k8s manifest를 적용하는 과정에서 자동으로 부여되는 값.
  • Scheduler는 pod의 nodeName 필드를 탐색하면서, 해당 필드가 정의되지 않은 pod를 찾아낸다. 이 Pod들이 scheduling 대상이 됨.
  • 선별된 pod에 scheduling 알고리즘을 돌려서, 해당 pod가 실행될 nodeName을 결정한다.

 

스크린샷 2022-02-23 오후 10 22 49

pod에 스케줄링이 되지 않으면, pod는 pending 상태를 유지한다.

스케줄링 알고리즘을 사용하지 않는 상태에서 가장 쉬운 pod 스케줄 방법은 nodeName 필드를 직접 설정하는 것.

 

 

스크린샷 2022-02-23 오후 10 26 52

이미 생성된 pod에 스케줄링을 적용하고 싶다면,

  • pod-bind-definition.yaml 처럼 pod과 node를 연결할 수 있는 오브젝트를 정의한 뒤
  • binding API에 post Request를 보낸다. 이 때, yaml파일은 json 형식으로 변환해서 post Request의 body에 담아야 한다.

 

Labels & Selectors

스크린샷 2022-02-24 오후 1 43 54

  • labels : 특정한 것들을 그룹핑하기 위한 표준 (보통 criteria 기준)
  • selectors : label 기준으로 선택하기 위한 입력값.

생성 방법

 

스크린샷 2022-02-24 오후 1 44 44

 

적용 방법

kubectl get pods --selector app=App1

-> k8s 내부적으로도 labels & selectors를 활용한다.

 

 

스크린샷 2022-02-24 오후 1 47 23

ReplicaSet의 경우

  • spec.template.metadata.labels 필드 - replicaset이 생성하고 관리하는 pod에 붙은 label
  • metadata.labels 필드 - replicaset 객체 자체에 붙는 label. 외부에서 replicaset을 찾아야 할 때 사용하는 필드.
  • selector.matchLabels 필드 - replicaset 객체가 관리해야 하는 pod를 select하기 위한 필드.
    • replicaSet을 생성할 때 pod의 label과 replicaset의 selector가 일치해야 정상적으로 객체가 생성됨.

 

Annotations

스크린샷 2022-02-24 오후 1 56 08

정보 기록을 위해 사용하는 optional 필드.

 

 

 

Taints & Tolerations

scheduling 과정에서 restriction을 추가하는 기능.

스크린샷 2022-02-24 오후 7 26 31

기본적으로는 세 개의 노드에, 네 개의 pod 중 어떤 것이든 할당될 수 있다.

  • 만약 node 1에 Taint=blue 라고 설정을 해두면, unwanted pod은 해당 노드에 할당될 수 없다.
  • node 1에 할당되어야 하는 pod에게는 toleration을 설정한다. 예컨대 pod D에 toleration을 설정하면 된다.

스크린샷 2022-02-24 오후 7 30 26

 

Taints는 Node에 설정하는 것이고, Tolerant는 Pod에 설정하는 것이다.

  • pod가 특정 Node에는 접근하지 못하도록 조건을 걸어두는 개념임.

 

스크린샷 2022-02-24 오후 7 31 11

taint를 노드에 설정했을 때, taint 조건에 부합하지 않는 pod를 어떻게 할 것인지 설정하는 세 가지 옵션이 있다.

  • NoSchedule : 절대 해당 노드에 pod를 추가하지 않는다.
  • preferNoSchedule : 스케줄링에서 가급적이면 제외하지만, 100% 제외를 보장하지는 않는다.
  • NoExecute : 조건에 부합하지 않는 pod은 전부 받지 않음 + 기존에 노드에서 돌고 있던 pod에도 조건을 적용해서, taint에 안 맞는 pod는 전부 노드에서 제외한다.

 

 

스크린샷 2022-02-24 오후 7 35 39

pod의 경우 kubectl taint nodes node1 app=myapp:NoSchedule 는 아래의 yaml 파일과 동일하다.

  • yaml파일에서 이 필드를 적용하려면 반드시 Double Quote를 사용해야 한다.
  • effect 항목에 정의한 값에 따라 신규 생성된 / 기존에 만들어져 있던 pod의 스케줄링이 결정된다.
  • ... tolerations: - key: "app" operator: "Equal" value: "myapp" effect: "NoSchedule"

 

 

예시: NoExecute

스크린샷 2022-02-24 오후 7 40 13

 

Pod D에 NoExecute 옵션을 붙여서 toleration을 설정하면,
기존에 노드에서 돌고 있던 pod C는 toleration 조건을 만족하지 못할 경우 killed된다.

 

스크린샷 2022-02-24 오후 7 43 23

선술했듯, taint와 toleration 자체의 목적은 "pod가 특정 Node에는 접근하지 못하도록 조건을 걸어두는 개념" 이다.

  • 따라서 node에 taint를 설정하고 pod에 toleration을 설정했다고 해서,
    반드시 toleration을 설정한 pod이 taint 걸려 있는 노드에 매핑되는 것은 아니다.
  • node에 'toleration이 있는 pod만 수용하도록' 조건을 걸어둔 개념이기 때문.
  • 만약 특정 노드와 특정 pod를 매핑하고 싶다면 NodeAffinity 개념을 사용해야 한다.

스크린샷 2022-02-24 오후 7 48 22

마스터 노드의 경우, 클러스터에서 처음 생성될 때 이미 "어느 pod도 master node에서는 실행되지 않도록" taint를 걸어두었다.

  • 물론 마스터 노드에 toleration을 가진 pod를 생성할 수는 있지만, best practice는 아니다.

 

Node Selectors

스크린샷 2022-02-24 오후 8 02 13

  • pod 3개 중 큰 pod은 node2나 3에서는 실행될 수 없는 스펙이다.
  • 디폴트 스케줄링 알고리즘은 랜덤. 따라서 랜덤한 방식으로 적용하는 건 효율적이지 않다.

 

 

pod 자체에 limitation을 적용해서, 스펙을 만족하는 노드에만 해당 pod를 할당하도록 결정할 수 있다.

스크린샷 2022-02-26 오전 8 47 26

)

 

pod의 nodeSelector에서 label을 사용해 특정 node를 정의하면 된다.

  • nodeSelector의 key-value는 node에서 정의해야 하며, pod를 생성하기 전에 node에 세팅되어 있어야 한다.

 

 

스크린샷 2022-02-26 오전 8 51 11

한계점

  • key-value 조합만으로는 할 수 없는, 복잡한 조건이 필요한 경우 적용이 어려움

 

 

Node Affinity

스크린샷 2022-02-26 오전 10 45 55

두 개의 yaml파일은 정확히 같은 동작을 수행하지만, node affinity를 사용하면 보다 디테일하게 pod에 제약을 걸 수 있다.

  • operator In : value에 해당하는 값이 label에 포함되기만 하면 된다. 예시의 경우 key라는 label값에 Large라는 문자열이 있기만 하면 됨.
  • or 형태로 label 체크를 하고 싶다면 values를 리스트 형태로 넘겨주면 된다.
...
- key: size
  operator: In
  values: 
  - Large
  - Medium

스크린샷 2022-02-26 오후 5 49 09스크린샷 2022-02-26 오후 5 49 25

  • NotIn : 없는지 체크하는 것 == Exists

자세한 내용은 docs에 소개되어 있다.

 

 


 

 

위와 같은 옵션은 pod가 생성되는 시점에 적용되고, node에 할당될 때 조건으로 동작함.

그런데 만약

  • pod 할당을 하는 시점에서 '조건을 만족하는 node'가 없다면?
  • 또는 어느 시점에 node 라벨이 갑자기 바뀌어서, 더 이상 조건절에 부합하지 않는다면?

Node Affinity의 type으로 위와 같은 상황을 대비하도록 설정할 수 있다.

 

스크린샷 2022-02-26 오후 6 36 57

  • pod의 lifeCycle 시점에 적용되는 로직.
  • 현재 Available 타입으로는 두 가지 옵션이 있고, Planned 타입의 옵션은 하나.

스크린샷 2022-02-26 오후 6 40 03

  • DuringScheduling : pod가 처음 생성될 때
    • required: 해당 node는 조건을 반드시 만족해야 함. 만족하는 노드가 없다면 pod는 스케줄 대상이 되지 않음.
    • preferred : 어쨌든 pod 자체가 뜨는 게 우선이므로, 만족하는 노드가 없어도 일단 pod를 노드에 스케줄링한다.
  • DuringExecution : pod는 돌고 있는 상황에서 node 정보에 변경이 있을 경우. label 값이 변경된다던가
    • Ignored : 일단 스케줄된 pod는 새로운 조건을 무시한다.
    • Required : 조건을 만족하지 않게 되면 evicted. pod를 중지하고 다른 노드에 스케줄링한다.

 

Taints / Toleration, NodeAffinity

스크린샷 2022-02-26 오후 6 48 26

Taint and Toleration

  • taint로 노드에 제약조건을 걸고, 조건에 맞지 않는 pod가 스케줄링되지 않도록 한다.
  • toleration으로 pod에 조건을 걸어서, taint가 걸려 있는 pod에도 스케줄링이 가능하도록 한다.

단, 스케줄링 자체는 랜덤이므로, taint와 toleration이 완벽한 매칭 스케줄링을 보장하는 건 아니다.

  • 위 예시처럼, red pod가 할당될 수 있는 노드는 두 개다.
    • red node
    • taint를 걸어두지 않은 일반 노드

 

스크린샷 2022-02-26 오후 6 52 19

Node affinity

  • 노드에 label을 생성하고 pod에 조건을 걸어서, 특정 조건을 만족하거나 만족하지 않는 노드에만 스케줄링되도록 조정한다.
    • 단, 아무런 조건을 걸지 않은 pod의 경우 label 여부에 관계없이 스케줄링될 수 있다.
    • 예시의 경우 red node에 아무런 조건 없는 일반 pod가 할당될 수 있음.

 

스크린샷 2022-02-26 오후 6 54 22

 

따라서 두 개를 혼용해 사용해야 node - pod를 완벽히 통제할 수 있는 형태로 스케줄링이 가능하다.

 

 

Resource Limitation / Limits

스크린샷 2022-02-26 오후 6 58 56

노드 세 개가 있는 k8s 클러스터를 가정하자.

  • cpu 2, 1 memory, disk space가 있다
  • pod는 생성될 때, 필요로 하는 리소스만큼의 여유분을 보장받은 채 노드에 할당된다.
  • kubelet의 스케줄러는 리소스 여유분이 있는 노드에 pod을 할당한다.

 

어느 노드에도 pod을 정상적으로 할당할 수 없을 경우... pod는 노드에서 실행되지 못하고 pending 상태를 유지한다.

 

스크린샷 2022-02-26 오후 8 41 21스크린샷 2022-02-26 오후 8 43 42

Pod의 리소스 request에서 디폴트 설정은 0.5 CPU, 256MB.

  • cpu 0.1 = 100m (단위 m은 '밀리'). 설정 가능한 최솟값은 1m.

default limit request 값을 사용하도록 설정하려면 아래와 같이 LimitRange 객체를 k8s에 생성하고 등록해야 한다.

apiVersion: v1
kind: LimitRange
metadata:
  name: mem-limit-range
spec:
  limits:
  - default:
      memory: 512Mi
    defaultRequest:
      memory: 256Mi
    type: Container

apiVersion: v1
kind: LimitRange
metadata:
  name: cpu-limit-range
spec:
  limits:
  - default:
      cpu: 1
    defaultRequest:
      cpu: 0.5
    type: Container

스크린샷 2022-02-26 오후 8 44 02

 

Memory의 경우 Mi와 M 값의 차이를 알아야 함. 서로 다른 단위다.

 

 

스크린샷 2022-02-26 오후 8 49 46

  • docker의 경우 container가 사용할 수 있는 리소스 제한을 두지 않는다.
  • 반면 k8s pod의 경우, 사용할 수 있는 리소스 제한의 디폴트 값이 정해져 있다.
    • cpu : 1 vCpu
    • Mem : 512MB
  • 리소스 관련 설정은 pod 내부에 정의한 컨테이너별로 설정할 수 있다. request 요청값이나 limit값을 전부 개별로 설정 가능함.

만약 리소스 사용량이 Limit을 초과할 경우?

  1. cpu limit 초과 시: 쓰로틀링 가동해서 cpu 사용량이 limit을 넘지 않도록 통제
  2. memory limit 초과 시: 사용랑 초과가 지속될 경우 OOMKilled. pod을 제거해 버린다.

 

 

DaemonSets

스크린샷 2022-02-26 오후 8 57 19

  • ReplicaSet과 유사함: deploy multiple instances of pod.
  • 차이점: 단 하나의 copy of your pod만 클러스터에서 동작한다.
  • 노드가 추가될 때마다 자동으로 DaemonSet pod 한 개가 실행되며, 노드가 삭제되면 자동으로 pod도 사라진다.

 

 

DaemonSet: 최소한 한 개의 pod가 모든 노드에서 실행되도록 보장하는 형태의 k8s Object

스크린샷 2022-02-26 오후 8 58 53

 

로그 모니터링을 위한 pod같은 경우가 가장 정확한 usecase.

 

 

스크린샷 2022-02-26 오후 8 59 43

  • kube-proxy가 DaemonSet의 예시 중 하나다. 모든 노드에서 반드시 실행되며, pod 간 통신을 담당하고 있음.
  • 네트워킹을 담당하는 solution 중 하나인 Weave-net도 비슷한 예시.

 

스크린샷 2022-02-26 오후 9 01 48

 

생성 방법은 ReplicaSet과 동일하다. Kind만 DaemonSet으로 설정하면 됨.

DaemonSet의 동작 원리?

  • 모든 노드에 반드시 하나의 pod는 동작하도록 스케줄링?
    • k8s 1.12 이전까지는 각 pod당 nodeName을 지정하고, 해당 노드에 스케줄링되는 식.
    • 1.12 이상부터는 NodeAffinity + default scheduler로 처리하고 있다.

 

Static Pod

Worker node를 관리하는 kubelet이 할 수 있는 작업은 Pod 생성과 관리.

  • 보통 kubelet은 master node의 kube-api로부터 값을 입력받아 pod을 생성해왔다.
  • 만약 master node가 없고, Kube-api도 없는 상황이라면?

스크린샷 2022-02-26 오후 11 48 26

  • 개별 컨테이너(호스트)의 특정 경로, 예컨대 /etc/kubernetes/manifest 경로에
  • pod을 정의한 yaml파일을 생성하면, kubelet이 해당 정보를 토대로 pod를 직접 생성하고 관리할 수 있다.
    • 이 pod는 kubelet의 관리 하에서만 만들어진다. kube-api나 다른 worker node와는 관계 없음.
  • 단, pod만 생성 가능하다. replicaset이나 deployment 같은 오브젝트는 생성 불가능.
    • 얘네는 control plane을 거쳐서 만들어져야 하는 k8s 오브젝트이기 때문.

 

스크린샷 2022-02-26 오후 11 52 40

 

static pod를 생성할 수 있도록 하는 호스트 경로는 kubelet service를 실행할 때 옵션값으로 정의할 수 있다.

 

 

스크린샷 2022-02-26 오후 11 52 59

 

 

--config 옵션으로 yaml파일을 옵션으로 줄 수도 있다.

yaml파일 형태는

staticPodPath: /etc/kubernetes/manifest

 

kubelet에서 직접 생성한 static pod는 docker ps로 확인할 수 있다.

  • kubelet 외에는 아무런 객체가 없기 때문에 kubectl utils를 사용할 수 없음. (kubectl은 kube-apiserver를 사용한다.)

 

스크린샷 2022-02-27 오전 9 55 11

 

물론, master node가 있는 상태에서도 static pod를 생성할 수 있다.

  • 이 경우 static pod의 존재 자체는 kubectl에서 조회할 수 있다.
  • 단, 조회가 되는 pod은 kubectl에서는 read only mirror다. 따라서 수정, 삭제 등은 불가능하다.
    • 삭제하려면 kubelet의 manifest 폴더에서 직접 yaml파일을 지워야 한다.

확인해보면 실제로 pod가 할당된 노드가 정확히 특정되어 있는 걸 볼 수 있다. 위 예시의 경우 node 1.

 

 

스크린샷 2022-02-27 오전 10 00 20

 

Static Pod을 사용하는 이유?

  • 노드control plane에 독립적으로 동작하면서, pod를 kubelet이 yaml파일을 토대로 직접 관리한다.
  • 따라서 k8s master node에서 controller-manager, apiserver, etcd 등 컴포넌트를 구성하기 위한 yaml파일을 static pod으로 사용한다.
    • manifests 경로에서 직접 파일을 삭제하지 않는 이상, 해당 컴포넌트는 master node에 pod형태로 반드시 존재한다.

 

스크린샷 2022-02-27 오전 10 06 31

 

 

스크린샷 2022-02-27 오전 10 06 40

DaemonSet과 Static Pod 비교하기

  • 공통점: 둘 다 scheduler 없이 노드에 설치된다.
  • 차이점
    • static pod는 노드 내의 control plane 컴포넌트 설치에 주로 이용된다. kubelet이 직접 설치한다.
    • DaemonSet은 monitoring / logging agent 설치 목적으로 사용한다. kube-apiserver를 통해서 설치할 수 있다. (DaemonSet Controller로 설치됨.)

static pod를 생성하기 위한 definition file을 찾고, 생성하는 방법을 알아둬야 한다.

  • view / edit this options... 두 가지 방법 다 알아둬야 함.

 

multiple Schedulers

스케줄링 알고리즘은 기본적으로는 랜덤하게 + 조건(taint / toleration, nodeAffinity 등) 기반으로 노드에 pod 할당.

스크린샷 2022-02-27 오전 10 51 32

  • 필요하다면 customized scheduler를 생성하고 배포해서 사용할 수 있다.
  • 특정 애플리케이션 (pod)에는 특정 scheduler를 사용해서 노드에 배포하도록 pod 생성단계에서 설정할 수 있다.

 

 

스크린샷 2022-02-27 오전 10 52 59

 

scheduler 바이너리 파일을 가져와서, scheduler-name 필드만 변경한 채 k8s에 배포해보는 예시.

  • scheduler name 필드가 pod 생성 시 스케줄러를 선택하는 key값이 된다.

 

스크린샷 2022-02-27 오전 10 54 33

kubeAdm으로 배포하는 방법

  • command에 --scheduler-name 필드가 스케줄러 이름을 지정하는 필드.
  • --leader-elect: 여러 개의 스케줄러가 master node에 있다고 할 때, 어떤 스케줄러가 실행될 것인지 결정하는 필드 (스케줄러는 여러 개 배포할 수 있지만, 마스터 노드에서는 한 개만 실행 가능하다.)
    • 마스터 노드가 하나만 있는 상황에서 스케줄러가 여러 개라면, 하나를 뺀 나머지는 false 옵션을 줘야 한다.
    • HA를 위해 multiple master node 를 세팅할 경우 --lock-object-name=<스케줄러 이름> 을 붙여줘야 한다.
      • 이 옵션은 마스터 노드가 스케줄러를 선택할 때, 여러 개의 스케줄러를 구분하기 위한 목적으로 사용함.

 

스크린샷 2022-02-27 오전 11 30 55

 

 

정상적으로 배포가 완료되었다면 scheduler 두 개가 kube-system Namespace에 떠 있는 걸 확인할 수 있다.

pod 생성 시 schedulerName 필드에 scheduler pod 이름을 입력하면, 해당 스케줄러를 사용해서 배포할 수 있다.

  • 만약 scheduler에 오류가 있을 경우 배포 요청한 pod는 pending 상태가 됨.

 

scheduler 이벤트 / 로그 확인하기.

 

스크린샷 2022-02-27 오전 11 33 22스크린샷 2022-02-27 오전 11 33 29

 

 

Reference

스크린샷 2022-02-27 오전 11 34 46

반응형