쿠버네티스 교육

13. Kube 교육 - Label & Affinity

Jerry_이정훈 2021. 6. 4. 16:39
728x90

실습

  • label 이용 NGINX service, pod 연결 
  • 동일 노드에 MySQL Application 동시에 실행되지 않도록 설정(advanced scheduling) 

Why Label & Affinity 

Kube 환경은 Object 중 특정 Object(예: 여러 NGINX POD 중 특정 NGINX POD)만 선택을 해야하는 경우가 많습니다. 예를 들어 Service에서 특정 POD를 선택하고 여러 노드 중 GPU 노드에서만 특정 POD를 실행해야 하는 경우가 대표적입니다.

 

이러한 경우 사용자가 임의로 지정한 Label(상표 정도로 번역) 기준으로 선택합니다. 예를 들어 POD에 app=nginx, app=tomcat 등으로 label을 지정하고 해당 label 기준으로 다른 Object에서 해당 POD를 선택(selector) 합니다.

 

그리고 Kube Cluster 내에서 특정 Application은 시스템 부하 등의 이유로 같은 노드에 중복해서 실행하지 않아야 되는 경우가 있습니다. 또한 POD 이중화를 하였는데 만약 POD가 동일 노드에서 실행된다면 해당 노드가 다운되면 서비스 장애가 발생하므로 서로 다른 노드에서 실행되도록 설정이 필요합니다. 혹은 복수의 특정 Application이 같은 노드에 실행되도록 설정이 필요한 경우도 있습니다.

 

이 때 POD 속성으로 affinity(친밀감 정도 번역)를 이용하여 해당 기능 수행합니다. 노드가 총 4개라면 MariaDB 3중화 구성 시 서로 다른 노드에 분산시켜 노드가 다운되어도 서비스 영향이 없도록 하는 것이 대표적인 사례입니다. 

 

Label 실습

외부 사용자가 내부 Pod에 접속하기 위해서는 Service의 NodePort 혹은 LoadBalancer 설정이 필요합니다. (service에 대한 자세한 설명은 service 포스팅을 참조 바랍니다.) 이 때 Service와 POD를 서로 연결하기 위해서 Label (+ Selector)을 사용합니다. Label은 key:value 형식(app: nginx 등)으로 지정합니다.

 

이전 포스팅에서 실행 중인 NGINX Helm 챠트를 삭제 합니다.

[spkr@erdia22 ~ (k3s:nginx)]$ helm delete nginx
release "nginx" uninstalled

 NGINX Deploy YAML 예시. spec.template.metadata.labels을 주의 합니다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx  # Deploy Label 이름 
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx  # Service에서 사용할 Label 
    spec:
      containers:
      - name: nginx
        image: nginxdemos/hello

NGINX Service YAML 예시
: spec.selector 에서 위에서 지정한 POD label (app: nginx)을 사용합니다.

apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
spec:
  clusterIP: None
  type: ClusterIP
  ports:
  - name: tcp
    port: 80
    protocol: TCP
    targetPort: 80
  selector:  # 선택해야 할 POD 선택
    app: nginx  # POD Label 과 동일

각각 k apply -f 하고 확인해 보겠습니다. 

[spkr@erdia22 13.Label (spkn01:nginx)]$ ka nginxhello-deploy.yml 
deployment.apps/nginx-deployment created
[spkr@erdia22 13.Label (spkn01:nginx)]$ ka loadbalancer-svc.yml 
service/nginx-svc created

정상적으로 POD label 확인이 가능합니다. 

[spkr@erdia22 13.Label (spkn01:nginx)]$ k get pod --show-labels 
NAME                               READY   STATUS        RESTARTS   AGE    LABELS
nginx-deployment-c97cdb45c-p6fqf   1/1     Running       0          7m1s   app=nginx,pod-template-hash=c97cdb45c

해당 label로 Service에서 정상적으로 POD를 선택 하였다면, 아래와 같이 endpoint 확인이 가능합니다. endpoint가 생성된 것은 Serivce에서 POD를 Label 기반으로 정상적으로 선택한 것을 의미 합니다. 

[spkr@erdia22 13.Label (spkn01:nginx)]$ k get endpoints
NAME        ENDPOINTS          AGE
nginx-svc   10.233.90.198:80   50s

참고로 전체 POD의 label을 확인하면 모든 POD가 label을 가지는 것을 알 수 있습니다. (--show-label 옵션을 추가합니다.)

