EKS Node 그룹


1. Amazon Linux 2

EKS Node Bootstrap

  1. AWS에서 제공하는 eks-node AMI를 사용하며 /etc/eks/bootstrap.sh 호출

  2. Bootstrap populates several files and make API queries to EKS

  3. Kubelet starts connects to the API server.

  4. Kubelet tries to register itself, role must match

  5. Kubelet creates Certificate Signing Request(CSR) and wait for it to be approved by EKS signer

  6. Certificated is signed, kubelet downloads it and serve traffic using 10250 port

    • https://kubernetes.io/docs/reference/access-authn-authz/kubelet-tls-bootstrapping/


EKS Node AMI

기본적으로 AMI가 설정된 Custom Launch Template을 사용하게되면 Userdata는 사용자에서 지정해야 한다. 그렇지 않은 모든 경우에는 EKS 서비스에서 자동으로 Injection 해준다.

Customized AMI Launch Template Userdata

Origin

MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="==MYBOUNDARY=="

--==MYBOUNDARY==
Content-Type: text/x-shellscript; charset="us-ascii"

#!/bin/bash
set -ex
/etc/eks/bootstrap.sh my-cluster \
  --b64-cluster-ca certificate-authority \
  --apiserver-endpoint api-server-endpoint \
  --dns-cluster-ip service-cidr.10 \
  --container-runtime containerd \
  --kubelet-extra-args '--max-pods=my-max-pods-value' \
  --use-max-pods false

--==MYBOUNDARY==--

Optional을 제외한 적용가능 Userdata - 클러스터명만 들어가면 모두 bootstrap.sh 에서 찾음

MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="==MYBOUNDARY=="

--==MYBOUNDARY==
Content-Type: text/x-shellscript; charset="us-ascii"

set -ex
/etc/eks/bootstrap.sh bys-dev-eks-main

--==MYBOUNDARY==--

kubelet에 추가적인 파라미터 설정을 위서 kubelet-extra-args

/etc/eks/bootstrap.sh cluster_name --kubelet-extra-args '--node-labels=something=hello,somethingelse=bye --register-with-taints=taint1=true'

Bootstrap.sh에서는 기본적으로 kubelet을 구동하기 위한 설정 및 값들이 적용된다.
버전에 따라 적용되는 내용이 다를 수 있으니 해당 내용을 꼭 참고한다.



2. Amazon Linux 2023

  • AL2023은 IMDSv2가 기본 설정
  • AL2023은 cgroupv2 가 사용됨
  • AL2023 부터는 bootstrap.sh 을 사용하지 않으며 nodeadm 프로세스를 통해서 node initialization 프로세스가 시작된다. bootstrap.sh 에서는 describe를 통해 필수 파라미터를 조회하였지만 AL2023에서는 Throttling 방지를 위해 제공하지 않는다. 따라서 아래의 필수 파라미터를 모두 설정해야 한다.

Requirement Configuration

---
apiVersion: node.eks.aws/v1alpha1
kind: NodeConfig
spec:
  cluster:
    name: my-cluster
    apiServerEndpoint: https://example.com
    certificateAuthority: Y2VydGlmaWNhdGVBdXRob3JpdHk=
    cidr: 172.20.0.0/16

Example

MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="BOUNDARY"

--BOUNDARY
Content-Type: application/node.eks.aws

---
apiVersion: node.eks.aws/v1alpha1
kind: NodeConfig
spec:
  cluster:
    name: my-cluster
    apiServerEndpoint: https://example.com
    certificateAuthority: Y2VydGlmaWNhdGVBdXRob3JpdHk=
    cidr: 172.20.0.0/16

--BOUNDARY--
Content-Type: application/node.eks.aws

---
apiVersion: node.eks.aws/v1alpha1
kind: NodeConfig
spec:
  kubelet:
    config:
      shutdownGracePeriod: 30s
      featureGates:
        DisableKubeletCloudCredentialProviders: true

