들어가기에 앞서...
0. 블로깅 목적
- Affinity의 정의 및 종류를 알아보고 이해한다.
- Pod 배치 전략 중 nodeAffinity에 대해 이해하고 실습한다.
- Pod 배치 전략 중 podAffinity에 대해 이해하고 실습한다.
- Pod 배치 전략 중 podAntiAffinity에 대해 이해하고 실습한다.
1. Affinity의 정의 및 종류를 알아보고 이해한다.
1) Affinity?
- Affinity란 선호도란 뜻이다. Pod는 항상 Node에서 띄워져야 하는데, 이러한 배치를 함에 있어 선호하는 Node나 Pod를 설정할 수 있게끔 하는 리소스이다.
2) Affinity 종류
- nodeAffinity는 어떤 Node를 선호할 것인가? 에 관련한 리소스이다. 즉, Pod를 배치할 때 어떤 Node에 스케쥴링할지 설정을 해준다.
- podAffinity는 Pod가 배치될 때, 실행 중인 Pod들 중에 선호하는 Pod를 찾아 해당 Pod와 동일한 Node로 배치하는 걸 설정해준다.
- podAnitAffinity는 실행 중인 Pod들 중에, 선호하지 않은 Pod가 실행 중인 Node는 피해서 배치를 하겠다는 걸 설정해준다.
2. Pod 배치 전략 중 nodeAffinity에 대해 이해하고 실습한다.
1) nodeAffinity?
- 선호하는 노드를 설정하는 방법으로, nodeSelector 보다 확장된 Label Selector 기능을 지원한다. 그래서 좀 더 실무환경에 적합한 배Pod 배치 전략이다.
- matchExpressions 사용 가능하다. (In, NotIn, Exists, DoesNotExist, Gt, Lt 등의 옵션이 있다.)
- 여러 유즈케이스에 활용 가능한 2가지 옵션이 있는데. Hard, Soft로 나뉜다. 매우 조건이 길기 때문에 2등분해서 의미를 이해하면 좋다.
- 반드시 충족해야 하는 조건 (Hard)
- requiredDuringSchedulingIgnoredDuringExecution
- 즉, 스케쥴링되는 워크로드에는 필수 조건이고, 실행 중인 워크로드는 조건을 무시한다는 의미이다.
- 선호하는 조건 (Soft)
- preferredDuringSchedulingIgnoredDuringExecution
- 즉, 스케쥴링되는 워크로드에는 선호 조건이고, 실행 중인 워크로드는 조건을 무시한다는 의미이다.
- 용어 설명:
- IgnoredDuringExecution: 실행 중인 워크로드에 대해서는 해당 규칙을 무시한다.
- RequiredDuringExecution: 위와 반대개념으로 실행 중인 워크로드에 대해서 해당 규칙을 반드시 필요로 한다.
2) nodeAffinity 실습
- 말했듯 minikube 멀티 클러스터 환경에서 진행된다.
- selector 경로에 node-affinity 디렉토리를 만든다.
- yml, sh 파일을 준비한다.
required.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: node-affinity-required
spec:
replicas: 3
selector:
matchLabels:
app: hello
template:
metadata:
name: hello
labels:
app: hello
spec:
containers:
- name: nginx
image: nginxdemos/hello:plain-text
ports:
- name: http
containerPort: 80
protocol: TCP
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: team
operator: In
values:
- blue
- red
- nodeAffinity 룰만 잘 이해하면 된다.
- 스케쥴링 될때는 필수적으로 적용될 조건이지만 실행 중인 노드에는 무시한다.
- operator In은 or 연산자라고 이해하면 된다. 즉, key가 team이고, value가 blue 혹은 red인 조건을 필수로 한다.
preferred.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: node-affinity-preferred
spec:
replicas: 4
selector:
matchLabels:
app: hello
template:
metadata:
name: hello
labels:
app: hello
spec:
containers:
- name: nginx
image: nginxdemos/hello:plain-text
ports:
- name: http
containerPort: 80
protocol: TCP
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 40
preference:
matchExpressions:
- key: team
operator: In
values:
- green
- 여기도 nodeAffinity 룰만 잘 이해하면 된다.
- 스케쥴링 될때는 선호되는 조건이지만 실행 중인 노드에는 무시한다.
- operator In은 or 연산자라고 이해하면 된다. 즉, key가 team이고, value가 green인 조건을 선호 한다.
- preferred는 weight가 있고, 각각에 matchExpressions을 넣는 구조로 되어 있다.
- weight는 0~100 사이의 값을 가질 수 있다. 각각의 rule을 기반으로 weight를 설정해서 점수를 줌으로써 Pod가 어떤 Node에 최종 배치될지 우선순위를 설정하는 것으로 이해하면 된다.
- 즉, weight는 여러개 설정할 수 있고, 점수를 매겨 우선순위를 결정하는 데 쓰이는 것이다.
- 근데 참고로 weight는 이번 실습에 특별히 적용되는 건 없다. 그냥 preferred 구조를 이해시키기 위한 설명 용도로만 언급하였다.
set-node-labels.sh
kubectl label node minikube --overwrite team=green
kubectl label node minikube-m02 --overwrite team=red
kubectl label node minikube-m03 --overwrite team=blue
- 특정 node 각각에 label(key-valu)를 지정하는 sh 스크립트이다.
실습을 진행해보자.
- 싱글 노드 환경이라면 현재 환경을 제거하고, minikube start --nodes 3 --driver=docker 커맨드로 3개의 멀티 노드 환경을 구성해준다.
- node-affinity 디렉토리에서 실습을 진행한다.
- 참고로 권한 문제가 발생하면 다음 커맨드를 입력한다.
- chmod +x set-node-labels.sh
- 우선 watch 모드 실행을 위해 2개의 터미널을 새로 띄운다.
- watch kubectl get pod
- 실습 중에
1번 watch 터미널
이라고 하겠다.
- Pod의 상태를 실시간으로 볼 수 있다. 아직 아무것도 존재하지 않는다.
- watch kubectl get nodes --label-columns team
- 실습 중에
2번 watch 터미널
이라고 하겠다.
- Node의 상태를 실시간으로 볼 수 있. 특정 label 컬럼도 볼 수 있도록 옵션을 주었다.
- 현재 node가 3종류 임을 알 수 있다. (minikube, minikube-m02, minikube-m03)
- 다만 아직 label의 team key에 할당된 value는 없다.
- set-node-labels.sh
- 일단 Node label 세팅을 진행해줘야 한다.
2번 watch 터미널
에 TEAM 레이블이 업데이트 된 걸 볼 수 있다.
- kubectl apply -f required.yml
- apply하면
1번 watch 터미널
에 새로운 Pod들이 생성 및 실행된 걸 확인할 수 있다.
- 각각이 어떤 Node에 할당되었는지도 보인다. Node의 label 설정을 blue 혹은 red로만 해뒀기 때문에, minikube 02, minikube 03에만 할당이 된 것이다.
- kubectl delete -f required.yml로 제거해준다.
- kubectl apply -f preferred.yml
- apply하면
1번 watch 터미널
에 4개의 Pod들이 생성 및 실행된 걸 확인할 수 있다.
- Node의 label 설정을 green으로만 해뒀기 때문에, minikube에만 할당이 되었다.
- kubectl delete -f preferred.yml로 제거해준다.
3. Pod 배치 전략 중 podAffinity에 대해 이해하고 실습한다.
1) podAffinity?
- 선호하는 파드를 설정하는 방법으로, 사용법은 nodeAffinity와 거의 동일하다.
- 역시 여러 유즈케이스에 활용 가능한 2가지 옵션을 제공하며, Hard, Soft로 나뉜다. nodeAffinity와 동일하여 자세한 설명은 생략한다.
- 반드시 충족해야 하는 조건 (Hard)
- requiredDuringSchedulingIgnoredDuringExecution
- 선호하는 조건 (Soft)
- preferredDuringSchedulingIgnoredDuringExecution
- 그러나 podAffinity의 가장 중요한 부분은 다음의 개념이다.
- 토폴로지 키 (Topology Key)
- 쿠버네티스 Node에 설정된 Label에 대해서, Label Selector를 수행할 노드의 범위를 결정한다.
- Topology Key는 노드의 레이블 key를 설정하는 것이며, 어떠한 값을 key name으로 넣어도 상관없지만 다음과 같은 3가지 key를 주로 쓴다.
- 노드 단위: kubernetes.io/hostname
- 존 단위: topology.kubernetes.io/zone
- AZ(Availablity Zone: 가용영역)
- 리전 단위: topology.kubernetes.io/region
=> AWS EKS 기준으로 3가지 key는 노드를 띄우면 그냥 기본 설정 된다. (단, minikube에서는 노드 단위 key만 기본 설정이 되고, 존과 리전은 안된다.)
2) podAffinity 실습
- minikube 멀티 클러스터 환경에서 진행된다.
- selector 경로에 pod-affinity 디렉토리를 만든다.
- yml, sh 파일을 준비한다. (sh는 기존 nodeAffiniy와 같다.)
mysql.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
spec:
selector:
matchLabels:
app: mysql
template:
metadata:
name: mysql
labels:
app: mysql
spec:
containers:
- name: mysql
image: mariadb:10.7
env:
- name: MYSQL_ROOT_PASSWORD
value: bakumando
- name: MYSQL_DATABASE
value: kubernetes
ports:
- name: http
containerPort: 3306
protocol: TCP
- podAffinity 테스트를 위해 특정 node에 Pod 하나를 띄울 필요가 있다. mysql Pod를 준비해보았다.
required.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: pod-affinity-required
spec:
replicas: 3
selector:
matchLabels:
app: hello
template:
metadata:
name: hello
labels:
app: hello
spec:
containers:
- name: nginx
image: nginxdemos/hello:plain-text
ports:
- name: http
containerPort: 80
protocol: TCP
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- mysql
topologyKey: kubernetes.io/hostname
- nodeAffinity와 비교하면 podAffinity를 사용했다는 점과 topologyKey가 추가되었다는 점이 다르다.
- 조건은 app key가 mysql인 것을 필수로 한다는 정의이다.
- topologyKey로는 노드 단위의 범위 설정만 적용을 하였다.
preferred.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: pod-affinity-preferred
spec:
replicas: 4
selector:
matchLabels:
app: hello
template:
metadata:
name: hello
labels:
app: hello
spec:
containers:
- name: nginx
image: nginxdemos/hello:plain-text
ports:
- name: http
containerPort: 80
protocol: TCP
affinity:
podAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- mysql
topologyKey: kubernetes.io/hostname
- required.yml와 비슷하게 정의되어있다.
- weight는 마찬가지로 실습에 특별히 활용하진 않을 것이다.
실습을 진행해보자.
- 기본 환경구성과 초반 세팅은 nodeAffinity 테스트와 같다.
- sh 파일 실행까지 동일하게 진행하고 다음 순서를 따르면 된다.
- kubectl apply -f mysql.yml
- mysql.yml을 apply 해준다.
1번 watch 터미널
에 생성되었다.
- kubectl get node --show-labels
- 각각의 노드들이 가진 label이 출력되는 걸 확인할 수 있다.
- minikube 노드들 모두 kubernetes.io/hostname를 가지고 있음을 알 수 있다.
- kubectl apply -f required.yml
1번 watch 터미널
을 보면 Pod가 추가되었다.
- required.yml에 동일한 노드 범위로 배치되게끔 설정을 해두었기 때문에, label이 app:mysql인 Pod가 배치되어 있는 노드에 required의 Pod들이 전부 생성된 것이다.
- 테스트가 잘 성공했으니, kubectl delete -f required.yml 제거해준다.
- kubectl apply -f preferred.yml
1번 watch 터미널
을 보면 mysql Pod와 같은 노드 범위에 preferred Pod들이 모두 추가되었다.
- preferred.yml도 required.yml의 원리와 같다.
- 테스트가 잘 성공했으니, kubectl delete -f preferred.yml 로 마무리 한다.
4. Pod 배치 전략 중 podAntiAffinity에 대해 이해하고 실습한다.
1) podAntiAffinity?
- 선호하지 않는 파드를 설정하는 방법으로, podAffinity를 podAntiAffinity로만 변경하면 사용법 동일하다.
- 역시 여러 유즈케이스에 활용 가능한 2가지 옵션을 제공하며, Hard, Soft로 나뉜다. podAffinity와 동일하여 자세한 설명은 생략한다.
- 반드시 충족해야 하는 조건 (Hard)
- requiredDuringSchedulingIgnoredDuringExecution
- 선호하는 조건 (Soft)
- preferredDuringSchedulingIgnoredDuringExecution
- 토폴로지 키 (Topology Key)도 podAffinity와 같은 방식이다. 자세한 건 3번 챕터(podAffinity)를 참고하면 된다.
2) podAntiAffinity 실습
- minikube 멀티 클러스터 환경에서 진행된다.
- selector 경로에 pod-anti-affinity 디렉토리를 만든다.
- yml, sh 파일을 준비한다.
- sh파일과 mysql.yml은 기존 podAffiniy와 같다.)
required.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: pod-anti-affinity-required
spec:
replicas: 3
selector:
matchLabels:
app: hello
template:
metadata:
name: hello
labels:
app: hello
spec:
containers:
- name: nginx
image: nginxdemos/hello:plain-text
ports:
- name: http
containerPort: 80
protocol: TCP
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- mysql
topologyKey: kubernetes.io/hostname
- podAffinity => podAntiAffinity로 바꿔주기만 했다. 이제 podAffinity 방식과 반대로 label에 매칭된 대상은 같은 노드 범위에 묶이지 않게 될 것이다.
preferred.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: pod-anti-affinity-preferred
spec:
replicas: 4
selector:
matchLabels:
app: hello
template:
metadata:
name: hello
labels:
app: hello
spec:
containers:
- name: nginx
image: nginxdemos/hello:plain-text
ports:
- name: http
containerPort: 80
protocol: TCP
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- mysql
topologyKey: kubernetes.io/hostname
- required와 거의 동일한 명세로 이루어져있다.
실습을 진행해보자.
- 기본 환경구성과 초반 세팅은 nodeAffinity, podAffinity와 같다.
- podAffinity 실습의 mysql.yml 파일 실행까지 동일하게 진행하고 다음 순서를 따르면 된다.
- kubectl apply -f required.yml
- mysql Pod가 띄워져있는 minikube-m02 노드를 제외하고, 다른 노드 범위에만 required의 Pod들이 실행되었음을 알 수 있다.
- kubectl delete -f required.yml 지우고 다음으로 넘어가자.
- kubectl apply -f preferred.yml
- requierd와 마찬가지로 mysql Pod가 있는 노드를 제외한, 다른 노드 범위에만 preferred의 Pod들이 실행되었음을 알 수 있다.
- kubectl delete -f preferred.yml 지우고 마무리 한다.