Kubernetes Scheduler 살펴보기
Pod 가 새롭게 생성되면 Scheduler 를 통해서 Pod 가 실행될 노드가 결정된다. 즉 모든 Pod 는 Scheduler 에 의해서 최적의 노드가 결정되고 실행된다.
kube-scheduler 는 Control Plane 에 속하는 컴포넌트 중에 하나이며, 디폴트 scheduler 이다. 만약 특별한 스케줄링을 원한다며 커스텀 스케줄러를 만들어서 대체할 수 도 있다.
Pod 가 스케줄링 되기 위해서는 노드가 스케줄링 요구 사항에 맞아야 하는데 이 때, 요구사항에 맞는 노드를 피저블 노드(feasible node) 라고 한다.
스케줄러는 피저블 노드를 찾고 적절한 로직을 통해 노드에 점수를 부여한다. 가장 최고 점수를 갖는 노드에 Pod 가 실행된다.
스케줄링을 결정하는 데에는 하드웨어, 소프트웨어, policy contraints, affinity 와 anti-affinity spec, local data 등 여러 요구 사항들이 있다.
kube-scheduler 가 결정하는 2-step operation
- Filtering
- Scoring
Filtering 단계에서는 피저블 노드를 찾는 단계이다. 예를 들면, PodFitsResources 필터는 Pod 의 request 리소스를 만족하는 리소스를 갖는 후보 노드를 추려낸다. 이 단계 이후에는 적합한 노드 리스트가 도출되는데 만약 노드 리스트의 값이 비었다면 Pod 는 스케줄링 되지 않는다.
Scoring 단계에서는 필터링된 노드 리스트 중에서 Pod 가 실행될 최적의 노드를 찾는다. 스케줄러는 노드 리스트에 있는 각 노드들에 점수를 부여한다.
마지막으로 kube-scheduler 는 최고 점수의 노드에 Pod 를 배치한다.
Filtering 과 Scoring 을 설정할 수 있는 방법은 2가지가 있다.
- Scheduling Policies (kubernetes 1.22 버전까지만 사용)
- Predicates : filtering 을 조절할 수 있음
- Priorities: scoring 을 조절할 수 있음
- Scheduling Profiles (https://kubernetes.io/docs/reference/scheduling/config/#profiles)
- KubeSchedulerConfiguration 리소스 타입으로 조절할 수 있음
- plugins 를 통해 Filter 와 Score 를 조절 가능
nodeSelector
Node 에 label 을 설정하고 Pod 에서 nodeSelector 로 Node 지정하는 방식이다.
$ kubectl get nodes -L nginx
NAME STATUS ROLES AGE VERSION NGINX
k1-master01 Ready control-plane,master 92d v1.21.6
k1-node01 Ready ingress 92d v1.21.6
k1-node02 Ready <none> 92d v1.21.6 enabled
k1-node03 Ready <none> 92d v1.21.6 enabled
k1-node04 Ready <none> 92d v1.21.6
k1-node05 Ready <none> 92d v1.21.6
k1-node02 와 k1-node03 에 nginx=enabled 로 label 이 지정되어 있다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-nodeselector
namespace: kube-sample
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.21.0
ports:
- containerPort: 80
nodeSelector:
nginx: enabled
nodeSelector 를 활용하여 deployment 를 배포하면 아래와 같이 k1-node02 혹은 k1-node03 에 배포된다.
$ kubectl get pods -n kube-sample -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-nodeselector-68977d7759-sf48l 1/1 Running 0 9s 10.233.113.7 k1-node02 <none> <none>
Node affinity
nodeSelector 와 동작 방식이 유사하지만 다음과 같은 차이가 있다.
requiredDuringSchedulingIgnoredDuringExecution: 스케줄링에서 만족하는 노드가 없으면 스케줄링 되지 않는다.
preferredDuringSchedulingIgnoredDuringExecution: 스케줄링에서 만족하는 노드를 찾고 노드가 없더라도 스케줄링은 된다. weight 필드의 값을 scoring 에 포함시킨다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-node-affinity
namespace: kube-sample
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: nginx
operator: In
values:
- enabled
- matchExpressions:
- key: kubernetes.io/os
operator: In
values:
- windows
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: another-node-label-key
operator: In
values:
- another-node-label-value
containers:
- name: nginx
image: nginx:1.21.0
ports:
- containerPort: 80
위와 같이 nodeSelectorTerms 에 여러 개의 matchExpressions 가 있으면 이 중 하나만 만족해도 스케줄링은 된다. nodeSelectorTerms 에 하나의 matchExpressions 가 있고, key value 가 여러 개가 있으면 matchExpressions 내의 모든 조건을 만족해야 스케줄링이 된다.
operator 로 쓸 수 있는 값은 In
, NotIn
, Exists
, DoesNotExist
, Gt
, Lt
이다.
NotIn
과 DoesNotExist
는 node anti-affinity 로 사용 가능하다.
$ kubectl get pods -n kube-sample -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-node-affinity-755cf7f85d-ss9q5 1/1 Running 0 5s 10.233.113.10 k1-node02 <none> <none>
Pod affinity and anti-affinity
Pod 를 서로 다른 노드에 배치시키는 방법은 pod anti-affinity 를 통해서 구현할 수 있다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-pod-anti-affinity
namespace: kube-sample
spec:
replicas: 2
selector:
matchLabels:
app: nginx-pod-anti-affinity
template:
metadata:
labels:
app: nginx-pod-anti-affinity
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- nginx-pod-anti-affinity
topologyKey: kubernetes.io/hostname
containers:
- name: nginx
image: nginx:1.21.0
ports:
- containerPort: 80
위와 같이 pod label 에 pod anti affinity 를 설정하면 replicas 값 2 에 의해서 생성되는 2개의 pod 는 서로 다른 노드에 배치된다. topologyKey 는 kubernetes.io/hostname 값으로 세팅하고 특정 node lable 로 하고 싶으면 node affinity 를 추가하면 된다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-pod-anti-affinity
namespace: kube-sample
spec:
replicas: 2
selector:
matchLabels:
app: nginx-pod-anti-affinity
template:
metadata:
labels:
app: nginx-pod-anti-affinity
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- nginx-pod-anti-affinity
topologyKey: kubernetes.io/hostname
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: nginx
operator: In
values:
- enabled
containers:
- name: nginx
image: nginx:1.21.0
ports:
- containerPort: 80
$ kubectl get pods -n kube-sample -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-pod-anti-affinity-7cfcbcff8f-c2429 1/1 Running 0 14s 10.233.96.3 k1-node03 <none> <none>
nginx-pod-anti-affinity-7cfcbcff8f-dmqqm 1/1 Running 0 14s 10.233.113.12 k1-node02 <none> <none>
Taint 와 Toleration
노드에 taint 를 지정하면 해당 노드에 taint effect 를 줄 수 있다. 예를 들어 taint effect 에 NoSchedule 을 준다면 해당 노드에는 pod 가 스케줄링 되지 않는다.
taint effect 를 제거할 수 있는 방법은 Pod 에 toleration 을 세팅하는 것이다.
마스터에는 기본적으로 taint 를 지정해서 스케줄링 안되게 세팅해 놓는 것을 알 수 있다.
$ kubectl describe node k1-master01
Name: k1-master01
Roles: control-plane,master
Labels: beta.kubernetes.io/arch=amd64
beta.kubernetes.io/os=linux
kubernetes.io/arch=amd64
kubernetes.io/hostname=k1-master01
kubernetes.io/os=linux
node-role.kubernetes.io/control-plane=
node-role.kubernetes.io/master=
node.kubernetes.io/exclude-from-external-load-balancers=
Annotations: kubeadm.alpha.kubernetes.io/cri-socket: /var/run/containerd/containerd.sock
node.alpha.kubernetes.io/ttl: 0
projectcalico.org/IPv4Address: 192.168.30.13/24
volumes.kubernetes.io/controller-managed-attach-detach: true
CreationTimestamp: Tue, 08 Feb 2022 21:27:12 +0900
Taints: node-role.kubernetes.io/master:NoSchedule
마지막의 Taints 값은 kubectl 로 세팅할 수 있다.
$ kubectl taint nodes k1-master01 node-role.kubernetes.io/master=:NoSchedule
그럼 master 노드에 toleration 을 활용하여 pod 를 배치해 보자.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-toleration
namespace: kube-sample
spec:
replicas: 1
selector:
matchLabels:
app: nginx-toleration
template:
metadata:
labels:
app: nginx-toleration
spec:
tolerations:
- key: node-role.kubernetes.io/master
operator: Exists
effect: NoSchedule
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node-role.kubernetes.io/control-plane
operator: Exists
containers:
- name: nginx
image: nginx:1.21.0
ports:
- containerPort: 80
toleration 으로 taint 효과를 없애고 node affinity 를 활용하여 control-plane, 즉 master 노드에 pod 를 배치 시켰다. 물론 master 노드에 node-role.kubernetes.io/control-plane 이라는 label 이 존재해야 한다.
taint effect 는 NoSchedule, NoExecute, PreferNoSchedule 이 있다.
NoSchedule 은 스케줄링을 막는 것이고, NoExecute 는 실행중인 pod 를 eviction 시킨다. PreferNoSchedule 은 가능하면 스케줄링 하지 않는다는 의미이다.
operator 에는 Exists 와 Equal 이 있다. Exists 는 key 값이 존재하는 지를 판단하기 때문에 value 값이 필요 없으며, Equal 은 key 와 value 값이 지정된 값과 일치해야 한다.
$ kubectl get pods -n kube-sample -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-toleration-fb74cd98c-hkql8 1/1 Running 0 5m31s 10.233.117.4 k1-master01 <none> <none>