--BOUNDARY--

Injected Userdata

# cat user-data.txt
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="//"

--//
Content-Type: application/node.eks.aws

---
apiVersion: node.eks.aws/v1alpha1
kind: NodeConfig
spec:
  cluster:
    apiServerEndpoint: https://1111122222.sk1.ap-northeast-2.eks.amazonaws.com
    certificateAuthority: Y2VydGlmaWNhdGVBdXRob3JpdHk==
    cidr: 172.20.0.0/16
    name: my-cluster
  kubelet:
    config:
      shutdownGracePeriod: 30s
      featureGates:
        DisableKubeletCloudCredentialProviders: true
      maxPods: 17
      clusterDNS:
      - 172.20.0.10
    flags:
    - "--node-labels=eks.amazonaws.com/nodegroup-image=ami-057051082a0861071,eks.amazonaws.com/capacityType=ON_DEMAND,eks.amazonaws.com/nodegroup=ng-al2023"
  # instance:
  #   localStorage: 
  #     strategy: 
  # containerd:
  #   config: |
  #     [plugins."io.containerd.grpc.v1.cri".containerd]
  #     discard_unpacked_layers = false

--//--



EKS managed node upgrade

EKS 클러스터 버전을 업데이트 한 이 후, EKS managed node group에 대한 버전 업데이트를 진행하면 다음과 같은 스텝으로 진행된다.

  1. 노드 그룹의 maximum unavailable 설정 값 까지 업그레이드해야 하는 노드를 무작위로 선택한다.
  2. 노드에서 Pods를 Draining 한다. Pods가 15분 이내에 노드를 떠나지 않고 force 플래그가 없으면 PodEvictionFailure 오류와 함께 업그레이드 단계가 실패한다. 이 경우에는 force 플래그를 적용하여 Pods를 강제로 삭제할 수 있다.
  3. 모든 Pod가 eviction되면 노드를 Cordon하고 60초를 기다린다. 이 작업은 service controller가 이 노드에 새 요청을 보내지 않고 active 노드 목록에서 이 노드를 제거하도록 하기 위해 수행된다.
  4. Cordon 노드를 위해 Auto Scaling 그룹에 종료 요청을 보냅니다.
  5. 이전 버전의 시작 템플릿으로 배포된 노드 그룹에 노드가 없을 때까지 이전 업그레이드 단계를 반복합니다.

이 단계에서 PodEvictionFailure 오류가 발생하는 이유는 다음과 같을 수 있다.

  • Aggressive PDB (Aggressive PDB가 존재하는 경우)
    Aggressive PDB is defined on the Pod or there are multiple PDBs pointing to the same Pod.
  • Deployment tolerating all the taints (모든 Taint를 허용하는 Toleration을 가진 Pod가 존재하는 경우)
    Once every Pod is evicted, it’s expected for the node to be empty because the node is tainted in the earlier steps. However, if the deployment tolerates every taint, then the node is more likely to be non-empty, leading to Pod eviction failure.
  • Subnet의 IP부족으로 인하여 신규 파드가 생성이 되지 않는 경우에는 Pod eviction이 실패할 수 있다.
    NodeCreationFailure Couldn’t proceed with upgrade process as new nodes are not joining node group ng-v1



3. Bottlerocket

기본적으로 다음과 같은 ng-bottlerocket.yaml 파일을 생성하여 eksctl create nodegroup -f ng-bottlerocket.yaml 커맨드를 통해 생성할 수 있다.

apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: bys-dev-eks-sec
  region: ap-northeast-2

vpc:
  id: "vpc-0ca96cd5c37d3bae8"
  subnets:
    private:
      bys-dev-sbn-az1-app:
          id: "subnet-0ea5be4984975e8ed"
      bys-dev-sbn-az2-app:
          id: "subnet-0b4076508ce121c27"

