Cloud Tech

EKS 노드 그룹

Hanhorang31 2025. 2. 23. 06:06
EKS 구성 노드 그룹과 OS를 살펴보겠습니다.

본 글은 가시다님이 운영하는 AEWS3기 스터디 내용을 참고하여 작성하였습니다.

 

 

AWS Graviton (ARM) 노드 그룹

64-bit Arm 프로세서 코어 기반의 AWS 커스텀 반도체로 고객 사례와 필요에 맞게 최적화된 칩입니다.

동급 인스턴스 성능 비교시 20%의 낮은 비용, 40% 가격대비 성능 향상을 기대할 수 있습니다.

인스턴스 타입 중 패밀리에 g가 들어간 것이 gravition을 뜻합니다.

다음과 같이 AWS Graviton (ARM) Instance 노드그룹 (ng3) 배포하겠습니다.

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
cat myng3.yaml
addonsConfig: {}
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
managedNodeGroups:
- amiFamily: AmazonLinux2
  desiredCapacity: 1
  disableIMDSv1: true
  disablePodIMDS: false
  iam:
    withAddonPolicies:
      albIngress: false
      appMesh: false
      appMeshPreview: false
      autoScaler: false
      awsLoadBalancerController: false
      certManager: false
      cloudWatch: false
      ebs: false
      efs: false
      externalDNS: false
      fsx: false
      imageBuilder: false
      xRay: false
  instanceSelector: {}
  instanceType: t4g.medium # 인스턴스 타입 추가 
  labels:
    alpha.eksctl.io/cluster-name: myeks
    alpha.eksctl.io/nodegroup-name: ng3
    family: graviton # 라벨 추가 
  maxSize: 1
  minSize: 1
  name: ng3
  privateNetworking: false
  releaseVersion: ""
  securityGroups:
    withLocal: null
    withShared: null
  ssh:
    allow: false
    publicKeyPath: ""
  subnets:
  - subnet-004dfe0699f45baf7
  - subnet-09c51f45dda9535dd
  - subnet-090ed5b44f89e983a
  tags:
    alpha.eksctl.io/nodegroup-name: ng3
    alpha.eksctl.io/nodegroup-type: managed
  volumeIOPS: 3000
  volumeSize: 30
  volumeThroughput: 125
  volumeType: gp3
metadata:
  name: myeks
  region: ap-northeast-2
  version: "1.31"
  
  

# 노드 그룹 구성
eksctl create nodegroup -f myng3.yaml 

# taints 셋팅 -> 적용에 2~3분 정도 시간 소요
aws eks update-nodegroup-config --cluster-name $CLUSTER_NAME --nodegroup-name ng3 --taints "addOrUpdateTaints=[{key=frontend, value=true, effect=NO_EXECUTE}]"
  

예제 파드를 배포하겠습니다.

파드 배포시 지원 아키텍처에서 arm 지원을 확인해야 합니다.

앞서 구성한 taints 설정으로 ng3 노드에 파드가 배치되도록 설정합니다.

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: frontend
      operator: Exists
  nodeSelector:
    family: graviton
EOF

kubectl get pods -o wide 

 

Spot 노드 그룹

AWS Spot 인스턴스를 EKS 노드 그룹으로 사용할 수 있습니다.

Spot 인스턴스를 통해 최대 90% 요금 할인을 받을 수 있습니다.

다만, 다른 고객이 필요한 경우 2분 후 반납해야 하기 때문에 상태 비저장 API 엔드포인트, 일괄 처리, ML 학습 워크로드, Apache Spark를 사용한 빅데이터 ETL, 대기열 처리 애플리케이션, CI/CD 파이프라인과 같은 워크로드에 매우 인기 있는 사용 패턴입니다.

적절한 인스턴스 스펙을 확인하여 Spot 노드 그룹을 구성하겠습니다.

curl -Lo ec2-instance-selector https://github.com/aws/amazon-ec2-instance-selector/releases/download/v2.4.1/ec2-instance-selector-`uname | tr '[:upper:]' '[:lower:]'`-amd64 && chmod +x ec2-instance-selector
mv ec2-instance-selector /usr/local/bin/
ec2-instance-selector --version

# 스펙 설정 확인
ec2-instance-selector --vcpus 2 --memory 4 --gpus 0 --current-generation -a x86_64 --deny-list 't.*' --output table-wide
# 노드 그룹 생성
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
  
  # 노드 그룹이 "ACTIVE" 상태가 될 때까지 블로킹(Blocking) 상태로 대기
  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
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 pods -o wide 

Spot 중단을 처리하기 위해 AWS Node Termination Handler와 같은 클러스터에 추가 자동화 도구를 설치할 필요가 없습니다. 관리형 노드 그룹은 사용자를 대신하여 Amazon EC2 Auto Scaling 그룹을 구성하고 다음과 같은 방식으로 Spot 중단을 처리합니다.

 

 

Bottlerocket 노드 그룹

