Manual Scheduling
How scheduling works
우선, 간단한 파드 정의 파일을 보자.
// pod-definition.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
name: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 8080
nodeName: // not default
모든 파드에는 nodeName 필드가 있지만 nodeName 필드는 기본값으로 설정되어 있지 않고, 쿠버네티스가 자동으로 nodeName 필드를 추가한다. 스케줄러는 모든 파드를 보고 속성 세트가 없는 노드를 찾는다. 찾은 노드는 바인딩 개체를 만들어 노드의 이름 속성을 yaml에 설정한다.
스케줄러가 없다면?! 파드는 pending 상태기 때문에 직접 노드에 파드를 할당할 수 있다. 그 방법은 yaml에 직접 작성하는 것이다. 노드 이름은 파드가 생성될 때만 지정할 수 있다. 이미 있는 파드에 노드를 할당하는 방법은 바인딩 개체를 생성하고 바인딩 api에 post 요청을 보낸다. 그러면 실제 스케줄러가 하는 일을 바인딩 개체가 모방하게 된다.
아래와 같이 바인딩 yaml 파일을 작성한다.
// pod-bind-definition.yaml
apiVersion: v1
kind: Binding
metadata:
name: nginx
target:
apiVersion: v1
kind: Node
name: node02
그 후 포맷의 바인딩 개체의 데이터셋을 사용해 바인딩 api에 post 요청을 보낸다.
curl --header "Content-Type:application/json" \
--request POST \
--data "$(yq eval -o=json pod-bind-definition.yaml)" \
http://$SERVER/api/v1/namespaces/default/pods/$PODNAME/binding/
실습
1) k create -f nginx.yaml
2) k get pods
3) why is the pod in a pending state? -> k describe pod nginx -> node 필드가 비어있기 때문에 scheduler가 할당되지 않은거
k get pods -n kube-system (스케줄러가 kube-system 네임스페이스에서 파드 형태로 실행되기 때문에 해당 네임스페이스를 봄) -> 스케줄러가 없는걸 확인할 수 있음
4,5) vi nginx.yaml -> nodeName: node01 필드 추가 && k replace --force -f nginx.yaml (delete and create) && k get pods --watch (계속 파드를 감시) && kubectl get pods -o wide
* 파드는 컨테이너이고, 실행되는 또 다른 프로세스기 때문에 한 시스템에서 다른 시스템으로 옮길 수 없어서 파드를 삭제하고 다시 만들어야 함
Labels and Selectors
아래 yaml은 pod yaml이고, labels는 원하는 만큼 붙일 수 있다.
// pod-definition.yaml
apiVersion: v1
kind: Pod
metadata:
name: simple-webapp
labels:
app: App1
function: Front-end
spec:
containers:
- name: simple-webapp
image: simple-webapp
ports:
- containerPort: 8080
이렇게 label을 붙이면 아래 명령어로 원하는 label만 가져올 수 있다.
kubectl get pods --selector app=App1
이를 이용해 쿠버네티스는 label과 selector로 서로 다른 오브젝트를 연결할 수 있다. 예를 들어 세 개의 다른 파드로 구성된 replicaset을 만들려면 파드 정의를 통해 레이블링하고 rs의 selector를 이용해 파드를 그룹으로 묶는다.
// replicaset-definition.yaml
apiVersion: v1
kind: ReplicaSet
metadata:
name: simple-webapp
labels:
app: App1
function: Front-end
annotations:
buildVersion: 1.34
spec:
replicas: 3
selector:
matchLabels:
app: App1
template:
metadata:
labels:
app: App1
function: Front-end
spec:
containers:
- name: simple-webapp
image: simple-webapp
실습
1) k get pods --selector env=dev --no-headers | wc -l
- k get pods --selector env=dev까지만 작성해서 파드를 직접 세도 되지만 파드의 수가 많다면 wc -l을 이용해 개수를 세자
2) k get pods --selector bu=finance --no-headers | wc -l
3) k get all --selector env=prod--no-header | wc -l
4) k get all --selector env=prod,bu=finance,tier=frontend
5) k create -f replicaset-definition-1.yaml -> invalid value: {tier:nginx} -> fix the yaml to tier: front-end
Taints and Tolerations
taint는 노드에 toleration은 파드에 설정한다. taint와 toleration이 꼭 어떤 노드로 파드를 지정해주는건 아니다. taint에 맞는 toleration이 설정되어 있으면 해당 노드로 갈 수 있다는 것이다!
kubectl taint nodes node-name key=value:taint-effect
3가지 Taint effect가 있다.
1. NoSchedule : 파드를 노드에 올릴 예정이 없는 것
2. PreferNoSchedule : 시스템은 노드에 파드를 설치하는걸 피하려고 함
3. NoExecute : 새 파드가 노드에서 지정되지 않고 노드 위의 기존 파드가 있다면 사용자가 거부하면 퇴거해야 함
node에 taint 설정하는 방법
kubectl taint nodes node1 app=blue:NoSchedule
pod에 toleration 설정하는 방법
#pod-definition.yaml
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
spec:
containers:
- name: nginx-container
image: nginx
tolerations:
- key: app
operator: Equal
value: blue
effect: NoSchdule
스케줄러는 마스터 노드에 어떤 파드도 스케줄링 하지 않는다. 왜냐하면 파스터 노드에 테인트 설정이 자동으로 되기 때문에 이 노드에 파드가 지정되는 것을 막는다. 아래와 같이 명령어로 마스터 노드를 확인해보면 파드가 설정되지 않았다는 것을 볼 수 있다.
kubectl describe node kubemaster | grep Taint
실습
1) k get nodes
2) k describe node node01 | grep Taint
3) k taint node node01 spray=mortein:NoSchedule
4) k run mosquito --image=nginx
5) k get pod
6) k describe pod -> the pod didnt tolerate
7) k run bee --image=nginx --dry-run=client -o yaml > bee.yaml
---
apiVersion: v1
kind: Pod
metadata:
name: bee
spec:
containers:
- image: nginx
name: bee
tolerations:
- key: spray
value: mortein
effect: NoSchedule
operator: Equal
k create -f bee.yaml
8) k get pods -o wide
9) k describe node controlplane | grep Taints
10) k taint node controlplane node-role.kubernetes.io/control-plane:NoSchedule-
11) k get pods
12) k get pods -o wide
Node Selectors
노드에 label 붙이는 명령어
kubectl label nodes <node-name> <label-key>=<label-value>
kubectl label nodes node-1 size=Large
#pod-definition.yaml
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
spec:
containers:
- name: data-processor
image: data-processor
nodeSelector:
size: Large
Node Affinity
node affinity의 특징은 파드가 특정 노드에 호스트될 수 있도록 하는 것이다.
apiVersion: v1
kind: Pod
metadata:
name: affinity-pod
spec:
containers:
- name: nginx
image: nginx
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: size
operator: In #NotIn #Exists - dont need the values
values:
- Large #Small #Medium
Node Affinity의 Types
- available
- requiredDuringSchedulingIgnoredDuringExecution
- preferredDuringSchedulingIgnoredDuringExecution
- Planned
- requiredDuringSchedulingRequiredDuringExecution
실습
1,2) k describe node node01
3) k label node node01 color=blue && k describe node node01
4) k create deployment blue --image=nginx --replicas=3
5) k get nodes && k describe node node01 | grep Taints && k describe node controlplane | grep Taints
6) kubectl edit deployment blue && 직접 yaml에 주어진 조건대로 affinity.nodeAffinity 넣기
7) k get pods -o wide
8) k create deployment red --image=nginx --replicas=2 --dry-run=client -o yaml> red.yaml && 직접 yaml에 주어진 조건대로 nodeAffinity 넣기 && k create -f red.yaml
Node Affinity vs Taints and Tolerations
taint와 toleration을 설정해도 이것이 절대적인 요소가 아니기 때문에 파드가 원하는 노드와 매칭이 되지 않을 수 있다. 이 문제는 node afftinity로 해결할 수 있다. 먼저 각각 노드에 label을 붙이고, 파드의 node selector를 설정해 파드와 노드를 연결해줍니다. 이렇게 하면 label과 짝꿍인 파드를 설정할 수 있는데 그 외의 파드가 함께 매칭될 가능성이 있다. 그래서 taints/tolerations와 node affinity 조합을 사용해야 한다.