[spkr@erdia22 13.Label (spkn01:nginx)]$ kgpa --show-labels
NAMESPACE          NAME                                                     READY   STATUS         RESTARTS   AGE     IP              NODE    NOMINATED NODE   READINESS GATES   LABELS
cert-manager       cert-manager-5597cff495-5cgds                            1/1     Running        0          24d     10.233.96.47    node2   <none>           <none>            app.kubernetes.io/component=controller,app.kubernetes.io/instance=cert-manager,app.kubernetes.io/name=cert-manager,app=cert-manager,pod-template-hash=5597cff495
(생략)
kube-system        calico-kube-controllers-8b5ff5d58-79lsf                  1/1     Running        0          24d     172.17.16.152   node2   <none>           <none>            k8s-app=calico-kube-controllers,pod-template-hash=8b5ff5d58
kube-system        calico-node-8h4hq                                        1/1     Running        2          115d    172.17.16.153   node3   <none>           <none>            controller-revision-hash=6759d45b94,k8s-app=calico-node,pod-template-generation=1
kube-system        calico-node-mn52j                                        1/1     Running        2          115d    172.17.16.152   node2   <none>           <none>            controller-revision-hash=6759d45b94,k8s-app=calico-node,pod-template-generation=1

Label 정의 (kube 공식 홈페이지)

레이블은 파드와 같은 오브젝트에 첨부된 키와 값의 쌍이다. 레이블은 오브젝트의 특성을 식별하는 데 사용되어 사용자에게 중요하지만, 코어 시스템에 직접적인 의미는 없다. 레이블로 오브젝트의 하위 집합을 선택하고, 구성하는데 사용할 수 있다. 레이블은 오브젝트를 생성할 때에 붙이거나 생성 이후에 붙이거나 언제든지 수정이 가능하다.

레이블은 UI와 CLI에서 효율적인 쿼리를 사용하고 검색에 사용하기에 적합하다. 

 Label은 POD 뿐만 아니라 여러 Kube Object(Service, StatefulSet 등)를 구분하는 단위입니다. 

 

출처 : Kodekloud

쇼핑몰 분류도 하나의 예시입니다. 

 

Label은 VM 환경에서는 사용하지 않는 부문이라 생소하지만 어렵지 않은 내용이라 몇번만 사용하다 보면 금방 익힐 수 있습니다. 

 

(참고로 annotation(주석 정도로 번역)는 label과 아주 유사하지만 형식에 제약 없이 보다 다양한 값을 가질 수 있습니다. 주로 사용자가 아닌 시스템 또는 Tool 등에서 참조할 때 사용합니다.)

Kube Annotations 설명(공식 홈페이지)
어노테이션의 메타데이터는 작거나 크고, 구조적이거나 구조적이지 않을 수 있으며, 레이블에서 허용되지 않는 문자를 포함할 수 있다.
Annotation 예시

apiVersion: v1
kind: Service
metadata:
  name: quote
  annotations:
    a8r.io/owner: “@sally”
    a8r.io/repository: "https://github.com/ambassadorlabs/k8s-for-humans/"
spec:
  ports:
  - name: http
    port: 80
    targetPort: 8080
  selector:
    app: quote

Kube Node Affinity

다음으로 Node Affinity에 대하여 알아 보겠습니다. RabbitMQ Helm Chart를 설치하면 아래와 같이 Affinity 확인이 가능합니다.

[spkr@erdia22 ~ (dzbumin:rabbitmq)]$ k get sts -o yaml rabbitmq |k neat
	(생략)
    spec:
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - podAffinityTerm:
              labelSelector:
                matchLabels:
                  app.kubernetes.io/instance: rabbitmq
                  app.kubernetes.io/name: rabbitmq
              namespaces:
              - rabbitmq
              topologyKey: kubernetes.io/hostname
            weight: 1
(생략)

podAntiAffinity 즉 POD 실행 시 Anti(반대) Affinity 속성을 가지도록 Scheduling 됩니다. 기존 노드에 rabbitmq 라는 label을 가진 pod가 실행 중이면 다른 Node에서 실행하는 것을 선호하도록 (preferredDuringSchedulingIgnoredDuringExecution:) 설정되어 있습니다. (강제(required)는 아니고 선호(preferred) 한다 입니다.)

 

확인해보면 POD가 서로 다른 노드에 실행 중 입니다. 

[spkr@erdia22 ~ (dzbumin:rabbitmq)]$ kgp
NAME         READY   STATUS    RESTARTS   AGE   IP              NODE    NOMINATED NODE   READINESS GATES
rabbitmq-0   1/1     Running   0          12d   10.233.92.115   node3   <none>           <none>
rabbitmq-1   1/1     Running   0          12d   10.233.90.102   node1   <none>           <none>
rabbitmq-2   1/1     Running   0          10d   10.233.96.25    node2   <none>           <none>

POD를 삭제하여도 동일하게 기존 rabbitmq가 실행되지 않은 노드에서 실행 됩니다. 

[spkr@erdia22 ~ (dzbumin:rabbitmq)]$ k delete pod rabbitmq-2
pod "rabbitmq-2" deleted

