노드 어피니티, 테인트 및 톨러레이션을 사용하여 Amazon EKS에 Kubernetes 포드 배치 - AWS 권장 가이드

기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.

노드 어피니티, 테인트 및 톨러레이션을 사용하여 Amazon EKS에 Kubernetes 포드 배치

작성: 히테시 패릭(AWS), 라구 바미디마리(AWS)

환경: PoC 또는 파일럿

기술: 컨테이너 및 마이크로서비스

워크로드: 오픈 소스

AWS 서비스: Amazon EKS

요약

이 패턴은 Kubernetes 노드 어피티니, 노드 테인트 및 포드 톨러레이션을 사용하여 Amazon Web Services(AWS) 클라우드의 Amazon Elastic Kubernetes Service(Amazon EKS) 클러스터에 있는 특정 워커 노드에 애플리케이션 포드를 의도적으로 예약하는 것을 보여줍니다.

테인트는 노드가 포드 세트를 거부할 수 있도록 하는 노드 속성입니다. 톨러레이션은 Kubernetes 스케줄러가 일치하는 테인트가 있는 노드에서 포드를 예약할 수 있게 해주는 포드 속성입니다.

하지만 톨러레이션만으로는 스케줄러가 테인트가 없는 워커 노드에 포드를 배치하는 것을 방지할 수 없습니다. 예를 들어, 톨러레이션이 있는 컴퓨팅 집약적 포드는 의도치 않게 테인트가 없는 범용 노드에 예약될 수 있습니다. 이 경우, 포드의 노드 어피니티 속성은 스케줄러에게 노드 어피니티에 지정된 노드 선택 기준을 충족하는 노드에 포드를 배치하도록 지시합니다.

테인트, 톨러레이션, 노드 어피니티를 함께 사용하면 스케줄러가 포드에 지정된 노드 어피니티 노드 선택 기준과 일치하는 테인트가 있는 노드와 노드 레이블에 일관되게 포드를 예약하도록 지시합니다.

이 패턴은 Kubernetes 배포 매니페스트 파일 예제와 EKS 클러스터를 생성하고 애플리케이션을 배포하고 포드 배치를 검증하는 단계를 제공합니다.

사전 조건 및 제한 사항

사전 조건

제한 사항

  • 이 패턴은 Java 코드를 제공하지 않으며 사용자가 이미 Java에 익숙하다고 가정합니다. 기본 Java 마이크로서비스를 생성하려면 Amazon EKS에 샘플 Java 마이크로서비스 배포를 참조하세요.

  • 이 문서의 단계는 비용이 발생할 수 있는 AWS 리소스를 생성합니다. 패턴을 구현하고 검증하기 위한 단계를 완료한 후에는 AWS 리소스를 정리해야 합니다.

아키텍처

대상 기술 스택

  • Amazon EKS

  • Java

  • Docker

  • Amazon Elastic Container Registry (Amazon ECR)

대상 아키텍처 

솔루션 아키텍처 다이어그램은 두 개의 포드(배포 1 및 배포 2)와 각각 두 개의 노드가 있는 두 개의 노드 그룹(ng1 및 ng2)이 있는 Amazon EKS를 보여줍니다. 포드 및 노드에는 다음과 같은 속성이 있습니다.

 

배포 1 포드

배포 2 포드

노드 그룹 1(ng1)

노드 그룹 2 (ng2)

톨러레이션

키: 분류_워크로드, 값: true, 효과: NoSchedule

키: 머신러닝_워크로드, 값: true, 효과: NoSchedule

None

 

 

노드 어피니티

키: alpha.eksctl.io/nodegroup-name = ng1;

None

nodeGroups.name = ng1

 

테인트

 

 

키: 분류_워크로드, 값: true, 효과: NoSchedule

키: 머신러닝_워크로드, 값: true, 효과: NoSchedule