managedNodeGroups:
  - name: ng-bottlerocket
    amiFamily: Bottlerocket
    instanceType: m5.large
    volumeSize: 20
    minSize: 2
    maxSize: 4
    desiredCapacity: 2
    privateNetworking: true
    subnets:
      - bys-dev-sbn-az1-app
      - bys-dev-sbn-az2-app
    ssh:
      allow: true
      publicKeyName: "bys-console"
    tags:
      auto-delete: "no"

Custom AMI를 사용하는 LT를 통해 Bottlerocket을 이용할 때는 다음과 같이 userdata를 정의할 수 있다. 참고. Description of settings

settings.kubernetes.cluster-name = 'bys-dev-eks-sec'
settings.kubernetes.api-server = 'https://A2FAB60F0DBB94CDCD057CE2227F2252.yl4.ap-northeast-2.eks.amazonaws.com'
settings.kubernetes.cluster-certificate = 'LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUMvakNDQWVhZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRJek1ESXdNekF5TWpjeU4xb1hEVE16TURFek1UQXlNamN5TjFvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTUM2CktwVURDVVRobUpaZjlKaVd3Ulg2cFREa1M3aWpFTVdpTWJoU0g1UlR5UDBHaVJCSWxYM1c4ZmZ4VlJTclN3T1YKQWJTU3FCV2NUUVcvM08vM3NUdGpORzZuUy94ZjhOaXZlVkRhOUtvY3FCZWVDODZjeEk5MU5EMnVoeUxGZmo5ZApCNDVJamdMeVc2N2ovRTdLQUxkaXhNd2lBMGhvbk44SVFZM0NJUWUrM0tHNWxCbkkzOFVlemJ3TUVFNmRVTGJGCkFZWERjMzhTQ3Byc1VxUkUzS1FlQ21TUk85andKbGJEcUhPWjF6djI0ekdseUZDS2hvdVJWVHQzQU9RT2ppZUkKYWNHUmNDK2ZoRDZlK1FpbHJyR05TcUdtaEI1RUZISVFYT2Zac0JBLytaTmkvNm82NEZOS3NsZFVlNGplNzJDYQp3Sng3TTBOZEpRNkNtYzJZT2IwQ0F3RUFBYU5aTUZjd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0hRWURWUjBPQkJZRUZHSDdOOWJydDBjVVpLN1I1eHVaZHhNeVU5UEpNQlVHQTFVZEVRUU8KTUF5Q0NtdDFZbVZ5Ym1WMFpYTXdEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBRW9sQkJyaUdIc2RCRXlTU0xycQp1UjNmUnFOMW02c3BPR1oyZjVybzFmaExCMXV0LzVEdnNqKzUxaDlzOWpIampDcGhyUnQxVURMdWsyTlMweTFWCkhSMDdIU1dKL0tXSjBRRWNUQmxoYm1jbEYrTDNvcEtIcXZZYmx0OW1sdGszQXZEQlA5dzBQRG4wZ3E5dm85YXgKb0FJbGJHQTNtZ0xCZmlUQi9hR3VTQkd2Ry8vVXhxNWlsRWVGdGJKcU5yRkMxZEpleGU3U0l6THVmOUtSbmxOOApvSTd0UFRDUE9mcDQyLzFSVGE5VXBxL0hzN2JlaWxYQ05wNFhPRzNzYkdvNm5ZYnhUQlE3Q3BuTnE4NEFDeHNYCk9yMm56ek1GbHB0T0lob0VETFNJSVZCMEZ2Sm9oeTZXaXQvZjRrQkRYQlkvalYrVWRZK0ZNcmk5SkxGWTZidWkKMDhBPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg=='
settings.kubernetes.cluster-dns-ip = '172.20.0.10'
settings.kubernetes.max-pods = 20
settings.kubernetes.node-labels.'eks.amazonaws.com/sourceLaunchTemplateVersion' = '1'
settings.kubernetes.node-labels.'alpha.eksctl.io/cluster-name' = 'bys-dev-eks-sec'
settings.kubernetes.node-labels.'alpha.eksctl.io/nodegroup-name' = 'ng-bottlerocket'
settings.kubernetes.node-labels.'eks.amazonaws.com/nodegroup-image' = 'ami-0c0c53d11a3f898ea'
settings.kubernetes.node-labels.'eks.amazonaws.com/capacityType' = 'ON_DEMAND'
settings.kubernetes.node-labels.'eks.amazonaws.com/nodegroup' = 'ng-bottlerocket'
settings.kubernetes.node-labels.'eks.amazonaws.com/sourceLaunchTemplateId' = 'lt-05fd1822cf902ddbe'
settings.host-containers.admin.enabled = true

