Node selector vs. Node affinity, 어떤 것을 사용할까?
오늘의 주제는 node selector와 node affinity 입니다.
Kubernetes에서 pod가 스케쥴링 될 node를 지정하는 기능에 대한 이야기인데요,
고민하지 마시고 node affinity를 사용하시면 됩니다.
- 끝 -
농담입니다 😉 결론이 저거는 맞는데 저렇게 포스팅하면 안 되고, 차근히 알아보도록 하겠습니다.
- 주의: 제가 여기서 이야기하는 '서비스'는 회사 입장에서의 '서비스'이고, 쿠버네티스의 service 타입 리소스에 국한되어 언급하는 것이 아닙니다. deployment 타입을 포함하며 특정 리소스 타입에 국한되지 않는, 예를 들어 로그인 서버라고 한다면 로그인 서비스와 같은 추상적이고 포괄적인 서비스를 일컫습니다.
pod 할당 node 지정이 무슨 소리인가요?
pod의 스케쥴링 환경 할당에 대해서는, Kubernetes 공식 문서에 잘 나와 있습니다. https://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes/
대부분의 쿠버네티스 환경에서 우리는 여러 개의 node를 사용하게 됩니다. (노드 풀두요...)
예를 들어 노드가 node-a, node-b, node-c 이런 식으로 존재하고 service-a와 service-b가 존재한다면, node를 지정하여 pod 스케쥴링을 한다는 것은 service-a는 node-a에, service-b 는 node-b에만 스케쥴링되도록 지정하는 것입니다.
Assign Pods to Nodes
This page shows how to assign a Kubernetes Pod to a particular node in a Kubernetes cluster. Before you begin You need to have a Kubernetes cluster, and the kubectl command-line tool must be configured to communicate with your cluster. It is recommended to
kubernetes.io
어디든 서비스 뜨기만 하면 되지 노드를 왜 지정해서 띄워야 하나요?
환경 분리, 비용 문제, 보안성 및 가용성 확보 등 여러 이점이 있습니다.
엔터프라이즈 환경에서는, 특정 서비스는 법적으로 다른 서비스와는 분리된 환경에서 구성되어야 하는 경우가 있습니다.
클러스터 자체를 분리하는 게 가장 편하겠지만, 그 정도로 비즈니스 요구 조건이 엄격하지 않다면 굳이 더 큰 비용을 지불해 가면서 클러스터를 분리하지 않고 노드풀 정도만 분리해도 되겠죠...
또 클러스터 내에서 서비스들 간 필요 스펙 차이가 심한 경우에도 분리가 필요합니다.
특!히! 퍼블릭 클라우드를 사용하는 경우 스펙/사용량에 따라 컴퓨팅 자원의 과금이 결정되는데 인공지능 관련 서비스와 단순한 형태의 웹앱을 올려 두고 사용한다고 가정해 봅시다. 기껏 비싼 돈 주고 gpu 달린 하이스펙 노드풀과, 트래픽 낮은 단순웹앱용 노드풀을 분리해서 만들어 놨는데 웹앱들이 하이스펙 노드풀에 우르르 스케쥴링된다면 보기 좋지는 않겠죠... 😎
가용성을 놓고 봐도 서비스나 프로파일 등의 환경에 따라 나눠 두고 특정 노드풀이 죽는 일이 생기더라도 관련없는 다른 서비스에 영향을 주지 않도록 분리해서 운영하며 임팩을 최소화 하는 것이 좋습니다.
여기서 드는 생각은, 그러면 아예 임팩을 주지 못하도록 클러스터를 분리하면 되지 않느냐? 일 건데요.
아하! 그런 방법이! 너무나 맞는 말입니다! 클러스터를 분리하면 모든 문제가 해결되겠군요!
문제는 그렇게 다 나눠서 클러스터를 운영하다간 한달 뒤 인보이스에 찍힌 숫자가 과거의 님을 매우 치려 들 것입니다...
그것도 그렇고 클러스터 늘어날 때마다 관리포인트가 늘어나는것도...
Node selector는 어떤 방식인가요?
가장 단순한 방법으로, 해당 pod가 스케쥴 될 node를 라벨 기준으로 지정하는 방법입니다. podspec 안에 선언하는 형태이고, 키-값 쌍으로 라벨을 지정합니다. 적용은 아래와 같은 형태로 할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
nodeSelector:
disktype: ssd
|
cs |
볼드 처리 된 disktype: ssd부분이 보이시나요? 이게 nodeSelector의 기본적인 지정 방식입니다.
node들은 각자 자체적으로 제공되는 것이든, 퍼블릭 클라우드에서 제공되는 것이든, 아니면 유저가 스스로 부여한 라벨이든간에 여러 라벨들을 가지고 있는데, nodeSelector를 통해 라벨 기준으로 지정이 가능합니다!
예시처럼 한다면 클러스터 내 노드들 중 disktype 이라는 label의 ssd 라는 값을 가진 노드에만 해당 pod가 스케쥴링 되게 됩니다 😎
이건 어떻게 매기냐고요~!? kubectl label nodes 노드이름 disktype=ssd 이런 식으로 매기실 수 있습니다~!
다른 방법도 있지만 이 포스팅은 node selector와 affinity에 대해 이야기하는 포스팅이니까요~~
아무튼 이런 식으로 특정 환경을 지정할 수 있습니다. 노드 한개에만 동일한 라벨을 매길 수 있는 것이 아니므로, 라벨링을 통해 pod 스케쥴링 환경을 통제할 수 있겠죠.
Node affinity는 어떤 방식인가요?
nodeselector보다 조금 더 세밀하게 정책을 적용할 수 있습니다. 더 번거롭다는 뜻이겠죠, 하지만 엔터프라이즈 환경이라면 nodeSelector보다는 nodeAffinity를 사용하실 확률이 매우!매우! 높습니다. 아래와 같은 방식으로 적용합니다. 느낌 오실 거예요.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: disktype
operator: In
values:
- ssd
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
|
cs |
볼드 처리된 부분의 동작 방식은 위의 nodeSelector:disktype:ssd 와 동일합니다.
그런데 조금 길어졌죠. 하나씩 보자면, 우선 pod 의 spec 안에 동일하게 위치하는 것을 보실 수 있습니다.
affinity 안에 nodeAffinity가 별도로 있죠? 다른 affinity, pod affinity같은 것도 존재한다는 게 감이 오실 겁니다.
그 다음 읽다가 숨 넘어가는, **requiredDuringSchedulingIgnoredDuringExecution**조건 구문이 나옵니다.
이건 무슨 뜻일까요? 반으로 쪼개 보겠습니다.
requiredDuringScheduling 스케쥴링 할 때에는 필수적이라는 뜻 입니다.
즉, 후술할 nodeaffinity의 조건이 새롭게 스케쥴링되는 파드에 대해서는 반드시 지켜져야 한다는 말입니다.
그러면 필수값만 줄 수 있느냐?
아닙니다. preferredDuringScheduling으로 '필수는 아니지만 가급적이면' 해당 노드로 스케쥴링되도록 할 수 있습니다.
preferred로 지정한다면 조건에 부합하는 노드가 어떤 사정이 있어(자원이 모자란다든가) 해당 pod를 스케쥴링 받아줄 수 없는 상태가 되었을 때에는 다른 가능한 노드로 스케쥴링 되게 됩니다.
IgnoreDuringExecution은 실행 중이라면 무시하라는 뜻 입니다.
이미 실행 중 상태의 pod라면 해당 노드가 더이상 그 룰을 충족하지 않는 상태가 되더라도 굳이 pod를 내리고 다시 옮겨서 실행하지 않습니다. 지금 릴리즈가 됐는지는 모르겠으나 반대 개념으로 RequiredDuringExecution 이 있습니다.
그리고 이후 matchExpression 부분을 보시면, key에 드디어 라벨명이 나옵니다.
해당 라벨을 찾아서, Operator로 연산을 수행합니다. In은 INCLUDE를 의미하고요, 다른 연산자로 NotIn, Exists, DoesNotExist, Gt, Lt 가 있습니다.
결국 저 길고 긴 조건들로, disktype=ssd에 부합하는 노드에 스케쥴링을 하는 것을 확인할 수 있습니다.
그래서 뭘 쓰란 건가요?
가급적 nodeaffinity를 이용한 스케쥴링 제한을 권장 드립니다. 조건이 길어지지만, 그건 그만큼 세밀한 조정이 가능하다는 뜻입니다. antiaffinity를 적용할 수도 있고, preferred 설정을 통해 스케쥴링 자체가 되지 않는 상황을 방지하는 것 또한 가능합니다 🙂
기본적인 사용법만 공유 드렸지만, 조건을 여러 개를 줄 수도 있고, 각 조건에 weight를 줄 수도 있어 확장성을 생각한다면 nodeaffinity를 사용하는 것을 추천 드립니다. 그런 게 아니고 개인용으로 사용하는 거고 번거로운게 싫다면 nodeSelector를 쓰셔도 무관합니다!
더 알고 싶어요!
많은 유즈케이스와 더 자세한 설명법을 보고 싶다면, 공식 문서를 찾아주세요~! https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity
Assigning Pods to Nodes
You can constrain a Pod so that it can only run on particular set of Node(s). There are several ways to do this and the recommended approaches all use label selectors to facilitate the selection. Generally such constraints are unnecessary, as the scheduler
kubernetes.io
'DevOps' 카테고리의 다른 글
[Kubernetes & Azure] TLS 인증서(SSL 인증서) Keyvault 연동하기 (1) | 2021.11.13 |
---|---|
[Kubernetes] Application Gateway의 기본 기능, Connection Draining에 대해 알아보자! (0) | 2021.11.10 |
Jenkins에서 API를 이용하여 특정 설정을 가진 Job 생성하기(젠킨스 API 잡 생성, config.xml 주입, Jenkins API 인증) (1) | 2021.10.05 |
DNS 레코드 삭제 후 재생성 시 도메인 질의가 되지 않는 문제 해결하기 (3) | 2021.09.21 |
ARP Protocol은 어떻게 맥 어드레스를 쿼리하고 수신할까? (1) | 2021.08.16 |