[spkr@erdia22 ~ (dzbumin:rabbitmq)]$ kgpw
NAME         READY   STATUS    RESTARTS   AGE   IP              NODE    NOMINATED NODE   READINESS GATES
rabbitmq-0   1/1     Running   0          12d   10.233.92.115   node3   <none>           <none>
rabbitmq-1   1/1     Running   0          12d   10.233.90.102   node1   <none>           <none>
rabbitmq-2   1/1     Running   0          10d   10.233.96.25    node2   <none>           <none>
rabbitmq-2   1/1     Terminating   0          10d   10.233.96.25    node2   <none>           <none>
rabbitmq-2   1/1     Terminating   0          10d   10.233.96.25    node2   <none>           <none>
rabbitmq-2   0/1     Terminating   0          10d   10.233.96.25    node2   <none>           <none>
rabbitmq-2   0/1     Terminating   0          10d   10.233.96.25    node2   <none>           <none>
rabbitmq-2   0/1     Terminating   0          10d   10.233.96.25    node2   <none>           <none>
rabbitmq-2   0/1     Pending       0          <invalid>   <none>          <none>   <none>           <none>
rabbitmq-2   0/1     Pending       0          0s          <none>          node2    <none>           <none>
rabbitmq-2   0/1     ContainerCreating   0          0s          <none>          node2    <none>           <none>
rabbitmq-2   0/1     ContainerCreating   0          1s          <none>          node2    <none>           <none>
rabbitmq-2   0/1     Running             0          2s          10.233.96.108   node2    <none>           <none>
rabbitmq-2   0/1     Completed           0          15s         10.233.96.108   node2    <none>           <none>
rabbitmq-2   0/1     Running             1          16s         10.233.96.108   node2    <none>           <none>
rabbitmq-2   1/1     Running             1          48s         10.233.96.108   node2    <none>           <none>

[spkr@erdia22 ~ (dzbumin:rabbitmq)]$ kgp
NAME         READY   STATUS    RESTARTS   AGE     IP              NODE    NOMINATED NODE   READINESS GATES
rabbitmq-0   1/1     Running   0          12d     10.233.92.115   node3   <none>           <none>
rabbitmq-1   1/1     Running   0          12d     10.233.90.102   node1   <none>           <none>
rabbitmq-2   1/1     Running   1          2m23s   10.233.96.108   node2   <none>           <none>

참고로 pod affinity 설정을 hard/soft 로 구분하여 기존 pod가 실행 중인 node에 반드시 실행되지 않도록 설정(hard) 또는 반드시는 아니지만(예를 들어 노드가 2대 뿐이라서 3개 application이 실행하려면 어쩔 수 없는 경우) 가능하면 실행(soft)하게 등 선택이 가능합니다. 

 

또한 대부분의 Application Helm Chart가 default로 affinity 설정이 되어 있어 신규로 affinity 생성하지 않고 기존 설정 내역을 이해만 하시면 됩니다.  

예를 들어 blue, red, green 노드 선택이 가능합니다.

참고로 Node에도 label이 설정되어 있어 특정 노드만 선택(예를 들어 GPU  Node)하여 pod scheduling이 가능합니다.

[spkr@erdia22 ~ (spkn01:rabbitmq-system)]$ k get nodes --show-labels
NAME    STATUS   ROLES    AGE    VERSION   LABELS
node1   Ready    master   115d   v1.19.7   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node1,kubernetes.io/os=linux,node-role.kubernetes.io/master=
node2   Ready    master   115d   v1.19.7   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node2,kubernetes.io/os=linux,node-role.kubernetes.io/master=
node3   Ready    <none>   115d   v1.19.7   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node3,kubernetes.io/os=linux
어피니티(affinity)와 안티-어피니티(anti-affinity) - 공식 홈페이지 설명

nodeSelector 는 파드를 특정 레이블이 있는 노드로 제한하는 매우 간단한 방법을 제공한다. 어피니티/안티-어피니티 기능은 표현할 수 있는 제약 종류를 크게 확장한다. 주요 개선 사항은 다음과 같다. 

어피니티/안티-어피니티 언어가 더 표현적이다. 언어는 논리 연산자인 AND 연산으로 작성된 정확한 매칭 항목 이외에 더 많은 매칭 규칙을 제공한다. 
규칙이 엄격한 요구 사항이 아니라 "유연한(soft)"/"선호(preference)" 규칙을 나타낼 수 있기에 스케줄러가 규칙을 만족할 수 없더라도, 파드가 계속 스케줄되도록 한다.
노드 자체에 레이블을 붙이기보다는 노드(또는 다른 토폴로지 도메인)에서 실행 중인 다른 파드의 레이블을 제한할 수 있다. 이를 통해 어떤 파드가 함께 위치할 수 있는지와 없는지에 대한 규칙을 적용할 수 있다.

https://kubernetes.io/ko/docs/concepts/scheduling-eviction/assign-pod-node/

 

노드에 파드 할당하기

특정한 노드(들) 집합에서만 동작하도록 파드를 제한할 수 있다. 이를 수행하는 방법에는 여러 가지가 있으며 권장되는 접근 방식은 모두 레이블 셀렉터를 사용하여 선택을 용이하게 한다. 보통

kubernetes.io

 

이상 간단히 Label 과 Affinity에 대하여 알아 보았습니다.


Kubernetes 교육 및 기술 지원 문의 : leejunghoon@spkr.co.kr

반응형