None

  1. 배포 1 포드에는 톨러레이션과 노드 어피니티가 정의되어 있으며, 이는 Kubernetes 스케줄러가 배포 포드를 노드 그룹 1(ng1) 노드에 배치하도록 지시합니다.

  2. 노드 그룹 2 (ng2)에는 배포 1의 노드 어피니티 노드 선택기 표현식과 일치하는 노드 레이블이 없으므로, 포드는 ng2 노드에 스케줄링되지 않습니다.

  3. 배포 2 포드에는 베포 매니페스트에 정의된 톨러레이션이나 노드 어피니티가 없습니다. 스케줄러는 노드의 테인트 때문에 노드 그룹 1에 배포 2 포드를 스케줄링하는 것을 거부합니다.

  4. 노드에 테인트가 없기 때문에 배포 2 포드는 대신 노드 그룹 2에 배치됩니다.

이 패턴은 테인트와 톨러레이션을 노드 어피니티와 함께 사용하면 특정 워커 노드 세트에 포드를 배치하는 것을 제어할 수 있음을 보여줍니다.

도구

서비스

기타 도구

  • Docker는 운영 체제 수준의 가상화를 사용하여 컨테이너에 소프트웨어를 제공하는 서비스형 플랫폼(PaaS) 제품 세트입니다.

  • kubectl는 Kubernetes 클러스터에 대해 명령의 실행을 돕는 명령줄 인터페이스입니다.

에픽

작업설명필요한 기술

cluster.yaml 파일을 생성합니다.

다음 코드를 이용해 cluster.yaml(이)라는 파일을 생성합니다.

apiVersion: eksctl.io/v1alpha5 kind: ClusterConfig metadata: name: eks-taint-demo region: us-west-1 # Unmanaged nodegroups with and without taints. nodeGroups: - name: ng1 instanceType: m5.xlarge minSize: 2 maxSize: 3 taints: - key: classified_workload value: "true" effect: NoSchedule - key: machine_learning_workload value: "true" effect: NoSchedule - name: ng2 instanceType: m5.xlarge minSize: 2 maxSize: 3
앱 소유자, AWS DevOps, 클라우드 관리자, DevOps 엔지니어

eksctl을 사용하여 클러스터를 생성합니다.

cluster.yaml 파일을 실행하여 EKS 클러스터를 생성합니다. 클러스터를 생성하는 데 몇 분 정도 걸릴 수 있습니다.

eksctl create cluster -f cluster.yaml
AWS DevOps, AWS 시스템 관리자, 앱 개발자
작업설명필요한 기술

Amazon ECR 프라이빗 리포지토리를 생성합니다.

Amazon ECR 리포지토리를 생성하려면 프라이빗 리포지토리 생성을 참조하세요. 리포지토리의 URI를 기록합니다.

AWS DevOps, DevOps 엔지니어, 앱 개발자

Dockerfile을 생성합니다.

패턴을 테스트하는 데 사용할 기존 Docker 컨테이너 이미지가 있는 경우 이 단계를 건너뛸 수 있습니다.

Dockerfile을 생성하려면 다음 스니펫을 참조로 사용합니다. 오류가 발생하는 경우 문제 해결 섹션을 참조하세요.

FROM adoptopenjdk/openjdk11:jdk-11.0.14.1_1-alpine RUN apk add maven WORKDIR /code # Prepare by downloading dependencies ADD pom.xml /code/pom.xml RUN ["mvn", "dependency:resolve"] RUN ["mvn", "verify"] # Adding source, compile and package into a fat jar ADD src /code/src RUN ["mvn", "package"] EXPOSE 4567 CMD ["java", "-jar", "target/eksExample-jar-with-dependencies.jar"]
AWS DevOps, DevOps 엔지니어

pom.xml 및 소스 파일을 생성하고 도커 이미지를 빌드하고 푸시합니다.

pom.xml 파일 및 Java 소스 파일을 생성하려면 Amazon EKS에 샘플 Java 마이크로서비스 배포 패턴을 참조하세요.

해당 패턴의 지침을 사용하여 도커 이미지를 빌드하고 푸시합니다.

AWS DevOps, DevOps 엔지니어, 앱 개발자
작업설명필요한 기술

deployment.yaml 파일을 생성합니다.

deployment.yaml 파일을 생성하려면 추가 정보 섹션의 코드를 사용합니다.