Bottlerocket log 확인하는 방법

# Access admin container  
$ apiclient exec admin bash

# Get a full root shell
$ sudo sheltie

# Obtain an archive of log 
$ logdog
......
logs are at: /var/log/support/bottlerocket-logs.tar.gz



4. GPU

EKS에서 GPU는 주로 Nvidia 사의 칩을 사용한다. Nvidia의 제품군으로는 Nvidia Tesla가 존재하며 대략적으로 아래와 같다.

2007년: Tesla C80 (G80 아키텍처)
2009년: Tesla M10 (GT200 아키텍처)
2010년: Tesla S10 (Fermi 아키텍처)
2012년: Tesla K10, K20 (Kepler 아키텍처)
2013년: Tesla K40 (Kepler 아키텍처)
2014년: Tesla M40 (Maxwell 아키텍처)
2016년: Tesla P40, P100 (Pascal 아키텍처)
2017년: Tesla V100 (Volta 아키텍처)
2018년: Tesla T40 (Turing 아키텍처)
2019년: Tesla A100 (Ampere 아키텍처)
2020년: Tesla A30, A40 (Ampere 아키텍처)
......
2023년: L4 (Ada Lovelace 아키텍처)
2023년: H100 (Hopper 아키텍처)
2024년: H200 (Hopper 아키텍처)
2024년: GB200 NVL72 (Blackwell 아키텍처)

2020년 5월 부터 Nvidia는 테슬라 브랜드를 중단하고, 데이터 센터 GPU 제품으로 이름을 변경하였다. Nvidia GPU 이름에서 볼 수 있는 Tensor Core는 Nvidia의 인공지능(AI) 및 고성능 컴퓨팅(HPC) 분야에서 복잡한 계산을 향상시키도록 설계된 전용 하드웨어 가속기로 Volta 아키텍처에 처음 도입된 후 Turing, Ampere, Ada Lovelace, Hopper, Blackwell 아키텍처 등에 지속적으로 적용되고 있다.


AWS GPU Node

Amazon EC2에는 P3, P4, G3, G4, G5 등의 타입이 존재하며 각 인스턴스 타입에 따라 Tesla V100, A100 등의 GPU가 탑재된다. 또한 AWS에서는 Graviton 이라고 하는 ARM기반의 GPU를 제공하는데 현재 G5g 인스턴스타입에는 NVIDIA T4G Tensor Core GPU가 탑재되어 있다. 이 GPU는 ARM 기반 프로세서에서 동작하도록 설계된 Nvidia GPU다.

  • Amazon EC2 P3 인스턴스에는 최대 8개의 NVIDIA Tesla V100 GPU가 있습니다.
  • Amazon EC2 P4 인스턴스에는 최대 8개의 NVIDIA Tesla A100 GPU가 있습니다.
  • Amazon EC2 G3 인스턴스에는 최대 4개의 NVIDIA Tesla M60 GPU가 있습니다.
  • Amazon EC2 G4 인스턴스에는 최대 4개의 NVIDIA T4 GPU가 있습니다.
  • Amazon EC2 G5 인스턴스에는 최대 8개의 NVIDIA A10G GPU가 있습니다.
  • Amazon EC2 G5g 인스턴스에는 ARM 기반 AWS Graviton2 프로세서가 탑재되어 있습니다.


