※주의
이 글은 제가 혼자 공부하면서 여기저기 찾아보고 정리하는 곳이라서
글의 퀄리티나 내용상 맞지 않는 부분들이 있습니다.
공식문서가 더욱 더 큰 도움이 될 수 있습니다!
궁금점
클러스터 안에 단일 노드가 아닌 멀티 노드일때,
파드 생성 시 어떤 방식으로 파드가 노드에 배치가 되고
파드는 노드를 어떻게 선택할까?
행동
1. 멀티 노드일 때 파드 생성 시 아무것도 지정안해줬을 때에 파드는 무슨 노드에 배치가 될까?
2. pod를 생성할 때 pod의 spec에 여러 조건을 추가함으로써 원하는 노드에 배치를 할 수 있습니다.
마스터 컴포넌트(컨트롤-플레인 컴포넌트)에서 kube-scheduler 라는 컴포넌트가 pod가 할당 될
노드를 결정합니다.
우리가 kubectl apply 등의 명령어를 통해서 사용자의 pod 생성 요청이
kube-apiserver에 제출되면 kube-apiserver는 etcd(클러스터의 저장소)에 pod 정보를 저장합니다.
이 때는 NodeName의 값이 설정이 되지 않아서 어떤 node에 배치가 되어야 하는지 모릅니다.
이 때, kube-scheduler가 watch하고 있다가 pod의 NodeName이 비어있다는 것을 확인하고
kube-scheduler가 해당 pod를 할당해 줄 적절한 노드를 찾는 일종의 스케줄링 작업을 시작합니다.
우리는 이 때 kube-scheduler가 pod를 할당해 줄 적절한 노드를 찾는 스케줄링 방식에 대해서 알고싶습니다.
1. 노드 필터링
여러 조건들을 따져서 최종적으로 할당 가능한 노드들을 선정한다.
2. 특정 노드 선택 및 Affinity
배치가 될 노드를 직접 선택하거나 (노드의 개수가 적을 때 유용하고 간단하다)
AND 연산과 더 많은 매칭 규칙을 제공하고 직접 선택하는 방식은
선택한 노드가 꼭 있어야하는 엄격한 규칙, 제한사양이 아닌
유연하고/선호되는 노드를 선택하도록 하는 규칙을 만들 수 있는 Affinity
어피니티 기능 안에도 두 가지 종류의 어피니티가 있는데
- 노드 어피니티
(노드에만 제약을 둔다)
- 파드 간 어피니티/안티-어피니티
(파드에도 제약을 둬서 파드가 생성될 때 어떤 파드와 함께 있으면 안된다,
혹은 꼭 같이 있어야 한다. 라는 것들을 정의할 수 있다.)
이렇게 두 종류로 구성됩니다.
노드 필터링
우선 노드 필터링에 대해서 설명하겠습니다.
노드를 필터링 하는 방법에는 Taints와 Toleration의 개념이 들어갑니다.
쿠버네티스의 노드에는 Taints를 설정함으로써 해당 노드에 Pod가 할당 또는 실행되는 것을 막을 수 있습니다.
하지만 Pod의 정의에 Toleration을 설정하면 Taints가 있는 노드에도 할당하는 것이 가능합니다.
한 마디로 노드의 taints(오염)에도 toleration(용인)할 수 있는 pod는 그 노드에 속할 수 있다.
그런식으로 저는 이해를 했습니다.
쿠버네티스의 마스터 노드에는 pod가 할당되지 않습니다.
왜냐하면 마스터 노드에는 Taints로 NoSchedule이라는 효과가 적용되어 있기 때문입니다.
Taints에 NoSchedule은 파드가 할당이 되지 않는다는 의미입니다.
PreferNoSchedule이라는 효과도 있습니다. NoSchedule과 비슷하게 작동하지만
되도록이면 이 노드에 schedule되지말아라. 라는 느낌이기 때문에 할당이 될 수도있습니다.
그리고 NoExecute라는 효과는 기존의 노드에 실행되고 있던 파드들을 전부 쫓아내는 역할을 합니다.
NoSchedule과 PreferNoSchedule은 기존의 노드에 실행되고 있던 파드들은 계속 실행이 됩니다.
만약 마스터 노드에 파드를 할당하고 싶다면 해당 pod나 deployment의 spec에
Tolerations를 생성해주면 됩니다.
node-role.kubernetes.io 라는 key로 설정된 NoSchedule 효과를
toleration(용인) 하겠다는 말이 됩니다.
이렇게하면 마스터의 taints를 이길 수 있기 때문에 이 pod는 스케줄링할 때
마스터의 노드도 할당가능한 노드로 생각을 하게 됩니다.
마스터의 노드가 default taints로 NoSchedule 효과가 적용되는 것처럼
Pod도 기본적으로 설정되어 있는 Toleration이 있습니다.
두 개의 Toleration을 설정하는데
이 두개의 Toleration이 견뎌 낼 Taints는 우리의 워커 노드가
통신이 불가능하거나 에러가 발생했을 때 노드들은 not-ready, unreachable 상태가 됩니다.
그럴 때 자동으로 NoExecution Taints가 설정이 됩니다.
그 Taints에 견뎌 낼 Toleration이 자동적으로 pod에 생기게 됩니다.
이 Toleration들은 노드가 오류가 생겨도 즉시 pod가 쫓겨나거나 다른 노드로 옮겨지지 않는데
이러한 이유들이 바로 Toleration 들이 default로 설정이 되어있기 때문입니다.
pod들은 기본적으로 노드에 오류가 생기면 300초 뒤에 옮겨지거나 쫓겨나거나 합니다.
특정 노드 선택 및 Affinity
특정 노드 선택
두 번째 방식으로 특정 노드 선택 및 Affinity에 관한 내용입니다.
특정 노드를 선택하는 방식은 nodeName , nodeSelector를 pod의 spec에 명시를 해줄 수 있습니다.
제일 원시적인 방법입니다.
Node Affinity
위에서 설명한 특정 노드를 명시하는 방식은 단순히 이름과 키 값이 같은지만을 비교하기 때문에
활용 방법이 제한이 되어있습니다. 더 넓은 방식, 더 활용성이 큰 Node Affinity 방식입니다.
Affinity 방식은 두 가지 방식으로 나눠서 사용할 수 있습니다.
반드시(HARD) / 가능하면(SOFT)
requiredDuringSchedulingIgnoredDuringExecution / preferredDuringSchedulingIgnoredDuringExecution
이름이 엄청 깁니다..
전자는 반드시 규칙을 만족해야 하는 것(nodeSelector와 비슷하지만 보다 표현적인 구문을 사용)을 지정하고
후자는 스케줄러가 시도하려고는 하지만, 보증하지 않는 선호를 지정합니다.
두 종류의 이름 뒷부분에 "IgnoredDuringExecution"은
노드의 레이블이 런타임 중에 변경되어 파드의 어피니티 규칙이 더 이상 충족되지 않으면
파드가 여전히 그 노드에서 동작한다는 의미입니다.
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, NotIn 과 DoesNotExist 를 사용해서 안티-어피니티를 수행하거나,
특정 노드에서 파드를 쫓아내는 노드 테인트(taint)를 설정할 수 있습니다.
파드간 Affinity & Anti-Affinity
Pod Affinity
Node affinity가 node의 label을 기준으로 Pod가 배포될 node는 선택한다면, Inter pod affinity는 기존에 배포된 Pod를 기준으로 해서, 배포될 node를 결정합니다.
Pod affinity는 데이타 베이스의 Master / Slave pod 가 다른 node 에 배포되도록 하기 위해서 master pod가 배포된 node를 피해서 배포하게 한다던가. 클러스터 시스템에서 클러스터를 이루는 각각의 Pod가 다른 node에 배포도록 하는 등에 전략에 사용할 수 있습니다.
Node affinity와 마찬가지로 Hard affinity와 Soft affinity가 있습니다. Node affinity아 마찬가지로 requiredDuringSchedulingIgnoredDuringExecution 로 hard affinity를,preferredDuringSchedulingIgnoredDuringExecution로 soft affinity를 정의합니다.
Node를 선택할때 Inter pod affinity는 node affinity와 다르게 topology key 라는 것을 사용합니다.
Pod affinity는 Pod affinity에 의해서 해당 node를 선택한 후에, 그 node의 label을 하나 선택합니다.
선택하는 label은 topology key로 지정하는데, 이 topology key에 매칭 되는 node 들을 배포 대상으로 선택합니다. Node affinity와 마찬가지로 특정 Pod와 같이 배포되는 것을 피하는 AntiAffinity 도 있습니다.
Pod affinity는 podAffinity라는 notation을 사용하고, Anti affinity는 PodAntiAffinity라는 Notation을 사용합니다.
apiVersion: v1
kind: Pod
metadata:
name: with-pod-affinity
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S1
topologyKey: cloud/zone
containers:
- name: with-pod-affinity
image: k8s.gcr.io/pause:2.0
PodAffinity에서 requiredDuringSchedulingIgnoredDuringExecution 로 Hard affinity를 정의하였다.
이렇게 되면, 기존에 배포된 Pod 중에서, key가 “security”이고, value가 “S1”인,
Pod1이 배포된 node인 Node 1을 기준으로 하는데,
topologyKey로 정의된 cloud/zone label의 값”z1”을 기준 Node인 Node 1에서 읽어서,
Node 들 중에 label이 “cloud/zone=z1” 인 Node 들만을 후보로 선택해서,
Node 1, Node 2 를 배포 가능 Node 로 선택합니다.
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
PodAntiAffinity
podAffinity와 반대의 기능을 하는 PodAntiAffinity 입니다.
PodAntiAffinity를 사용한 예로, requiredDuringSchedulingIgnoredDuringExecution 를 이용한 Hard affinity입니다.
Pod의 label이 “app=store”가 있는 Pod가 배포되어 있는 Node 중에서
topologyKey가 kubernetes.io/hostname (쿠버네티스에서 자동으로 미리 저장하는 label로 Node의 이름을 정의하는 label 입니다.) 로 되어 있기 때문에,
node의 kubernetes.io/hostname label 값이 이 Node와 다른 Node를 배포 타겟으로 설정합니다.
즉 “app=store” 이름으로 배포된 Pod가 없는 Node에 배포하는 설정입니다.
이 Redis pod는 “app=store” 라는 label을 가지고 있는 Pod이기 때문에,
이미 Node에 이 Redis Pod가 배포되어 있으면,
그 Node에는 배포되지 않기 때문에 Redis Pod가 배포되지 않은 다른 Node에 중첩되서 배포되지 않도록 해줍니다.
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.12-alpine
먼저 podAntiAffinity로 “app:web-store” Pod가 배포되어 있는 node를 찾은 후에,
그 Node이 kubernetes.io/hostname 를 topologyKey로 해서, 그 node들을 제외합니다.
즉 “app:web-store” 라벨을 가지고 있는 Pod 들이 배포된 Node를 제외하는 설정입니다.
그런데, 이 Pod는 label을 “app:web-store” 라벨을 가지도록 되어 있기 때문에,
이 Pod가 배포되어 있는 Node에 배포하지 말고, 다른 Node에 분산해서 배포하라는 내용입니다.
여기에 PodAffinity로 “app:store”인 Pod가 배포되어 있는 Node에서 topologyKey로 “kubernetes.io/hostname”을 사용하였기 때문에, “app:store”인 라벨을 가지고 있는 Pod가 배포되어 있는 Node를 찾아서 배포하라는 내용입니다.
다시 말해서 “app:store”인 Pod와 같은 Node에 배포하라는 의미입니다.
아래는 실행결과 인데, web-server가 각각 다른 Node에 겹치지 않고 분리되서 부탁되어 있는 것을 볼 수 있고, redis-cache Pod도 역시 서로 다른 Node에 겹치지 않게 배포되어 있는 것을 확인할 수 있습니다. 그리고 web-server와 redis-cache pod들은 하나씩 같은 Node에 배포된것을 확인할 수 있습니다.
NAME READY STATUS RESTARTS AGE IP NODE
redis-cache-1450370735-6dzlj 1/1 Running 0 8m 10.192.4.2 kube-node-3
redis-cache-1450370735-j2j96 1/1 Running 0 8m 10.192.2.2 kube-node-1
redis-cache-1450370735-z73mh 1/1 Running 0 8m 10.192.3.1 kube-node-2
web-server-1287567482-5d4dz 1/1 Running 0 7m 10.192.2.3 kube-node-1
web-server-1287567482-6f7v5 1/1 Running 0 7m 10.192.4.3 kube-node-3
web-server-1287567482-s330j 1/1 Running 0 7m 10.192.3.2 kube-node-2
참고
https://m.blog.naver.com/alice_k106/221511412970
'Kubernetes & Docker > 궁금한 것' 카테고리의 다른 글
쿠버네티스 네트워킹(networking)과 포트 포워딩(port forwarding)에 대해서 (1) | 2020.07.17 |
---|---|
쿠버네티스 레플리카셋(replicaset)의 실패 복구(failover)와 proxy에 대해서 (0) | 2020.07.17 |
쿠버네티스 "kubectl get all" 명령어에 나오는 모든 것들에 대해서 (0) | 2020.07.17 |
쿠버네티스 YAML 파일 작성 시 실행 순서에 대해서 (0) | 2020.07.17 |
쿠버네티스 apiVersion에 대해서 (0) | 2020.07.17 |