Kubernetes의 노드를 EKS에서는 노드 그룹으로 제공하고 있습니다. 본 포스트에서는 EKS의 노드그룹 유형을 살펴보고, 세부적으로는 관리형 노드 그룹(Managed node groups)에서 생성 가능한 노드 유형을 살펴보도록 하겠습니다.

 

 

목차

  1. CSP에서 제공하는 Kubernetes 노드 리소스
  2. EKS 노드 그룹 유형
  3. EKS 노드 그룹 AL2 -> AL2023
  4. 다양한 노드 그룹 사용해 보기
  5. 리소스 정리

 

 

1. CSP에서 제공하는 Kubernetes 노드 리소스

Kubernetes의 노드는 워크로드를 실행하기 위한 리소스입니다. 이는 가상 머신이 될 수도 물리 머신이 될 수도 있습니다. 그리고 노드는 파드를 실행하기 위한 컴포넌트를 가지는데 파드를 실행하기 위한 Kubelet, 컨테이너 런타임, 그리고 Service 를 구현하는 kube-proxy 입니다.

출처: https://learn.microsoft.com/ko-kr/azure/architecture/aws-professional/eks-to-aks/node-pools

 

 

이때 CSP(Cloud Service Provider)에서 제공하는 Managed Kubernetes Service에서는 컨트롤 플레인은 내부적으로 관리하므로, 사용자는 보통 데이터 플레인 혹은 워커 노드라고 불리는 노드들을 관리하게 됩니다.

 

보통 CSP에는 Stateless한 애플리케이션의 스케일 인/아웃을 지원하는 목적으로 동일한 목적을 가지는 가상 머신을 이미지로 만들고 이를 바탕으로 가상 머신의 스케일링을 지원하는 서비스를 제공합니다. Kubernetes의 노드는 상태를 가질 필요가 없기 때문에 이러한 가상 머신의 세트를 기반으로 노드를 제공합니다. 

 

그래서 EKS에서는 ASG(Auto Scaling Group)를 바탕으로 노드 그룹(Node Group)이란 이름으로 Kubernetes 노드를 오토 스케일링 하도록 제공하고 있으며, AKS에서는 VMSS(Virtual Machine Scale Set)을 바탕으로 한 노드 풀(Node Pool)로 동일한 기능을 제공하고 있습니다.

 

그래서 Managed Kubernetes Service에서 Kubernetes의 노드를 늘이거나 줄이는 행위, 여기서 더 나아가 ClusterAutoscaler의 동작은 이러한 제반 가상 머신 세트의 API를 호출하는 방식으로 구현됩니다.

 

 

2. EKS 노드 그룹 유형

문서의 카테고리를 바탕으로 확인해보면 EKS에서 제공하는 노드 그룹에는 아래와 같은 유형이 있습니다.

https://docs.aws.amazon.com/eks/latest/userguide/eks-compute.html

  • 관리형 노드 그룹(Managed node grups)
  • 자체 관리형 노드그룹(Self-managed nodes)
  • AWS Fargate
  • 하이브리드 노드(Hybrid nodes)

 

또한 Pre-built optimized AMI라는 카테고리가 있는데, 작성자가 이해하기로는 노드 그룹의 유형이라기 보다는 AWS에서 미리 Amazon Linux, Windows, Bottlerocket, Ubuntu와 같은 OS를 기반으로 최적화된 AMI를 선택할 수 있다는 의미로 이해했습니다.

 

그리고 관리형 노드 그룹의 용량 유형(Capacity type)은 온디맨드(OnDemand)와 스팟(Spot)으로 나뉩니다. 지정하지 않은 경우 온디맨드가 기본이며, Spot은 큰폭의 할인을 제공하는 여분 인스턴스를 사용하는 의미로, 필요한 경우 AWS에 의해서 리소스를 빼앗길 수 있습니다.

 