EKS GPU

EKS 클러스터에 GPU 노드를 설치하기 위해서는 GPU 기반의 최적화 이미지를 사용해야 한다. EKS AMI Release를 확인하면 AL2_x86_64_GPU 이미지에는 efa(Elastic Fabric Adapter, 고성능 컴퓨팅(HPC) 및 머신 러닝 애플리케이션의 성능을 크게 향상시키는 네트워크 디바이스), cuda-12-2, nvidia-driver-latest-dkms 등의 패키지가 기본적으로 설치된 것을 알 수 있다.

the accelerated AMI includes the following:

  • NVIDIA drivers
  • The nvidia-container-runtime (as the default runtime)
  • AWS Neuron container runtime


NVIDIA device plugin for Kubernetes

쿠버네티스 NVIDIA device plugin 이란 GPU노드에 있는 GPU를 사용가능하도록 하는 Device 장치다. 기본적으로 쿠버네티스는 GPU 같은 특수 하드웨어 리소스를 직접 관리하지 않기 때문에 NVIDIA device plugin을 사용하여 쿠버네티스가 NVIDIA GPU를 인식하고 이를 컨테이너에 할당할 수 있게 해야한다[5]. 만약, Nvidia device plugin을 배포하지 않고, nvidia.com/gpu 리소스를 요청하는 파드를 배포하면 gpu 노드가 존재하더라도 pending 상태로 스케줄이 불가능하다.

Deployment via helm

helm upgrade -i nvdp nvdp/nvidia-device-plugin \
  --namespace nvidia-device-plugin \
  --create-namespace \
  --version 0.15.0

이 때 기본으로 적용되는 values.yaml파일의 특정 Affinity가 적용되도록 GPU 노드의 레이블을 추가해준다.

정상적인 배포가 완료되면 nvidia-device-plugin이 Running 상태가 되고, nvidia.com/gpu 리소스를 요청하는 파드는 정상 생성된다.

$ k get po -n nvidia-device-plugin
NAME                              READY   STATUS    RESTARTS   AGE
nvdp-nvidia-device-plugin-srhsw   1/1     Running   0          3m30s

이 후 nvidia-smi(System Management Interface)를 통해 정보를 출력해 볼 수 있다. 이를 통해 Nvidia Driver 버전 535.161.08을 사용하고 있음을 알 수 있다.

$ kubectl logs -f nvidia-smi

Mon May 27 04:39:23 2024
+---------------------------------------------------------------------------------------+
| NVIDIA-SMI 535.161.08             Driver Version: 535.161.08   CUDA Version: 12.4     |
|-----------------------------------------+----------------------+----------------------+
| GPU  Name                 Persistence-M | Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |         Memory-Usage | GPU-Util  Compute M. |
|                                         |                      |               MIG M. |
|=========================================+======================+======================|
|   0  Tesla T4                       On  | 00000000:00:1E.0 Off |                    0 |
| N/A   26C    P8              11W /  70W |      0MiB / 15360MiB |      0%      Default |
|                                         |                      |                  N/A |
+-----------------------------------------+----------------------+----------------------+

+---------------------------------------------------------------------------------------+
| Processes:                                                                            |
|  GPU   GI   CI        PID   Type   Process name                            GPU Memory |
|        ID   ID                                                             Usage      |
|=======================================================================================|
|  No running processes found                                                           |
+---------------------------------------------------------------------------------------+


NVIDIA GPU Operator with Amazon EKS

EKS 최적화 AMI와 NVIDIA device plugin for Kubernetes를 배포하여 사용하는 것은 가장 기본설정이지만 몇 가지 제약사항이 존재한다.

  1. AMI에 설치되어 릴리즈되는 NVIDIA GPU driver 버전과 NVIDIA container runtime 버전은 Nvidia에서 릴리즈하는 일정보다 느리다.
  2. NVIDIA device plugin을 항상 배포해야 하며, 사용자가 업그레이드를 통해 버전을 관리해야 한다.