Bottlerocket은 컨테이너 실행을 위한 Linux 기반 운영 체제입니다.

  • 장점 - Kr
    • 운영 비용 절감 및 관리 복잡성 감소로 가동 시간 증가 – Bottlerocket은 다른 Linux 배포판보다 리소스 공간이 작고 부팅 시간이 짧으며 보안 위협에 덜 취약합니다. Bottlerocket은 공간이 작아 스토리지, 컴퓨팅 및 네트워킹 리소스를 적게 사용하여 비용을 절감할 수 있습니다.
    • 자동 OS 업데이트로 보안 강화 – Bottlerocket 업데이트는 필요한 경우 롤백할 수 있는 단일 단위로 적용됩니다. 이렇게 하면 시스템을 사용 불가 상태로 둘 수 있는 손상되거나 실패한 업데이트의 위험이 제거됩니다. Bottlerocket을 사용하면 보안 업데이트를 사용할 수 있는 즉시 중단을 최소화하는 방식으로 자동 적용하고 장애 발생 시 롤백할 수 있습니다.
    • 프리미엄 지원 – AWS에서 제공하는 Amazon EC2 기반 Bottlerocket 빌드에는 Amazon EC2, Amazon EKS, Amazon ECR 등의 AWS 서비스에도 적용되는 동일한 AWS Support 플랜이 적용됩니다
    • Bottlerocket의 설계 덕분에 OS 바이너리 업데이트와 보안 패치 주기를 분리하여 사전 페치된 이미지로 데이터 볼륨을 쉽게 연결할 수 있습니다.
  • 고려사항
    • Bottlerocket AMI를 Inferentia 칩이 있는 Amazon EC2 인스턴스와 함께 사용하는 것은 권장되지 않습니다.
    • Bottlerocket 이미지에는 SSH 서버 또는 쉘이 포함되지 않습니다. 대체 액세스 방법을 사용하여 SSH를 허용할 수 있습니다.

cat << EOF > ng-br.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
  name: myeks
  region: ap-northeast-2
  version: "1.31"

managedNodeGroups:
- name: ng-bottlerocket
  instanceType: m5.large
  amiFamily: Bottlerocket
  bottlerocket:
    enableAdminContainer: true
    settings:
      motd: "Hello, eksctl!"
  desiredCapacity: 1
  maxSize: 1
  minSize: 1
  labels:
    alpha.eksctl.io/cluster-name: myeks
    alpha.eksctl.io/nodegroup-name: ng-bottlerocket
    ami: bottlerocket
  subnets:
  - $PubSubnet1
  - $PubSubnet2
  - $PubSubnet3
  tags:
    alpha.eksctl.io/nodegroup-name: ng-bottlerocket
    alpha.eksctl.io/nodegroup-type: managed

- name: ng-bottlerocket-ssh
  instanceType: m5.large
  amiFamily: Bottlerocket
  desiredCapacity: 1
  maxSize: 1
  minSize: 1
  ssh: # ssh 접근 허용
    allow: true
    publicKeyName: $SSHKEYNAME
  labels:
    alpha.eksctl.io/cluster-name: myeks
    alpha.eksctl.io/nodegroup-name: ng-bottlerocket-ssh
    ami: bottlerocket
  subnets:
  - $PubSubnet1
  - $PubSubnet2
  - $PubSubnet3
  tags:
    alpha.eksctl.io/nodegroup-name: ng-bottlerocket-ssh
    alpha.eksctl.io/nodegroup-type: managed
EOF

구성한 노드 그룹 접근시 ssh 가 허용된 노드만 접근이 가능합니다.

whoami
pwd
ip -c a # 명령어 없음
lsblk
yum install jq -y
ps
ps -ef
ps 1
sestatus # 비활성화
getenforce # 비활성화

bottlerocket AMI 노드에서 호스트 네임스페이스를 탈취해보겠습니다.

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: root-shell
  namespace: kube-system
spec:
  containers:
  - command:
    - /bin/cat
    image: alpine:3
    name: root-shell
    securityContext:
      privileged: true
    tty: true
    stdin: true
    volumeMounts:
    - mountPath: /host
      name: hostroot
  hostNetwork: true
  hostPID: true
  hostIPC: true
  tolerations:
  - effect: NoSchedule
    operator: Exists
  - effect: NoExecute
    operator: Exists
  volumes:
  - hostPath:
      path: /
    name: hostroot
  nodeSelector:
    ami: bottlerocket
EOF

kubectl -n kube-system exec -it root-shell -- chroot /host /bin/bash
kubectl -n kube-system exec -it root-shell -- chroot /host /bin/sh
kubectl -n kube-system exec -it root-shell -- chroot /host /usr/bin/bash
kubectl -n kube-system exec -it root-shell -- chroot /host /usr/bin/sh

불가능합니다.

 

 

apiclient 파드를 배포하여 enter-admin-container 진입

  • 이 컨테이너는 Bottlerocket API에 접근할 수 있도록 해주며, 이를 통해 시스템을 점검하고 설정을 변경할 수 있음.
  • 이를 위해 apiclient 도구를 사용하는 것이 일반적입니다. 예) 시스템 점검 apiclient -u /settings | jq
  • 고급 디버깅을 위해서는 Admin Container를 사용 : 호스트에 대한 root 접근을 허용
  • Admin Container를 활성화한 후, SSH로 접근 가능.

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: apiclient
  namespace: kube-system
spec:
  containers:
  - command:
    - sleep
    - infinity
    image: fedora
    imagePullPolicy: Always
    name: regain-access
    securityContext:
      seLinuxOptions:
        level: s0
        role: system_r
        type: control_t
        user: system_u
    volumeMounts:
    - mountPath: /usr/bin/apiclient
      name: apiclient
      readOnly: true
    - mountPath: /run/api.sock
      name: apiserver-socket
  restartPolicy: Always
  terminationGracePeriodSeconds: 0
  volumes:
  - hostPath:
      path: /usr/bin/apiclient
      type: File
    name: apiclient
  - hostPath:
      path: /run/api.sock
      type: Socket
    name: apiserver-socket
  nodeSelector:
    ami: bottlerocket
EOF

kubectl exec -i -t -n kube-system apiclient -- apiclient exec -t control enter-admin-container

# apiclient 사용
apiclient --help
apiclient -u /settings | jq
apiclient get | jq
apiclient get | grep motd

apiclient report cis

 

 

자원 삭제

사용한 리소스는 다음의 명령어로 자원을 삭제합니다.

eksctl delete cluster --name $CLUSTER_NAME
aws cloudformation delete-stack --stack-name myeks