지금까지 살펴본 EKS와 AKS에는 몇 가지 부분에서 사상이 다른 점을 느낄 수 있는데, 먼저 EKS는 가볍하다는 느낌이 있습니다. 앞서 살펴본 바와 같이 노드 구성 요소가 최소화 되어 있고 CNI나 네트워크/스토리지 구현체도 최소화되어 있습니다.

 

두번째 차이는 EKS는 Managed Service의 일부를 customize 하도록 제공하고 있다는 부분입니다. 노드를 제공하는 방식에서도 AKS와 크게 다른데 AKS는 Fully Managed Service를 지향하기 때문에 관리형 노드 풀만 제공하며, 또한 노드의 설정을 Customize하는 데 있어도 인터페이스를 제공(Custom Node Configuration for AKS node pools, https://learn.microsoft.com/en-us/azure/aks/custom-node-configuration?tabs=linux-node-pools)하거나 그게 아니면 엄격하게 제한하는 점에 차이가 있습니다.

 

노드 customization에 대한 관점의 차이를 살펴보면 관리형 노드 그룹에서도 preBootstrapCommands 과 같은 방식으로 노드에 추가로 필요한 명령을 전달하는 형태로 customize를 가능 하도록 합니다.

 

아래는 amazon linux 2023 노드 그룹에서 dnf(패키지 관리자)로 추가 패키지를 설치하는 예시입니다.

managedNodeGroups:
- amiFamily: AmazonLinux2023
  desiredCapacity: 3
  iam:
    withAddonPolicies:
      certManager: true # Enable cert-manager
      externalDNS: true # Enable ExternalDNS
  instanceType: t3.medium
  preBootstrapCommands:
    # install additional packages
    - "dnf install nvme-cli links tree tcpdump sysstat ipvsadm ipset bind-utils htop -y"
  labels:
    alpha.eksctl.io/cluster-name: myeks
    alpha.eksctl.io/nodegroup-name: ng1
  maxPodsPerNode: 100
  maxSize: 3
  minSize: 3
  name: ng1
  ssh:
    allow: true
    publicKeyName: $SSHKEYNAME
  tags:
    alpha.eksctl.io/nodegroup-name: ng1
    alpha.eksctl.io/nodegroup-type: managed
  volumeIOPS: 3000
  volumeSize: 120
  volumeThroughput: 125
  volumeType: gp3

 

그 외에도 eksctl 의 Config File Schema(https://eksctl.io/usage/schema/)를 살펴보면 kubelet custom config을 제공하는 kubeletExtraConfig 와 같은 필드도 있으며, 아래와 같이 Launch Template을 통해서 User Data를 전달하면서 가능한 변경사항을 살펴보실 수 있습니다.

출처: https://www.youtube.com/watch?v=lHYiew91iHY

 

내용을 살펴보면 userdata를 base64로 변경하여 LaunchTempateData.UserData로 전달하는 것으로 이해됩니다.

출처: https://youtu.be/lHYiew91iHY?t=821

 

마지막으로 이를 lauch template으로 생성해 새로운 노드 그룹을 생성합니다.

 

결국엔 특별한 목적이나 요구 사항이 있는 경우에는 Custom AMI 를 통해서 자체 관리형 노드 그룹을 사용할 수 있다는 결론에 다다릅니다. AKS는 자체 이미지를 통한 노드 풀 사용이 불가합니다.

 

또 한가지 차이로 EKS의 노드 그룹은 여러가서 인스턴스 타입(아래 --instance-types 옵션)을 지정할 수 있는 점이 있습니다.

aws eks create-nodegroup \
  --cluster-name $CLUSTER_NAME \
  --nodegroup-name managed-spot \
  --subnets $PubSubnet1 $PubSubnet2 $PubSubnet3 \
  --node-role $NODEROLEARN \
  --instance-types c5.large c5d.large c5a.large \
  --capacity-type SPOT \
  --scaling-config minSize=2,maxSize=3,desiredSize=2 \
  --disk-size 20

 

최근 AKS에서는 Virtual Machine 노드 풀이라는 새로운 유형의 노드 풀을 Preview로 발표했으며, 이 가상 머신 노드풀은 여러가지 유형의 VMSize를 가질 수 있습니다.

https://learn.microsoft.com/en-us/azure/aks/virtual-machines-node-pools

 

노드와 관련해 AKS에만 있는 개념은 시스템 노드 풀과 유저 노드 풀의 분리입니다. 이는 사용자 워크로드로 인해 시스템 컴포넌트(coredns, metrics-server 등)에 영향을 미치지 않도록 분리하려는 의도로 만들어진 구분입니다. dedicated system node pool은 taint로 구성될 수 있으며, 상세한 내용은 아래 문서를 참고 하시기 바랍니다.

https://learn.microsoft.com/en-us/azure/aks/use-system-pools?tabs=azure-cli

 

살펴보기로는 EKS는 사용자에 상당 부분의 자율성을 제공하는 것으로도 이해되고, 반대로 AKS는 최대한 Managed 영역으로 서비스를 제공하려는 입장(상품에서 검증된 인터페이스만 제공)을 가진 것으로도 보입니다.

 

몇 가지 근본적인 차이 외에는 제공하는 노드 형태는 EKS와 AKS와 유사합니다. EKS의 노드 그룹과 AKS의 노드풀에 대해서 아래 문서를 참고하실 수 있습니다.

https://learn.microsoft.com/ko-kr/azure/architecture/aws-professional/eks-to-aks/node-pools

 

 

3. EKS 노드 그룹 AL2 -> AL2023

앞선 실습을 진행한 상황이라면 Amazon Linux 2023을 기반으로한 노드풀이 생성되어 있을 것입니다.

만약 앞선 실습 환경 배포를 하지 않았다면 [3-1] EKS의 스토리지 옵션(https://a-person.tistory.com/33)을 확인 부탁드립니다.

Amazon Linux2 는 /etc/eks/bootstrap.sh 를 사용해 노드 초기화 프로세스를 가지고 있었다면, Amazon Linux 2023는 선언형 방식으로 YAML 구성 스키마를 사용하는 nodeadm을 통해서 노드 세팅을 하도록 변경하였습니다.

[root@ip-192-168-1-6 /]# cat  /etc/eks/bootstrap.sh
#!/usr/bin/env bash

echo >&2 '
!!!!!!!!!!
!!!!!!!!!! ERROR: bootstrap.sh has been removed from AL2023-based EKS AMIs.
!!!!!!!!!!
!!!!!!!!!! EKS nodes are now initialized by nodeadm.
!!!!!!!!!!
!!!!!!!!!! To migrate your user data, see:
!!!!!!!!!!
!!!!!!!!!!     https://awslabs.github.io/amazon-eks-ami/nodeadm/
!!!!!!!!!!
'

exit 1

 

예를 들어, Max Pod 개수의 변경을 한 경우 /etc/kubernetes/kubelet/config.json.d/00-nodeadm.conf 을 생성하여 기본 설정 파일인 /etc/kubernetes/kubelet/config.json을 overwrite하는 방식을 사용 합니다.

[root@ip-192-168-1-6 /]# cat /etc/kubernetes/kubelet/config.json | grep maxPods
    "maxPods": 17,
[root@ip-192-168-1-6 /]# cat /etc/kubernetes/kubelet/config.json.d/00-nodeadm.conf | grep maxPods
    "maxPods": 100

 

또한 cgroupv1cgroupv2로 변경되는 것과 같은 주요 변경이 있으므로, 상세한 내용은 아래를 참고 부탁드립니다.

https://docs.aws.amazon.com/ko_kr/eks/latest/userguide/al2023.html

 

 

4. 다양한 노드 사용해 보기

EKS에서 제공하는 다양한 노드 그룹을 생성해보겠습니다.

 

Graviton Instance 노드 그룹

Graviton Instance는 Amazon에서 제공하는 ARM 인스턴스 입니다. 아래와 같이 테스트 할 수 있습니다.

# 노드의 아키텍처를 확인
kubectl get nodes -L kubernetes.io/arch

NAME                                               STATUS   ROLES    AGE    VERSION               ARCH
ip-192-168-1-6.ap-northeast-2.compute.internal     Ready    <none>   156m   v1.31.5-eks-5d632ec   amd64
ip-192-168-2-172.ap-northeast-2.compute.internal   Ready    <none>   157m   v1.31.5-eks-5d632ec   amd64
ip-192-168-3-246.ap-northeast-2.compute.internal   Ready    <none>   156m   v1.31.5-eks-5d632ec   amd64

# 신규 노드 그룹 생성 (eksctl create nodegroup --help)
eksctl create nodegroup -c $CLUSTER_NAME -r ap-northeast-2 --subnet-ids "$PubSubnet1","$PubSubnet2","$PubSubnet3" \
  -n ng3 -t t4g.medium -N 1 -m 1 -M 1 --node-volume-size=30 --node-labels family=graviton --dry-run > myng3.yaml
eksctl create nodegroup -f myng3.yaml

# 확인 (arm64)
kubectl get nodes -L kubernetes.io/arch
NAME                                               STATUS   ROLES    AGE     VERSION               ARCH
ip-192-168-1-6.ap-northeast-2.compute.internal     Ready    <none>   161m    v1.31.5-eks-5d632ec   amd64
ip-192-168-2-172.ap-northeast-2.compute.internal   Ready    <none>   161m    v1.31.5-eks-5d632ec   amd64
ip-192-168-3-188.ap-northeast-2.compute.internal   Ready    <none>   2m12s   v1.31.5-eks-5d632ec   arm64
ip-192-168-3-246.ap-northeast-2.compute.internal   Ready    <none>   161m    v1.31.5-eks-5d632ec   amd64

 kubectl get nodes --label-columns eks.amazonaws.com/nodegroup,kubernetes.io/arch,eks.amazonaws.com/capacityType
NAME                                               STATUS   ROLES    AGE     VERSION               NODEGROUP   ARCH    CAPACITYTYPE
ip-192-168-1-6.ap-northeast-2.compute.internal     Ready    <none>   162m    v1.31.5-eks-5d632ec   ng1         amd64   ON_DEMAND
ip-192-168-2-172.ap-northeast-2.compute.internal   Ready    <none>   162m    v1.31.5-eks-5d632ec   ng1         amd64   ON_DEMAND
ip-192-168-3-188.ap-northeast-2.compute.internal   Ready    <none>   2m53s   v1.31.5-eks-5d632ec   ng3         arm64   ON_DEMAND
ip-192-168-3-246.ap-northeast-2.compute.internal   Ready    <none>   162m    v1.31.5-eks-5d632ec   ng1         amd64   ON_DEMAND

 

다만 이러한 환경은 multi-platform을 가진 노드를 가지기 때문에 파드들의 스케줄링에 주의가 필요합니다. 잘못된 스케줄링을 방지하기 위해서 taint를 사용할 수 있습니다.

# taint 정보 확인
aws eks describe-nodegroup --cluster-name $CLUSTER_NAME --nodegroup-name ng3 | jq .nodegroup.taints

# taints 적용 (바로 적용되지 않음)
aws eks update-nodegroup-config --cluster-name $CLUSTER_NAME --nodegroup-name ng3 --taints "addOrUpdateTaints=[{key=arm64, value=true, effect=NO_EXECUTE}]"

# 확인
kubectl describe nodes --selector family=graviton | grep Taints
Taints:             arm64=true:NoExecute

aws eks describe-nodegroup --cluster-name $CLUSTER_NAME --nodegroup-name ng3 | jq .nodegroup.taints
[
  {
    "key": "arm64",
    "value": "true",
    "effect": "NO_EXECUTE"
  }
]

 

또한 워크로드 자체도 ARM은 CPU 아키텍처가 AMD64와 상이하기 때문에 실행되는 이미지 빌드 시점에 다른 아키텍처로 빌드가 이루어져야 합니다. Multi(Cross)-Platform build 에 대해서는 docker buildx와 같은 문서를 참고 부탁드립니다.

https://docs.docker.com/build/building/multi-platform/

 

busybox는 다양한 Plaform을 지원하고 있어, 샘플 예제는 busybox를 통해 진행 하였습니다.

# 파드 생성
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: busybox
spec:
  terminationGracePeriodSeconds: 3
  containers:
  - name: busybox
    image: busybox
    command:
    - "/bin/sh"
    - "-c"
    - "while true; do date >> /home/pod-out.txt; cd /home; sync; sync; sleep 10; done"
  tolerations:
    - effect: NoExecute
      key: arm64
      operator: Exists
  nodeSelector:
    family: graviton
EOF

# 파드가 배포된 노드 정보 확인
kubectl get pod -owide
NAME             READY   STATUS    RESTARTS   AGE   IP             NODE                                               NOMINATED NODE   READINESS GATES
busybox          1/1     Running   0          12s   192.168.3.97   ip-192-168-3-188.ap-northeast-2.compute.internal   <none>           <none>

kubectl exec -it busybox -- arch
aarch64

# 삭제
kubectl delete pod busybox

 

실습을 종료하고 해당 노드 그룹을 삭제하도록 하겠습니다.

eksctl delete nodegroup -c $CLUSTER_NAME -n ng3

 

 

Spot 노드 그룹

앞서 노드 그룹에 용량 유형(Capacity Type)을 지정할 수 있다고 했습니다. 아래에서는 Spot 노드 그룹을 추가로 생성해 보겠습니다.

# 노드의 Capacity Type 확인
kubectl get nodes -l eks.amazonaws.com/capacityType=ON_DEMAND
kubectl get nodes -L eks.amazonaws.com/capacityType
NAME                                               STATUS   ROLES    AGE    VERSION               CAPACITYTYPE
ip-192-168-1-6.ap-northeast-2.compute.internal     Ready    <none>   170m   v1.31.5-eks-5d632ec   ON_DEMAND
ip-192-168-2-172.ap-northeast-2.compute.internal   Ready    <none>   170m   v1.31.5-eks-5d632ec   ON_DEMAND
ip-192-168-3-246.ap-northeast-2.compute.internal   Ready    <none>   170m   v1.31.5-eks-5d632ec   ON_DEMAND

# 노드 그룹 생성
NODEROLEARN=$(aws iam list-roles --query "Roles[?contains(RoleName, 'nodegroup-ng1')].Arn" --output text)
echo $NODEROLEARN

aws eks create-nodegroup \
  --cluster-name $CLUSTER_NAME \
  --nodegroup-name managed-spot \
  --subnets $PubSubnet1 $PubSubnet2 $PubSubnet3 \
  --node-role $NODEROLEARN \
  --instance-types c5.large c5d.large c5a.large \
  --capacity-type SPOT \
  --scaling-config minSize=2,maxSize=3,desiredSize=2 \
  --disk-size 20


# 명령이 바로 프롬프트가 떨어지므로, spot 노드그룹이 완전 Ready가 될때까지 대기하도록 합니다.
aws eks wait nodegroup-active --cluster-name $CLUSTER_NAME --nodegroup-name managed-spot

# 확인
kubectl get nodes -L eks.amazonaws.com/capacityType,eks.amazonaws.com/nodegroup
kubectl get nodes -L eks.amazonaws.com/capacityType,eks.amazonaws.com/nodegroup
NAME                                               STATUS   ROLES    AGE    VERSION               CAPACITYTYPE   NODEGROUP
ip-192-168-1-167.ap-northeast-2.compute.internal   Ready    <none>   68s    v1.31.5-eks-5d632ec   SPOT           managed-spot
ip-192-168-1-6.ap-northeast-2.compute.internal     Ready    <none>   173m   v1.31.5-eks-5d632ec   ON_DEMAND      ng1
ip-192-168-2-15.ap-northeast-2.compute.internal    Ready    <none>   68s    v1.31.5-eks-5d632ec   SPOT           managed-spot
ip-192-168-2-172.ap-northeast-2.compute.internal   Ready    <none>   173m   v1.31.5-eks-5d632ec   ON_DEMAND      ng1
ip-192-168-3-246.ap-northeast-2.compute.internal   Ready    <none>   173m   v1.31.5-eks-5d632ec   ON_DEMAND      ng1

 

2대의 Spot 노드가 추가되었습니다. 노드풀에 실행한 파드를 생성하겠습니다.

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: busybox
spec:
  terminationGracePeriodSeconds: 3
  containers:
  - name: busybox
    image: busybox
    command:
    - "/bin/sh"
    - "-c"
    - "while true; do date >> /home/pod-out.txt; cd /home; sync; sync; sleep 10; done"
  nodeSelector:
    eks.amazonaws.com/capacityType: SPOT
EOF

# 파드가 배포된 노드 정보 확인
kubectl get pod -owide
NAME             READY   STATUS    RESTARTS   AGE   IP             NODE                                              NOMINATED NODE   READINESS GATES
busybox          1/1     Running   0          8s    192.168.2.98   ip-192-168-2-15.ap-northeast-2.compute.internal   <none>           <none>

# 삭제
kubectl delete pod busybox

 

파드가 Spot 노드 중 하나에 배포된 것을 알 수 있습니다. 다만 Spot 노드는 Preempt 될 수 있기 때문에 일시적이거나 혹은 distruption에 문제가 없는 워크로드를 실행하는 것을 권장합니다.

Spot 노드 그룹 또한 taint를 주는 방법으로 일반 워크로드가 실행되지 않도록 유도하는 것이 알맞습니다.

 

AKS에서도 spot 노드 풀을 생성할 수 있습니다.

https://learn.microsoft.com/en-us/azure/aks/spot-node-pool

 

AKS에서는 spot 노드 풀에 기본적으로 kubernetes.azure.com/scalesetpriority=spot:NoSchedule taint를 적용합니다. 이는 고객의 워크로드가 의도치 않게 스케줄링 되는 이슈를 방지하고자 하는 목적으로, Spot 노드풀에 실행하고자 하는 워크로드에는 toleration이 필요한 점을 유의해야 합니다.

spec:
  containers:
  - name: spot-example
  tolerations:
  - key: "kubernetes.azure.com/scalesetpriority"
    operator: "Equal"
    value: "spot"
    effect: "NoSchedule"
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: "kubernetes.azure.com/scalesetpriority"
            operator: In
            values:
            - "spot"
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: another-node-label-key
            operator: In
            values:
            - another-node-label-value

 

마지막으로 실습에 사용한 노드풀을 삭제합니다.

 eksctl delete nodegroup -c $CLUSTER_NAME -n managed-spot

 

 

5. 리소스 정리

실습을 모두 마쳤다면, 아래와 같이 EKS를 삭제하고, 삭제를 확인한 뒤 CloudFormation으로 생성한 실습 환경을 삭제합니다.

# EKS 삭제
eksctl delete cluster --name $CLUSTER_NAME

# 실습 환경 삭제
aws cloudformation delete-stack --stack-name myeks

 

 

마무리

EKS의 스토리지 옵션과 노드 그룹에 대해서 확인해보았습니다.

 

Azure 환경을 주로 사용하기 때문에, EKS에 생성된 리소스를 바탕으로 AKS와 일부 비교를 해볼 수 있었습니다.

생각보다 시간이 너무 오래걸리기도 했고, 또 어떤 부분은 아직 EKS의 개념이 완벽하지 않은 부분도 있어서, 이후 학습을 더 진행해보고 내용을 보강하도록 하겠습니다.

 

다음 시간에는 EKS의 Observability에 대해서 학습해보고 내용을 정리하도록 하겠습니다.

+ Recent posts