k8s는 파드를 어떤 노드에 실행할 것인지에 관한 다양한 옵션이 있다. 특정 파드들을 노드 하나에 모아두거나, 같은 기능이 있는 파드들이 노드 하나에 몰려있지 않게 분산해서 실행할 수도 있다.
테인트는 특정 노드에서 파드를 멀리 떨어뜨리는 데 사용되지만, 노드 어피니티(=친화성)를 이용하면 파드를 특정 노드로 유인할 수 있다. 즉, k8s에게 노드의 특정 하위 집합에만 파드를 스케줄하도록 지시할 수 있다.
노드 어피니티
는 노드 셀렉터
와 같은 방식으로 라벨을 기반으로 노드를 선택한다.
🧩 기존의 노드 셀렉터를 이용하는 방식
apiVersion: v1
kind: Pod
metadata:
name: kubia-gpu
spec:
nodeSelector:
gpu: "true" # 이 파드는 gpu=true 라벨을 갖고 있는 노드에만 스케줄된다
...
노드 셀렉터를 사용해 GPU가 있는 노드에만 GPU가 필요한 파드를 배포한다.
🧩 노드 어피니티 규칙을 이용하는 방식
apiVersion: v1
kind: Pod
metadata:
name: kubia-gpu
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions: # 이 파드는 gpu=true 라벨이 있는 노드로만 스케줄
- key: gpu
operator: In
values:
- "true"
...
노드 셀렉터보다 훨씬 복잡해졌지만 훨씬 더 세부적으로 설정할 수 있다.
🔎 requiredDuringSchedulingIgnoredDuringExecution
requiredDuringScheduling...
: 노드가 파드를 노드에 스케줄하기 위해 갖고 있어야 하는 라벨을 지정함을 의미한다....IgnoredDuringExecution
: 노드에서 이미 실행 중인 파드에는 영향을 미치지 않는다는 의미이다.즉, 스케줄링하는 동안 꼭 필요한 조건을 의미한다.
🔎 nodeSelctorTerms
nodeSelectorTerms
필드와 matchExpressions
필드를 이용해서 파드를 의도한 노드에 스케줄링되도록 정의할 수 있다.
파드의 노드 어피니티은 파드에 대해 노드가 스케줄해야 하는 라벨을 지정한다 (출처: github.com/sungsu9022/study-kubernetes-in-action/issues/16)
노드 어피니티의 preferredDuringSchedulingIgnoredDuringExecution
필드는 스케줄링하는 동안 만족하면 좋은 조건을 의미한다. prefer 글자 그대로 만족하면 좋은 것이지 꼭 이 조건을 만족해야 하는 것은 아니다.
이를 이용해 스케줄링 중에 노드의 우선순위를 지정할 수 있다. 즉, 특정 파드를 스케줄할 때 스케줄러가 선호하는 노드를 지정할 수 있다.
먼저 동작 확인을 위해 노드에 라벨링을 한다.
노드에 라벨 설정하기 (출처: github.com/sungsu9022/study-kubernetes-in-action/issues/16)
예제에서는 각 노드에 노드가 속한 가용 영역
을 지정하는 라벨과, 이를 전용
또는 공유
노드로 표시하는 라벨을 설정한다.
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: pref
spec:
replicas: 5
template:
...
spec:
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution: # 필수가 아닌 선호 어피니티 속성 지정
- weight: 80 # 가중치 80의 availability-zone 선호도 지정
preference: # 파드가 zone1에 스케줄되는 것을 더 선호한다
matchExpressions:
- key: availability-zone
operator: In
values:
- zone1
- weight: 20 # 가중치 20의 share-type 선호도 지정
preference: # 파드를 전용 노드로 스케줄하는 것을 선호하지만 가용 영역 선호도보다 4배는 덜 중요하다
matchExpressions:
- key: share-type
operator: In
values:
- dedicated
...
weight
필드는 1부터 100까지의 값을 설정할 수 있다.
zone1을 전용 노드로 선호하는 디플로이먼트를 만들 수 있다. availability-zone=zone1
과
share-type=dedicated
라벨이 있는 노드에 스케줄되길 원하는 것을 확인할 수 있다. 첫 번째 어피니티 규칙은 매우 중요하기 때문에 가중치를 80으로 설정했고, 두 번째 규칙은 덜 중요하므로 20으로 설정했다.
파드의 노드 어피니티에 기반한 노드 우선순위 정하기 (출처: github.com/sungsu9022/study-kubernetes-in-action/issues/16)
노드는 네 개의 그룹으로 분할된다. 가용성 영역
과 공유 유형
라벨이 파드의 노드 어피니티와 일치하는 노드에는 가장 높은 순위가 매겨진다. 어피니티 규칙의 가중치에 따라 zone1의 공유 노드
가 그 다음 우선권을 갖고 그 다음으로 다른 영역의 전용 노드
가 우선권을 가진다. 가장 낮은 우선순위는 그 외의 다른 모든 노드
다.
🔎 SelectorSpreadPriority
스케줄러가 노드를 결정하는 데 있어서 어피니티 우선순위 지정 기능 외에도 다른 우선순위 지정 기능을 사용한다. 그 중 하나는 Selector-SpreadPriority
이다.
Selector-SpreadPriority
기능은 동일한 레플리카셋 또는 서비스에 속하는 파드를 여러 노드에 분산시켜 노드 장애로 인해 전체 서비스가 중단되지 않도록 하는 기능이다.
만약 노드가 2개인 클러스터에 20개의 파드를 배포한다고 하면 노드 선호도 우선순위에 따라 한쪽 노드에만 20개가 스케줄 될 것 같지만, 20개 중 2개만 다른 한쪽의 노드로 스케줄된다. 노드 어피니티를 지정하지 않은 경우라면 파드는 두 노드 주위에 고르게 확산된다.
노드 어피니티를 이용하면 오직 파드와 노드 사이의 친화성에만 영향을 미친다. k8s에서는 파드 어피니티
를 통해 파드들 간의 친화성 또한 지정할 수 있다.
예를 들어 프론트엔드 파드와 백엔드 파드가 있다고 가정할 때, 이 두 파드를 가까이 배치할 수 있다면 대기 시간을 줄이고 어플리케이션의 성능을 향상시킬 수 있다. 이를 파드 어피니티를 이용해 달성할 수 있다.
# app=backend 레이블을 붙인 백앤드 파드 생성
$ kubectl run backend -l app=backend --image busybox -- sleep 99999
deployment "backend" created
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: frontend
spec:
replicas: 5
template:
...
spec:
affinity:
podAffinity: # podAffinity 규칙 정의
requiredDuringSchedulingIgnoredDuringExecution: # 필수 파드 어피니티 속성 정의
- topologyKey: kubernetes.io/hostname # 셀렉터와 부합하는 파드와 동일한 노드에 배포
labelSelector:
matchLabels:
app: backend
...
노드 어피니티와 다르게 파드 어피니티에는 topologyKey
필드를 사용한다. topologyKey
필드는 노드 레이블의 키로, 다른 파드가 파드와 얼마나 가까운 위치에 배포되어야 하는지 지정한다. 파드를 스케줄링할 때 먼저 파드의 라벨 기준으로 대상 노드를 찾고 topologyKey에 매칭되는 노드들을 배포 대상으로 선택한다.
예제에서는 값을 kubernetes.io/hostname으로 지정했기 때문에 hostname을 기준으로 같은 노드에 파드를 실행한다.
파드 어피니티를 이용 (출처: github.com/sungsu9022/study-kubernetes-in-action/issues/16)
app=backend
라벨이 있는 파드
와 디플로이먼트
가 동일한 노드에 배포되도록 하는 필수 요구 사항을 갖는 파드를 생성한다.
백엔드 파드가 스케줄되었던 노드 확인 (출처: github.com/sungsu9022/study-kubernetes-in-action/issues/16)
프론트엔드 파드 배포 및 노드 확인 (출처: github.com/sungsu9022/study-kubernetes-in-action/issues/16)
프론트엔드 파드가 생성되면 백엔드 파드와 동일한 node2 노드로 스케줄된 것을 확인할 수 있다.
🔎 필수 요구 사항 대신 파드 어피니티 선호도 표시하기
스케줄러에게 프론트엔드 파드를 백엔드 파드와 동일한 노드에 스케줄하는 것을 선호
하도록 할 수 있다. podAffinity
의 preferredDuringSchedulingIgnoredDuringExecution
속성을 이용하여 노드 어피니티 설정한 것과 동일한 처리를 할 수 있다.
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: frontend
spec:
replicas: 5
template:
...
spec:
affinity:
podAffinity:
preferredDuringSchedulingIgnoredDuringExecution: # 선호 파드 어피니티
- weight: 80 # 가중치 80의 availability-zone 선호도 지정
podAffinityTerm:
topologyKey: kubernetes.io/hostname
labelSelector:
matchLabels:
app: backend
...
노드 어피니티의 선호도 설정 규칙과 마찬가지로 각 규칙의 가중값을 정의해야 한다.
파드 어피니티의 선호도 설정 (출처: github.com/sungsu9022/study-kubernetes-in-action/issues/16)
파드 어피니티 선호도를 통해 배포된 파드 (출처: github.com/sungsu9022/study-kubernetes-in-action/issues/16)
백엔드 파드와 동일한 노드에 4개의 파드가 배포되고 다른 노드에는 파드가 하나가 배포되는 것을 확인할 수 있다.
파드 어피니티를 통해 스케줄러가 파드를 동일한 위치에 배포할 수 있다.
파드를 서로 멀리 떨어뜨려 놓고 싶은 경우 파드 안티-어피니티 속성을 사용해서 처리할 수 있다.
podAffinity
대신 podAntiAffinity
속성을 사용하는 것 외에는 podAffinity 작성 방식과 동일하다. podAntiAffinity의 라벨 셀렉터와 일치하는 파드가 실행 중인 노드를 선택하지 않는다.
파드 안티-어피니티를 사용해 특정 라벨의 파드가 실행 중인 노드와 떨어지게 한다 (출처: github.com/sungsu9022/study-kubernetes-in-action/issues/16)
위 파드는 app=foo
레이블이 있는 파드가 실행중인 노드로 스케줄링 되지 않는다.
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: frontend
spec:
replicas: 5
template:
metadata:
labels: # 프론트엔드 파드는 app=frontend 라벨을 갖는다
app: frontend
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution: # 필수의 파드 안티 어피니티 사용
- topologyKey: kubernetes.io/hostname
labelSelector: # 프론트엔드 파드가 app=frontend 라벨을 가진 파드와 같은 노드에서 실행되지 않도록 설정
matchLabels:
app: frontend
containers: ...
파드 안티-어피니티를 활용해서 같은 디플로이먼트의 파드를 분산시켰다.
디플로이먼트에 의해 생성된 파드 (출처: github.com/sungsu9022/study-kubernetes-in-action/issues/16)
2개의 파드 중 1개는 노드 1
에, 나머지 1개는 노드 2
에 스케줄됐다. 스케줄러가 동일한 노드에 스케줄할 수 없기 때문에 나머지 3개의 파드는 대기(pending) 상태
가 된다.
🔎 파드 안티-어피니티 활용 방안
두 개의 파드 세트가 동일한 노드에서 실행되는 경우 서로의 성능을 방해할 수도 있는 경우에 이 기능을 사용할 수 있다. 또한 스케줄러가 동일한 그룹의 파드를 다른 가용 영역 또는 리전에 분산시켜 전체 가용 영역에 장애가 발생해도 서비스가 완전히 중단되지 않도록 하는 경우 사용할 수 있다.
노드 어피니티
를 이용해 파드를 스케줄할 노드를 지정할 수 있다.파드 어피니티
는 동일한 노드에 파드를 배포하는 데 사용된다.파드 안티-어피니티
를 이용해 특정 파드 간에 서로 멀리 떨어지게 할 수 있다.
References