위 제약사항을 수용할 수 있다면 최적화 이미지에 Nvidia 장치 플러그인을 직접 배포(설치)하여 사용하면 되지만, 이러한 제약사항을 벗어나기 위해서는 NVIDIA GPU Operator를 사용할 수 있다.

  • Installing
    • https://docs.nvidia.com/datacenter/cloud-native/gpu-operator/latest/getting-started.html#install-gpu-operator



5. EKS Windows node

Windows 노드를 사용하기 위해서는 Windows 문서를 확인할 필요가 있다.

Summary

  • 하나 이상의 Linux 노드를 반드시 포함해야한다.
  • Windows 노드의 group으로 eks:kube-proxy-windows 반드시 포함되어야 한다.

    mapRoles:
    ----
    - groups:
      - system:bootstrappers
      - system:nodes
      rolearn: arn:aws:iam::558846430793:role/eksctl-bys-dev-eks-win-nodegroup-NodeInstanceRole-1MIR40S58HUY7
      username: system:node:
    - groups:
      - system:bootstrappers
      - system:nodes
      - eks:kube-proxy-windows
      rolearn: arn:aws:iam::558846430793:role/eksctl-bys-dev-eks-win-nodegroup-NodeInstanceRole-VD0XFSFTRF2Q
      username: system:node:
    
  • Can’t use SGP
  • Can’t use custom networking
  • Can’t use IPv6
  • Number of Pods: Number of private IPv4 addresses for each interface on the node - 1

  • amazon-vpc-cni ConfigMap 배포 (중요)
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: amazon-vpc-cni
      namespace: kube-system
    data:
      enable-windows-ipam: "true"
    
    • 이 작업을 진행하지 않으면 파드에 IP가 할당되지 않으며 아래와 같은 오류메세지가 확인된다
      Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox "6a614a0ee5842556403f696a9d6b33347e622a1e5442ca5e07c29c90f925954d": plugin type="vpc-bridge" name="vpc" failed (add): failed to parse Kubernetes args: failed to get pod IP address windows-server-iis-797bf57f76-vwxgz: error executing k8s connector: error executing connector binary: exit status 1 with execution error: pod windows-server-iis-797bf57f76-vwxgz does not have label vpc.amazonaws.com/PrivateIPv4Address
      
    • IIS 컨테이너 기동시 약 1분 30초 정도의 시간이 소요 되는 것을 확인

Creation

eksctl create nodegroup \
    --region ap-northeast-2 \
    --cluster eks-win \
    --name ng-win-v1 \
    --node-type m5.large \
    --nodes 2 \
    --nodes-min 2 \
    --nodes-max 2 \
    --managed=false \
    --node-ami-family WindowsServer2022FullContainer

managedNodeGroups:
  - name: ng-win-v1
    amiFamily: WindowsServer2022FullContainer
    instanceType: m5.large
    volumeSize: 80
    minSize: 2
    maxSize: 2
    desiredCapacity: 2
    privateNetworking: true
    subnets:
      - subnet1
      - subnet2
    ssh:
      allow: true
      publicKeyName: "keypair"
    tags:
      auto-delete: "no"




  • References [1] https://kubernetes.io/docs/concepts/architecture/nodes/ [2] Kubelet flags - https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet [3] Kube-controller-manager flags - https://kubernetes.io/docs/reference/command-line-tools-reference/kube-controller-manager/ [4] Bottlerocket - https://github.com/bottlerocket-os/bottlerocket/blob/develop/README.md [5] Enabling GPU Support in Kubernetes - https://github.com/NVIDIA/k8s-device-plugin?tab=readme-ov-file#enabling-gpu-support-in-kubernetes [6] Deployment via helm - https://github.com/NVIDIA/k8s-device-plugin?tab=readme-ov-file#deployment-via-helm

Tag: [ aws  cloud  eks  node  al2023  ]