코드에서 노드 어피니티의 키는 노드 그룹을 생성할 때 생성하는 모든 레이블입니다. 이 패턴은 eksctl이 생성한 기본 레이블을 사용합니다. 레이블 사용자 지정에 대한 자세한 내용은 Kubernetes 설명서의 노드에 포드 할당을 참조하세요.

노드 어피니티 키의 값은 cluster.yaml에서 생성한 노드 그룹의 이름입니다.

테인트의 키와 값을 가져오려면 다음 명령을 실행합니다.

kubectl get nodes -o json | jq '.items[].spec.taints'

이미지는 이전 단계에서 생성한 Amazon ECR 리포지토리의 URI 입니다.

AWS DevOps, DevOps 엔지니어, 앱 개발자

파일을 배포합니다.

Amazon EKS에 배포하려면 다음 명령을 실행합니다.

kubectl apply -f deployment.yaml
앱 개발자, DevOps 엔지니어, AWS DevOps

배포를 확인합니다.

  1. 포드가 READY 상태인지 확인하려면 다음 명령을 실행합니다.

    kubectl get pods -o wide

    포드가 준비되면 출력은 다음처럼 표시되며, STATUS이(가) 실행 중으로 표시됩니다.

    NAME        READY    STATUS    RESTARTS   AGE   IP  NODE  NOMINATED NODE   READINESS GATES <pod_name>   1/1     Running   0          12d   192.168.18.50   ip-192-168-20-110.us-west-1.compute.internal   <none>           <none>

    포드 이름과 노드 이름을 기록합니다. 다음 단계를 건너뛸 수 있습니다.

  2. (선택 사항) 포드에 대한 추가 세부 정보를 확인하고 포드의 톨러레이션을 확인하려면 다음 명령을 실행합니다.

    kubectl describe pod <pod_name>

    출력의 예는 추가 정보 섹션에 있습니다.

  3. 노드의 포드 배치가 올바른지 확인하려면 다음 명령을 실행합니다.

    kubectl describe node <node name> | grep -A 1 "Taints"

    노드의 테인트가 톨러레이션과 일치하고 노드의 레이블이 deployment.yaml에 정의된 노드 어피니티와 일치하는지 확인합니다.

    톨러레이션과 노드 어피니티가 있는 포드는 테인트와 노드 어피니티 레이블이 일치하는 노드에 배치해야 합니다. 이전 명령은 노드의 테인트를 제공합니다. 다음은 예시 출력입니다.

    kubectl describe node ip-192-168-29-181.us-west-1.compute.internal | grep -A 1 "Taints" Taints:             classifled_workload=true:NoSchedule                     machine_learning_workload=true:NoSchedule

    또한 다음 명령을 실행하여 포드가 배치된 노드에 노드 어피니티 노드 레이블과 일치하는 레이블이 있는지 확인합니다.

    kubectl get node <node name> --show-labels
  4. 애플리케이션이 의도한 대로 작동하는지 확인하려면 다음 명령을 실행하여 포드 로그를 확인합니다.

    kubectl logs -f <name-of-the-pod>
앱 개발자, DevOps 엔지니어, AWS DevOps

톨러레이션 및 노드 어피니티 없이 두 번째 배포 .yaml 파일을 생성합니다.

이 추가 단계는 배포 매니페스트 파일에 노드 어피니티 또는 톨러레이션이 지정되지 않은 경우 결과 포드가 테인트가 있는 노드에 스케줄링되지 않는지 확인하기 위한 것입니다. (테인트가 없는 노드에 스케줄링해야 합니다.) 다음 코드를 사용하여 deploy_no_taint.yaml(이)라는 새 배포 파일을 생성합니다.

apiVersion: apps/v1 kind: Deployment metadata: name: microservice-deployment-non-tainted spec: replicas: 1 selector: matchLabels: app.kubernetes.io/name: java-microservice-no-taint template: metadata: labels: app.kubernetes.io/name: java-microservice-no-taint spec: containers: - name: java-microservice-container-2 image: <account_number>.dkr.ecr<region>.amazonaws.com/<repository_name>:latest ports: - containerPort: 4567
앱 개발자, AWS DevOps, DevOps 엔지니어

두 번째 배포 .yaml 파일 배포 및 포드 배치 검증

  1. 다음 명령을 실행합니다.

    kubectl apply -f deploy_no_taint.yaml
  2. 배포가 성공하면 이전에 실행한 것과 동일한 명령을 실행하여 테인트가 없는 노드 그룹의 포드 배치를 확인합니다.

    kubectl describe node <node_name> | grep "Taints"

    출력은 다음과 같아야합니다.

    Taints: <none>

    이것으로 테스트가 완료됩니다.

앱 개발자, AWS DevOps, DevOps 엔지니어
작업설명필요한 기술

리소스를 정리합니다.

실행 중인 리소스에 대한 AWS 요금이 발생하지 않도록 하려면 다음 명령을 사용합니다.

eksctl delete cluster --name <Name of the cluster> --region <region-code>
AWS DevOps, 앱 개발자

문제 해결

문제Solution

시스템에서 arm64 아키텍처를 사용하는 경우(특히 M1 Mac에서 실행하는 경우) 이러한 명령 중 일부가 실행되지 않을 수 있습니다. 다음 줄에 오류가 있을 수 있습니다.

FROM adoptopenjdk/openjdk11:jdk-11.0.14.1_1-alpine

Dockerfile을 실행할 때 오류가 발생하면 해당 FROM 줄을 다음 줄로 바꿉니다.

FROM bellsoft/liberica-openjdk-alpine-musl:17

관련 리소스

추가 정보

deployment.yaml

apiVersion: apps/v1 kind: Deployment metadata: name: microservice-deployment spec: replicas: 1 selector: matchLabels: app.kubernetes.io/name: java-microservice template: metadata: labels: app.kubernetes.io/name: java-microservice spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: alpha.eksctl.io/nodegroup-name operator: In values: - <node-group-name-from-cluster.yaml> tolerations: #only this pod has toleration and is viable to go to ng with taint - key: "<Taint key>" #classified_workload in our case operator: Equal value: "<Taint value>" #true effect: "NoSchedule" - key: "<Taint key>" #machine_learning_workload in our case operator: Equal value: "<Taint value>" #true effect: "NoSchedule" containers: - name: java-microservice-container image: <account_number>.dkr.ecr<region>.amazonaws.com/<repository_name>:latest ports: - containerPort: 4567

포드 예제 출력 설명

Name: microservice-deployment-in-tainted-nodes-5684cc495b-vpcfx Namespace: default Priority: 0 Node: ip-192-168-29-181.us-west-1.compute.internal/192.168.29.181 Start Time: Wed, 14 Sep 2022 11:06:47 -0400 Labels: app.kubernetes.io/name=java-microservice-taint pod-template-hash=5684cc495b Annotations: kubernetes.io/psp: eks.privileged Status: Running IP: 192.168.13.44 IPs: IP: 192.168.13.44 Controlled By: ReplicaSet/microservice-deployment-in-tainted-nodes-5684cc495b Containers: java-microservice-container-1: Container ID: docker://5c158df8cc160de8f57f62f3ee16b12725a87510a809d90a1fb9e5d873c320a4 Image: 934188034500.dkr.ecr.us-east-1.amazonaws.com/java-eks-apg Image ID: docker-pullable://934188034500.dkr.ecr.us-east-1.amazonaws.com/java-eks-apg@sha256:d223924aca8315aab20d54eddf3443929eba511b6433017474d01b63a4114835 Port: 4567/TCP Host Port: 0/TCP State: Running Started: Wed, 14 Sep 2022 11:07:02 -0400 Ready: True Restart Count: 0 Environment: <none> Mounts: /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-ddvvw (ro) Conditions: Type Status Initialized True Ready True ContainersReady True PodScheduled True Volumes: kube-api-access-ddvvw: Type: Projected (a volume that contains injected data from multiple sources) TokenExpirationSeconds: 3607 ConfigMapName: kube-root-ca.crt ConfigMapOptional: <nil> DownwardAPI: true QoS Class: BestEffort Node-Selectors: <none> Tolerations: classifled_workload=true:NoSchedule machine_learning_workload=true:NoSchedule node.kubernetes.io/not-ready:NoExecute op=Exists for 300s node.kubernetes.io/unreachable:NoExecute op=Exists for 300s Events: <none>