Kubernetes

AEWS [3기] 3주차 - EKS Storage, Managed Node Groups

yu3papa 2025. 2. 22. 12:20

EKS 에서 사용하는 스토리지에 대해 알아보기전에 온프레미스 환경에서 구축한 쿠버네티스에서 K8S 스토리지 관련 리소스인  PV(Persistent Volume), PVC(Persistenet Volume Cliaim), SC(Storage Class)에 대해 먼저 알아보겠습니다.

1. 쿠버네티스 클러스터의 PV, PVC, SC 리소스

1.1. K8S v1.31 클러스터 구성

Terraform을 이용하여 아래와 같이 EC2 3대를 이용하여 바닐라 쿠버네티스를 구성하겠습니다.

 

lab-setup.zip
0.01MB

 

첨부한 lab-setup.zip  파일을 압축하고 명령프롬프트 또는 쉘을 열고 terraform 실행파일을 OS 환경에 맞게 다운로드 합니다.

C:\eks-workshop\lab-setup> curl -sO https://releases.hashicorp.com/terraform/1.10.5/terraform_1.10.5_windows_amd64.zip
C:\eks-workshop\lab-setup> tar -xf terraform_1.10.5_windows_amd64.zip

 

variables.tf 파일을 열고 AWS "access_key" 및 "secret_key"를 본인 소유의 키로 변경합니다.

 

EC2 접속을 위한 SSH Key-Pair 생성

C:\eks-workshop\lab-setup> ssh-keygen -t rsa -f edukey -N ""
Generating public/private rsa key pair.
Your identification has been saved in edukey
Your public key has been saved in edukey.pub
The key fingerprint is:
SHA256:mwFKs5R2KdWdtSR3qq2FC8YpwSiFBg4mHhodZKVLTho administrator@JHY

 

“terraform init” 명령을 수행하여 AWS Provider 초기화하고 “terraform apply” 명령을 수행하여 AWS 리소스 생성

C:\eks-workshop\lab-setup> terraform apply --auto-approve
...(생략)
Apply complete! Resources: 5 added, 0 changed, 0 destroyed.

Outputs:

k8s-cp_public-ip = "13.209.150.124"

# “k8s-cp” 인스턴스에 Private Key 복사
C:\eks-workshop\lab-setup> scp -i edukey edukey root@13.209.150.124:~/.ssh/id_rsa
The authenticity of host '13.209.150.124 (13.209.150.124)' can't be established.
ED25519 key fingerprint is SHA256:NpdHkxNyzqWJjVLVNjW8kC+Vl7iqlH9u5kqR4aiatb8.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])?
Warning: Permanently added '13.209.150.124' (ED25519) to the list of known hosts.
edukey                                                                                100% 2602   363.0KB/s   00:00

 

k8s-cp_public-ip 와 SSH Private Key를 이용하여 k8s-cp EC2에 root 계정으로 SSH 로그인

 

k8s-cp 노드에서 k8s-w1, k8s-w2에 SSH Key를 이용한 로그인 설정

[root@k8s-cp ~]# chmod 400 ~/.ssh/id_rsa
[root@k8s-cp ~]# ssh k8s-w1
[root@k8s-cp ~]# ssh k8s-w2

 

현재 K8S는 Control-plane 노드만 구성되어 있습니다.

[root@k8s-cp ~]# kubectl get no
NAME     STATUS   ROLES           AGE     VERSION
k8s-cp   Ready    control-plane   4m47s   v1.31.4

 

join-command를 생성한 후 k8s-w1, k8s-w2 노드를 클러스터에 조인합니다.

[root@k8s-cp ~]# kubeadm token create --print-join-command
kubeadm join 172.31.1.10:6443 --token njseik.wna0odfa83txmr2l --discovery-token-ca-cert-hash sha256:ce6b8504ea710f82aeea8bf3acadd82df870544134de6de1339f19a446c03a1f

[root@k8s-cp ~]# ssh k8s-w1
[root@k8s-w1 ~]# kubeadm join 172.31.1.10:6443 --token njseik.wna0odfa83txmr2l --discovery-token-ca-cert-hash sha256:ce6b8504ea710f82aeea8bf3acadd82df870544134de6de1339f19a446c03a1f
...(생략)...
Run 'kubectl get nodes' on the control-plane to see this node join the cluster.
[root@k8s-w1 ~]# exit

[root@k8s-cp ~]# ssh k8s-w2
[root@k8s-w2 ~]# kubeadm join 172.31.1.10:6443 --token njseik.wna0odfa83txmr2l --discovery-token-ca-cert-hash sha256:ce6b8504ea710f82aeea8bf3acadd82df870544134de6de1339f19a446c03a1f
...(생략)...
Run 'kubectl get nodes' on the control-plane to see this node join the cluster.
[root@k8s-w2 ~]# exit

[root@k8s-cp ~]# kubectl get no
NAME     STATUS   ROLES           AGE     VERSION
k8s-cp   Ready    control-plane   8m48s   v1.31.4
k8s-w1   Ready    <none>          56s     v1.31.4
k8s-w2   Ready    <none>          21s     v1.31.4

 

1.2. 방명록 샘플어플리케이션 소개

 

실습에서 사용할 간단한 방명록 웹플리케이션입니다.

게시글 저장을 위한 MySQL DBMS는 아래와 같이 도커 컨테이너로 k8s-cp 머신(172.31.1.10) 에서 실행해 놓겠습니다.

[root@k8s-cp ~]# yum install -y docker-ce-27.4.1
[root@k8s-cp ~]# systemctl enable --now docker
[root@k8s-cp ~]# docker container run -d \
      --name=guestbookdb \
      --restart=always \
      --network=host \
      -e MYSQL_ROOT_PASSWORD=jadecross \
      -e MYSQL_DATABASE=guestbook \
      mysql:8

 

방명록 웹어플리케이션은 게시글을 등록하면서 파일을 첨부할 수 있는 기능이 있는데, 파일을 첨부하게 되면 컨테이너 파일시스템에 업로드가 됩니다. 

쿠버네티스 환경에서 컨테이너는 POD라는 논리적인 컴퓨터 안에서 실행되게 되는데, POD는 일시적인 리소스입니다. POD에 문제가 있으면 지워버리고, 그러면 Self-Healing 되면서 레플리카 개수를 맞추게 됩니다.

이러한 상황에서 방명록 POD를 삭제하면 첨부했던 파일들도 모두 사라지게 되어 첨부한 파일의 영속성을 보장하지 못합니다.

 

POD가 삭제되거나 비정상일 때도
업로드한 첨부파일을 안전하게
영구히 보존하는 방법은?

이러한 문제점을 해결하기 위해서 K8S 에서는 POD 에 Volume 을 선언하고, Container는 선언된 Volume을 레퍼런스 하면서 컨테이너 파일 시스템에 마운트하는 방식을 이용합니다.

쿠버네티스가 지원하는 볼륨을 타입으로 구분해 보면 아래와 같이 3가지 타입으로 구분해 볼 수 있습니다.

위의 3가지 타입중 emptDir 과 HostPath 타입의 방식은 기본적으로 공유스토리지로 사용할수가 없어서, Network을 지원하는 Storage를 사용할 수 밖에 없습니다.

실습환경에서 사용가능한 NFS 를 이용한 볼륨 실습을 해 보겠습니다.

아래와 같이 k8s-cp 머신에서 NFS 공유폴더 설정을 합니다.

[root@k8s-cp ~]# dnf install -y nfs-utils ◀━ NFS 패키지 설치
[root@k8s-cp ~]# systemctl enable --now nfs-server
[root@k8s-cp ~]# mkdir -p /nfs-share/guestbook-data ◀━ 공유 디렉토리 생성
[root@k8s-cp ~]# chmod 777 /nfs-share/guestbook-data

[root@k8s-cp ~]# vi /etc/exports ◀━ 공유 디렉토리 설정파일 편집
/nfs-share/guestbook-data   172.31.0.0/20(rw,sync,no_root_squash)

[root@k8s-cp ~]# systemctl restart nfs-server ◀━ nfs 서비스 시작

[root@k8s-cp ~]# exportfs -v ◀━ nfs 공유정보 확인
/nfs-share/guestbook-data
                172.31.0.0/20(sync,...)

 

NFS 스토리지를 사용하는 방명록 POD를 생성합니다.

# guestbook-pod-nfs.yaml
apiVersion: v1
kind: Pod
metadata:
  name: guestbook-nfs
spec:
  volumes:
  - name: guestbook-data
    nfs:
      server: 172.31.1.10
      path: /nfs-share/guestbook-data
  containers:
  - image: yu3papa/k8s_guestbook:1.0
    name: guestbook
    volumeMounts:
    - name: guestbook-data
      mountPath: /app/upload
    ports:
    - containerPort: 8080
      protocol: TCP

 

[root@k8s-cp ~]# k apply -f guestbook-pod-nfs.yaml
pod/guestbook-nfs created

 

생성된 POD의 IP를 확인하고 웹화면에서 파일을 첨부하면 컨테이너 파일시스템이 첨부가 되지만 최종적으로 NFS 공유폴더를 제공하는 k8s-cp 머신에 파일 저장됩니다.


이런 상태에서 POD를 삭제하여도 첨부한 파일은 NFS 공유폴더에 보존되어 있습니다.

 

Network 타입을 지원하는 스토리지는 정말 많습니다.

위와 같은 상황에서 POD YAML파일을 작성하는 개발자는 NFS 스토리지에 대한 기술을 알고 있어야 합니다. 또는 AWS 환경을 이용한다면 EBS에 대한 지식도 있어야 합니다.

이러한 상황에서 쿠버네티스는 "스토리지 기술에서 POD 를 분리"하는 방법을 고안하게 됩니다.

이러한 맥락에서 PersistentVolume과 PersistentVolumeClaim 리소스를 이용합니다.

전체적인 사용흐름은 아래와 같으며, 핵심은 "개발자가 스토리지 인프라 세부 사항을 처리하지 않고도 쿠버네티스 클러스터의 스토리지를 요청할 수 있게 하는것"입니다.

 

 

PV, PVC를 사용하는 방명록을 구성해 보겠습니다.

guestbook-pv-nfs.yaml guestbook-pvc.yaml guestbook-pod-pvc.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: guestbook-pv
  labels:
    disk: ssd
spec:
  capacity: 
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
    - ReadOnlyMany
  persistentVolumeReclaimPolicy: Retain
  nfs:
    path: /nfs-share/guestbook-pv
    server: 172.31.1.10

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: guestbook-pvc 
spec:
  resources:
    requests:
      storage: 1Gi
  selector:
    matchLabels:
      disk: ssd
  accessModes:
    - ReadWriteOnce
  storageClassName: ""
apiVersion: v1
kind: Pod
metadata:
  name: guestbook-pod-pvc
spec:
  containers:
  - image: yu3papa/k8s_guestbook:1.0
    name: guestbook
    volumeMounts:
    - name: guestbook-data
      mountPath: /app/upload
    ports:
    - containerPort: 8080
      protocol: TCP
  volumes:
  - name: guestbook-data
    persistentVolumeClaim:
      claimName: guestbook-pvc

 

 

여기에서 또 해결해야할 이슈가 있습니다. 클러스터가 수 많은 스토리지 기술의 전문가 일 수가 없습니다. 스토리지 전문가는 해당 스토리지 벤더의 엔지니어가 전문가입니다.

쿠버네티스는 이러한 문제를 해결하기 위해 CSI (Contaier Storage Interface) 를 제안합니다.

  • CSI 를 사용하면, K8S 의 공통화된 CSI 인터페이스를 통해 다양한 프로바이더를 사용할 수 있다.

해당 스토리지 벤더들이 CSI 규격에 맞는 Provisioner를 쿠버네티스에 배포하고 클러스터 관리자는 SC(Storage Class) 리소스에 Provisioner를 매핑해 놓으면 PVC 리소스만 만들면 자동으로 PV 및 실제 스토리지도 만들어지게 되어 "Dynamic PersistentVolume 프로비저닝" 이 가능해 집니다. 도식화해보면 아래와 같습니다.

 

온프레미스환경에서 NFS를 이용하여 "Dynamic PersistentVolume 프로비저닝"을 할 수 있도록 K8S Sig그룹에서 "nfs-subdir-external-provisioner"를 제공합니다.

 

GitHub - kubernetes-sigs/nfs-subdir-external-provisioner: Dynamic sub-dir volume provisioner on a remote NFS server.

Dynamic sub-dir volume provisioner on a remote NFS server. - kubernetes-sigs/nfs-subdir-external-provisioner

github.com

 

1.2. K8S v1.31 클러스터 삭제

테라폼을 이용하여 지금까지 실습한 환경을 삭제합니다.

C:\eks-workshop\lab-setup> terraform destroy --auto-approve
...(생략)...
aws_instance.k8s-cp: Still destroying... [id=i-0311028a5562d2fc2, 1m1s elapsed]
aws_instance.k8s-cp: Destruction complete after 1m1s
aws_key_pair.edukey-pair: Destroying... [id=edukey]
aws_key_pair.edukey-pair: Destruction complete after 0s

Destroy complete! Resources: 5 destroyed.

 

2. EKS 실습환경 구성

아래와 같은 환경으로 EKS 실습환경을 구성합니다.

 

 2개의 VPC(EKS 배포, 운영용 구분), myeks-vpc의 public 에 EFS 추가

  • myeks-vpc 에 각기 AZ를 사용하는 퍼블릭/프라이빗 서브넷 배치
    • EFS 스토리지 배포, 3개의 퍼블릭 서브넷에 네트워크 인터페이스 연동
    • 로그밸런서 배포를 위한 퍼블릭/프라이빗 서브넷에 태그 설정 - [Docs]
    • Amazon EKS optimized Amazon Linux 2023 accelerated AMIs now available - [Link]v
  • operator-vpc 에 AZ1를 사용하는 퍼블릭/프라이빗 서브넷 배치 : 172.20.1.100 운영서버 EC2 배포
  • 내부 통신을 위한 VPC Peering 배치

2.1. AWS CloudFormation 을 통해 기본 실습 환경 배포

myeks-3week.yaml
0.01MB

 

제 실습환경은 Rocky Linux 9.5 환경에서 진행합니다.

aws CLI 도구를 설정하고 configure 작업을 수행합니다.

[yu3papa@iworks ~]$ curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
[yu3papa@iworks ~]$ unzip awscliv2.zip
[yu3papa@iworks ~]$ sudo ./aws/install
[yu3papa@iworks ~]$ aws --version
aws-cli/2.24.10 Python/3.12.9 Linux/5.14.0-503.23.1.el9_5.x86_64 exe/x86_64.rocky.9

[yu3papa@iworks ~]$ aws configure
AWS Access Key ID [None]: AKIAVVZO********OVO56J
AWS Secret Access Key [None]: wDT3QQyYiljs2i***********9zIGTiIH
Default region name [None]: ap-northeast-2
Default output format [None]: json

 

① SSH 키페어를 생성하고 Private Key를 로컬에 잘 보관합니다.

② 보안을 위해 작업 EC2 에 본인의 집에서만 접속이 되되록 "내 IP"를 확인합니다.

C:\> curl -s ipinfo.io/ip
221.149.127.57

 

③ CloudFormation 을 이용하여 실습환경 배포

# yaml 파일 다운로드
[yu3papa@iworks ~]$ curl -O https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/K8S/myeks-3week.yaml

# 배포
# aws cloudformation deploy --template-file myeks-1week.yaml --stack-name mykops --parameter-overrides KeyName=<My SSH Keyname> SgIngressSshCidr=<My Home Public IP Address>/32 --region <리전>
[yu3papa@iworks ~]$ aws cloudformation deploy \
--template-file ./myeks-3week.yaml \
--stack-name myeks \
--parameter-overrides KeyName=aews-yu3papa SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32 \
--region ap-northeast-2

Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - myeks

# CloudFormation 스택 배포 완료 후 운영서버 EC2 IP 출력
[yu3papa@iworks ~]$ aws cloudformation describe-stacks --stack-name myeks --query 'Stacks[*].Outputs[*].OutputValue' --output text
15.164.244.197

# 운영서버 EC2 에 SSH 접속
[yu3papa@iworks ~]$ ssh -i aews-yu3papa.pem ec2-user@15.164.244.197
   ,     #_
   ~\_  ####_        Amazon Linux 2
  ~~  \_#####\
  ~~     \###|       AL2 End of Life is 2026-06-30.
  ~~       \#/ ___
   ~~       V~' '->
    ~~~         /    A newer version of Amazon Linux is available!
      ~~._.   _/
         _/ _/       Amazon Linux 2023, GA and supported until 2028-03-15.
       _/m/'           https://aws.amazon.com/linux/amazon-linux-2023/

# 운영서버에서도 aws configure 작업을 수행
[root@operator-host ~]# aws configure
AWS Access Key ID [None]: AKIAVVZO********OVO56J
AWS Secret Access Key [None]: wDT3QQyYiljs2i***********9zIGTiIH
Default region name [None]: ap-northeast-2
Default output format [None]: json

 

위 CloudFormation 을 이용하면 EFS 가 하나 만들어지며, 향후 EKS 에서 볼륨으로 사용할 예정입니다.

EFS 상세정보에서 "네트워크" 탭을 확인하면 향후 EKS 워커노드에서 연결할때 사용할 IP를 확인할 수 있습니다.

 

2.2. eksctl 을 통해 EKS 배포

Child로 실행한 쉘에서 EKS 관련 환경을 공유하기 위해 실습에 필요한 환경변수를 설정하고 .bashrc 에 추가하여 실습을 좀 더 원할하게 진행하겠습니다.

[root@operator-host ~]# export CLUSTER_NAME=myeks
# myeks-VPC/Subnet 정보 확인 및 변수 지정
[root@operator-host ~]# export VPCID=$(aws ec2 describe-vpcs --filters "Name=tag:Name,Values=$CLUSTER_NAME-VPC" --query 'Vpcs[*].VpcId' --output text)
[root@operator-host ~]# echo $VPCID
vpc-040caab837037a18c
[root@operator-host ~]# export PubSubnet1=$(aws ec2 describe-subnets --filters Name=tag:Name,Values="$CLUSTER_NAME-Vpc1PublicSubnet1" --query "Subnets[0].[SubnetId]" --output text)
[root@operator-host ~]# export PubSubnet2=$(aws ec2 describe-subnets --filters Name=tag:Name,Values="$CLUSTER_NAME-Vpc1PublicSubnet2" --query "Subnets[0].[SubnetId]" --output text)
[root@operator-host ~]# export PubSubnet3=$(aws ec2 describe-subnets --filters Name=tag:Name,Values="$CLUSTER_NAME-Vpc1PublicSubnet3" --query "Subnets[0].[SubnetId]" --output text)
[root@operator-host ~]# echo $PubSubnet1 $PubSubnet2 $PubSubnet3
subnet-08b83e58f1cad6098 subnet-0ad3d1ce95ad8a965 subnet-0f643cf03e72dc812
[root@operator-host ~]# SSHKEYNAME=aews-yu3papa

 

myeks.yaml 파일 작성

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

iam:
  withOIDC: true # enables the IAM OIDC provider as well as IRSA for the Amazon CNI plugin

  serviceAccounts: # service accounts to create in the cluster. See IAM Service Accounts
  - metadata:
      name: aws-load-balancer-controller
      namespace: kube-system
    wellKnownPolicies:
      awsLoadBalancerController: true

vpc:
  cidr: 192.168.0.0/16
  clusterEndpoints:
    privateAccess: true # if you only want to allow private access to the cluster
    publicAccess: true # if you want to allow public access to the cluster
  id: $VPCID
  subnets:
    public:
      ap-northeast-2a:
        az: ap-northeast-2a
        cidr: 192.168.1.0/24
        id: $PubSubnet1
      ap-northeast-2b:
        az: ap-northeast-2b
        cidr: 192.168.2.0/24
        id: $PubSubnet2
      ap-northeast-2c:
        az: ap-northeast-2c
        cidr: 192.168.3.0/24
        id: $PubSubnet3

addons:
  - name: vpc-cni # no version is specified so it deploys the default version
    version: latest # auto discovers the latest available
    attachPolicyARNs: # attach IAM policies to the add-on's service account
      - arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy
    configurationValues: |-
      enableNetworkPolicy: "true"

  - name: kube-proxy
    version: latest

  - name: coredns
    version: latest

  - name: metrics-server
    version: latest

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
EOF

 

최종 yaml 로 eks 를 배포합니다.

[root@operator-host ~]# eksctl create cluster -f myeks.yaml --verbose 4
# 실행 로그
2025-02-22 19:57:03 [▶]  Setting credentials expiry window to 30 minutes
2025-02-22 19:57:03 [▶]  role ARN for the current session is "arn:aws:iam::390402552337:root"
2025-02-22 19:57:03 [ℹ]  eksctl version 0.204.0
2025-02-22 19:57:03 [ℹ]  using region ap-northeast-2
2025-02-22 19:57:03 [✔]  using existing VPC (vpc-040caab837037a18c) and subnets (private:map[] public:map[ap-northeast-2a:{subnet-08b83e58f1cad6098 ap-northeast-2a 192.168.1.0/24 0 } ap-northeast-2b:{subnet-0ad3d1ce95ad8a965 ap-northeast-2b 192.168.2.0/24 0 } ap-northeast-2c:{subnet-0f643cf03e72dc812 ap-northeast-2c 192.168.3.0/24 0 }])
2025-02-22 19:57:03 [!]  custom VPC/subnets will be used; if resulting cluster doesn't function as expected, make sure to review the configuration of VPC/subnets
2025-02-22 19:57:03 [ℹ]  nodegroup "ng1" will use "" [AmazonLinux2023/1.31]
2025-02-22 19:57:03 [ℹ]  using EC2 key pair "aews-yu3papa"
2025-02-22 19:57:03 [ℹ]  using Kubernetes version 1.31
2025-02-22 19:57:03 [ℹ]  creating EKS cluster "myeks" in "ap-northeast-2" region with managed nodes
2025-02-22 19:57:03 [▶]  cfg.json = \
{
    "kind": "ClusterConfig",
    "apiVersion": "eksctl.io/v1alpha5",
    "metadata": {
        "name": "myeks",
        "region": "ap-northeast-2",
        "version": "1.31"
    },
    "iam": {
        "withOIDC": true,
        "serviceAccounts": [
            {
                "metadata": {
                    "name": "aws-load-balancer-controller",
                    "namespace": "kube-system"
                },
                "wellKnownPolicies": {
                    "imageBuilder": false,
                    "autoScaler": false,
                    "awsLoadBalancerController": true,
                    "externalDNS": false,
                    "certManager": false,
                    "ebsCSIController": false,
                    "efsCSIController": false
                }
            }
        ],
        "vpcResourceControllerPolicy": true
    },
    "accessConfig": {
        "authenticationMode": "API_AND_CONFIG_MAP"
    },
    "vpc": {
        "id": "vpc-040caab837037a18c",
        "cidr": "192.168.0.0/16",
        "subnets": {
            "public": {
                "ap-northeast-2a": {
                    "id": "subnet-08b83e58f1cad6098",
                    "az": "ap-northeast-2a",
                    "cidr": "192.168.1.0/24"
                },
                "ap-northeast-2b": {
                    "id": "subnet-0ad3d1ce95ad8a965",
                    "az": "ap-northeast-2b",
                    "cidr": "192.168.2.0/24"
                },
                "ap-northeast-2c": {
                    "id": "subnet-0f643cf03e72dc812",
                    "az": "ap-northeast-2c",
                    "cidr": "192.168.3.0/24"
                }
            }
        },
        "manageSharedNodeSecurityGroupRules": true,
        "nat": {
            "gateway": "Single"
        },
        "clusterEndpoints": {
            "privateAccess": true,
            "publicAccess": true
        }
    },
    "addons": [
        {
            "name": "vpc-cni",
            "version": "latest",
            "attachPolicyARNs": [
                "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy"
            ],
            "wellKnownPolicies": {
                "imageBuilder": false,
                "autoScaler": false,
                "awsLoadBalancerController": false,
                "externalDNS": false,
                "certManager": false,
                "ebsCSIController": false,
                "efsCSIController": false
            },
            "configurationValues": "enableNetworkPolicy: \"true\""
        },
        {
            "name": "kube-proxy",
            "version": "latest",
            "wellKnownPolicies": {
                "imageBuilder": false,
                "autoScaler": false,
                "awsLoadBalancerController": false,
                "externalDNS": false,
                "certManager": false,
                "ebsCSIController": false,
                "efsCSIController": false
            }
        },
        {
            "name": "coredns",
            "version": "latest",
            "wellKnownPolicies": {
                "imageBuilder": false,
                "autoScaler": false,
                "awsLoadBalancerController": false,
                "externalDNS": false,
                "certManager": false,
                "ebsCSIController": false,
                "efsCSIController": false
            }
        },
        {
            "name": "metrics-server",
            "version": "latest",
            "wellKnownPolicies": {
                "imageBuilder": false,
                "autoScaler": false,
                "awsLoadBalancerController": false,
                "externalDNS": false,
                "certManager": false,
                "ebsCSIController": false,
                "efsCSIController": false
            }
        }
    ],
    "addonsConfig": {},
    "privateCluster": {
        "enabled": false,
        "skipEndpointCreation": false
    },
    "managedNodeGroups": [
        {
            "name": "ng1",
            "amiFamily": "AmazonLinux2023",
            "instanceType": "t3.medium",
            "desiredCapacity": 3,
            "minSize": 3,
            "maxSize": 3,
            "volumeSize": 120,
            "ssh": {
                "allow": true,
                "publicKeyName": "aews-yu3papa"
            },
            "labels": {
                "alpha.eksctl.io/cluster-name": "myeks",
                "alpha.eksctl.io/nodegroup-name": "ng1"
            },
            "privateNetworking": false,
            "tags": {
                "alpha.eksctl.io/nodegroup-name": "ng1",
                "alpha.eksctl.io/nodegroup-type": "managed"
            },
            "iam": {
                "withAddonPolicies": {
                    "imageBuilder": false,
                    "autoScaler": false,
                    "externalDNS": true,
                    "certManager": true,
                    "appMesh": null,
                    "appMeshPreview": null,
                    "ebs": false,
                    "fsx": false,
                    "efs": false,
                    "awsLoadBalancerController": false,
                    "albIngress": false,
                    "xRay": false,
                    "cloudWatch": false
                }
            },
            "securityGroups": {
                "withShared": null,
                "withLocal": null
            },
            "maxPodsPerNode": 100,
            "volumeType": "gp3",
            "volumeIOPS": 3000,
            "volumeThroughput": 125,
            "preBootstrapCommands": [
                "dnf install nvme-cli links tree tcpdump sysstat ipvsadm ipset bind-utils htop -y"
            ],
            "disableIMDSv1": true,
            "disablePodIMDS": false,
            "instanceSelector": {},
            "releaseVersion": ""
        }
    ],
    "availabilityZones": [
        "ap-northeast-2a",
        "ap-northeast-2c",
        "ap-northeast-2b"
    ]
}

2025-02-22 19:57:03 [ℹ]  1 nodegroup (ng1) was included (based on the include/exclude rules)
2025-02-22 19:57:03 [ℹ]  will create a CloudFormation stack for cluster itself and 1 managed nodegroup stack(s)
2025-02-22 19:57:03 [ℹ]  if you encounter any issues, check CloudFormation console or try 'eksctl utils describe-stacks --region=ap-northeast-2 --cluster=myeks'
2025-02-22 19:57:03 [ℹ]  Kubernetes API endpoint access will use provided values {publicAccess=true, privateAccess=true} for cluster "myeks" in "ap-northeast-2"
2025-02-22 19:57:03 [ℹ]  CloudWatch logging will not be enabled for cluster "myeks" in "ap-northeast-2"
2025-02-22 19:57:03 [ℹ]  you can enable it with 'eksctl utils update-cluster-logging --enable-types={SPECIFY-YOUR-LOG-TYPES-HERE (e.g. all)} --region=ap-northeast-2 --cluster=myeks'
2025-02-22 19:57:03 [ℹ]
2 sequential tasks: { create cluster control plane "myeks",
    2 sequential sub-tasks: {
        5 sequential sub-tasks: {
            1 task: { create addons },
            wait for control plane to become ready,
            associate IAM OIDC provider,
            2 sequential sub-tasks: {
                create IAM role for serviceaccount "kube-system/aws-load-balancer-controller",
                create serviceaccount "kube-system/aws-load-balancer-controller",
            },
            update VPC CNI to use IRSA if required,
        },
        create managed nodegroup "ng1",
    }
}
2025-02-22 19:57:03 [▶]  started task: create cluster control plane "myeks"
2025-02-22 19:57:03 [ℹ]  building cluster stack "eksctl-myeks-cluster"
2025-02-22 19:57:03 [▶]  CreateStackInput = &cloudformation.CreateStackInput{StackName:(*string)(0xc000f06a40), Capabilities:[]types.Capability{"CAPABILITY_IAM"}, ClientRequestToken:(*string)(nil), DisableRollback:(*bool)(0xc000f12b28), EnableTerminationProtection:(*bool)(nil), NotificationARNs:[]string(nil), OnFailure:"", Parameters:[]types.Parameter(nil), ResourceTypes:[]string(nil), RetainExceptOnCreate:(*bool)(nil), RoleARN:(*string)(nil), RollbackConfiguration:(*types.RollbackConfiguration)(nil), StackPolicyBody:(*string)(nil), StackPolicyURL:(*string)(nil), Tags:[]types.Tag{types.Tag{Key:(*string)(0xc000ee4e30), Value:(*string)(0xc000ee4e40), noSmithyDocumentSerde:document.NoSerde{}}, types.Tag{Key:(*string)(0xc000ee4e50), Value:(*string)(0xc000ee4e60), noSmithyDocumentSerde:document.NoSerde{}}, types.Tag{Key:(*string)(0xc000ee4e70), Value:(*string)(0xc000ee4e80), noSmithyDocumentSerde:document.NoSerde{}}, types.Tag{Key:(*string)(0xc000f07920), Value:(*string)(0xc000f07930), noSmithyDocumentSerde:document.NoSerde{}}}, TemplateBody:(*string)(0xc000f07940), TemplateURL:(*string)(nil), TimeoutInMinutes:(*int32)(nil), noSmithyDocumentSerde:document.NoSerde{}}
2025-02-22 19:57:03 [ℹ]  deploying stack "eksctl-myeks-cluster"
2025-02-22 19:57:33 [ℹ]  waiting for CloudFormation stack "eksctl-myeks-cluster"
2025-02-22 19:58:03 [ℹ]  waiting for CloudFormation stack "eksctl-myeks-cluster"
2025-02-22 19:59:03 [ℹ]  waiting for CloudFormation stack "eksctl-myeks-cluster"
2025-02-22 20:00:04 [ℹ]  waiting for CloudFormation stack "eksctl-myeks-cluster"
2025-02-22 20:01:04 [ℹ]  waiting for CloudFormation stack "eksctl-myeks-cluster"
2025-02-22 20:02:04 [ℹ]  waiting for CloudFormation stack "eksctl-myeks-cluster"
2025-02-22 20:03:04 [ℹ]  waiting for CloudFormation stack "eksctl-myeks-cluster"
2025-02-22 20:04:04 [ℹ]  waiting for CloudFormation stack "eksctl-myeks-cluster"
2025-02-22 20:05:04 [ℹ]  waiting for CloudFormation stack "eksctl-myeks-cluster"
2025-02-22 20:06:04 [ℹ]  waiting for CloudFormation stack "eksctl-myeks-cluster"
2025-02-22 20:06:04 [▶]  processing stack outputs
2025-02-22 20:06:04 [▶]  completed task: create cluster control plane "myeks"
2025-02-22 20:06:04 [▶]  started task:
    2 sequential sub-tasks: {
        5 sequential sub-tasks: {
            1 task: { create addons },
            wait for control plane to become ready,
            associate IAM OIDC provider,
            2 sequential sub-tasks: {
                create IAM role for serviceaccount "kube-system/aws-load-balancer-controller",
                create serviceaccount "kube-system/aws-load-balancer-controller",
            },
            update VPC CNI to use IRSA if required,
        },
        create managed nodegroup "ng1",
    }

2025-02-22 20:06:04 [▶]  started task:
    5 sequential sub-tasks: {
        1 task: { create addons },
        wait for control plane to become ready,
        associate IAM OIDC provider,
        2 sequential sub-tasks: {
            create IAM role for serviceaccount "kube-system/aws-load-balancer-controller",
            create serviceaccount "kube-system/aws-load-balancer-controller",
        },
        update VPC CNI to use IRSA if required,
    }

2025-02-22 20:06:04 [▶]  started task: 1 task: { create addons }
2025-02-22 20:06:04 [▶]  started task: create addons
2025-02-22 20:06:04 [▶]  cluster = &types.Cluster{AccessConfig:(*types.AccessConfigResponse)(0xc000571620), Arn:(*string)(0xc000705890), CertificateAuthority:(*types.Certificate)(0xc0007058e0), ClientRequestToken:(*string)(nil), ComputeConfig:(*types.ComputeConfigResponse)(nil), ConnectorConfig:(*types.ConnectorConfigResponse)(nil), CreatedAt:time.Date(2025, time.February, 22, 10, 57, 28, 280000000, time.UTC), EncryptionConfig:[]types.EncryptionConfig(nil), Endpoint:(*string)(0xc000705800), Health:(*types.ClusterHealth)(0xc000571640), Id:(*string)(nil), Identity:(*types.Identity)(0xc0007058b0), KubernetesNetworkConfig:(*types.KubernetesNetworkConfigResponse)(0xc000905470), Logging:(*types.Logging)(0xc0005716e0), Name:(*string)(0xc000705810), OutpostConfig:(*types.OutpostConfigResponse)(nil), PlatformVersion:(*string)(0xc000705900), RemoteNetworkConfig:(*types.RemoteNetworkConfigResponse)(nil), ResourcesVpcConfig:(*types.VpcConfigResponse)(0xc0008100e0), RoleArn:(*string)(0xc0007058a0), Status:"ACTIVE", StorageConfig:(*types.StorageConfigResponse)(nil), Tags:map[string]string{"Name":"eksctl-myeks-cluster/ControlPlane", "alpha.eksctl.io/cluster-name":"myeks", "alpha.eksctl.io/cluster-oidc-enabled":"true", "alpha.eksctl.io/eksctl-version":"0.204.0", "aws:cloudformation:logical-id":"ControlPlane", "aws:cloudformation:stack-id":"arn:aws:cloudformation:ap-northeast-2:390402552337:stack/eksctl-myeks-cluster/bfda6530-f10b-11ef-b7e5-0a7559f07843", "aws:cloudformation:stack-name":"eksctl-myeks-cluster", "eksctl.cluster.k8s.io/v1alpha1/cluster-name":"myeks"}, UpgradePolicy:(*types.UpgradePolicyResponse)(0xc0007d35c0), Version:(*string)(0xc000705820), ZonalShiftConfig:(*types.ZonalShiftConfigResponse)(nil), noSmithyDocumentSerde:document.NoSerde{}}
2025-02-22 20:06:05 [▶]  resolve conflicts set to OVERWRITE
2025-02-22 20:06:05 [▶]  addon: &{vpc-cni v1.19.2-eksbuild.5  [arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy] map[]  {false false false false false false false} map[]  <nil> false enableNetworkPolicy: "true" true [] [] []}
2025-02-22 20:06:06 [!]  IRSA config is set for "vpc-cni" addon, but since OIDC is disabled on the cluster, eksctl cannot configure the requested permissions; the recommended way to provide IAM permissions for "vpc-cni" addon is via pod identity associations; after addon creation is completed, add all recommended policies to the config file, under `addon.PodIdentityAssociations`, and run `eksctl update addon`
2025-02-22 20:06:06 [ℹ]  creating addon: vpc-cni
2025-02-22 20:06:06 [▶]  EKS Create Addon output: {%!s(*string=0xc0005248b0) %!s(*string=0xc0005248c0) %!s(*string=0xc0005248a0) %!s(*string=0xc0005248d0) %!s(*string=0xc000524890) 2025-02-22 11:06:06.513 +0000 UTC %!s(*types.AddonHealth=&{[] {}}) %!s(*types.MarketplaceInformation=<nil>) 2025-02-22 11:06:06.528 +0000 UTC %!s(*string=<nil>) [] %!s(*string=<nil>) %!s(*string=<nil>) CREATING map[] {}}
2025-02-22 20:06:06 [ℹ]  successfully created addon: vpc-cni
2025-02-22 20:06:06 [▶]  resolve conflicts set to OVERWRITE
2025-02-22 20:06:06 [▶]  addon: &{kube-proxy v1.31.3-eksbuild.2  [] map[]  {false false false false false false false} map[]  <nil> false  true [] [] []}
2025-02-22 20:06:06 [ℹ]  creating addon: kube-proxy
2025-02-22 20:06:07 [▶]  EKS Create Addon output: {%!s(*string=0xc0006530f0) %!s(*string=0xc0006530c0) %!s(*string=0xc0006530e0) %!s(*string=0xc0006530d0) %!s(*string=<nil>) 2025-02-22 11:06:07.128 +0000 UTC %!s(*types.AddonHealth=&{[] {}}) %!s(*types.MarketplaceInformation=<nil>) 2025-02-22 11:06:07.142 +0000 UTC %!s(*string=<nil>) [] %!s(*string=<nil>) %!s(*string=<nil>) CREATING map[] {}}
2025-02-22 20:06:07 [ℹ]  successfully created addon: kube-proxy
2025-02-22 20:06:07 [▶]  resolve conflicts set to OVERWRITE
2025-02-22 20:06:07 [▶]  addon: &{coredns v1.11.4-eksbuild.2  [] map[]  {false false false false false false false} map[]  <nil> false  true [] [] []}
2025-02-22 20:06:07 [ℹ]  creating addon: coredns
2025-02-22 20:06:08 [▶]  EKS Create Addon output: {%!s(*string=0xc000a7da60) %!s(*string=0xc000a7da40) %!s(*string=0xc000a7da30) %!s(*string=0xc000a7da50) %!s(*string=<nil>) 2025-02-22 11:06:08.086 +0000 UTC %!s(*types.AddonHealth=&{[] {}}) %!s(*types.MarketplaceInformation=<nil>) 2025-02-22 11:06:08.104 +0000 UTC %!s(*string=<nil>) [] %!s(*string=<nil>) %!s(*string=<nil>) CREATING map[] {}}
2025-02-22 20:06:08 [ℹ]  successfully created addon: coredns
2025-02-22 20:06:08 [▶]  resolve conflicts set to OVERWRITE
2025-02-22 20:06:08 [▶]  addon: &{metrics-server v0.7.2-eksbuild.2  [] map[]  {false false false false false false false} map[]  <nil> false  true [] [] []}
2025-02-22 20:06:08 [ℹ]  creating addon: metrics-server
2025-02-22 20:06:08 [▶]  EKS Create Addon output: {%!s(*string=0xc000db5050) %!s(*string=0xc000db5040) %!s(*string=0xc000db5030) %!s(*string=0xc000db5020) %!s(*string=<nil>) 2025-02-22 11:06:08.682 +0000 UTC %!s(*types.AddonHealth=&{[] {}}) %!s(*types.MarketplaceInformation=<nil>) 2025-02-22 11:06:08.698 +0000 UTC %!s(*string=<nil>) [] %!s(*string=<nil>) %!s(*string=<nil>) CREATING map[] {}}
2025-02-22 20:06:08 [ℹ]  successfully created addon: metrics-server
2025-02-22 20:06:08 [▶]  completed task: create addons
2025-02-22 20:06:08 [▶]  completed task: 1 task: { create addons }
2025-02-22 20:06:08 [▶]  started task: wait for control plane to become ready
2025-02-22 20:08:09 [▶]  cluster = &types.Cluster{AccessConfig:(*types.AccessConfigResponse)(0xc000bf95c0), Arn:(*string)(0xc000ee5050), CertificateAuthority:(*types.Certificate)(0xc000ee4fc0), ClientRequestToken:(*string)(nil), ComputeConfig:(*types.ComputeConfigResponse)(nil), ConnectorConfig:(*types.ConnectorConfigResponse)(nil), CreatedAt:time.Date(2025, time.February, 22, 10, 57, 28, 280000000, time.UTC), EncryptionConfig:[]types.EncryptionConfig(nil), Endpoint:(*string)(0xc000ee4f90), Health:(*types.ClusterHealth)(0xc000bf9600), Id:(*string)(nil), Identity:(*types.Identity)(0xc000ee5020), KubernetesNetworkConfig:(*types.KubernetesNetworkConfigResponse)(0xc000f3fd10), Logging:(*types.Logging)(0xc000bf9620), Name:(*string)(0xc000ee4eb0), OutpostConfig:(*types.OutpostConfigResponse)(nil), PlatformVersion:(*string)(0xc000ee4fe0), RemoteNetworkConfig:(*types.RemoteNetworkConfigResponse)(nil), ResourcesVpcConfig:(*types.VpcConfigResponse)(0xc0004a3ea0), RoleArn:(*string)(0xc000ee4ff0), Status:"ACTIVE", StorageConfig:(*types.StorageConfigResponse)(nil), Tags:map[string]string{"Name":"eksctl-myeks-cluster/ControlPlane", "alpha.eksctl.io/cluster-name":"myeks", "alpha.eksctl.io/cluster-oidc-enabled":"true", "alpha.eksctl.io/eksctl-version":"0.204.0", "aws:cloudformation:logical-id":"ControlPlane", "aws:cloudformation:stack-id":"arn:aws:cloudformation:ap-northeast-2:390402552337:stack/eksctl-myeks-cluster/bfda6530-f10b-11ef-b7e5-0a7559f07843", "aws:cloudformation:stack-name":"eksctl-myeks-cluster", "eksctl.cluster.k8s.io/v1alpha1/cluster-name":"myeks"}, UpgradePolicy:(*types.UpgradePolicyResponse)(0xc000ecca68), Version:(*string)(0xc000ee4ec0), ZonalShiftConfig:(*types.ZonalShiftConfigResponse)(nil), noSmithyDocumentSerde:document.NoSerde{}}
2025-02-22 20:08:09 [▶]  completed task: wait for control plane to become ready
2025-02-22 20:08:09 [▶]  started task: associate IAM OIDC provider
2025-02-22 20:08:09 [▶]  completed task: associate IAM OIDC provider
2025-02-22 20:08:09 [▶]  started task:
    2 sequential sub-tasks: {
        create IAM role for serviceaccount "kube-system/aws-load-balancer-controller",
        create serviceaccount "kube-system/aws-load-balancer-controller",
    }
2025-02-22 20:08:09 [▶]  waiting for 1 parallel tasks to complete
2025-02-22 20:08:09 [▶]  started task:
    2 sequential sub-tasks: {
        create IAM role for serviceaccount "kube-system/aws-load-balancer-controller",
        create serviceaccount "kube-system/aws-load-balancer-controller",
    }

2025-02-22 20:08:09 [▶]  started task: create IAM role for serviceaccount "kube-system/aws-load-balancer-controller"
2025-02-22 20:08:09 [ℹ]  building iamserviceaccount stack "eksctl-myeks-addon-iamserviceaccount-kube-system-aws-load-balancer-controller"
2025-02-22 20:08:09 [▶]  service account location provided: kube-system/aws-node, adding sub condition
2025-02-22 20:08:09 [▶]  CreateStackInput = &cloudformation.CreateStackInput{StackName:(*string)(0xc000ac58e0), Capabilities:[]types.Capability{"CAPABILITY_IAM"}, ClientRequestToken:(*string)(nil), DisableRollback:(*bool)(0xc000f13cc4), EnableTerminationProtection:(*bool)(nil), NotificationARNs:[]string(nil), OnFailure:"", Parameters:[]types.Parameter(nil), ResourceTypes:[]string(nil), RetainExceptOnCreate:(*bool)(nil), RoleARN:(*string)(nil), RollbackConfiguration:(*types.RollbackConfiguration)(nil), StackPolicyBody:(*string)(nil), StackPolicyURL:(*string)(nil), Tags:[]types.Tag{types.Tag{Key:(*string)(0xc000ee54b0), Value:(*string)(0xc000ee54c0), noSmithyDocumentSerde:document.NoSerde{}}, types.Tag{Key:(*string)(0xc000ee54d0), Value:(*string)(0xc000ee54e0), noSmithyDocumentSerde:document.NoSerde{}}, types.Tag{Key:(*string)(0xc000ee54f0), Value:(*string)(0xc000ee5500), noSmithyDocumentSerde:document.NoSerde{}}, types.Tag{Key:(*string)(0xc000f40890), Value:(*string)(0xc000f408a0), noSmithyDocumentSerde:document.NoSerde{}}}, TemplateBody:(*string)(0xc000f408b0), TemplateURL:(*string)(nil), TimeoutInMinutes:(*int32)(nil), noSmithyDocumentSerde:document.NoSerde{}}
2025-02-22 20:08:10 [ℹ]  deploying stack "eksctl-myeks-addon-iamserviceaccount-kube-system-aws-load-balancer-controller"
2025-02-22 20:08:10 [ℹ]  waiting for CloudFormation stack "eksctl-myeks-addon-iamserviceaccount-kube-system-aws-load-balancer-controller"
2025-02-22 20:08:40 [ℹ]  waiting for CloudFormation stack "eksctl-myeks-addon-iamserviceaccount-kube-system-aws-load-balancer-controller"
2025-02-22 20:09:10 [ℹ]  waiting for CloudFormation stack "eksctl-myeks-addon-iamserviceaccount-kube-system-aws-load-balancer-controller"
2025-02-22 20:09:10 [▶]  completed task: create IAM role for serviceaccount "kube-system/aws-load-balancer-controller"
2025-02-22 20:09:10 [▶]  started task: create serviceaccount "kube-system/aws-load-balancer-controller"
2025-02-22 20:09:10 [ℹ]  created serviceaccount "kube-system/aws-load-balancer-controller"
2025-02-22 20:09:10 [▶]  completed task: create serviceaccount "kube-system/aws-load-balancer-controller"
2025-02-22 20:09:10 [▶]  completed task:
    2 sequential sub-tasks: {
        create IAM role for serviceaccount "kube-system/aws-load-balancer-controller",
        create serviceaccount "kube-system/aws-load-balancer-controller",
    }

2025-02-22 20:09:10 [▶]  completed task:
    2 sequential sub-tasks: {
        create IAM role for serviceaccount "kube-system/aws-load-balancer-controller",
        create serviceaccount "kube-system/aws-load-balancer-controller",
    }
2025-02-22 20:09:10 [▶]  started task: update VPC CNI to use IRSA if required
2025-02-22 20:09:11 [ℹ]  addon "vpc-cni" active
2025-02-22 20:09:11 [▶]  addon: &{vpc-cni v1.19.2-eksbuild.5  [arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy] map[]  {false false false false false false false} map[]  <nil> false enableNetworkPolicy: "true" true [] [] []}
2025-02-22 20:09:11 [▶]  resolve conflicts set to OVERWRITE
2025-02-22 20:09:11 [▶]  addon: &{vpc-cni v1.19.2-eksbuild.5  [arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy] map[]  {false false false false false false false} map[]  <nil> false enableNetworkPolicy: "true" true [] [] []}
2025-02-22 20:09:11 [▶]  found known service account location kube-system/aws-node
2025-02-22 20:09:11 [▶]  service account location provided: kube-system/aws-node, adding sub condition
2025-02-22 20:09:11 [▶]  CreateStackInput = &cloudformation.CreateStackInput{StackName:(*string)(0xc00007f660), Capabilities:[]types.Capability{"CAPABILITY_IAM"}, ClientRequestToken:(*string)(nil), DisableRollback:(*bool)(0xc000f13d78), EnableTerminationProtection:(*bool)(nil), NotificationARNs:[]string(nil), OnFailure:"", Parameters:[]types.Parameter(nil), ResourceTypes:[]string(nil), RetainExceptOnCreate:(*bool)(nil), RoleARN:(*string)(nil), RollbackConfiguration:(*types.RollbackConfiguration)(nil), StackPolicyBody:(*string)(nil), StackPolicyURL:(*string)(nil), Tags:[]types.Tag{types.Tag{Key:(*string)(0xc000f40240), Value:(*string)(0xc000f40250), noSmithyDocumentSerde:document.NoSerde{}}, types.Tag{Key:(*string)(0xc000f40260), Value:(*string)(0xc000f40270), noSmithyDocumentSerde:document.NoSerde{}}, types.Tag{Key:(*string)(0xc000f40280), Value:(*string)(0xc000f40290), noSmithyDocumentSerde:document.NoSerde{}}, types.Tag{Key:(*string)(0xc00007fd90), Value:(*string)(0xc00007fda0), noSmithyDocumentSerde:document.NoSerde{}}}, TemplateBody:(*string)(0xc00031ab80), TemplateURL:(*string)(nil), TimeoutInMinutes:(*int32)(nil), noSmithyDocumentSerde:document.NoSerde{}}
2025-02-22 20:09:11 [ℹ]  deploying stack "eksctl-myeks-addon-vpc-cni"
2025-02-22 20:09:11 [ℹ]  waiting for CloudFormation stack "eksctl-myeks-addon-vpc-cni"
2025-02-22 20:09:41 [ℹ]  waiting for CloudFormation stack "eksctl-myeks-addon-vpc-cni"
2025-02-22 20:09:41 [ℹ]  updating addon
2025-02-22 20:09:41 [▶]  &{AddonName:0xc0009e6960 ClusterName:0xc000a5c910 AddonVersion:0xc000f41f60 ClientRequestToken:<nil> ConfigurationValues:0xc0009e69f0 PodIdentityAssociations:[] ResolveConflicts:OVERWRITE ServiceAccountRoleArn:0xc0004b3160 noSmithyDocumentSerde:{}}
2025-02-22 20:09:42 [▶]  &{CreatedAt:2025-02-22 11:09:42.606 +0000 UTC Errors:[] Id:0xc00058e530 Params:[{Type:AddonVersion Value:0xc00058e4d0 noSmithyDocumentSerde:{}} {Type:ServiceAccountRoleArn Value:0xc00058e4e0 noSmithyDocumentSerde:{}} {Type:ResolveConflicts Value:0xc00058e500 noSmithyDocumentSerde:{}} {Type:ConfigurationValues Value:0xc00058e520 noSmithyDocumentSerde:{}}] Status:InProgress Type:AddonUpdate noSmithyDocumentSerde:{}}
2025-02-22 20:09:53 [ℹ]  addon "vpc-cni" active
2025-02-22 20:09:53 [▶]  completed task: update VPC CNI to use IRSA if required
2025-02-22 20:09:53 [▶]  completed task:
    5 sequential sub-tasks: {
        1 task: { create addons },
        wait for control plane to become ready,
        associate IAM OIDC provider,
        2 sequential sub-tasks: {
            create IAM role for serviceaccount "kube-system/aws-load-balancer-controller",
            create serviceaccount "kube-system/aws-load-balancer-controller",
        },
        update VPC CNI to use IRSA if required,
    }

2025-02-22 20:09:53 [▶]  started task: create managed nodegroup "ng1"
2025-02-22 20:09:53 [▶]  waiting for 1 parallel tasks to complete
2025-02-22 20:09:53 [▶]  started task: create managed nodegroup "ng1"
2025-02-22 20:09:53 [▶]  started task: create managed nodegroup "ng1"
2025-02-22 20:09:53 [▶]  started task: create managed nodegroup "ng1"
2025-02-22 20:09:53 [ℹ]  building managed nodegroup stack "eksctl-myeks-nodegroup-ng1"
2025-02-22 20:09:53 [▶]  CreateStackInput = &cloudformation.CreateStackInput{StackName:(*string)(0xc000db4ab0), Capabilities:[]types.Capability{"CAPABILITY_IAM"}, ClientRequestToken:(*string)(nil), DisableRollback:(*bool)(0xc00097c5d8), EnableTerminationProtection:(*bool)(nil), NotificationARNs:[]string(nil), OnFailure:"", Parameters:[]types.Parameter(nil), ResourceTypes:[]string(nil), RetainExceptOnCreate:(*bool)(nil), RoleARN:(*string)(nil), RollbackConfiguration:(*types.RollbackConfiguration)(nil), StackPolicyBody:(*string)(nil), StackPolicyURL:(*string)(nil), Tags:[]types.Tag{types.Tag{Key:(*string)(0xc000ee4e30), Value:(*string)(0xc000ee4e40), noSmithyDocumentSerde:document.NoSerde{}}, types.Tag{Key:(*string)(0xc000ee4e50), Value:(*string)(0xc000ee4e60), noSmithyDocumentSerde:document.NoSerde{}}, types.Tag{Key:(*string)(0xc000ee4e70), Value:(*string)(0xc000ee4e80), noSmithyDocumentSerde:document.NoSerde{}}, types.Tag{Key:(*string)(0xc000e8c0d0), Value:(*string)(0xc000e8c0e0), noSmithyDocumentSerde:document.NoSerde{}}, types.Tag{Key:(*string)(0xc000e8c0f0), Value:(*string)(0xc000e8c100), noSmithyDocumentSerde:document.NoSerde{}}}, TemplateBody:(*string)(0xc000e8c110), TemplateURL:(*string)(nil), TimeoutInMinutes:(*int32)(nil), noSmithyDocumentSerde:document.NoSerde{}}
2025-02-22 20:09:53 [ℹ]  deploying stack "eksctl-myeks-nodegroup-ng1"
2025-02-22 20:09:53 [ℹ]  waiting for CloudFormation stack "eksctl-myeks-nodegroup-ng1"
2025-02-22 20:10:23 [ℹ]  waiting for CloudFormation stack "eksctl-myeks-nodegroup-ng1"
2025-02-22 20:11:01 [ℹ]  waiting for CloudFormation stack "eksctl-myeks-nodegroup-ng1"
2025-02-22 20:12:10 [ℹ]  waiting for CloudFormation stack "eksctl-myeks-nodegroup-ng1"
2025-02-22 20:12:52 [ℹ]  waiting for CloudFormation stack "eksctl-myeks-nodegroup-ng1"
2025-02-22 20:12:52 [▶]  processing stack outputs
2025-02-22 20:12:52 [▶]  completed task: create managed nodegroup "ng1"
2025-02-22 20:12:52 [▶]  completed task: create managed nodegroup "ng1"
2025-02-22 20:12:52 [▶]  completed task: create managed nodegroup "ng1"
2025-02-22 20:12:52 [▶]  completed task: create managed nodegroup "ng1"
2025-02-22 20:12:52 [▶]  completed task:
    2 sequential sub-tasks: {
        5 sequential sub-tasks: {
            1 task: { create addons },
            wait for control plane to become ready,
            associate IAM OIDC provider,
            2 sequential sub-tasks: {
                create IAM role for serviceaccount "kube-system/aws-load-balancer-controller",
                create serviceaccount "kube-system/aws-load-balancer-controller",
            },
            update VPC CNI to use IRSA if required,
        },
        create managed nodegroup "ng1",
    }

2025-02-22 20:12:52 [ℹ]  waiting for the control plane to become ready
2025-02-22 20:12:53 [▶]  merging kubeconfig files
2025-02-22 20:12:53 [▶]  setting current-context to iam-root-account@myeks.ap-northeast-2.eksctl.io
2025-02-22 20:12:53 [✔]  saved kubeconfig as "/root/.kube/config"
2025-02-22 20:12:53 [ℹ]  1 task: { install Nvidia device plugin }
2025-02-22 20:12:53 [▶]  waiting for 1 parallel tasks to complete
2025-02-22 20:12:53 [▶]  started task: install Nvidia device plugin
2025-02-22 20:12:53 [ℹ]  created "kube-system:DaemonSet.apps/nvidia-device-plugin-daemonset"
2025-02-22 20:12:53 [ℹ]  as you are using the EKS-Optimized Accelerated AMI with a GPU-enabled instance type, the Nvidia Kubernetes device plugin was automatically installed.
        to skip installing it, use --install-nvidia-plugin=false.
2025-02-22 20:12:53 [▶]  completed task: install Nvidia device plugin
2025-02-22 20:12:53 [✔]  all EKS cluster resources for "myeks" have been created
2025-02-22 20:12:53 [ℹ]  nodegroup "ng1" has 3 node(s)
2025-02-22 20:12:53 [ℹ]  node "ip-192-168-1-193.ap-northeast-2.compute.internal" is ready
2025-02-22 20:12:53 [ℹ]  node "ip-192-168-2-200.ap-northeast-2.compute.internal" is ready
2025-02-22 20:12:53 [ℹ]  node "ip-192-168-3-188.ap-northeast-2.compute.internal" is ready
2025-02-22 20:12:53 [ℹ]  waiting for at least 3 node(s) to become ready in "ng1"
2025-02-22 20:12:53 [▶]  event = watch.Event{Type:"ADDED", Object:(*v1.Node)(0xc0007d7508)}
2025-02-22 20:12:53 [▶]  node "ip-192-168-1-193.ap-northeast-2.compute.internal" is ready in "ng1"
2025-02-22 20:12:53 [▶]  event = watch.Event{Type:"ADDED", Object:(*v1.Node)(0xc0007d7b08)}
2025-02-22 20:12:53 [▶]  node "ip-192-168-2-200.ap-northeast-2.compute.internal" is ready in "ng1"
2025-02-22 20:12:53 [▶]  event = watch.Event{Type:"ADDED", Object:(*v1.Node)(0xc000cd6008)}
2025-02-22 20:12:53 [▶]  node "ip-192-168-3-188.ap-northeast-2.compute.internal" is ready in "ng1"
2025-02-22 20:12:53 [ℹ]  nodegroup "ng1" has 3 node(s)
2025-02-22 20:12:53 [ℹ]  node "ip-192-168-1-193.ap-northeast-2.compute.internal" is ready
2025-02-22 20:12:53 [ℹ]  node "ip-192-168-2-200.ap-northeast-2.compute.internal" is ready
2025-02-22 20:12:53 [ℹ]  node "ip-192-168-3-188.ap-northeast-2.compute.internal" is ready
2025-02-22 20:12:53 [✔]  created 1 managed nodegroup(s) in cluster "myeks"
2025-02-22 20:12:53 [▶]  found authenticator: aws
2025-02-22 20:12:53 [▶]  kubectl: "/usr/local/bin/kubectl"
2025-02-22 20:12:53 [▶]  kubectl version: v1.31.2-eks-94953ac
2025-02-22 20:12:54 [ℹ]  kubectl command should work with "/root/.kube/config", try 'kubectl get nodes'
2025-02-22 20:12:54 [✔]  EKS cluster "myeks" in "ap-northeast-2" region is ready
2025-02-22 20:12:54 [▶]  cfg.json = \
{
    "kind": "ClusterConfig",
    "apiVersion": "eksctl.io/v1alpha5",
    "metadata": {
        "name": "myeks",
        "region": "ap-northeast-2",
        "version": "1.31"
    },
    "kubernetesNetworkConfig": {
        "ipFamily": "IPv4",
        "serviceIPv4CIDR": "10.100.0.0/16"
    },
    "iam": {
        "serviceRoleARN": "arn:aws:iam::390402552337:role/eksctl-myeks-cluster-ServiceRole-yzipC6igslPD",
        "withOIDC": true,
        "serviceAccounts": [
            {
                "metadata": {
                    "name": "aws-load-balancer-controller",
                    "namespace": "kube-system",
                    "annotations": {
                        "eks.amazonaws.com/role-arn": "arn:aws:iam::390402552337:role/eksctl-myeks-addon-iamserviceaccount-kube-sys-Role1-zzbfj7a4hBaW"
                    }
                },
                "wellKnownPolicies": {
                    "imageBuilder": false,
                    "autoScaler": false,
                    "awsLoadBalancerController": true,
                    "externalDNS": false,
                    "certManager": false,
                    "ebsCSIController": false,
                    "efsCSIController": false
                },
                "status": {
                    "roleARN": "arn:aws:iam::390402552337:role/eksctl-myeks-addon-iamserviceaccount-kube-sys-Role1-zzbfj7a4hBaW"
                },
                "tags": {
                    "alpha.eksctl.io/iamserviceaccount-name": "kube-system/aws-load-balancer-controller"
                }
            }
        ],
        "vpcResourceControllerPolicy": true
    },
    "accessConfig": {
        "authenticationMode": "API_AND_CONFIG_MAP"
    },
    "vpc": {
        "id": "vpc-040caab837037a18c",
        "cidr": "192.168.0.0/16",
        "securityGroup": "sg-0c3354bd19a8894b4",
        "subnets": {
            "public": {
                "ap-northeast-2a": {
                    "id": "subnet-08b83e58f1cad6098",
                    "az": "ap-northeast-2a",
                    "cidr": "192.168.1.0/24"
                },
                "ap-northeast-2b": {
                    "id": "subnet-0ad3d1ce95ad8a965",
                    "az": "ap-northeast-2b",
                    "cidr": "192.168.2.0/24"
                },
                "ap-northeast-2c": {
                    "id": "subnet-0f643cf03e72dc812",
                    "az": "ap-northeast-2c",
                    "cidr": "192.168.3.0/24"
                }
            }
        },
        "sharedNodeSecurityGroup": "sg-044ca4465e589b793",
        "manageSharedNodeSecurityGroupRules": true,
        "nat": {
            "gateway": "Single"
        },
        "clusterEndpoints": {
            "privateAccess": true,
            "publicAccess": true
        }
    },
    "addons": [
        {
            "name": "vpc-cni",
            "version": "v1.19.2-eksbuild.5",
            "attachPolicyARNs": [
                "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy"
            ],
            "wellKnownPolicies": {
                "imageBuilder": false,
                "autoScaler": false,
                "awsLoadBalancerController": false,
                "externalDNS": false,
                "certManager": false,
                "ebsCSIController": false,
                "efsCSIController": false
            },
            "configurationValues": "enableNetworkPolicy: \"true\""
        },
        {
            "name": "kube-proxy",
            "version": "v1.31.3-eksbuild.2",
            "wellKnownPolicies": {
                "imageBuilder": false,
                "autoScaler": false,
                "awsLoadBalancerController": false,
                "externalDNS": false,
                "certManager": false,
                "ebsCSIController": false,
                "efsCSIController": false
            }
        },
        {
            "name": "coredns",
            "version": "v1.11.4-eksbuild.2",
            "wellKnownPolicies": {
                "imageBuilder": false,
                "autoScaler": false,
                "awsLoadBalancerController": false,
                "externalDNS": false,
                "certManager": false,
                "ebsCSIController": false,
                "efsCSIController": false
            }
        },
        {
            "name": "metrics-server",
            "version": "v0.7.2-eksbuild.2",
            "wellKnownPolicies": {
                "imageBuilder": false,
                "autoScaler": false,
                "awsLoadBalancerController": false,
                "externalDNS": false,
                "certManager": false,
                "ebsCSIController": false,
                "efsCSIController": false
            }
        }
    ],
    "addonsConfig": {},
    "privateCluster": {
        "enabled": false,
        "skipEndpointCreation": false
    },
    "managedNodeGroups": [
        {
            "name": "ng1",
            "amiFamily": "AmazonLinux2023",
            "instanceType": "t3.medium",
            "desiredCapacity": 3,
            "minSize": 3,
            "maxSize": 3,
            "volumeSize": 120,
            "ssh": {
                "allow": true,
                "publicKeyName": "aews-yu3papa"
            },
            "labels": {
                "alpha.eksctl.io/cluster-name": "myeks",
                "alpha.eksctl.io/nodegroup-name": "ng1"
            },
            "privateNetworking": false,
            "tags": {
                "alpha.eksctl.io/nodegroup-name": "ng1",
                "alpha.eksctl.io/nodegroup-type": "managed"
            },
            "iam": {
                "withAddonPolicies": {
                    "imageBuilder": false,
                    "autoScaler": false,
                    "externalDNS": true,
                    "certManager": true,
                    "appMesh": null,
                    "appMeshPreview": null,
                    "ebs": false,
                    "fsx": false,
                    "efs": false,
                    "awsLoadBalancerController": false,
                    "albIngress": false,
                    "xRay": false,
                    "cloudWatch": false
                }
            },
            "securityGroups": {
                "withShared": null,
                "withLocal": null
            },
            "maxPodsPerNode": 100,
            "volumeType": "gp3",
            "volumeIOPS": 3000,
            "volumeThroughput": 125,
            "preBootstrapCommands": [
                "dnf install nvme-cli links tree tcpdump sysstat ipvsadm ipset bind-utils htop -y"
            ],
            "disableIMDSv1": true,
            "disablePodIMDS": false,
            "instanceSelector": {},
            "releaseVersion": ""
        }
    ],
    "availabilityZones": [
        "ap-northeast-2a",
        "ap-northeast-2c",
        "ap-northeast-2b"
    ]
}

 

EKS 배보후 기본정보를 확인해 보겠습니다.

[root@operator-host ~]# kubectl cluster-info
Kubernetes control plane is running at https://67FF602331201F8B161177C2E32D332A.gr7.ap-northeast-2.eks.amazonaws.com
CoreDNS is running at https://67FF602331201F8B161177C2E32D332A.gr7.ap-northeast-2.eks.amazonaws.com/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

# 네임스페이스 default 변경 적용
[root@operator-host ~]# kubectl config set-context --current --namespace=default

# eksworkshop 으로 context  변경
[root@operator-host ~]# kubectl config rename-context "iam-root-account@myeks.ap-northeast-2.eksctl.io" "eksworkshop"
Context "iam-root-account@myeks.ap-northeast-2.eksctl.io" renamed to "eksworkshop".
(eksworkshop:default) [root@operator-host ~]#

# 노드 정보 확인
[root@operator-host ~]# kubectl get node --label-columns=node.kubernetes.io/instance-type,eks.amazonaws.com/capacityType,topology.kubernetes.io/zone
NAME                                               STATUS   ROLES    AGE     VERSION               INSTANCE-TYPE   CAPACITYTYPE   ZONE
ip-192-168-1-193.ap-northeast-2.compute.internal   Ready    <none>   9m34s   v1.31.5-eks-5d632ec   t3.medium       ON_DEMAND      ap-northeast-2a
ip-192-168-2-200.ap-northeast-2.compute.internal   Ready    <none>   9m41s   v1.31.5-eks-5d632ec   t3.medium       ON_DEMAND      ap-northeast-2b
ip-192-168-3-188.ap-northeast-2.compute.internal   Ready    <none>   9m38s   v1.31.5-eks-5d632ec   t3.medium       ON_DEMAND      ap-northeast-2c
[root@operator-host ~]# kubectl get node -v=6
I0222 20:21:28.872244    3254 loader.go:395] Config loaded from file:  /root/.kube/config
I0222 20:21:29.672931    3254 round_trippers.go:553] GET https://67FF602331201F8B161177C2E32D332A.gr7.ap-northeast-2.eks.amazonaws.com/api/v1/nodes?limit=500 200 OK in 794 milliseconds
NAME                                               STATUS   ROLES    AGE     VERSION
ip-192-168-1-193.ap-northeast-2.compute.internal   Ready    <none>   9m42s   v1.31.5-eks-5d632ec
ip-192-168-2-200.ap-northeast-2.compute.internal   Ready    <none>   9m49s   v1.31.5-eks-5d632ec
ip-192-168-3-188.ap-northeast-2.compute.internal   Ready    <none>   9m46s   v1.31.5-eks-5d632ec

# POD 정보 확인
[root@operator-host ~]# kubectl get pod -A
NAMESPACE     NAME                                   READY   STATUS    RESTARTS   AGE
kube-system   aws-node-bblcc                         2/2     Running   0          10m
kube-system   aws-node-lhw2m                         2/2     Running   0          11m
kube-system   aws-node-x24kb                         2/2     Running   0          11m
kube-system   coredns-86f5954566-8n62v               1/1     Running   0          16m
kube-system   coredns-86f5954566-bv27z               1/1     Running   0          16m
kube-system   kube-proxy-gtcvw                       1/1     Running   0          10m
kube-system   kube-proxy-rw2fb                       1/1     Running   0          11m
kube-system   kube-proxy-vrngn                       1/1     Running   0          11m
kube-system   metrics-server-6bf5998d9c-bqzjj        1/1     Running   0          16m
kube-system   metrics-server-6bf5998d9c-w2x7t        1/1     Running   0          16m
kube-system   nvidia-device-plugin-daemonset-7wpg4   1/1     Running   0          9m51s
kube-system   nvidia-device-plugin-daemonset-bc7bf   1/1     Running   0          9m51s
kube-system   nvidia-device-plugin-daemonset-s4v47   1/1     Running   0          9m51s

[root@operator-host ~]# kubectl get pod -n kube-system
NAME                                   READY   STATUS    RESTARTS   AGE
aws-node-bblcc                         2/2     Running   0          11m
aws-node-lhw2m                         2/2     Running   0          11m
aws-node-x24kb                         2/2     Running   0          11m
coredns-86f5954566-8n62v               1/1     Running   0          16m
coredns-86f5954566-bv27z               1/1     Running   0          16m
kube-proxy-gtcvw                       1/1     Running   0          11m
kube-proxy-rw2fb                       1/1     Running   0          11m
kube-proxy-vrngn                       1/1     Running   0          11m
metrics-server-6bf5998d9c-bqzjj        1/1     Running   0          16m
metrics-server-6bf5998d9c-w2x7t        1/1     Running   0          16m
nvidia-device-plugin-daemonset-7wpg4   1/1     Running   0          10m
nvidia-device-plugin-daemonset-bc7bf   1/1     Running   0          10m
nvidia-device-plugin-daemonset-s4v47   1/1     Running   0          10m

# 관리형 노드 그룹 확인
[root@operator-host ~]# eksctl get nodegroup --cluster $CLUSTER_NAME
CLUSTER NODEGROUP       STATUS  CREATED                 MIN SIZE        MAX SIZE        DESIRED CAPACITY        INSTANCE TYPE   IMAGE ID              ASG NAME                                        TYPE
myeks   ng1             ACTIVE  2025-02-22T11:10:19Z    3               3               3                       t3.medium       AL2023_x86_64_STANDARD        eks-ng1-26ca96ac-e8f7-d4d5-ab4d-a806e337b5ec    managed

[root@operator-host ~]# aws eks describe-nodegroup --cluster-name $CLUSTER_NAME --nodegroup-name ng1 | jq
{
  "nodegroup": {
    "nodegroupName": "ng1",
    "nodegroupArn": "arn:aws:eks:ap-northeast-2:390402552337:nodegroup/myeks/ng1/26ca96ac-e8f7-d4d5-ab4d-a806e337b5ec",
    "clusterName": "myeks",
    "version": "1.31",
    "releaseVersion": "1.31.5-20250212",
    "createdAt": "2025-02-22T20:10:19.037000+09:00",
    "modifiedAt": "2025-02-22T20:21:24.169000+09:00",
    "status": "ACTIVE",
    "capacityType": "ON_DEMAND",
    "scalingConfig": {
      "minSize": 3,
      "maxSize": 3,
      "desiredSize": 3
    },
    "instanceTypes": [
      "t3.medium"
    ],
    "subnets": [
      "subnet-08b83e58f1cad6098",
      "subnet-0ad3d1ce95ad8a965",
      "subnet-0f643cf03e72dc812"
    ],
    "amiType": "AL2023_x86_64_STANDARD",
    "nodeRole": "arn:aws:iam::390402552337:role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-1etJJTz6hRou",
    "labels": {
      "alpha.eksctl.io/cluster-name": "myeks",
      "alpha.eksctl.io/nodegroup-name": "ng1"
    },
    "resources": {
      "autoScalingGroups": [
        {
          "name": "eks-ng1-26ca96ac-e8f7-d4d5-ab4d-a806e337b5ec"
        }
      ]
    },
    "health": {
      "issues": []
    },
    "updateConfig": {
      "maxUnavailable": 1
    },
    "launchTemplate": {
      "name": "eksctl-myeks-nodegroup-ng1",
      "version": "1",
      "id": "lt-039071ae3f622f1c0"
    },
    "tags": {
      "aws:cloudformation:stack-name": "eksctl-myeks-nodegroup-ng1",
      "alpha.eksctl.io/cluster-name": "myeks",
      "alpha.eksctl.io/nodegroup-name": "ng1",
      "aws:cloudformation:stack-id": "arn:aws:cloudformation:ap-northeast-2:390402552337:stack/eksctl-myeks-nodegroup-ng1/8a7d96d0-f10d-11ef-876d-06d9644ecb71",
      "eksctl.cluster.k8s.io/v1alpha1/cluster-name": "myeks",
      "aws:cloudformation:logical-id": "ManagedNodeGroup",
      "alpha.eksctl.io/nodegroup-type": "managed",
      "alpha.eksctl.io/eksctl-version": "0.204.0"
    }
  }
}

# eks addon 확인
[root@operator-host ~]# eksctl get addon --cluster $CLUSTER_NAME
2025-02-22 20:30:34 [ℹ]  Kubernetes version "1.31" in use by cluster "myeks"
2025-02-22 20:30:34 [ℹ]  getting all addons
2025-02-22 20:30:35 [ℹ]  to see issues for an addon run `eksctl get addon --name <addon-name> --cluster <cluster-name>`
NAME            VERSION                 STATUS  ISSUES  IAMROLE                                                                      UPDATE AVAILABLE CONFIGURATION VALUES            POD IDENTITY ASSOCIATION ROLES
coredns         v1.11.4-eksbuild.2      ACTIVE  0
kube-proxy      v1.31.3-eksbuild.2      ACTIVE  0
metrics-server  v0.7.2-eksbuild.2       ACTIVE  0
vpc-cni         v1.19.2-eksbuild.5      ACTIVE  0       arn:aws:iam::390402552337:role/eksctl-myeks-addon-vpc-cni-Role1-fr0GM10fLspD enableNetworkPolicy: "true"

# aws-load-balancer-controller를 위한 iam service account 생성 확인 : AWS IAM role bound to a Kubernetes service account
[root@operator-host ~]# eksctl get iamserviceaccount --cluster $CLUSTER_NAME
NAMESPACE       NAME                            ROLE ARN
kube-system     aws-load-balancer-controller    arn:aws:iam::390402552337:role/eksctl-myeks-addon-iamserviceaccount-kube-sys-Role1-zzbfj7a4hBaW


 

EKS 워커노드에 할당된 IAM Role 확인

 

관리형 노드 그룹(EC2) 접속 및 노드 정보 확인 : max-pods

 

# 인스턴스 정보 확인 1
[root@operator-host ~]# aws ec2 describe-instances --query "Reservations[*].Instances[*].{InstanceID:InstanceId, PublicIPAdd:PublicIpAddress, PrivateIPAdd:PrivateIpAddress, InstanceName:Tags[?Key=='Name']|[0].Value, Status:State.Name}" --filters Name=instance-state-name,Values=running --output table
-----------------------------------------------------------------------------------------
|                                   DescribeInstances                                   |
+----------------------+-----------------+----------------+-----------------+-----------+
|      InstanceID      |  InstanceName   | PrivateIPAdd   |   PublicIPAdd   |  Status   |
+----------------------+-----------------+----------------+-----------------+-----------+
|  i-0fabeb5dd693c0671 |  myeks-ng1-Node |  192.168.3.188 |  3.36.132.80    |  running  |
|  i-03d19e4196f4c68aa |  operator-host  |  172.20.1.100  |  15.164.244.197 |  running  |
|  i-02c24086e5ef7bd2f |  myeks-ng1-Node |  192.168.1.193 |  3.36.125.37    |  running  |
|  i-064da1db46bbd4d0e |  myeks-ng1-Node |  192.168.2.200 |  43.203.149.39  |  running  |
+----------------------+-----------------+----------------+-----------------+-----------+


# 인스턴스 정보 확인 2 : AZ, ID, 공인IP
[root@operator-host ~]# aws ec2 describe-instances \
     --filters "Name=tag:Name,Values=myeks-ng1-Node" \
     --query "Reservations[].Instances[].{InstanceID:InstanceId, PublicIP:PublicIpAddress, AZ:Placement.AvailabilityZone}" \
     --output table
-------------------------------------------------------------
|                     DescribeInstances                     |
+-----------------+-----------------------+-----------------+
|       AZ        |      InstanceID       |    PublicIP     |
+-----------------+-----------------------+-----------------+
|  ap-northeast-2c|  i-0fabeb5dd693c0671  |  3.36.132.80    |
|  ap-northeast-2a|  i-02c24086e5ef7bd2f  |  3.36.125.37    |
|  ap-northeast-2b|  i-064da1db46bbd4d0e  |  43.203.149.39  |
+-----------------+-----------------------+-----------------+

# AZ1 배치된 EC2 공인 IP
[root@operator-host ~]# aws ec2 describe-instances \
     --filters "Name=tag:Name,Values=myeks-ng1-Node" "Name=availability-zone,Values=ap-northeast-2a" \
     --query 'Reservations[*].Instances[*].PublicIpAddress' \
     --output text
3.36.125.37

# AZ2 배치된 EC2 공인 IP
[root@operator-host ~]# aws ec2 describe-instances \
     --filters "Name=tag:Name,Values=myeks-ng1-Node" "Name=availability-zone,Values=ap-northeast-2b" \
     --query 'Reservations[*].Instances[*].PublicIpAddress' \
     --output text
43.203.149.39

# AZ3 배치된 EC2 공인 IP
[root@operator-host ~]# aws ec2 describe-instances \
     --filters "Name=tag:Name,Values=myeks-ng1-Node" "Name=availability-zone,Values=ap-northeast-2c" \
     --query 'Reservations[*].Instances[*].PublicIpAddress' \
     --output text
3.36.132.80
# EC2 공인 IP 변수 지정
[root@operator-host ~]# export N1=$(aws ec2 describe-instances --filters "Name=tag:Name,Values=myeks-ng1-Node" "Name=availability-zone,Values=ap-northeast-2a" --query 'Reservations[*].Instances[*].PublicIpAddress' --output text)

[root@operator-host ~]# export N2=$(aws ec2 describe-instances --filters "Name=tag:Name,Values=myeks-ng1-Node" "Name=availability-zone,Values=ap-northeast-2b" --query 'Reservations[*].Instances[*].PublicIpAddress' --output text)

[root@operator-host ~]# export N3=$(aws ec2 describe-instances --filters "Name=tag:Name,Values=myeks-ng1-Node" "Name=availability-zone,Values=ap-northeast-2c" --query 'Reservations[*].Instances[*].PublicIpAddress' --output text)

[root@operator-host ~]# echo $N1, $N2, $N3
3.36.125.37, 43.203.149.39, 3.36.132.80

# *remoteAccess* 포함된 보안그룹 ID
[root@operator-host ~]# aws ec2 describe-security-groups --filters "Name=group-name,Values=*remoteAccess*" | jq
{
  "SecurityGroups": [
    {
      "GroupId": "sg-09c76d5ef9c8d8091",
      "IpPermissionsEgress": [
        {
          "IpProtocol": "-1",
          "UserIdGroupPairs": [],
          "IpRanges": [
            {
              "CidrIp": "0.0.0.0/0"
            }
          ],
          "Ipv6Ranges": [],
          "PrefixListIds": []
        }
      ],
      "Tags": [
        {
          "Key": "aws:cloudformation:logical-id",
          "Value": "SSH"
        },
        {
          "Key": "aws:cloudformation:stack-name",
          "Value": "eksctl-myeks-nodegroup-ng1"
        },
        {
          "Key": "Name",
          "Value": "eksctl-myeks-nodegroup-ng1/SSH"
        },
        {
          "Key": "aws:cloudformation:stack-id",
          "Value": "arn:aws:cloudformation:ap-northeast-2:390402552337:stack/eksctl-myeks-nodegroup-ng1/8a7d96d0-f10d-11ef-876d-06d9644ecb71"
        },
        {
          "Key": "alpha.eksctl.io/nodegroup-type",
          "Value": "managed"
        },
        {
          "Key": "alpha.eksctl.io/cluster-name",
          "Value": "myeks"
        },
        {
          "Key": "eksctl.cluster.k8s.io/v1alpha1/cluster-name",
          "Value": "myeks"
        },
        {
          "Key": "alpha.eksctl.io/eksctl-version",
          "Value": "0.204.0"
        },
        {
          "Key": "alpha.eksctl.io/nodegroup-name",
          "Value": "ng1"
        }
      ],
      "VpcId": "vpc-040caab837037a18c",
      "SecurityGroupArn": "arn:aws:ec2:ap-northeast-2:390402552337:security-group/sg-09c76d5ef9c8d8091",
      "OwnerId": "390402552337",
      "GroupName": "eksctl-myeks-nodegroup-ng1-remoteAccess",
      "Description": "Allow SSH access",
      "IpPermissions": [
        {
          "IpProtocol": "tcp",
          "FromPort": 22,
          "ToPort": 22,
          "UserIdGroupPairs": [],
          "IpRanges": [
            {
              "Description": "Allow SSH access to managed worker nodes in group ng1",
              "CidrIp": "0.0.0.0/0"
            }
          ],
          "Ipv6Ranges": [
            {
              "Description": "Allow SSH access to managed worker nodes in group ng1",
              "CidrIpv6": "::/0"
            }
          ],
          "PrefixListIds": []
        }
      ]
    }
  ]
}

[root@operator-host ~]# export MNSGID=$(aws ec2 describe-security-groups --filters "Name=group-name,Values=*remoteAccess*" --query 'SecurityGroups[*].GroupId' --output text)

# 해당 보안그룹 inbound 에 자신의 집 공인 IP 룰 추가
[root@operator-host ~]# aws ec2 authorize-security-group-ingress --group-id $MNSGID --protocol '-1' --cidr $(curl -s ipinfo.io/ip)/32
{
    "Return": true,
    "SecurityGroupRules": [
        {
            "SecurityGroupRuleId": "sgr-097f02c88d155093e",
            "GroupId": "sg-09c76d5ef9c8d8091",
            "GroupOwnerId": "390402552337",
            "IsEgress": false,
            "IpProtocol": "-1",
            "FromPort": -1,
            "ToPort": -1,
            "CidrIpv4": "15.164.244.197/32",
            "SecurityGroupRuleArn": "arn:aws:ec2:ap-northeast-2:390402552337:security-group-rule/sgr-097f02c88d155093e"
        }
    ]
}

# 해당 보안그룹 inbound 에 운영서버 내부 IP 룰 추가
[root@operator-host ~]# aws ec2 authorize-security-group-ingress --group-id $MNSGID --protocol '-1' --cidr 172.20.1.100/32
{
    "Return": true,
    "SecurityGroupRules": [
        {
            "SecurityGroupRuleId": "sgr-0b7b05294bd0d65e6",
            "GroupId": "sg-09c76d5ef9c8d8091",
            "GroupOwnerId": "390402552337",
            "IsEgress": false,
            "IpProtocol": "-1",
            "FromPort": -1,
            "ToPort": -1,
            "CidrIpv4": "172.20.1.100/32",
            "SecurityGroupRuleArn": "arn:aws:ec2:ap-northeast-2:390402552337:security-group-rule/sgr-0b7b05294bd0d65e6"
        }
    ]
}

# ping 테스트
[root@operator-host ~]# ping -c 2 $N1
PING 3.36.125.37 (3.36.125.37) 56(84) bytes of data.
64 bytes from 3.36.125.37: icmp_seq=1 ttl=126 time=0.742 ms
64 bytes from 3.36.125.37: icmp_seq=2 ttl=126 time=1.05 ms

--- 3.36.125.37 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1007ms
rtt min/avg/max/mdev = 0.742/0.896/1.051/0.157 ms

[root@operator-host ~]# ping -c 2 $N2
PING 43.203.149.39 (43.203.149.39) 56(84) bytes of data.
64 bytes from 43.203.149.39: icmp_seq=1 ttl=126 time=0.861 ms
64 bytes from 43.203.149.39: icmp_seq=2 ttl=126 time=1.31 ms

--- 43.203.149.39 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1008ms
rtt min/avg/max/mdev = 0.861/1.085/1.310/0.226 ms


[root@operator-host ~]# ping -c 2 $N3
PING 3.36.132.80 (3.36.132.80) 56(84) bytes of data.
64 bytes from 3.36.132.80: icmp_seq=1 ttl=126 time=1.24 ms
64 bytes from 3.36.132.80: icmp_seq=2 ttl=126 time=1.25 ms

--- 3.36.132.80 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 1.245/1.250/1.255/0.005 ms

 

myeks.yaml 설정파일에 ssh 설정을 allow: true 로 하게 되면 워커노드에  remoteAccess 보안그룹이 자동으로 추가됩니다.

[root@operator-host ~]# cat myeks.yaml | grep ssh -A 1
  ssh:
    allow: true

 

워커노드에 NodePort를 바로 접속할 수 있도롤 아래와 같이 설정합니다.

# 워커 노드 SSH 접속
[root@operator-host ~]# ssh -i aews-yu3papa.pem -o StrictHostKeyChecking=no ec2-user@$N1 hostname
ip-192-168-1-193.ap-northeast-2.compute.internal

[root@operator-host ~]# for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh -i aews-yu3papa.pem -o StrictHostKeyChecking=no ec2-user@$i hostname; echo; done
>> node 3.36.125.37 <<
ip-192-168-1-193.ap-northeast-2.compute.internal

>> node 43.203.149.39 <<
ip-192-168-2-200.ap-northeast-2.compute.internal

>> node 3.36.132.80 <<
ip-192-168-3-188.ap-northeast-2.compute.internal


 

이제 노드의 기본정보를 확인해 보겠습니다.

# 노드 기본 정보 확인
[root@operator-host ~]# for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh -i aews-yu3papa.pem ec2-user@$i hostnamectl; echo; done
>> node 3.36.125.37 <<
 Static hostname: ip-192-168-1-193.ap-northeast-2.compute.internal
       Icon name: computer-vm
         Chassis: vm 🖴
      Machine ID: ec29aa23ed9e53f7254d15a6afa0b19b
         Boot ID: 84cb77b76f36400e95686ad7ba0e2fa7
  Virtualization: amazon
Operating System: Amazon Linux 2023.6.20250203
     CPE OS Name: cpe:2.3:o:amazon:amazon_linux:2023
          Kernel: Linux 6.1.127-135.201.amzn2023.x86_64
    Architecture: x86-64
 Hardware Vendor: Amazon EC2
  Hardware Model: t3.medium
Firmware Version: 1.0

>> node 43.203.149.39 <<
 Static hostname: ip-192-168-2-200.ap-northeast-2.compute.internal
       Icon name: computer-vm
         Chassis: vm 🖴
      Machine ID: ec288cecc66f4a88c4c6a2431bd6cb50
         Boot ID: 76c4da2e77f44a8385e5dd4c9d834e6c
  Virtualization: amazon
Operating System: Amazon Linux 2023.6.20250203
     CPE OS Name: cpe:2.3:o:amazon:amazon_linux:2023
          Kernel: Linux 6.1.127-135.201.amzn2023.x86_64
    Architecture: x86-64
 Hardware Vendor: Amazon EC2
  Hardware Model: t3.medium
Firmware Version: 1.0

>> node 3.36.132.80 <<
 Static hostname: ip-192-168-3-188.ap-northeast-2.compute.internal
       Icon name: computer-vm
         Chassis: vm 🖴
      Machine ID: ec2cd3803c17e46194c1af15a865f00a
         Boot ID: c889707c7b3f4a4682b78fba66e634d9
  Virtualization: amazon
Operating System: Amazon Linux 2023.6.20250203
     CPE OS Name: cpe:2.3:o:amazon:amazon_linux:2023
          Kernel: Linux 6.1.127-135.201.amzn2023.x86_64
    Architecture: x86-64
 Hardware Vendor: Amazon EC2
  Hardware Model: t3.medium
Firmware Version: 1.0

[root@operator-host ~]# for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh -i aews-yu3papa.pem ec2-user@$i sudo ip -c addr; echo; done
>> node 3.36.125.37 <<
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host noprefixroute
       valid_lft forever preferred_lft forever
2: ens5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc mq state UP group default qlen 1000
    link/ether 02:b0:9b:41:8e:e3 brd ff:ff:ff:ff:ff:ff
    altname enp0s5
    inet 192.168.1.193/24 metric 1024 brd 192.168.1.255 scope global dynamic ens5
       valid_lft 3590sec preferred_lft 3590sec
    inet6 fe80::b0:9bff:fe41:8ee3/64 scope link proto kernel_ll
       valid_lft forever preferred_lft forever
3: enifae84e9e26b@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc noqueue state UP group default
    link/ether 8e:1d:bf:a3:11:2b brd ff:ff:ff:ff:ff:ff link-netns cni-8832b236-62e5-04bc-db6a-6badb52ccc0f
    inet6 fe80::8c1d:bfff:fea3:112b/64 scope link proto kernel_ll
       valid_lft forever preferred_lft forever
4: ens6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc mq state UP group default qlen 1000
    link/ether 02:2f:4d:ba:4a:a3 brd ff:ff:ff:ff:ff:ff
    altname enp0s6
    inet 192.168.1.89/24 brd 192.168.1.255 scope global ens6
       valid_lft forever preferred_lft forever
    inet6 fe80::2f:4dff:feba:4aa3/64 scope link proto kernel_ll
       valid_lft forever preferred_lft forever

>> node 43.203.149.39 <<
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host noprefixroute
       valid_lft forever preferred_lft forever
2: ens5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc mq state UP group default qlen 1000
    link/ether 06:87:62:db:d5:2f brd ff:ff:ff:ff:ff:ff
    altname enp0s5
    inet 192.168.2.200/24 metric 1024 brd 192.168.2.255 scope global dynamic ens5
       valid_lft 3582sec preferred_lft 3582sec
    inet6 fe80::487:62ff:fedb:d52f/64 scope link proto kernel_ll
       valid_lft forever preferred_lft forever
3: enif92a418c609@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc noqueue state UP group default
    link/ether 4a:c6:8d:25:ac:ad brd ff:ff:ff:ff:ff:ff link-netns cni-d55e793c-17c4-3f2b-b514-18ca15e776b3
    inet6 fe80::48c6:8dff:fe25:acad/64 scope link proto kernel_ll
       valid_lft forever preferred_lft forever
4: eni98f6ca7773f@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc noqueue state UP group default
    link/ether 72:34:d6:a7:9b:7a brd ff:ff:ff:ff:ff:ff link-netns cni-3ae4ec85-681f-d28c-6072-df506911ca44
    inet6 fe80::7034:d6ff:fea7:9b7a/64 scope link proto kernel_ll
       valid_lft forever preferred_lft forever
5: eni72b14925119@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc noqueue state UP group default
    link/ether 02:98:13:84:05:76 brd ff:ff:ff:ff:ff:ff link-netns cni-5ea5e6af-faa1-7316-f0f4-609f36b37609
    inet6 fe80::98:13ff:fe84:576/64 scope link proto kernel_ll
       valid_lft forever preferred_lft forever
6: eni1a9fabe96b3@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc noqueue state UP group default
    link/ether 3a:41:34:dc:62:d1 brd ff:ff:ff:ff:ff:ff link-netns cni-6479f9c4-8ffc-1c68-60a4-28667c9eb8ca
    inet6 fe80::3841:34ff:fedc:62d1/64 scope link proto kernel_ll
       valid_lft forever preferred_lft forever
7: ens6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc mq state UP group default qlen 1000
    link/ether 06:dd:cf:cd:23:99 brd ff:ff:ff:ff:ff:ff
    altname enp0s6
    inet 192.168.2.235/24 brd 192.168.2.255 scope global ens6
       valid_lft forever preferred_lft forever
    inet6 fe80::4dd:cfff:fecd:2399/64 scope link proto kernel_ll
       valid_lft forever preferred_lft forever
8: eni5d39fccd7d1@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc noqueue state UP group default
    link/ether 02:e0:8e:9e:f1:c0 brd ff:ff:ff:ff:ff:ff link-netns cni-72e58089-9fe7-cb48-66d3-b0d193f1df0d
    inet6 fe80::e0:8eff:fe9e:f1c0/64 scope link proto kernel_ll
       valid_lft forever preferred_lft forever

>> node 3.36.132.80 <<
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host noprefixroute
       valid_lft forever preferred_lft forever
2: ens5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc mq state UP group default qlen 1000
    link/ether 0a:be:1f:e2:43:bf brd ff:ff:ff:ff:ff:ff
    altname enp0s5
    inet 192.168.3.188/24 metric 1024 brd 192.168.3.255 scope global dynamic ens5
       valid_lft 3585sec preferred_lft 3585sec
    inet6 fe80::8be:1fff:fee2:43bf/64 scope link proto kernel_ll
       valid_lft forever preferred_lft forever
3: eni7877746a959@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc noqueue state UP group default
    link/ether 9e:24:bf:8d:d2:45 brd ff:ff:ff:ff:ff:ff link-netns cni-1b14fc63-2dbd-59af-676d-e728e21dc2c9
    inet6 fe80::9c24:bfff:fe8d:d245/64 scope link proto kernel_ll
       valid_lft forever preferred_lft forever
4: ens6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc mq state UP group default qlen 1000
    link/ether 0a:1d:ff:8c:b0:41 brd ff:ff:ff:ff:ff:ff
    altname enp0s6
    inet 192.168.3.88/24 brd 192.168.3.255 scope global ens6
       valid_lft forever preferred_lft forever
    inet6 fe80::81d:ffff:fe8c:b041/64 scope link proto kernel_ll
       valid_lft forever preferred_lft forever

[root@operator-host ~]# for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh -i aews-yu3papa.pem  ec2-user@$i lsblk; echo; done
>> node 3.36.125.37 <<
NAME          MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
nvme0n1       259:0    0  120G  0 disk
├─nvme0n1p1   259:1    0  120G  0 part /
├─nvme0n1p127 259:2    0    1M  0 part
└─nvme0n1p128 259:3    0   10M  0 part /boot/efi

>> node 43.203.149.39 <<
NAME          MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
nvme0n1       259:0    0  120G  0 disk
├─nvme0n1p1   259:1    0  120G  0 part /
├─nvme0n1p127 259:2    0    1M  0 part
└─nvme0n1p128 259:3    0   10M  0 part /boot/efi

>> node 3.36.132.80 <<
NAME          MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
nvme0n1       259:0    0  120G  0 disk
├─nvme0n1p1   259:1    0  120G  0 part /
├─nvme0n1p127 259:2    0    1M  0 part
└─nvme0n1p128 259:3    0   10M  0 part /boot/efi

[root@operator-host ~]# for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh -i aews-yu3papa.pem ec2-user@$i df -hT /; echo; done
>> node 3.36.125.37 <<
Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/nvme0n1p1 xfs   120G  3.9G  117G   4% /

>> node 43.203.149.39 <<
Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/nvme0n1p1 xfs   120G  4.1G  116G   4% /

>> node 3.36.132.80 <<
Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/nvme0n1p1 xfs   120G  3.9G  117G   4% /

 

워커노드의 사이즈를 120G로 설정하여 놓았습니다. 

[root@operator-host ~]# for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh -i aews-yu3papa.pem  ec2-user@$i lsblk; echo; done
>> node 3.36.125.37 <<
NAME          MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
nvme0n1       259:0    0  120G  0 disk
├─nvme0n1p1   259:1    0  120G  0 part /
├─nvme0n1p127 259:2    0    1M  0 part
└─nvme0n1p128 259:3    0   10M  0 part /boot/efi

>> node 43.203.149.39 <<
NAME          MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
nvme0n1       259:0    0  120G  0 disk
├─nvme0n1p1   259:1    0  120G  0 part /
├─nvme0n1p127 259:2    0    1M  0 part
└─nvme0n1p128 259:3    0   10M  0 part /boot/efi

>> node 3.36.132.80 <<
NAME          MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
nvme0n1       259:0    0  120G  0 disk
├─nvme0n1p1   259:1    0  120G  0 part /
├─nvme0n1p127 259:2    0    1M  0 part
└─nvme0n1p128 259:3    0   10M  0 part /boot/efi

[root@operator-host ~]# for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh -i aews-yu3papa.pem ec2-user@$i df -hT /; echo; done
>> node 3.36.125.37 <<
Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/nvme0n1p1 xfs   120G  3.9G  117G   4% /

>> node 43.203.149.39 <<
Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/nvme0n1p1 xfs   120G  4.1G  116G   4% /

>> node 3.36.132.80 <<
Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/nvme0n1p1 xfs   120G  3.9G  117G   4% /

 

EKS 는 디폴트로 gp2 SC(Storage Class)가 존재합니다. 추후에 gp3를 사용하는 SC를 만들어서 사용해 보겠습니다.

# 스토리지클래스 및 CSI 노드 확인
[root@operator-host ~]# kubectl get sc
NAME   PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
gp2    kubernetes.io/aws-ebs   Delete          WaitForFirstConsumer   false                  71m

[root@operator-host ~]# kubectl describe sc gp2
Name:            gp2
IsDefaultClass:  No
Annotations:     kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"storage.k8s.io/v1","kind":"StorageClass","metadata":{"annotations":{},"name":"gp2"},"parameters":{"fsType":"ext4","type":"gp2"},"provisioner":"kubernetes.io/aws-ebs","volumeBindingMode":"WaitForFirstConsumer"}

Provisioner:           kubernetes.io/aws-ebs
Parameters:            fsType=ext4,type=gp2
AllowVolumeExpansion:  
MountOptions:          
ReclaimPolicy:         Delete
VolumeBindingMode:     WaitForFirstConsumer
Events:                

 

현재 CRD는 아래와 같으며 추후 실습에서 추가 컨트롤러를 설치하면 CRD 가 추가되는것을 확인해 보겠습니다.

[root@operator-host ~]# kubectl get crd
NAME                                         CREATED AT
cninodes.vpcresources.k8s.aws                2025-02-22T11:02:36Z
eniconfigs.crd.k8s.amazonaws.com             2025-02-22T11:06:12Z
policyendpoints.networking.k8s.aws           2025-02-22T11:02:36Z
securitygrouppolicies.vpcresources.k8s.aws   2025-02-22T11:02:36Z

[root@operator-host ~]# kubectl get csinodes
NAME                                               DRIVERS   AGE
ip-192-168-1-193.ap-northeast-2.compute.internal   0         63m
ip-192-168-2-200.ap-northeast-2.compute.internal   0         63m
ip-192-168-3-188.ap-northeast-2.compute.internal   0         63m

 

기본 POD 개수는 워커노드당 25개인데 설정값을 100으로 설정하여 놓았습니다.

# max-pods 정보 확인
[root@operator-host ~]# kubectl describe node | grep Capacity: -A13
Capacity:
  cpu:                2
  ephemeral-storage:  125751276Ki
  hugepages-1Gi:      0
  hugepages-2Mi:      0
  memory:             3919536Ki
  pods:               100
Allocatable:
  cpu:                1930m
  ephemeral-storage:  114818633946
  hugepages-1Gi:      0
  hugepages-2Mi:      0
  memory:             3364528Ki
  pods:               100
--
Capacity:
  cpu:                2
  ephemeral-storage:  125751276Ki
  hugepages-1Gi:      0
  hugepages-2Mi:      0
  memory:             3919544Ki
  pods:               100
Allocatable:
  cpu:                1930m
  ephemeral-storage:  114818633946
  hugepages-1Gi:      0
  hugepages-2Mi:      0
  memory:             3364536Ki
  pods:               100
--
Capacity:
  cpu:                2
  ephemeral-storage:  125751276Ki
  hugepages-1Gi:      0
  hugepages-2Mi:      0
  memory:             3919544Ki
  pods:               100
Allocatable:
  cpu:                1930m
  ephemeral-storage:  114818633946
  hugepages-1Gi:      0
  hugepages-2Mi:      0
  memory:             3364536Ki
  pods:               100

[root@operator-host ~]# kubectl get nodes -o custom-columns="NAME:.metadata.name,MAXPODS:.status.capacity.pods"
NAME                                               MAXPODS
ip-192-168-1-193.ap-northeast-2.compute.internal   100
ip-192-168-2-200.ap-northeast-2.compute.internal   100
ip-192-168-3-188.ap-northeast-2.compute.internal   100

# Amazon Linux 2 타입은 워커노드 초기 설정을 BootStrap 스크립트를 사용하는데, Amazon Linux 2023은 사용하지 않습니다.

[root@operator-host ~]# for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh -i aews-yu3papa.pem ec2-user@$i cat /etc/eks/bootstrap.sh; echo; done
>> node 3.36.125.37 <<
#!/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/
!!!!!!!!!!
'

[root@operator-host ~]# ssh -i aews-yu3papa.pem ec2-user@$N1 sudo cat /etc/kubernetes/kubelet/config.json | jq
{
  "address": "0.0.0.0",
  "authentication": {
    "x509": {
      "clientCAFile": "/etc/kubernetes/pki/ca.crt"
    },
    "webhook": {
      "enabled": true,
      "cacheTTL": "2m0s"
    },
    "anonymous": {
      "enabled": false
    }
  },
  "authorization": {
    "mode": "Webhook",
    "webhook": {
      "cacheAuthorizedTTL": "5m0s",
      "cacheUnauthorizedTTL": "30s"
    }
  },
  "cgroupDriver": "systemd",
  "cgroupRoot": "/",
  "clusterDNS": [
    "10.100.0.10"
  ],
  "clusterDomain": "cluster.local",
  "containerRuntimeEndpoint": "unix:///run/containerd/containerd.sock",
  "evictionHard": {
    "memory.available": "100Mi",
    "nodefs.available": "10%",
    "nodefs.inodesFree": "5%"
  },
  "featureGates": {
    "RotateKubeletServerCertificate": true
  },
  "hairpinMode": "hairpin-veth",
  "kubeReserved": {
    "cpu": "70m",
    "ephemeral-storage": "1Gi",
    "memory": "442Mi"
  },
  "kubeReservedCgroup": "/runtime",
  "logging": {
    "verbosity": 2
  },
  "maxPods": 17,
  "protectKernelDefaults": true,
  "providerID": "aws:///ap-northeast-2a/i-02c24086e5ef7bd2f",
  "readOnlyPort": 0,
  "serializeImagePulls": false,
  "serverTLSBootstrap": true,
  "systemReservedCgroup": "/system",
  "tlsCipherSuites": [
    "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
    "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
    "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
    "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
    "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
    "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
    "TLS_RSA_WITH_AES_128_GCM_SHA256",
    "TLS_RSA_WITH_AES_256_GCM_SHA384"
  ],
  "kind": "KubeletConfiguration",
  "apiVersion": "kubelet.config.k8s.io/v1beta1"
}

[root@operator-host ~]# for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh -i aews-yu3papa.pem ec2-user@$i sudo cat /etc/kubernetes/kubelet/config.json | grep maxPods; echo; done
>> node 3.36.125.37 <<
    "maxPods": 17,

>> node 43.203.149.39 <<
    "maxPods": 17,

>> node 3.36.132.80 <<
    "maxPods": 17,

[root@operator-host ~]# for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh -i aews-yu3papa.pem ec2-user@$i sudo cat /etc/kubernetes/kubelet/config.json.d/00-nodeadm.conf | grep maxPods; echo; done
>> node 3.36.125.37 <<
    "maxPods": 100

>> node 43.203.149.39 <<
    "maxPods": 100

>> node 3.36.132.80 <<
    "maxPods": 100

 

(참고) If you don’t specify an AMI ID for the bootstrap.sh file included with Amazon EKS optimized Linux or Bottlerocket, managed node groups enforce a maximum number on the value of maxPods. For instances with less than 30 vCPUs, the maximum number is 110. For instances with greater than 30 vCPUs, the maximum number jumps to 250. These numbers are based on Kubernetes scalability thresholds and recommended settings by internal Amazon EKS scalability team testing. For more information, see the Amazon VPC CNI plugin increases pods per node limits blog post.

 

EKS에서 EFS를 SC로 사용할 예정입니다. 우선 운영EC2에서 CloudFormation으로 생성한 EFS를 마운트 해보겠습니다.

# 현재 EFS 정보 확인
[root@operator-host ~]# aws efs describe-file-systems | jq
{
  "FileSystems": [
    {
      "OwnerId": "390402552337",
      "CreationToken": "ElasticFileSystem-0Vg6jie256jJ",
      "FileSystemId": "fs-04bdbcbb996487caf",
      "FileSystemArn": "arn:aws:elasticfilesystem:ap-northeast-2:390402552337:file-system/fs-04bdbcbb996487caf",
      "CreationTime": "2025-02-22T18:18:50+09:00",
      "LifeCycleState": "available",
      "Name": "myeks-EFS",
      "NumberOfMountTargets": 3,
      "SizeInBytes": {
        "Value": 6144,
        "Timestamp": "2025-02-22T21:56:03+09:00",
        "ValueInIA": 0,
        "ValueInStandard": 6144,
        "ValueInArchive": 0
      },
      "PerformanceMode": "generalPurpose",
      "Encrypted": false,
      "ThroughputMode": "bursting",
      "Tags": [
        {
          "Key": "Name",
          "Value": "myeks-EFS"
        }
      ],
      "FileSystemProtection": {
        "ReplicationOverwriteProtection": "ENABLED"
      }
    }
  ]
}

# 파일 시스템 ID만 출력
[root@operator-host ~]# aws efs describe-file-systems --query "FileSystems[*].FileSystemId" --output text
fs-04bdbcbb996487caf

# EFS 마운트 대상 정보 확인
[root@operator-host ~]# aws efs describe-mount-targets --file-system-id $(aws efs describe-file-systems --query "FileSystems[*].FileSystemId" --output text) | jq

{
  "MountTargets": [
    {
      "OwnerId": "390402552337",
      "MountTargetId": "fsmt-024c4230e40771980",
      "FileSystemId": "fs-04bdbcbb996487caf",
      "SubnetId": "subnet-08b83e58f1cad6098",
      "LifeCycleState": "available",
      "IpAddress": "192.168.1.141",
      "NetworkInterfaceId": "eni-07792729b19999f22",
      "AvailabilityZoneId": "apne2-az1",
      "AvailabilityZoneName": "ap-northeast-2a",
      "VpcId": "vpc-040caab837037a18c"
    },
    {
      "OwnerId": "390402552337",
      "MountTargetId": "fsmt-070fc8743f88c3222",
      "FileSystemId": "fs-04bdbcbb996487caf",
      "SubnetId": "subnet-0f643cf03e72dc812",
      "LifeCycleState": "available",
      "IpAddress": "192.168.3.183",
      "NetworkInterfaceId": "eni-04a4f6f78e77ef232",
      "AvailabilityZoneId": "apne2-az3",
      "AvailabilityZoneName": "ap-northeast-2c",
      "VpcId": "vpc-040caab837037a18c"
    },
    {
      "OwnerId": "390402552337",
      "MountTargetId": "fsmt-072c118536be30869",
      "FileSystemId": "fs-04bdbcbb996487caf",
      "SubnetId": "subnet-0ad3d1ce95ad8a965",
      "LifeCycleState": "available",
      "IpAddress": "192.168.2.238",
      "NetworkInterfaceId": "eni-06a16b25394c36135",
      "AvailabilityZoneId": "apne2-az2",
      "AvailabilityZoneName": "ap-northeast-2b",
      "VpcId": "vpc-040caab837037a18c"
    }
  ]
}

# IP만 출력 :
[root@operator-host ~]# aws efs describe-mount-targets --file-system-id $(aws efs describe-file-systems --query "FileSystems[*].FileSystemId" --output text) --query "MountTargets[*].IpAddress" --output text
192.168.1.141   192.168.3.183   192.168.2.238

# EFS 도메인 이름(예시) : fs-040469b8fab273469.efs.ap-northeast-2.amazonaws.com
# EFS 마운트 테스트 -> EFSIP1=<IP만 출력에서 아무 IP나 지정>
[root@operator-host ~]# EFSIP1=192.168.1.141

[root@operator-host ~]# df -hT
Filesystem     Type      Size  Used Avail Use% Mounted on
devtmpfs       devtmpfs  981M     0  981M   0% /dev
tmpfs          tmpfs     990M     0  990M   0% /dev/shm
tmpfs          tmpfs     990M  428K  989M   1% /run
tmpfs          tmpfs     990M     0  990M   0% /sys/fs/cgroup
/dev/xvda1     xfs        30G  3.0G   28G  10% /
tmpfs          tmpfs     198M     0  198M   0% /run/user/1000

# 마운트 포인트 폴더 생성 + 마운트
[root@operator-host ~]# mkdir /mnt/myefs
[root@operator-host ~]# mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport $EFSIP1:/ /mnt/myefs
[root@operator-host ~]# findmnt -t nfs4
TARGET     SOURCE          FSTYPE OPTIONS
/mnt/myefs 192.168.1.141:/ nfs4   rw,relatime,vers=4.1,rsize=1048576,wsize=1048576,namlen=255,hard,noresvport,proto=tcp,timeo=600,retr
[root@operator-host ~]# df -hT --type nfs4
Filesystem      Type  Size  Used Avail Use% Mounted on
192.168.1.141:/ nfs4  8.0E     0  8.0E   0% /mnt/myefs

# 파일 작성 테스트
# 현재 NFS 통계 확인
[root@operator-host ~]# nfsstat
Client rpc stats:
calls      retrans    authrefrsh
17         0          17

Client nfs v4:
null         read         write        commit       open         open_conf
1         5% 0         0% 0         0% 0         0% 0         0% 0         0%
open_noat    open_dgrd    close        setattr      fsinfo       renew
0         0% 0         0% 0         0% 0         0% 2        11% 0         0%
setclntid    confirm      lock         lockt        locku        access
0         0% 0         0% 0         0% 0         0% 0         0% 0         0%
getattr      lookup       lookup_root  remove       rename       link
1         5% 0         0% 1         5% 0         0% 0         0% 0         0%
symlink      create       pathconf     statfs       readlink     readdir
0         0% 0         0% 1         5% 1         5% 0         0% 0         0%
server_caps  delegreturn  getacl       setacl       fs_locations rel_lkowner
3        17% 0         0% 0         0% 0         0% 0         0% 0         0%
secinfo      exchange_id  create_ses   destroy_ses  sequence     get_lease_t
0         0% 0         0% 2        11% 1         5% 0         0% 2        11%
reclaim_comp layoutget    getdevinfo   layoutcommit layoutreturn getdevlist
0         0% 1         5% 0         0% 0         0% 0         0% 0         0%
(null)
1         5%

[root@operator-host ~]# echo "EKS Workshop" > /mnt/myefs/memo.txt

# 파일 1개를 작성하고 NFS 통계를 보고 증가된 값 확인
[root@operator-host ~]# nfsstat
Client rpc stats:
calls      retrans    authrefrsh
21         0          21

Client nfs v4:
null         read         write        commit       open         open_conf
1         4% 0         0% 1         4% 0         0% 1         4% 0         0%
open_noat    open_dgrd    close        setattr      fsinfo       renew
0         0% 0         0% 1         4% 0         0% 2         9% 0         0%
setclntid    confirm      lock         lockt        locku        access
0         0% 0         0% 0         0% 0         0% 0         0% 1         4%
getattr      lookup       lookup_root  remove       rename       link
1         4% 0         0% 1         4% 0         0% 0         0% 0         0%
symlink      create       pathconf     statfs       readlink     readdir
0         0% 0         0% 1         4% 1         4% 0         0% 0         0%
server_caps  delegreturn  getacl       setacl       fs_locations rel_lkowner
3        14% 0         0% 0         0% 0         0% 0         0% 0         0%
secinfo      exchange_id  create_ses   destroy_ses  sequence     get_lease_t
0         0% 0         0% 2         9% 1         4% 0         0% 2         9%
reclaim_comp layoutget    getdevinfo   layoutcommit layoutreturn getdevlist
0         0% 1         4% 0         0% 0         0% 0         0% 0         0%
(null)
1         4%

[root@operator-host ~]# ls -l /mnt/myefs
total 4
-rw-r--r-- 1 root root 13 Feb 22 22:04 memo.txt
[root@operator-host ~]# cat /mnt/myefs/memo.txt
EKS Workshop

 

# EC2 재부팅 이후에도 mount 탑재가 될 수 있게 설정

vi /etc/fstab

# EFS Mount fs-04bdbcbb996487caf /mnt/myefs nfs4 nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport 0 0

 

EKS 배포 후 실습 편의를 위한 설정

  • Child 쉘에서도 똑같은 환경변수를 사용하기 위함
cat << EOF >> ~/.bashrc
export CLUSTER_NAME=myeks
export VPCID=$(aws ec2 describe-vpcs --filters "Name=tag:Name,Values=$CLUSTER_NAME-VPC" --query 'Vpcs[*].VpcId' --output text)
export PubSubnet1=$(aws ec2 describe-subnets --filters Name=tag:Name,Values="$CLUSTER_NAME-Vpc1PublicSubnet1" --query "Subnets[0].[SubnetId]" --output text)
export PubSubnet2=$(aws ec2 describe-subnets --filters Name=tag:Name,Values="$CLUSTER_NAME-Vpc1PublicSubnet2" --query "Subnets[0].[SubnetId]" --output text)
export PubSubnet3=$(aws ec2 describe-subnets --filters Name=tag:Name,Values="$CLUSTER_NAME-Vpc1PublicSubnet3" --query "Subnets[0].[SubnetId]" --output text)
export N1=$(aws ec2 describe-instances --filters "Name=tag:Name,Values=$CLUSTER_NAME-ng1-Node" "Name=availability-zone,Values=ap-northeast-2a" --query 'Reservations[*].Instances[*].PublicIpAddress' --output text)
export N2=$(aws ec2 describe-instances --filters "Name=tag:Name,Values=$CLUSTER_NAME-ng1-Node" "Name=availability-zone,Values=ap-northeast-2b" --query 'Reservations[*].Instances[*].PublicIpAddress' --output text)
export N3=$(aws ec2 describe-instances --filters "Name=tag:Name,Values=$CLUSTER_NAME-ng1-Node" "Name=availability-zone,Values=ap-northeast-2c" --query 'Reservations[*].Instances[*].PublicIpAddress' --output text)
MyDomain=yu3papa.shop
MyDnzHostedZoneId=$(aws route53 list-hosted-zones-by-name --dns-name "$MyDomain." --query "HostedZones[0].Id" --output text)
EOF

 

~/.bashrc 설정 파일을 확인해 보겠습니다.

[yu3papa@iworks ~]$ cat ~/.bashrc
...(생략)...
export CLUSTER_NAME=myeks
export VPCID=vpc-040caab837037a18c
export PubSubnet1=subnet-08b83e58f1cad6098
export PubSubnet2=subnet-0ad3d1ce95ad8a965
export PubSubnet3=subnet-0f643cf03e72dc812
export N1=3.36.125.37
export N2=43.203.149.39
export N3=3.36.132.80
MyDomain=yu3papa.shop
MyDnzHostedZoneId=/hostedzone/Z0888869ZZNUHC5WOC28

# 신규터미널을 열고 환경 확인
Last login: Sat Feb 22 20:51:49 2025 from 192.168.10.5

[yu3papa@iworks ~]$ echo $CLUSTER_NAME $VPCID $PubSubnet1 $PubSubnet2 $PubSubnet3
myeks vpc-040caab837037a18c subnet-08b83e58f1cad6098 subnet-0ad3d1ce95ad8a965 subnet-0f643cf03e72dc812

[yu3papa@iworks ~]$ echo $N1 $N2 $N3 $MyDomain $MyDnzHostedZoneId
3.36.125.37 43.203.149.39 3.36.132.80 yu3papa.shop /hostedzone/Z0888869ZZNUHC5WOC28

[yu3papa@iworks ~]$ tail -n 12 ~/.bashrc

unset rc
export CLUSTER_NAME=myeks
export VPCID=vpc-040caab837037a18c
export PubSubnet1=subnet-08b83e58f1cad6098
export PubSubnet2=subnet-0ad3d1ce95ad8a965
export PubSubnet3=subnet-0f643cf03e72dc812
export N1=3.36.125.37
export N2=43.203.149.39
export N3=3.36.132.80
MyDomain=yu3papa.shop
MyDnzHostedZoneId=/hostedzone/Z0888869ZZNUHC5WOC28

 

AWS LoadBalancerController, ExternalDNS, kube-ops-view 설치

# kube-ops-view
[yu3papa@iworks ~]$ helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
[yu3papa@iworks ~]$ helm repo list
NAME            URL
geek-cookbook   https://geek-cookbook.github.io/charts/
[yu3papa@iworks ~]$ helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set service.main.type=ClusterIP  --set env.TZ="Asia/Seoul" --namespace kube-system
NAME: kube-ops-view
LAST DEPLOYED: Sat Feb 22 22:40:01 2025
NAMESPACE: kube-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
1. Get the application URL by running these commands:
  export POD_NAME=$(kubectl get pods --namespace kube-system -l "app.kubernetes.io/name=kube-ops-view,app.kubernetes.io/instance=kube-ops-view" -o jsonpath="{.items[0].metadata.name}")
  echo "Visit http://127.0.0.1:8080 to use your application"
  kubectl port-forward $POD_NAME 8080:8080


# AWS LoadBalancerController
[yu3papa@iworks ~]$ helm repo add eks https://aws.github.io/eks-charts
"eks" has been added to your repositories

[yu3papa@iworks ~]$ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "eks" chart repository
...Successfully got an update from the "geek-cookbook" chart repository
Update Complete. ⎈Happy Helming!⎈

[yu3papa@iworks ~]$ kubectl get sa -n kube-system aws-load-balancer-controller
NAME                           SECRETS   AGE
aws-load-balancer-controller   0         151m

[yu3papa@iworks ~]$ helm install aws-load-balancer-controller eks/aws-load-balancer-controller -n kube-system --set clusterName=$CLUSTER_NAME \
  --set serviceAccount.create=false --set serviceAccount.name=aws-load-balancer-controller
NAME: aws-load-balancer-controller
LAST DEPLOYED: Sat Feb 22 22:41:05 2025
NAMESPACE: kube-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
AWS Load Balancer controller installed!

# ExternalDNS
[yu3papa@iworks ~]$ MyDomain=yu3papa.shop

[yu3papa@iworks ~]$ MyDnzHostedZoneId=$(aws route53 list-hosted-zones-by-name --dns-name "$MyDomain." --query "HostedZones[0].Id" --output text)

[yu3papa@iworks ~]$ echo $MyDnzHostedZoneId
/hostedzone/Z0888869ZZNUHC5WOC28

[yu3papa@iworks ~]$ curl -s https://raw.githubusercontent.com/gasida/PKOS/main/aews/externaldns.yaml | MyDomain=$MyDomain MyDnzHostedZoneId=$MyDnzHostedZoneId envsubst | kubectl apply -f -
serviceaccount/external-dns created
clusterrole.rbac.authorization.k8s.io/external-dns created
clusterrolebinding.rbac.authorization.k8s.io/external-dns-viewer created
deployment.apps/external-dns created

# 사용 리전의 인증서 ARN 확인 : 정상 상태 확인(만료 상태면 에러 발생!)
[yu3papa@iworks ~]$ aws acm list-certificates --query 'CertificateSummaryList[].CertificateArn[]' --output text
arn:aws:acm:ap-northeast-2:390402552337:certificate/55e87490-f59e-4099-ac6b-d796ab5a1ed8

[yu3papa@iworks ~]$ CERT_ARN=$(aws acm list-certificates --query 'CertificateSummaryList[].CertificateArn[]' --output text)
[yu3papa@iworks ~]$ echo $CERT_ARN
arn:aws:acm:ap-northeast-2:390402552337:certificate/55e87490-f59e-4099-ac6b-d796ab5a1ed8

# kubeopsview 용 Ingress 설정 : group 설정으로 1대의 ALB를 여러개의 ingress 에서 공용 사용
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    alb.ingress.kubernetes.io/certificate-arn: $CERT_ARN
    alb.ingress.kubernetes.io/group.name: study
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}, {"HTTP":80}]'
    alb.ingress.kubernetes.io/load-balancer-name: myeks-ingress-alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/ssl-redirect: "443"
    alb.ingress.kubernetes.io/success-codes: 200-399
    alb.ingress.kubernetes.io/target-type: ip
  labels:
    app.kubernetes.io/name: kubeopsview
  name: kubeopsview
  namespace: kube-system
spec:
  ingressClassName: alb
  rules:
  - host: kubeopsview.$MyDomain
    http:
      paths:
      - backend:
          service:
            name: kube-ops-view
            port:
              number: 8080
        path: /
        pathType: Prefix
EOF

ingress.networking.k8s.io/kubeopsview created

[yu3papa@iworks ~]$ kubectl -n kube-system get ing
NAME          CLASS   HOSTS                      ADDRESS                                                        PORTS   AGE
kubeopsview   alb     kubeopsview.yu3papa.shop   myeks-ingress-alb-833395103.ap-northeast-2.elb.amazonaws.com   80      21s

 

설치한 AWS LoadBalancerController, ExternalDNS, kube-ops-view 를 확인해 보겠습니다.

# 설치된 파드 정보 확인
[yu3papa@iworks ~]$ kubectl get pods -n kube-system
NAME                                          READY   STATUS    RESTARTS   AGE
aws-load-balancer-controller-554fbd9d-8kxrh   1/1     Running   0          24m
aws-load-balancer-controller-554fbd9d-r4bjd   1/1     Running   0          24m
aws-node-bblcc                                2/2     Running   0          173m
aws-node-lhw2m                                2/2     Running   0          173m
aws-node-x24kb                                2/2     Running   0          173m
coredns-86f5954566-8n62v                      1/1     Running   0          179m
coredns-86f5954566-bv27z                      1/1     Running   0          179m
external-dns-57c44fb644-r2vjn                 1/1     Running   0          23m
kube-ops-view-657dbc6cd8-kv9qm                1/1     Running   0          25m
kube-proxy-gtcvw                              1/1     Running   0          173m
kube-proxy-rw2fb                              1/1     Running   0          173m
kube-proxy-vrngn                              1/1     Running   0          173m
metrics-server-6bf5998d9c-bqzjj               1/1     Running   0          179m
metrics-server-6bf5998d9c-w2x7t               1/1     Running   0          179m
nvidia-device-plugin-daemonset-7wpg4          1/1     Running   0          172m
nvidia-device-plugin-daemonset-bc7bf          1/1     Running   0          172m
nvidia-device-plugin-daemonset-s4v47          1/1     Running   0          172m

# service, ep, ingress 확인
[yu3papa@iworks ~]$ kubectl get ingress,svc,ep -n kube-system
NAME                                    CLASS   HOSTS                      ADDRESS                                                        PORTS   AGE
ingress.networking.k8s.io/kubeopsview   alb     kubeopsview.yu3papa.shop   myeks-ingress-alb-833395103.ap-northeast-2.elb.amazonaws.com   80      2m21s

NAME                                        TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                  AGE
service/aws-load-balancer-webhook-service   ClusterIP   10.100.142.171   <none>        443/TCP                  24m
service/eks-extension-metrics-api           ClusterIP   10.100.247.125   <none>        443/TCP                  3h3m
service/kube-dns                            ClusterIP   10.100.0.10      <none>        53/UDP,53/TCP,9153/TCP   179m
service/kube-ops-view                       ClusterIP   10.100.1.128     <none>        8080/TCP                 25m
service/metrics-server                      ClusterIP   10.100.245.240   <none>        443/TCP                  179m

NAME                                          ENDPOINTS                                                       AGE
endpoints/aws-load-balancer-webhook-service   192.168.1.113:9443,192.168.3.72:9443                            24m
endpoints/eks-extension-metrics-api           172.0.32.0:10443                                                3h3m
endpoints/kube-dns                            192.168.2.169:53,192.168.2.24:53,192.168.2.169:53 + 3 more...   179m
endpoints/kube-ops-view                       192.168.1.114:8080                                              25m
endpoints/metrics-server                      192.168.2.154:10251,192.168.2.213:10251                         179m

# Kube Ops View 접속 정보 확인
[yu3papa@iworks ~]$ echo -e "Kube Ops View URL = https://kubeopsview.$MyDomain/#scale=1.5"
Kube Ops View URL = https://kubeopsview.yu3papa.shop/#scale=1.5

 

 

3. EKS 스토리지 이해

EKS 는 바닐라 쿠버네티스의 업스트림 버전이기 때문에 블로그 상단에 설명드렸던 PV, PVC, SC 리소스를 동일한 개념으로 사용합니다.

배경 소개

파드 내부의 데이터는 파드가 삭제되면 모두 삭제됨 → 즉, 파드가 모두 상태가 없는(Stateless) 애플리케이션이였음! : Temporary filesystem, Volume

https://aws.amazon.com/ko/blogs/tech/persistent-storage-for-kubernetes/

데이터베이스(파드)처럼 데이터 보존이 필요 == 상태가 있는(Stateful) 애플리케이션 : PV & PVC

  • 로컬 볼륨(hostPath) ⇒ 퍼시스턴트 볼륨(Persistent Volume, PV) - 어느 노드에서도 연결하여 사용 가능, 예시) NFS, AWS EBS, Ceph 등

https://aws.amazon.com/ko/blogs/tech/persistent-storage-for-kubernetes/

 

파드가 생성될 때 자동으로 볼륨을 마운트하여 파드에 연결하는 기능을 동적 프로비저닝(Dynamic Provisioning)이라고 함

https://aws.amazon.com/ko/blogs/tech/persistent-storage-for-kubernetes/

- 퍼시스턴트 볼륨의 사용이 끝났을 때 해당 볼륨은 어떻게 초기화할 것인지 별도로 설정할 수 있는데, 쿠버네티스는 이를 Reclaim Policy 라고 부릅니다.
- Reclaim Policy 에는 크게 Retain(보존), Delete(삭제, 즉 EBS 볼륨도 삭제됨), ~~Recycle~~ 방식이 있습니다.

 

CSI (Contaier Storage Interface) 소개

  • CSI Driver 배경
    • Kubernetes source code 내부에 존재하는 AWS EBS provisioner는 당연히 Kubernetes release lifecycle을 따라서 배포되므로, provisioner 신규 기능을 사용하기 위해서는 Kubernetes version을 업그레이드해야 하는 제약 사항이 있습니다. 따라서, Kubernetes 개발자는 Kubernetes 내부에 내장된 provisioner (in-tree)를 모두 삭제하고, 별도의 controller Pod을 통해 동적 provisioning을 사용할 수 있도록 만들었습니다. 이것이 바로 CSI (Container Storage Interface) driver 입니다
  • CSI 를 사용하면, K8S 의 공통화된 CSI 인터페이스를 통해 다양한 프로바이더를 사용할 수 있다.

  • 아래는 일반적인 CSI driver의 구조입니다. AWS EBS CSI driver 역시 아래와 같은 구조를 가지는데, 
    오른쪽 StatefulSet 또는 Deployment로 배포된 controller Pod이 AWS API를 사용하여 실제 EBS volume을 생성하는 역할을 합니다. 
    왼쪽 DaemonSet으로 배포된 node Pod은 AWS API를 사용하여 Kubernetes node (EC2 instance)에 EBS volume을 attach 해줍니다.

 

파드 기본 저장소 동작 확인

  • POD 안의 컨테이너는 기본적으로 임시 파일 시스템입니다. 
# redis 파드를 한개 생성
[yu3papa@iworks ~]$ cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: redis
spec:
  terminationGracePeriodSeconds: 0
  containers:
  - name: redis
    image: redis
EOF
pod/redis created

# default 네임스페이스에 POD가 1개 생성되어 RUNNING 중입니다.
[yu3papa@iworks ~]$ kubectl get po
NAME    READY   STATUS    RESTARTS   AGE
redis   1/1     Running   0          82s

# redis 파드 내에 파일 작성
kubectl exec -it redis -- pwd
kubectl exec -it redis -- sh -c "echo hello > /data/hello.txt"
kubectl exec -it redis -- cat /data/hello.txt

# ps 설치
kubectl exec -it redis -- sh -c "apt update && apt install procps -y"
kubectl exec -it redis -- ps aux

# redis 프로세스 강제 종료 : 파드가 어떻게 되나요? hint) restartPolicy
kubectl exec -it redis -- kill 1
kubectl get pod

# redis 파드 내에 파일 확인
kubectl exec -it redis -- cat /data/hello.txt
kubectl exec -it redis -- ls -l /data

# 파드 삭제
kubectl delete pod redis

위 실습내용에서 중요한 점은 POD 안의 컨테이너 파일시스템은 일시적이라서, POD가 재시작되면 컨테이너 파일시스템은 사라지게 됩니다.


emptyDir 볼륨

  • POD와 라이프사이클이 동일한 볼륨
  • POD를 삭제하기전까지는 파일이 유지
# 모니터링
kubectl get pod -w

# redis 파드 생성
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: redis
spec:
  terminationGracePeriodSeconds: 0
  containers:
  - name: redis
    image: redis
    volumeMounts:
    - name: redis-storage
      mountPath: /data/redis
  volumes:
  - name: redis-storage
    emptyDir: {}
EOF

# redis 파드 내에 파일 작성
kubectl exec -it redis -- pwd
kubectl exec -it redis -- sh -c "echo hello > /data/redis/hello.txt"
kubectl exec -it redis -- cat /data/redis/hello.txt

# ps 설치
kubectl exec -it redis -- sh -c "apt update && apt install procps -y"
kubectl exec -it redis -- ps aux

# redis 프로세스 강제 종료 : 파드가 어떻게 되나요? hint) restartPolicy
kubectl exec -it redis -- kill 1
kubectl get pod

# redis 파드 내에 파일 확인
kubectl exec -it redis -- cat /data/redis/hello.txt
kubectl exec -it redis -- ls -l /data/redis

# 파드 삭제 후 파일 확인
kubectl delete pod redis
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: redis
spec:
  terminationGracePeriodSeconds: 0
  containers:
  - name: redis
    image: redis
    volumeMounts:
    - name: redis-storage
      mountPath: /data/redis
  volumes:
  - name: redis-storage
    emptyDir: {}
EOF

# redis 파드 내에 파일 확인
kubectl exec -it redis -- cat /data/redis/hello.txt
kubectl exec -it redis -- ls -l /data/redis

# 파드 삭제
kubectl delete pod redis
emptyDir 볼륨은 POD가 생성될 때 생성되고, POD가 삭제 될 때 같이 삭제되는 임시 볼륨입니다.
POD 내의 컨테이너가 삭제/재시작 되더라도 생명주기는 컨테이너 단위가 아니라 POD 단위이므로 emptyDir 볼륨은 삭제 되지 않고 계속 사용 가능

 

hostPath  볼륨

  • 노드의 파일 시스템에 있는 특정 파일 또는 디렉터리를 마운트
  • 시스템 수준 POD(일반적으로 DaemonSet에서 관리됨)는 노드의 파일을 읽거나, 노드의 파일 시스템을 통해 노드의 장치에 액세스해야 한다. 쿠버네티스는 hostPath 볼륨을 통해 이것을 가능하게 한다
  • 영구 스토리지 타입이지만 POD가 스케쥴된 노드에 Dependency가 존재하므로, 영구적으로 사용 가능하게 하려면 NAS형태(NFS, ...)의 Storage를 사용해야 함
# hostPath 볼륨을을 사용하는 시스템 POD 검사
kubectl describe po etcd-k8s-cp --namespace=kube-system
~~~
Conditions:
  Type              Status
  Initialized       True
  Ready             True
  ContainersReady   True
  PodScheduled      True
Volumes:
  etcd-certs:
    Type:          HostPath (bare host directory volume)
    Path:          /etc/kubernetes/pki/etcd
    HostPathType:  DirectoryOrCreate
  etcd-data:
    Type:          HostPath (bare host directory volume)
    Path:          /var/lib/etcd
    HostPathType:  DirectoryOrCreate
QoS Class:         BestEffort
Node-Selectors:    <none>
Tolerations:       :NoExecute
Events:            <none>

hostPath 타입의 볼륨은 emptyDir과는 다르게 POD가 스케쥴링된 노드의 파일시스템을 사용하므로 파일의 영속성은 보장되나 공유스토리지로 사용할 수는 없습니다.

 

호스트 Path 를 사용하는 PV/PVC

  • hostPath 타입의 볼륨이긴 하지만 local-path-provisioner 를 이용하여 PV, PVC 기반의 볼륨을 제공
  • POD가 스케쥴링된 노드의 특정 폴더를 사용 : /opt/local-path-provisioner
  • https://github.com/rancher/local-path-provisioner

 

# 배포
[yu3papa@iworks ~]$ kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/v0.0.31/deploy/local-path-storage.yaml
namespace/local-path-storage created
serviceaccount/local-path-provisioner-service-account created
role.rbac.authorization.k8s.io/local-path-provisioner-role created
clusterrole.rbac.authorization.k8s.io/local-path-provisioner-role created
rolebinding.rbac.authorization.k8s.io/local-path-provisioner-bind created
clusterrolebinding.rbac.authorization.k8s.io/local-path-provisioner-bind created
deployment.apps/local-path-provisioner created
storageclass.storage.k8s.io/local-path created
configmap/local-path-config created

...
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: local-path
provisioner: rancher.io/local-path
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Delete

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: local-path-config
  namespace: local-path-storage
data:
  config.json: |-
    {
            "nodePathMap":[
            {
                    "node":"DEFAULT_PATH_FOR_NON_LISTED_NODES",
                    "paths":["/opt/local-path-provisioner"]
            }
            ]
    }
  setup: |-
    #!/bin/sh
    set -eu
    mkdir -m 0777 -p "$VOL_DIR"
  teardown: |-
    #!/bin/sh
    set -eu
    rm -rf "$VOL_DIR"
...

# 확인
[yu3papa@iworks ~]$ kubectl -n local-path-storage get all
NAME                                         READY   STATUS    RESTARTS   AGE
pod/local-path-provisioner-84967477f-x948v   1/1     Running   0          19s

NAME                                     READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/local-path-provisioner   1/1     1            1           19s

NAME                                               DESIRED   CURRENT   READY   AGE
replicaset.apps/local-path-provisioner-84967477f   1         1         1       19s


[yu3papa@iworks ~]$ kubectl get pod -n local-path-storage -owide
NAME                                     READY   STATUS    RESTARTS   AGE   IP              NODE                                               NOMINATED NODE   READINESS GATES
local-path-provisioner-84967477f-x948v   1/1     Running   0          44s   192.168.3.232   ip-192-168-3-188.ap-northeast-2.compute.internal   <none>           <none>


[yu3papa@iworks ~]$ kubectl describe cm -n local-path-storage local-path-config
Name:         local-path-config
Namespace:    local-path-storage
Labels:       <none>
Annotations:  <none>

Data
====
config.json:
----
{
        "nodePathMap":[
        {
                "node":"DEFAULT_PATH_FOR_NON_LISTED_NODES",
                "paths":["/opt/local-path-provisioner"]
        }
        ]
}

helperPod.yaml:
----
apiVersion: v1
kind: Pod
metadata:
  name: helper-pod
spec:
  priorityClassName: system-node-critical
  tolerations:
    - key: node.kubernetes.io/disk-pressure
      operator: Exists
      effect: NoSchedule
  containers:
  - name: helper-pod
    image: busybox
    imagePullPolicy: IfNotPresent

setup:
----
#!/bin/sh
set -eu
mkdir -m 0777 -p "$VOL_DIR"

teardown:
----
#!/bin/sh
set -eu
rm -rf "$VOL_DIR"


BinaryData
====

Events:  <none>


[yu3papa@iworks ~]$ kubectl get sc
NAME         PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
gp2          kubernetes.io/aws-ebs   Delete          WaitForFirstConsumer   false                  4h11m
local-path   rancher.io/local-path   Delete          WaitForFirstConsumer   false                  60s


[yu3papa@iworks ~]$ kubectl get sc local-path
NAME         PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
local-path   rancher.io/local-path   Delete          WaitForFirstConsumer   false                  67s

 

  • 동작 확인
    • PV/PVC 를 사용하는 파드 생성
# PVC 생성
[yu3papa@iworks ~]$ cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: localpath-claim
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: local-path
  resources:
    requests:
      storage: 1Gi
EOF
persistentvolumeclaim/localpath-claim created

# PVC 확인
[yu3papa@iworks ~]$ kubectl get pvc
NAME              STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
localpath-claim   Pending                                      local-path     <unset>                 13s


[yu3papa@iworks ~]$ kubectl describe pvc
Name:          localpath-claim
Namespace:     default
StorageClass:  local-path
Status:        Pending
Volume:
Labels:        <none>
Annotations:   <none>
Finalizers:    [kubernetes.io/pvc-protection]
Capacity:
Access Modes:
VolumeMode:    Filesystem
Used By:       <none>
Events:
  Type    Reason                Age                From                         Message
  ----    ------                ----               ----                         -------
  Normal  WaitForFirstConsumer  11s (x2 over 21s)  persistentvolume-controller  waiting for first consumer to be created before binding

 # 파드 생성
[yu3papa@iworks ~]$ cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: app
spec:
  terminationGracePeriodSeconds: 3
  containers:
  - name: app
    image: centos
    command: ["/bin/sh"]
    args: ["-c", "while true; do echo \$(date -u) >> /data/out.txt; sleep 5; done"]
    volumeMounts:
    - name: persistent-storage
      mountPath: /data
  volumes:
  - name: persistent-storage
    persistentVolumeClaim:
      claimName: localpath-claim
EOF
pod/app created

# 파드 확인
[yu3papa@iworks ~]$ kubectl get pod,pv,pvc
NAME      READY   STATUS    RESTARTS   AGE
pod/app   1/1     Running   0          22s

NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                     STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
persistentvolume/pvc-d8465f8f-a572-4d77-9ce9-3d587f3df05c   1Gi        RWO            Delete           Bound    default/localpath-claim   local-path     <unset>                          14s

NAME                                    STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
persistentvolumeclaim/localpath-claim   Bound    pvc-d8465f8f-a572-4d77-9ce9-3d587f3df05c   1Gi        RWO            local-path     <unset>                 60s

# Node Affinity 확인
[yu3papa@iworks ~]$ kubectl describe pv
Name:              pvc-d8465f8f-a572-4d77-9ce9-3d587f3df05c
Labels:            <none>
Annotations:       local.path.provisioner/selected-node: ip-192-168-1-193.ap-northeast-2.compute.internal
                   pv.kubernetes.io/provisioned-by: rancher.io/local-path
Finalizers:        [kubernetes.io/pv-protection]
StorageClass:      local-path
Status:            Bound
Claim:             default/localpath-claim
Reclaim Policy:    Delete
Access Modes:      RWO
VolumeMode:        Filesystem
Capacity:          1Gi
Node Affinity:
  Required Terms:
    Term 0:        kubernetes.io/hostname in [ip-192-168-1-193.ap-northeast-2.compute.internal]
Message:
Source:
    Type:          HostPath (bare host directory volume)
    Path:          /opt/local-path-provisioner/pvc-d8465f8f-a572-4d77-9ce9-3d587f3df05c_default_localpath-claim
    HostPathType:  DirectoryOrCreate
Events:            <none>
[yu3papa@iworks ~]$ kubectl exec -it app -- tail -f /data/out.txt
Sat Feb 22 15:18:45 UTC 2025
Sat Feb 22 15:18:50 UTC 2025
...(생략)...
^Ccommand terminated with exit code 130

 # 워커노드 중 현재 파드가 배포되어 있다만, 아래 경로에 out.txt 파일 존재 확인
[yu3papa@iworks ~]$ for node in $N1 $N2 $N3; do ssh -i aews-yu3papa.pem ec2-user@$node tree /opt/local-path-provisioner; done
/opt/local-path-provisioner
└── pvc-d8465f8f-a572-4d77-9ce9-3d587f3df05c_default_localpath-claim
    └── out.txt

1 directory, 1 file
/opt/local-path-provisioner [error opening dir]

0 directories, 0 files
/opt/local-path-provisioner [error opening dir]

0 directories, 0 files

# 해당 워커노드 자체에서 out.txt 파일 확인 : 아래 굵은 부분은 각자 실습 환경에 따라 다름
[yu3papa@iworks ~]$ ssh -i aews-yu3papa.pem ec2-user@$N1 tail -f /opt/local-path-provisioner/pvc-d8465f8f-a572-4d77-9ce9-3d587f3df05c_default_localpath-claim/out.txt
Sat Feb 22 15:20:35 UTC 2025
Sat Feb 22 15:20:40 UTC 2025
Sat Feb 22 15:20:45 UTC 2025
Sat Feb 22 15:20:50 UTC 2025
Sat Feb 22 15:20:55 UTC 2025
...(생략)...
^Ccommand terminated with exit code 130

  • 파드 삭제 후 파드 재생성해서 데이터 유지 되는지 확인
# 파드 삭제 후 PV/PVC 확인
[yu3papa@iworks ~]$ kubectl delete pod app
pod "app" deleted

# POD가 삭제되어도 파일은 영속성을 유지함
[yu3papa@iworks ~]$ for node in $N1 $N2 $N3; do ssh -i aews-yu3papa.pem ec2-user@$node tree /opt/local-path-provisioner; done
/opt/local-path-provisioner
└── pvc-d8465f8f-a572-4d77-9ce9-3d587f3df05c_default_localpath-claim
    └── out.txt

1 directory, 1 file
/opt/local-path-provisioner [error opening dir]

0 directories, 0 files
/opt/local-path-provisioner [error opening dir]

0 directories, 0 files

# 파드 다시 실행
[yu3papa@iworks ~]$ cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: app
spec:
  terminationGracePeriodSeconds: 3
  containers:
  - name: app
    image: centos
    command: ["/bin/sh"]
    args: ["-c", "while true; do echo \$(date -u) >> /data/out.txt; sleep 5; done"]
    volumeMounts:
    - name: persistent-storage
      mountPath: /data
  volumes:
  - name: persistent-storage
    persistentVolumeClaim:
      claimName: localpath-claim
EOF
pod/app created

[yu3papa@iworks ~]$ kubectl get po
NAME   READY   STATUS    RESTARTS   AGE
app    1/1     Running   0          11s

# 파일 영속성 확인
[yu3papa@iworks ~]$ kubectl exec -it app -- head /data/out.txt
Sat Feb 22 15:18:45 UTC 2025
Sat Feb 22 15:18:50 UTC 2025
Sat Feb 22 15:18:55 UTC 2025
Sat Feb 22 15:19:00 UTC 2025
Sat Feb 22 15:19:05 UTC 2025
Sat Feb 22 15:19:10 UTC 2025
Sat Feb 22 15:19:15 UTC 2025
Sat Feb 22 15:19:20 UTC 2025
Sat Feb 22 15:19:25 UTC 2025
Sat Feb 22 15:19:30 UTC 2025

[yu3papa@iworks ~]$ kubectl exec -it app -- tail -f /data/out.txt
Sat Feb 22 15:28:11 UTC 2025
Sat Feb 22 15:28:16 UTC 2025
Sat Feb 22 15:28:21 UTC 2025
<<<<< POD가 삭제되고 다시 만들어지기까지 시간정보가 비어 있는 구간 >>>>>
Sat Feb 22 15:29:00 UTC 2025
Sat Feb 22 15:29:05 UTC 2025
Sat Feb 22 15:29:10 UTC 2025
Sat Feb 22 15:29:15 UTC 2025
Sat Feb 22 15:29:20 UTC 2025
Sat Feb 22 15:29:25 UTC 2025
Sat Feb 22 15:29:30 UTC 2025
Sat Feb 22 15:29:35 UTC 2025
Sat Feb 22 15:29:40 UTC 2025
^Ccommand terminated with exit code 130
  • 다음 실습을 위해서 파드와 PVC 삭제
# 파드 삭제
[yu3papa@iworks ~]$ kubectl delete pod app
pod "app" deleted

# POD를 삭제해도 PVC, PV는 지워지지 않음
[yu3papa@iworks ~]$ kubectl get pv,pvc
NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                     STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
persistentvolume/pvc-d8465f8f-a572-4d77-9ce9-3d587f3df05c   1Gi        RWO            Delete           Bound    default/localpath-claim   local-path     <unset>                          16m

NAME                                    STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
persistentvolumeclaim/localpath-claim   Bound    pvc-d8465f8f-a572-4d77-9ce9-3d587f3df05c   1Gi        RWO            local-path     <unset>                 17m

# PVC 삭제하면 기본 ReclaimPolicy가 DELETE 여서 PV 도 삭제되고  /opt/local-path-provisioner 밑의 디렉토리도 삭제됨
[yu3papa@iworks ~]$ kubectl delete pvc localpath-claim
persistentvolumeclaim "localpath-claim" deleted

# 확인
[yu3papa@iworks ~]$ kubectl get pv
No resources found

[yu3papa@iworks ~]$ for node in $N1 $N2 $N3; do ssh -i aews-yu3papa.pem ec2-user@$node tree /opt/local-path-provisioner; done
/opt/local-path-provisioner

0 directories, 0 files
/opt/local-path-provisioner [error opening dir]

0 directories, 0 files
/opt/local-path-provisioner [error opening dir]

0 directories, 0 files

 

4. AWS EBS Controller

ebs-csi-controller

  • AWS CSI 드라이버는 크게 2개 구성요소가 있습니다. AWS API를 호출하면서 AWS 스토리지를 관리하는 CSI-Controller와 kubelet과 상호작용하면서 AWS스토리지를 pod에 마운트하는 CSI-Node가 있습니다. 

https://malwareanalysis.tistory.com/598

  • persistentvolume, persistentvolumeclaim의 accessModes는 ReadWriteOnce로 설정해야 합니다.
  • EBS스토리지 기본 설정이 동일 AZ에 있는 EC2 인스턴스(에 배포된 파드)에 연결해야 합니다.

설치 : Amazon EBS CSI driver as an Amazon EKS add-on

# 아래는 aws-ebs-csi-driver 전체 버전 정보와 기본 설치 버전(True) 정보 확인
[yu3papa@iworks ~]$ aws eks describe-addon-versions \
    --addon-name aws-ebs-csi-driver \
    --kubernetes-version 1.31 \
    --query "addons[].addonVersions[].[addonVersion, compatibilities[].defaultVersion]" \
    --output text
v1.39.0-eksbuild.1
True
v1.38.1-eksbuild.2
False
v1.38.1-eksbuild.1
False
v1.37.0-eksbuild.2
False
v1.37.0-eksbuild.1
False
v1.36.0-eksbuild.2
False
v1.36.0-eksbuild.1
False
v1.35.0-eksbuild.2
False
v1.35.0-eksbuild.1
False
v1.34.0-eksbuild.1
False
v1.33.0-eksbuild.1
False
v1.32.0-eksbuild.1
False
v1.31.0-eksbuild.1
False
v1.30.0-eksbuild.1
False
v1.29.1-eksbuild.1
False

# ISRA 설정 : AWS관리형 정책 AmazonEBSCSIDriverPolicy 사용
[yu3papa@iworks ~]$ eksctl create iamserviceaccount \
  --name ebs-csi-controller-sa \
  --namespace kube-system \
  --cluster ${CLUSTER_NAME} \
  --attach-policy-arn arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy \
  --approve \
  --role-only \
  --role-name AmazonEKS_EBS_CSI_DriverRole
2025-02-23 00:56:47 [ℹ]  1 existing iamserviceaccount(s) (kube-system/aws-load-balancer-controller) will be excluded
2025-02-23 00:56:47 [ℹ]  1 iamserviceaccount (kube-system/ebs-csi-controller-sa) was included (based on the include/exclude rules)
2025-02-23 00:56:47 [!]  serviceaccounts in Kubernetes will not be created or modified, since the option --role-only is used
2025-02-23 00:56:47 [ℹ]  1 task: { create IAM role for serviceaccount "kube-system/ebs-csi-controller-sa" }
2025-02-23 00:56:47 [ℹ]  building iamserviceaccount stack "eksctl-myeks-addon-iamserviceaccount-kube-system-ebs-csi-controller-sa"
2025-02-23 00:56:47 [ℹ]  deploying stack "eksctl-myeks-addon-iamserviceaccount-kube-system-ebs-csi-controller-sa"
2025-02-23 00:56:47 [ℹ]  waiting for CloudFormation stack "eksctl-myeks-addon-iamserviceaccount-kube-system-ebs-csi-controller-sa"
2025-02-23 00:57:17 [ℹ]  waiting for CloudFormation stack "eksctl-myeks-addon-iamserviceaccount-kube-system-ebs-csi-controller-sa"

# ISRA 확인
[yu3papa@iworks ~]$ eksctl get iamserviceaccount --cluster ${CLUSTER_NAME}
NAMESPACE       NAME                            ROLE ARN
kube-system     aws-load-balancer-controller    arn:aws:iam::390402552337:role/eksctl-myeks-addon-iamserviceaccount-kube-sys-Role1-zzbfj7a4hBaW
kube-system     ebs-csi-controller-sa           arn:aws:iam::390402552337:role/AmazonEKS_EBS_CSI_DriverRole

# Amazon EBS CSI driver addon 배포(설치)
[yu3papa@iworks ~]$ export ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text)
[yu3papa@iworks ~]$ eksctl create addon --name aws-ebs-csi-driver --cluster ${CLUSTER_NAME} --service-account-role-arn arn:aws:iam::${ACCOUNT_ID}:role/AmazonEKS_EBS_CSI_DriverRole --force
2025-02-23 00:57:59 [ℹ]  Kubernetes version "1.31" in use by cluster "myeks"
2025-02-23 00:58:00 [ℹ]  IRSA is set for "aws-ebs-csi-driver" addon; will use this to configure IAM permissions
2025-02-23 00:58:00 [!]  the recommended way to provide IAM permissions for "aws-ebs-csi-driver" addon is via pod identity associations; after addon creation is completed, run `eksctl utils migrate-to-pod-identity`
2025-02-23 00:58:00 [ℹ]  using provided ServiceAccountRoleARN "arn:aws:iam::390402552337:role/AmazonEKS_EBS_CSI_DriverRole"
2025-02-23 00:58:00 [ℹ]  creating addon: aws-ebs-csi-driver
[yu3papa@iworks ~]$ kubectl get sa -n kube-system ebs-csi-controller-sa -o yaml | head -5
apiVersion: v1
automountServiceAccountToken: true
kind: ServiceAccount
metadata:
  annotations:

# 확인
[yu3papa@iworks ~]$ eksctl get addon --cluster ${CLUSTER_NAME}
2025-02-23 00:58:24 [ℹ]  Kubernetes version "1.31" in use by cluster "myeks"
2025-02-23 00:58:24 [ℹ]  getting all addons
2025-02-23 00:58:26 [ℹ]  to see issues for an addon run `eksctl get addon --name <addon-name> --cluster <cluster-name>`
NAME                    VERSION                 STATUS          ISSUES  IAMROLE                                                                         UPDATE AVAILABLE        CONFIGURATION VALUES              POD IDENTITY ASSOCIATION ROLES
aws-ebs-csi-driver      v1.39.0-eksbuild.1      CREATING        0       arn:aws:iam::390402552337:role/AmazonEKS_EBS_CSI_DriverRole
coredns                 v1.11.4-eksbuild.2      ACTIVE          0
kube-proxy              v1.31.3-eksbuild.2      ACTIVE          0
metrics-server          v0.7.2-eksbuild.2       ACTIVE          0
vpc-cni                 v1.19.2-eksbuild.5      ACTIVE          0       arn:aws:iam::390402552337:role/eksctl-myeks-addon-vpc-cni-Role1-fr0GM10fLspD                            enableNetworkPolicy: "true"

[yu3papa@iworks ~]$ kubectl get deploy,ds -l=app.kubernetes.io/name=aws-ebs-csi-driver -n kube-system
NAME                                 READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/ebs-csi-controller   2/2     2            2           24s

NAME                                  DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR              AGE
daemonset.apps/ebs-csi-node           3         3         3       3            3           kubernetes.io/os=linux     24s
daemonset.apps/ebs-csi-node-windows   0         0         0       0            0           kubernetes.io/os=windows   24s

[yu3papa@iworks ~]$ kubectl get pod -n kube-system -l 'app in (ebs-csi-controller,ebs-csi-node)'
NAME                                 READY   STATUS    RESTARTS   AGE
ebs-csi-controller-7f8f8cb84-c44bx   6/6     Running   0          30s
ebs-csi-controller-7f8f8cb84-xp72g   6/6     Running   0          30s
ebs-csi-node-lx7gk                   3/3     Running   0          30s
ebs-csi-node-t9gjr                   3/3     Running   0          30s
ebs-csi-node-z55h4                   3/3     Running   0          30s

[yu3papa@iworks ~]$ kubectl get pod -n kube-system -l app.kubernetes.io/component=csi-driver
NAME                                 READY   STATUS    RESTARTS   AGE
ebs-csi-controller-7f8f8cb84-c44bx   6/6     Running   0          35s
ebs-csi-controller-7f8f8cb84-xp72g   6/6     Running   0          35s
ebs-csi-node-lx7gk                   3/3     Running   0          35s
ebs-csi-node-t9gjr                   3/3     Running   0          35s
ebs-csi-node-z55h4                   3/3     Running   0          35s

# ebs-csi-controller 파드에 6개 컨테이너 확인
[yu3papa@iworks ~]$ kubectl get pod -n kube-system -l app=ebs-csi-controller -o jsonpath='{.items[0].spec.containers[*].name}' ; echo
ebs-plugin csi-provisioner csi-attacher csi-snapshotter csi-resizer liveness-probe

# csinodes 확인
[yu3papa@iworks ~]$ kubectl api-resources | grep -i csi
csidrivers                                       storage.k8s.io/v1                 false        CSIDriver
csinodes                                         storage.k8s.io/v1                 false        CSINode
csistoragecapacities                             storage.k8s.io/v1                 true         CSIStorageCapacity

[yu3papa@iworks ~]$ kubectl get csinodes
NAME                                               DRIVERS   AGE
ip-192-168-1-193.ap-northeast-2.compute.internal   1         4h47m
ip-192-168-2-200.ap-northeast-2.compute.internal   1         4h47m
ip-192-168-3-188.ap-northeast-2.compute.internal   1         4h47m

[yu3papa@iworks ~]$ kubectl describe csinodes
Name:               ip-192-168-1-193.ap-northeast-2.compute.internal
Labels:             <none>
Annotations:        storage.alpha.kubernetes.io/migrated-plugins:
                      kubernetes.io/aws-ebs,kubernetes.io/azure-disk,kubernetes.io/azure-file,kubernetes.io/cinder,kubernetes.io/gce-pd,kubernetes.io/portworx-v...
CreationTimestamp:  Sat, 22 Feb 2025 20:11:47 +0900
Spec:
  Drivers:
    ebs.csi.aws.com:
      Node ID:  i-02c24086e5ef7bd2f
      Allocatables:
        Count:        25
      Topology Keys:  [kubernetes.io/os topology.ebs.csi.aws.com/zone topology.kubernetes.io/zone]
Events:               <none>


Name:               ip-192-168-2-200.ap-northeast-2.compute.internal
Labels:             <none>
Annotations:        storage.alpha.kubernetes.io/migrated-plugins:
                      kubernetes.io/aws-ebs,kubernetes.io/azure-disk,kubernetes.io/azure-file,kubernetes.io/cinder,kubernetes.io/gce-pd,kubernetes.io/portworx-v...
CreationTimestamp:  Sat, 22 Feb 2025 20:11:40 +0900
Spec:
  Drivers:
    ebs.csi.aws.com:
      Node ID:  i-064da1db46bbd4d0e
      Allocatables:
        Count:        25
      Topology Keys:  [kubernetes.io/os topology.ebs.csi.aws.com/zone topology.kubernetes.io/zone]
Events:               <none>


Name:               ip-192-168-3-188.ap-northeast-2.compute.internal
Labels:             <none>
Annotations:        storage.alpha.kubernetes.io/migrated-plugins:
                      kubernetes.io/aws-ebs,kubernetes.io/azure-disk,kubernetes.io/azure-file,kubernetes.io/cinder,kubernetes.io/gce-pd,kubernetes.io/portworx-v...
CreationTimestamp:  Sat, 22 Feb 2025 20:11:43 +0900
Spec:
  Drivers:
    ebs.csi.aws.com:
      Node ID:  i-0fabeb5dd693c0671
      Allocatables:
        Count:        25
      Topology Keys:  [kubernetes.io/os topology.ebs.csi.aws.com/zone topology.kubernetes.io/zone]
Events:               <none>

[yu3papa@iworks ~]$ kubectl get csidrivers
NAME              ATTACHREQUIRED   PODINFOONMOUNT   STORAGECAPACITY   TOKENREQUESTS   REQUIRESREPUBLISH   MODES        AGE
ebs.csi.aws.com   true             false            false             <unset>         false               Persistent   81s
efs.csi.aws.com   false            false            false             <unset>         false               Persistent   4h56m

[yu3papa@iworks ~]$ kubectl describe csidrivers ebs.csi.aws.com
Name:         ebs.csi.aws.com
Namespace:
Labels:       app.kubernetes.io/component=csi-driver
              app.kubernetes.io/managed-by=EKS
              app.kubernetes.io/name=aws-ebs-csi-driver
              app.kubernetes.io/version=1.39.0
Annotations:  <none>
API Version:  storage.k8s.io/v1
Kind:         CSIDriver
Metadata:
  Creation Timestamp:  2025-02-22T15:58:08Z
  Resource Version:    64188
  UID:                 114d32aa-bdac-4620-b619-273548eb63c9
Spec:
  Attach Required:     true
  Fs Group Policy:     ReadWriteOnceWithFSType
  Pod Info On Mount:   false
  Requires Republish:  false
  Se Linux Mount:      false
  Storage Capacity:    false
  Volume Lifecycle Modes:
    Persistent
Events:  <none>

# (참고) 노드에 최대 EBS 부착 수량 변경
[yu3papa@iworks ~]$ aws eks update-addon --cluster-name ${CLUSTER_NAME} --addon-name aws-ebs-csi-driver \
  --addon-version v1.39.0-eksbuild.1 --configuration-values '{
    "node": {
      "volumeAttachLimit": 31,
      "enableMetrics": true
    }
  }'
{
    "update": {
        "id": "c763589d-e192-3cc4-8049-3db99aeb9909",
        "status": "InProgress",
        "type": "AddonUpdate",
        "params": [
            {
                "type": "AddonVersion",
                "value": "v1.39.0-eksbuild.1"
            },
            {
                "type": "ConfigurationValues",
                "value": "{\n    \"node\": {\n      \"volumeAttachLimit\": 31,\n      \"enableMetrics\": true\n    }\n  }"
            }
        ],
        "createdAt": "2025-02-23T01:00:04.223000+09:00",
        "errors": []
    }
}

# 확인
[yu3papa@iworks ~]$ kubectl get ds -n kube-system ebs-csi-node -o yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  annotations:
    deprecated.daemonset.template.generation: "2"
  creationTimestamp: "2025-02-22T15:58:08Z"
  generation: 2
  labels:
    app.kubernetes.io/component: csi-driver
    app.kubernetes.io/managed-by: EKS
    app.kubernetes.io/name: aws-ebs-csi-driver
    app.kubernetes.io/version: 1.39.0
  name: ebs-csi-node
  namespace: kube-system
  resourceVersion: "65016"
  uid: fc40a2f9-e582-4238-a962-a0259b87c22a
spec:
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app: ebs-csi-node
      app.kubernetes.io/name: aws-ebs-csi-driver
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: ebs-csi-node
        app.kubernetes.io/component: csi-driver
        app.kubernetes.io/managed-by: EKS
        app.kubernetes.io/name: aws-ebs-csi-driver
        app.kubernetes.io/version: 1.39.0
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: eks.amazonaws.com/compute-type
                operator: NotIn
                values:
                - fargate
                - auto
                - hybrid
              - key: node.kubernetes.io/instance-type
                operator: NotIn
                values:
                - a1.medium
                - a1.large
                - a1.xlarge
                - a1.2xlarge
                - a1.4xlarge
      containers:
      - args:
        - node
        - --endpoint=$(CSI_ENDPOINT)
        - --http-endpoint=0.0.0.0:3302
        - --csi-mount-point-prefix=/var/lib/kubelet/plugins/kubernetes.io/csi/ebs.csi.aws.com/
        - --volume-attach-limit=31
        - --logging-format=text
        - --v=2
        env:
        - name: CSI_ENDPOINT
          value: unix:/csi/csi.sock
        - name: CSI_NODE_NAME
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: spec.nodeName
        image: 602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/eks/aws-ebs-csi-driver:v1.39.0
        imagePullPolicy: IfNotPresent
        lifecycle:
          preStop:
            exec:
              command:
              - /bin/aws-ebs-csi-driver
              - pre-stop-hook
        livenessProbe:
          failureThreshold: 5
          httpGet:
            path: /healthz
            port: healthz
            scheme: HTTP
          initialDelaySeconds: 10
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 3
        name: ebs-plugin
        ports:
        - containerPort: 9808
          name: healthz
          protocol: TCP
        resources:
          limits:
            memory: 256Mi
          requests:
            cpu: 10m
            memory: 40Mi
        securityContext:
          privileged: true
          readOnlyRootFilesystem: true
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
        volumeMounts:
        - mountPath: /var/lib/kubelet
          mountPropagation: Bidirectional
          name: kubelet-dir
        - mountPath: /csi
          name: plugin-dir
        - mountPath: /dev
          name: device-dir
      - args:
        - --csi-address=$(ADDRESS)
        - --kubelet-registration-path=$(DRIVER_REG_SOCK_PATH)
        - --v=2
        env:
        - name: ADDRESS
          value: /csi/csi.sock
        - name: DRIVER_REG_SOCK_PATH
          value: /var/lib/kubelet/plugins/ebs.csi.aws.com/csi.sock
        image: 602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/eks/csi-node-driver-registrar:v2.13.0-eks-1-31-12
        imagePullPolicy: IfNotPresent
        livenessProbe:
          exec:
            command:
            - /csi-node-driver-registrar
            - --kubelet-registration-path=$(DRIVER_REG_SOCK_PATH)
            - --mode=kubelet-registration-probe
          failureThreshold: 3
          initialDelaySeconds: 30
          periodSeconds: 90
          successThreshold: 1
          timeoutSeconds: 15
        name: node-driver-registrar
        resources:
          limits:
            memory: 256Mi
          requests:
            cpu: 10m
            memory: 40Mi
        securityContext:
          allowPrivilegeEscalation: false
          readOnlyRootFilesystem: true
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
        volumeMounts:
        - mountPath: /csi
          name: plugin-dir
        - mountPath: /registration
          name: registration-dir
        - mountPath: /var/lib/kubelet/plugins/ebs.csi.aws.com/
          name: probe-dir
      - args:
        - --csi-address=/csi/csi.sock
        image: 602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/eks/livenessprobe:v2.14.0-eks-1-31-12
        imagePullPolicy: IfNotPresent
        name: liveness-probe
        resources:
          limits:
            memory: 256Mi
          requests:
            cpu: 10m
            memory: 40Mi
        securityContext:
          allowPrivilegeEscalation: false
          readOnlyRootFilesystem: true
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
        volumeMounts:
        - mountPath: /csi
          name: plugin-dir
      dnsPolicy: ClusterFirst
      nodeSelector:
        kubernetes.io/os: linux
      priorityClassName: system-node-critical
      restartPolicy: Always
      schedulerName: default-scheduler
      securityContext:
        fsGroup: 0
        runAsGroup: 0
        runAsNonRoot: false
        runAsUser: 0
      serviceAccount: ebs-csi-node-sa
      serviceAccountName: ebs-csi-node-sa
      terminationGracePeriodSeconds: 30
      tolerations:
      - operator: Exists
      volumes:
      - hostPath:
          path: /var/lib/kubelet
          type: Directory
        name: kubelet-dir
      - hostPath:
          path: /var/lib/kubelet/plugins/ebs.csi.aws.com/
          type: DirectoryOrCreate
        name: plugin-dir
      - hostPath:
          path: /var/lib/kubelet/plugins_registry/
          type: Directory
        name: registration-dir
      - hostPath:
          path: /dev
          type: Directory
        name: device-dir
      - emptyDir: {}
        name: probe-dir
  updateStrategy:
    rollingUpdate:
      maxSurge: 0
      maxUnavailable: 10%
    type: RollingUpdate
status:
  currentNumberScheduled: 3
  desiredNumberScheduled: 3
  numberAvailable: 3
  numberMisscheduled: 0
  numberReady: 3
  observedGeneration: 2
  updatedNumberScheduled: 3

[yu3papa@iworks ~]$ kubectl describe csinodes
Name:               ip-192-168-1-193.ap-northeast-2.compute.internal
Labels:             <none>
Annotations:        storage.alpha.kubernetes.io/migrated-plugins:
                      kubernetes.io/aws-ebs,kubernetes.io/azure-disk,kubernetes.io/azure-file,kubernetes.io/cinder,kubernetes.io/gce-pd,kubernetes.io/portworx-v...
CreationTimestamp:  Sat, 22 Feb 2025 20:11:47 +0900
Spec:
  Drivers:
    ebs.csi.aws.com:
      Node ID:  i-02c24086e5ef7bd2f
      Allocatables:
        Count:        31
      Topology Keys:  [kubernetes.io/os topology.ebs.csi.aws.com/zone topology.kubernetes.io/zone]
Events:               <none>


Name:               ip-192-168-2-200.ap-northeast-2.compute.internal
Labels:             <none>
Annotations:        storage.alpha.kubernetes.io/migrated-plugins:
                      kubernetes.io/aws-ebs,kubernetes.io/azure-disk,kubernetes.io/azure-file,kubernetes.io/cinder,kubernetes.io/gce-pd,kubernetes.io/portworx-v...
CreationTimestamp:  Sat, 22 Feb 2025 20:11:40 +0900
Spec:
  Drivers:
    ebs.csi.aws.com:
      Node ID:  i-064da1db46bbd4d0e
      Allocatables:
        Count:        31
      Topology Keys:  [kubernetes.io/os topology.ebs.csi.aws.com/zone topology.kubernetes.io/zone]
Events:               <none>


Name:               ip-192-168-3-188.ap-northeast-2.compute.internal
Labels:             <none>
Annotations:        storage.alpha.kubernetes.io/migrated-plugins:
                      kubernetes.io/aws-ebs,kubernetes.io/azure-disk,kubernetes.io/azure-file,kubernetes.io/cinder,kubernetes.io/gce-pd,kubernetes.io/portworx-v...
CreationTimestamp:  Sat, 22 Feb 2025 20:11:43 +0900
Spec:
  Drivers:
    ebs.csi.aws.com:
      Node ID:  i-0fabeb5dd693c0671
      Allocatables:
        Count:        31
      Topology Keys:  [kubernetes.io/os topology.ebs.csi.aws.com/zone topology.kubernetes.io/zone]
Events:               <none>

gp3 스토리지 클래스 생성

 

스토리지 클래스

이 문서는 쿠버네티스의 스토리지클래스의 개념을 설명한다. 볼륨과 퍼시스턴트 볼륨에 익숙해지는 것을 권장한다. 소개 스토리지클래스는 관리자가 제공하는 스토리지의 "classes"를 설명할 수

kubernetes.io

 

aws-ebs-csi-driver/docs/parameters.md at master · kubernetes-sigs/aws-ebs-csi-driver

CSI driver for Amazon EBS https://aws.amazon.com/ebs/ - kubernetes-sigs/aws-ebs-csi-driver

github.com

 

# gp3 스토리지 클래스 생성
[yu3papa@iworks ~]$ kubectl get sc
NAME         PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
gp2          kubernetes.io/aws-ebs   Delete          WaitForFirstConsumer   false                  5h11m
local-path   rancher.io/local-path   Delete          WaitForFirstConsumer   false                  61m

[yu3papa@iworks ~]$ cat <<EOF | kubectl apply -f -
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: gp3
  annotations:
    storageclass.kubernetes.io/is-default-class: "true"
allowVolumeExpansion: true
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer
parameters:
  type: gp3
  #iops: "5000"
  #throughput: "250"
  allowAutoIOPSPerGBIncrease: 'true'
  encrypted: 'true'
  fsType: xfs # 기본값이 ext4
EOF
storageclass.storage.k8s.io/gp3 created

[yu3papa@iworks ~]$ kubectl get sc
NAME            PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
gp2             kubernetes.io/aws-ebs   Delete          WaitForFirstConsumer   false                  5h11m
gp3 (default)   ebs.csi.aws.com         Delete          WaitForFirstConsumer   true                   5s
local-path      rancher.io/local-path   Delete          WaitForFirstConsumer   false                  61m

[yu3papa@iworks ~]$ kubectl describe sc gp3 | grep Parameters
Parameters:            allowAutoIOPSPerGBIncrease=true,encrypted=true,fsType=xfs,type=gp3
  • volumeBindingMode 필드는 [볼륨 바인딩과 동적 프로비저닝] 의 시작 시기를 제어합니다. 설정되어 있지 않으면, Immediate 모드가 기본으로 사용된다.
    • Immediate 모드는 퍼시스턴트볼륨클레임이 생성되면 볼륨 바인딩과 동적 프로비저닝이 즉시 발생하는 것을 나타냅니다. 토폴로지 제약이 있고 클러스터의 모든 노드에서 전역적으로 접근할 수 없는 스토리지 백엔드의 경우, 파드의 스케줄링 요구 사항에 대한 파악없이 퍼시스턴트볼륨이 바인딩되거나 프로비저닝되며, 이로 인해 스케줄되지 않은 파드가 발생할 수 있습니다.
    • WaitForFirstConsumer 모드를 지정해서 이 문제를 해결할 수 있는데 이 모드는 퍼시스턴트볼륨클레임을 사용하는 파드가 생성될 때까지 퍼시스턴트볼륨의 바인딩과 프로비저닝을 지연시킵니다. 퍼시스턴트볼륨은 파드의 스케줄링 제약 조건에 의해 지정된 토폴로지에 따라 선택되거나 프로비저닝 됩니다. 여기에는 [리소스 요구 사항], [노드 셀렉터](, [파드 어피니티(affinity)와 안티-어피니티(anti-affinity)] 그리고 [테인트(taint)와 톨러레이션(toleration)]  포함됩니다.

PVC/PV 파드 테스트

# 워커노드의 EBS 볼륨 확인 : tag(키/값) 필터링
[yu3papa@iworks ~]$ aws ec2 describe-volumes --filters Name=tag:Name,Values=$CLUSTER_NAME-ng1-Node --output table
--------------------------------------------------------------
|                       DescribeVolumes                      |
+------------------------------------------------------------+
||                          Volumes                         ||
|+---------------------+------------------------------------+|
||  AvailabilityZone   |  ap-northeast-2b                   ||
||  CreateTime         |  2025-02-22T11:10:59.623000+00:00  ||
||  Encrypted          |  False                             ||
||  Iops               |  3000                              ||
||  MultiAttachEnabled |  False                             ||
||  Size               |  120                               ||
||  SnapshotId         |  snap-09234f0993e1a70d3            ||
||  State              |  in-use                            ||
||  Throughput         |  125                               ||
||  VolumeId           |  vol-0a83cf8d8a74eb723             ||
||  VolumeType         |  gp3                               ||
|+---------------------+------------------------------------+|
|||                       Attachments                      |||
||+------------------------+-------------------------------+||
|||  AttachTime            |  2025-02-22T11:10:59+00:00    |||
|||  DeleteOnTermination   |  True                         |||
|||  Device                |  /dev/xvda                    |||
|||  InstanceId            |  i-064da1db46bbd4d0e          |||
|||  State                 |  attached                     |||
|||  VolumeId              |  vol-0a83cf8d8a74eb723        |||
||+------------------------+-------------------------------+||
|||                        Operator                        |||
||+------------------------------+-------------------------+||
|||  Managed                     |  False                  |||
||+------------------------------+-------------------------+||
|||                          Tags                          |||
||+------------------------------------+-------------------+||
|||                 Key                |       Value       |||
||+------------------------------------+-------------------+||


[yu3papa@iworks ~]$ aws ec2 describe-volumes --filters Name=tag:Name,Values=$CLUSTER_NAME-ng1-Node --query "Volumes[*].Attachments" | jq
[
  [
    {
      "DeleteOnTermination": true,
      "VolumeId": "vol-0a83cf8d8a74eb723",
      "InstanceId": "i-064da1db46bbd4d0e",
      "Device": "/dev/xvda",
      "State": "attached",
      "AttachTime": "2025-02-22T11:10:59+00:00"
    }
  ],
  [
    {
      "DeleteOnTermination": true,
      "VolumeId": "vol-0b72a2124a83fa5a1",
      "InstanceId": "i-02c24086e5ef7bd2f",
      "Device": "/dev/xvda",
      "State": "attached",
      "AttachTime": "2025-02-22T11:10:59+00:00"
    }
  ],
  [
    {
      "DeleteOnTermination": true,
      "VolumeId": "vol-0f01e60b9b8617f5f",
      "InstanceId": "i-0fabeb5dd693c0671",
      "Device": "/dev/xvda",
      "State": "attached",
      "AttachTime": "2025-02-22T11:10:59+00:00"
    }
  ]
]
[yu3papa@iworks ~]$ aws ec2 describe-volumes --filters Name=tag:Name,Values=$CLUSTER_NAME-ng1-Node --query "Volumes[*].{ID:VolumeId,Tag:Tags}" | jq
[
  {
    "ID": "vol-0a83cf8d8a74eb723",
    "Tag": [
      {
        "Key": "alpha.eksctl.io/nodegroup-type",
        "Value": "managed"
      },
      {
        "Key": "Name",
        "Value": "myeks-ng1-Node"
      },
      {
        "Key": "alpha.eksctl.io/nodegroup-name",
        "Value": "ng1"
      },
      {
        "Key": "eks:nodegroup-name",
        "Value": "ng1"
      },
      {
        "Key": "eks:cluster-name",
        "Value": "myeks"
      }
    ]
  },
  {
    "ID": "vol-0b72a2124a83fa5a1",
    "Tag": [
      {
        "Key": "eks:nodegroup-name",
        "Value": "ng1"
      },
      {
        "Key": "alpha.eksctl.io/nodegroup-type",
        "Value": "managed"
      },
      {
        "Key": "Name",
        "Value": "myeks-ng1-Node"
      },
      {
        "Key": "eks:cluster-name",
        "Value": "myeks"
      },
      {
        "Key": "alpha.eksctl.io/nodegroup-name",
        "Value": "ng1"
      }
    ]
  },
  {
    "ID": "vol-0f01e60b9b8617f5f",
    "Tag": [
      {
        "Key": "eks:nodegroup-name",
        "Value": "ng1"
      },
      {
        "Key": "Name",
        "Value": "myeks-ng1-Node"
      },
      {
        "Key": "alpha.eksctl.io/nodegroup-name",
        "Value": "ng1"
      },
      {
        "Key": "alpha.eksctl.io/nodegroup-type",
        "Value": "managed"
      },
      {
        "Key": "eks:cluster-name",
        "Value": "myeks"
      }
    ]
  }
]
[yu3papa@iworks ~]$ aws ec2 describe-volumes --filters Name=tag:Name,Values=$CLUSTER_NAME-ng1-Node --query "Volumes[].[VolumeId, VolumeType, Attachments[].[InstanceId, State][]][]" | jq
[
  "vol-0a83cf8d8a74eb723",
  "gp3",
  [
    "i-064da1db46bbd4d0e",
    "attached"
  ],
  "vol-0b72a2124a83fa5a1",
  "gp3",
  [
    "i-02c24086e5ef7bd2f",
    "attached"
  ],
  "vol-0f01e60b9b8617f5f",
  "gp3",
  [
    "i-0fabeb5dd693c0671",
    "attached"
  ]
]
[yu3papa@iworks ~]$ aws ec2 describe-volumes --filters Name=tag:Name,Values=$CLUSTER_NAME-ng1-Node --query "Volumes[].{VolumeId: VolumeId, VolumeType: VolumeType, InstanceId: Attachments[0].InstanceId, State: Attachments[0].State}" | jq
[
  {
    "VolumeId": "vol-0a83cf8d8a74eb723",
    "VolumeType": "gp3",
    "InstanceId": "i-064da1db46bbd4d0e",
    "State": "attached"
  },
  {
    "VolumeId": "vol-0b72a2124a83fa5a1",
    "VolumeType": "gp3",
    "InstanceId": "i-02c24086e5ef7bd2f",
    "State": "attached"
  },
  {
    "VolumeId": "vol-0f01e60b9b8617f5f",
    "VolumeType": "gp3",
    "InstanceId": "i-0fabeb5dd693c0671",
    "State": "attached"
  }
]

# 워커노드에서 파드에 추가한 EBS 볼륨 확인
[yu3papa@iworks ~]$ aws ec2 describe-volumes --filters Name=tag:ebs.csi.aws.com/cluster,Values=true --output table
-----------------
|DescribeVolumes|
+---------------+
[yu3papa@iworks ~]$ aws ec2 describe-volumes --filters Name=tag:ebs.csi.aws.com/cluster,Values=true --query "Volumes[*].{ID:VolumeId,Tag:Tags}" | jq
[]
[yu3papa@iworks ~]$ aws ec2 describe-volumes --filters Name=tag:ebs.csi.aws.com/cluster,Values=true --query "Volumes[].{VolumeId: VolumeId, VolumeType: VolumeType, InstanceId: Attachments[0].InstanceId, State: Attachments[0].State}" | jq
[]

# 워커노드에서 파드에 추가한 EBS 볼륨 모니터링
[yu3papa@iworks ~]$ while true; do aws ec2 describe-volumes --filters Name=tag:ebs.csi.aws.com/cluster,Values=true --query "Volumes[].{VolumeId: VolumeId, VolumeType: VolumeType, InstanceId: Attachments[0].InstanceId, State: Attachments[0].State}" --output text; date; sleep 1; done
Sun Feb 23 01:22:41 AM KST 2025
Sun Feb 23 01:22:43 AM KST 2025
Sun Feb 23 01:22:45 AM KST 2025
Sun Feb 23 01:22:46 AM KST 2025
Sun Feb 23 01:22:48 AM KST 2025
Sun Feb 23 01:22:50 AM KST 2025
^C

# PVC 생성
[yu3papa@iworks ~]$ cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: ebs-claim
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 4Gi
  storageClassName: gp3
EOF
persistentvolumeclaim/ebs-claim created

[yu3papa@iworks ~]$ kubectl get pvc,pv
NAME                              STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
persistentvolumeclaim/ebs-claim   Pending                                      gp3            <unset>                 8s

# 파드 생성
[yu3papa@iworks ~]$ cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: app
spec:
  terminationGracePeriodSeconds: 3
  containers:
  - name: app
    image: centos
    command: ["/bin/sh"]
    args: ["-c", "while true; do echo \$(date -u) >> /data/out.txt; sleep 5; done"]
    volumeMounts:
    - name: persistent-storage
      mountPath: /data
  volumes:
  - name: persistent-storage
    persistentVolumeClaim:
      claimName: ebs-claim
EOF
pod/app created

# PVC, 파드 확인
[yu3papa@iworks ~]$ kubectl get pvc,pv,pod
NAME                              STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
persistentvolumeclaim/ebs-claim   Bound    pvc-de01bd9c-66c2-4f57-bb56-932b42ff8707   4Gi        RWO            gp3            <unset>                 42s

NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM               STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
persistentvolume/pvc-de01bd9c-66c2-4f57-bb56-932b42ff8707   4Gi        RWO            Delete           Bound    default/ebs-claim   gp3            <unset>                          14s

NAME      READY   STATUS    RESTARTS   AGE
pod/app   1/1     Running   0          17s

[yu3papa@iworks ~]$ kubectl get VolumeAttachment
NAME                                                                   ATTACHER          PV                                         NODE                                               ATTACHED   AGE
csi-dee1d07b3e8042f3e2ff3d303663acb042aa31bbe632c5a43e3ac84bbcf78066   ebs.csi.aws.com   pvc-de01bd9c-66c2-4f57-bb56-932b42ff8707   ip-192-168-1-193.ap-northeast-2.compute.internal   true       21s

[yu3papa@iworks ~]$ kubectl df-pv
error: unknown command "df-pv" for "kubectl"

# 추가된 EBS 볼륨 상세 정보 확인 : AWS 관리콘솔 EC2(EBS)에서 확인
[yu3papa@iworks ~]$ aws ec2 describe-volumes --volume-ids $(kubectl get pv -o jsonpath="{.items[0].spec.csi.volumeHandle}") | jq
{
  "Volumes": [
    {
      "Iops": 3000,
      "Tags": [
        {
          "Key": "kubernetes.io/created-for/pvc/namespace",
          "Value": "default"
        },
        {
          "Key": "KubernetesCluster",
          "Value": "myeks"
        },
        {
          "Key": "ebs.csi.aws.com/cluster",
          "Value": "true"
        },
        {
          "Key": "Name",
          "Value": "myeks-dynamic-pvc-de01bd9c-66c2-4f57-bb56-932b42ff8707"
        },
        {
          "Key": "kubernetes.io/created-for/pv/name",
          "Value": "pvc-de01bd9c-66c2-4f57-bb56-932b42ff8707"
        },
        {
          "Key": "kubernetes.io/cluster/myeks",
          "Value": "owned"
        },
        {
          "Key": "CSIVolumeName",
          "Value": "pvc-de01bd9c-66c2-4f57-bb56-932b42ff8707"
        },
        {
          "Key": "kubernetes.io/created-for/pvc/name",
          "Value": "ebs-claim"
        }
      ],
      "VolumeType": "gp3",
      "MultiAttachEnabled": false,
      "Throughput": 125,
      "Operator": {
        "Managed": false
      },
      "VolumeId": "vol-0f429b1f1d2588ede",
      "Size": 4,
      "SnapshotId": "",
      "AvailabilityZone": "ap-northeast-2a",
      "State": "in-use",
      "CreateTime": "2025-02-22T16:23:37.220000+00:00",
      "Attachments": [
        {
          "DeleteOnTermination": false,
          "VolumeId": "vol-0f429b1f1d2588ede",
          "InstanceId": "i-02c24086e5ef7bd2f",
          "Device": "/dev/xvdaa",
          "State": "attached",
          "AttachTime": "2025-02-22T16:23:40+00:00"
        }
      ],
      "Encrypted": true,
      "KmsKeyId": "arn:aws:kms:ap-northeast-2:390402552337:key/d5aee190-b097-4f00-a466-0f13e41a2435"
    }
  ]
}

# PV 상세 확인 : nodeAffinity 내용확인
[yu3papa@iworks ~]$ kubectl get pv -o yaml
apiVersion: v1
items:
- apiVersion: v1
  kind: PersistentVolume
  metadata:
    annotations:
      pv.kubernetes.io/provisioned-by: ebs.csi.aws.com
      volume.kubernetes.io/provisioner-deletion-secret-name: ""
      volume.kubernetes.io/provisioner-deletion-secret-namespace: ""
    creationTimestamp: "2025-02-22T16:23:39Z"
    finalizers:
    - external-provisioner.volume.kubernetes.io/finalizer
    - kubernetes.io/pv-protection
    - external-attacher/ebs-csi-aws-com
    name: pvc-de01bd9c-66c2-4f57-bb56-932b42ff8707
    resourceVersion: "71534"
    uid: d047b59d-c61e-4ff3-bd8d-06f67436bd93
  spec:
    accessModes:
    - ReadWriteOnce
    capacity:
      storage: 4Gi
    claimRef:
      apiVersion: v1
      kind: PersistentVolumeClaim
      name: ebs-claim
      namespace: default
      resourceVersion: "71507"
      uid: de01bd9c-66c2-4f57-bb56-932b42ff8707
    csi:
      driver: ebs.csi.aws.com
      fsType: xfs
      volumeAttributes:
        storage.kubernetes.io/csiProvisionerIdentity: 1740239893145-1296-ebs.csi.aws.com
      volumeHandle: vol-0f429b1f1d2588ede
    nodeAffinity:
      required:
        nodeSelectorTerms:
        - matchExpressions:
          - key: topology.kubernetes.io/zone
            operator: In
            values:
            - ap-northeast-2a
    persistentVolumeReclaimPolicy: Delete
    storageClassName: gp3
    volumeMode: Filesystem
  status:
    lastPhaseTransitionTime: "2025-02-22T16:23:39Z"
    phase: Bound
kind: List
metadata:
  resourceVersion: ""

[yu3papa@iworks ~]$ kubectl get node --label-columns=topology.ebs.csi.aws.com/zone,topology.k8s.aws/zone-id
NAME                                               STATUS   ROLES    AGE     VERSION               ZONE              ZONE-ID
ip-192-168-1-193.ap-northeast-2.compute.internal   Ready    <none>   5h13m   v1.31.5-eks-5d632ec   ap-northeast-2a   apne2-az1
ip-192-168-2-200.ap-northeast-2.compute.internal   Ready    <none>   5h13m   v1.31.5-eks-5d632ec   ap-northeast-2b   apne2-az2
ip-192-168-3-188.ap-northeast-2.compute.internal   Ready    <none>   5h13m   v1.31.5-eks-5d632ec   ap-northeast-2c   apne2-az3

# 파일 내용 추가 저장 확인
[yu3papa@iworks ~]$ kubectl exec app -- tail -f /data/out.txt
Sat Feb 22 16:24:25 UTC 2025
Sat Feb 22 16:24:30 UTC 2025
Sat Feb 22 16:24:35 UTC 2025
Sat Feb 22 16:24:40 UTC 2025
Sat Feb 22 16:24:45 UTC 2025
Sat Feb 22 16:24:50 UTC 2025
Sat Feb 22 16:24:55 UTC 2025
Sat Feb 22 16:25:00 UTC 2025
Sat Feb 22 16:25:05 UTC 2025
Sat Feb 22 16:25:10 UTC 2025
^C

# 파드 내에서 볼륨 정보 확인
[yu3papa@iworks ~]$ kubectl exec -it app -- sh -c 'df -hT --type=overlay'
Filesystem     Type     Size  Used Avail Use% Mounted on
overlay        overlay  120G  5.0G  115G   5% /

[yu3papa@iworks ~]$ kubectl exec -it app -- sh -c 'df -hT --type=xfs'
Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/nvme1n1   xfs   4.0G   61M  3.9G   2% /data
/dev/nvme0n1p1 xfs   120G  5.0G  115G   5% /etc/hosts
  • 볼륨 증가 - 링크 ⇒ 늘릴수는 있어도 줄일수는 없음 - 링크
 

Resizing Persistent Volumes using Kubernetes

Editor’s note: this post is part of a series of in-depth articles on what’s new in Kubernetes 1.11 In Kubernetes v1.11 the persistent volume expansion feature is being promoted to beta. This feature allows users to easily resize an existing volume by e

kubernetes.io

 

# 현재 pv 의 이름을 기준하여 4G > 10G 로 증가 : .spec.resources.requests.storage의 4Gi 를 10Gi로 변경
[yu3papa@iworks ~]$ kubectl get pvc ebs-claim -o jsonpath={.spec.resources.requests.storage} ; echo
4Gi
[yu3papa@iworks ~]$ kubectl get pvc ebs-claim -o jsonpath={.status.capacity.storage} ; echo
4Gi
[yu3papa@iworks ~]$ kubectl patch pvc ebs-claim -p '{"spec":{"resources":{"requests":{"storage":"10Gi"}}}}'
persistentvolumeclaim/ebs-claim patched

# 확인 : 볼륨 용량 수정 반영이 되어야 되니, 수치 반영이 조금 느릴수 있다
[yu3papa@iworks ~]$ kubectl exec -it app -- sh -c 'df -hT --type=xfs'
Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/nvme1n1   xfs   4.0G   61M  3.9G   2% /data
/dev/nvme0n1p1 xfs   120G  5.0G  115G   5% /etc/hosts


[yu3papa@iworks ~]$ kubectl exec -it app -- sh -c 'df -hT --type=xfs'
Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/nvme1n1   xfs    10G  105M  9.9G   2% /data
/dev/nvme0n1p1 xfs   120G  5.0G  115G   5% /etc/hosts


[yu3papa@iworks ~]$ aws ec2 describe-volumes --volume-ids $(kubectl get pv -o jsonpath="{.items[0].spec.csi.volumeHandle}") | jq
{
  "Volumes": [
    {
      "Iops": 3000,
      "Tags": [
        {
          "Key": "kubernetes.io/created-for/pvc/namespace",
          "Value": "default"
        },
        {
          "Key": "KubernetesCluster",
          "Value": "myeks"
        },
        {
          "Key": "ebs.csi.aws.com/cluster",
          "Value": "true"
        },
        {
          "Key": "Name",
          "Value": "myeks-dynamic-pvc-de01bd9c-66c2-4f57-bb56-932b42ff8707"
        },
        {
          "Key": "kubernetes.io/created-for/pv/name",
          "Value": "pvc-de01bd9c-66c2-4f57-bb56-932b42ff8707"
        },
        {
          "Key": "kubernetes.io/cluster/myeks",
          "Value": "owned"
        },
        {
          "Key": "CSIVolumeName",
          "Value": "pvc-de01bd9c-66c2-4f57-bb56-932b42ff8707"
        },
        {
          "Key": "kubernetes.io/created-for/pvc/name",
          "Value": "ebs-claim"
        }
      ],
      "VolumeType": "gp3",
      "MultiAttachEnabled": false,
      "Throughput": 125,
      "Operator": {
        "Managed": false
      },
      "VolumeId": "vol-0f429b1f1d2588ede",
      "Size": 10,
      "SnapshotId": "",
      "AvailabilityZone": "ap-northeast-2a",
      "State": "in-use",
      "CreateTime": "2025-02-22T16:23:37.220000+00:00",
      "Attachments": [
        {
          "DeleteOnTermination": false,
          "VolumeId": "vol-0f429b1f1d2588ede",
          "InstanceId": "i-02c24086e5ef7bd2f",
          "Device": "/dev/xvdaa",
          "State": "attached",
          "AttachTime": "2025-02-22T16:23:40+00:00"
        }
      ],
      "Encrypted": true,
      "KmsKeyId": "arn:aws:kms:ap-northeast-2:390402552337:key/d5aee190-b097-4f00-a466-0f13e41a2435"
    }
  ]
}
  • 삭제
[yu3papa@iworks ~]$ kubectl delete pod app
pod "app" deleted

[yu3papa@iworks ~]$ kubectl delete pvc ebs-claim
persistentvolumeclaim "ebs-claim" deleted

 

5. AWS EFS Controller

EFS 파일시스템 확인 및 EFS Controller Addon 설치

  • 구성 아키텍처

https://dev.to/awscommunity-asean/aws-eks-with-efs-csi-driver-and-irsa-using-cdk-dgc

# EFS 정보 확인
[yu3papa@iworks ~]$ aws efs describe-file-systems --query "FileSystems[*].FileSystemId" --output text
fs-04bdbcbb996487caf

# 아래는 aws-efs-csi-driver 전체 버전 정보와 기본 설치 버전(True) 정보 확인
[yu3papa@iworks ~]$ aws eks describe-addon-versions \
    --addon-name aws-efs-csi-driver \
    --kubernetes-version 1.31 \
    --query "addons[].addonVersions[].[addonVersion, compatibilities[].defaultVersion]" \
    --output text
v2.1.4-eksbuild.1
True
v2.1.3-eksbuild.1
False
v2.1.2-eksbuild.1
False
v2.1.1-eksbuild.1
False
v2.1.0-eksbuild.1
False
v2.0.9-eksbuild.1
False
v2.0.8-eksbuild.1
False
v2.0.7-eksbuild.1
False
v2.0.6-eksbuild.2
False
v2.0.6-eksbuild.1
False
v2.0.5-eksbuild.1
False
v2.0.4-eksbuild.1
False
v2.0.3-eksbuild.1
False
v2.0.2-eksbuild.1
False
v2.0.1-eksbuild.1
False
v2.0.0-eksbuild.1
False
v1.7.7-eksbuild.1
False

# ISRA 설정 : 고객관리형 정책 AmazonEKS_EFS_CSI_Driver_Policy 사용
[yu3papa@iworks ~]$ eksctl create iamserviceaccount \
  --name efs-csi-controller-sa \
  --namespace kube-system \
  --cluster ${CLUSTER_NAME} \
  --attach-policy-arn arn:aws:iam::aws:policy/service-role/AmazonEFSCSIDriverPolicy \
  --approve \
  --role-only \
  --role-name AmazonEKS_EFS_CSI_DriverRole
2025-02-23 01:43:48 [ℹ]  2 existing iamserviceaccount(s) (kube-system/aws-load-balancer-controller,kube-system/ebs-csi-controller-sa) will be excluded
2025-02-23 01:43:48 [ℹ]  1 iamserviceaccount (kube-system/efs-csi-controller-sa) was included (based on the include/exclude rules)
2025-02-23 01:43:48 [!]  serviceaccounts in Kubernetes will not be created or modified, since the option --role-only is used
2025-02-23 01:43:48 [ℹ]  1 task: { create IAM role for serviceaccount "kube-system/efs-csi-controller-sa" }
2025-02-23 01:43:48 [ℹ]  building iamserviceaccount stack "eksctl-myeks-addon-iamserviceaccount-kube-system-efs-csi-controller-sa"
2025-02-23 01:43:48 [ℹ]  deploying stack "eksctl-myeks-addon-iamserviceaccount-kube-system-efs-csi-controller-sa"
2025-02-23 01:43:48 [ℹ]  waiting for CloudFormation stack "eksctl-myeks-addon-iamserviceaccount-kube-system-efs-csi-controller-sa"
2025-02-23 01:44:18 [ℹ]  waiting for CloudFormation stack "eksctl-myeks-addon-iamserviceaccount-kube-system-efs-csi-controller-sa"

# ISRA 확인
[yu3papa@iworks ~]$ eksctl get iamserviceaccount --cluster ${CLUSTER_NAME}
NAMESPACE       NAME                            ROLE ARN
kube-system     aws-load-balancer-controller    arn:aws:iam::390402552337:role/eksctl-myeks-addon-iamserviceaccount-kube-sys-Role1-zzbfj7a4hBaW
kube-system     ebs-csi-controller-sa           arn:aws:iam::390402552337:role/AmazonEKS_EBS_CSI_DriverRole
kube-system     efs-csi-controller-sa           arn:aws:iam::390402552337:role/AmazonEKS_EFS_CSI_DriverRole

# Amazon EFS CSI driver addon 배포(설치)
[yu3papa@iworks ~]$ export ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text)
[yu3papa@iworks ~]$ eksctl create addon --name aws-efs-csi-driver --cluster ${CLUSTER_NAME} --service-account-role-arn arn:aws:iam::${ACCOUNT_ID}:role/AmazonEKS_EFS_CSI_DriverRole --force
2025-02-23 01:48:15 [ℹ]  Kubernetes version "1.31" in use by cluster "myeks"
2025-02-23 01:48:16 [ℹ]  IRSA is set for "aws-efs-csi-driver" addon; will use this to configure IAM permissions
2025-02-23 01:48:16 [!]  the recommended way to provide IAM permissions for "aws-efs-csi-driver" addon is via pod identity associations; after addon creation is completed, run `eksctl utils migrate-to-pod-identity`
2025-02-23 01:48:16 [ℹ]  using provided ServiceAccountRoleARN "arn:aws:iam::390402552337:role/AmazonEKS_EFS_CSI_DriverRole"
2025-02-23 01:48:16 [ℹ]  creating addon: aws-efs-csi-driver

[yu3papa@iworks ~]$ kubectl get sa -n kube-system efs-csi-controller-sa -o yaml | head -5
apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::390402552337:role/AmazonEKS_EFS_CSI_DriverRole

# 확인
[yu3papa@iworks ~]$ eksctl get addon --cluster ${CLUSTER_NAME}
2025-02-23 01:48:43 [ℹ]  Kubernetes version "1.31" in use by cluster "myeks"
2025-02-23 01:48:43 [ℹ]  getting all addons
2025-02-23 01:48:44 [ℹ]  to see issues for an addon run `eksctl get addon --name <addon-name> --cluster <cluster-name>`
NAME                    VERSION                 STATUS          ISSUES  IAMROLE                                                                         UPDATE AVAILABLE        CONFIGURATION VALUES                                                                    POD IDENTITY ASSOCIATION ROLES
aws-ebs-csi-driver      v1.39.0-eksbuild.1      ACTIVE          0                                                                                              {
    "node": {
      "volumeAttachLimit": 31,
      "enableMetrics": true
    }
  }
aws-efs-csi-driver      v2.1.4-eksbuild.1       CREATING        0       arn:aws:iam::390402552337:role/AmazonEKS_EFS_CSI_DriverRole
coredns                 v1.11.4-eksbuild.2      ACTIVE          0
kube-proxy              v1.31.3-eksbuild.2      ACTIVE          0
metrics-server          v0.7.2-eksbuild.2       ACTIVE          0
vpc-cni                 v1.19.2-eksbuild.5      ACTIVE          0       arn:aws:iam::390402552337:role/eksctl-myeks-addon-vpc-cni-Role1-fr0GM10fLspD           enableNetworkPolicy: "true"

[yu3papa@iworks ~]$ kubectl get pod -n kube-system -l "app.kubernetes.io/name=aws-efs-csi-driver,app.kubernetes.io/instance=aws-efs-csi-driver"
NAME                                  READY   STATUS    RESTARTS   AGE
efs-csi-controller-64fc4bc65d-84bq4   3/3     Running   0          31s
efs-csi-controller-64fc4bc65d-dg8h9   3/3     Running   0          31s
efs-csi-node-2q79t                    3/3     Running   0          31s
efs-csi-node-jwwh6                    3/3     Running   0          31s
efs-csi-node-qt24t                    3/3     Running   0          31s
[yu3papa@iworks ~]$ kubectl get pod -n kube-system -l app=efs-csi-controller -o jsonpath='{.items[0].spec.containers[*].name}' ; echo
efs-plugin csi-provisioner liveness-probe

[yu3papa@iworks ~]$ kubectl get csidrivers efs.csi.aws.com -o yaml
apiVersion: storage.k8s.io/v1
kind: CSIDriver
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"storage.k8s.io/v1","kind":"CSIDriver","metadata":{"annotations":{},"name":"efs.csi.aws.com"},"spec":{"attachRequired":false}}
  creationTimestamp: "2025-02-22T11:02:35Z"
  name: efs.csi.aws.com
  resourceVersion: "78511"
  uid: 4446fc0c-7d71-455b-bedd-9ac6716bf4d9
spec:
  attachRequired: false
  fsGroupPolicy: ReadWriteOnceWithFSType
  podInfoOnMount: false
  requiresRepublish: false
  seLinuxMount: false
  storageCapacity: false
  volumeLifecycleModes:
  - Persistent
  • AWS → EFS → 파일 시스템 : 네트워크 확인

  • EFS 파일시스템을 파드가 사용하게 설정 : Add empty StorageClasses from static example
# 모니터링
[yu3papa@iworks ~]$ watch 'kubectl get sc efs-sc; echo; kubectl get pv,pvc,pod'^C

# [운영 서버 EC2]
# 실습 코드 clone
[yu3papa@iworks ~]$ ssh -i aews-yu3papa.pem ec2-user@15.164.244.197
Last login: Sat Feb 22 22:38:31 2025 from 221.149.127.57
   ,     #_
   ~\_  ####_        Amazon Linux 2
  ~~  \_#####\
  ~~     \###|       AL2 End of Life is 2026-06-30.
  ~~       \#/ ___
   ~~       V~' '->
    ~~~         /    A newer version of Amazon Linux is available!
      ~~._.   _/
         _/ _/       Amazon Linux 2023, GA and supported until 2028-03-15.
       _/m/'           https://aws.amazon.com/linux/amazon-linux-2023/

Last login: Sat Feb 22 22:38:31 KST 2025 on pts/0

[root@operator-host ~]# git clone https://github.com/kubernetes-sigs/aws-efs-csi-driver.git /root/efs-csi
Cloning into '/root/efs-csi'...
remote: Enumerating objects: 30760, done.
remote: Counting objects: 100% (5451/5451), done.
remote: Compressing objects: 100% (1512/1512), done.
remote: Total 30760 (delta 4230), reused 4145 (delta 3902), pack-reused 25309 (from 3)
Receiving objects: 100% (30760/30760), 28.56 MiB | 11.57 MiB/s, done.
Resolving deltas: 100% (16621/16621), done.
Updating files: 100% (7926/7926), done.

[root@operator-host ~]# cd /root/efs-csi/examples/kubernetes/multiple_pods/specs && tree
.
├── claim.yaml
├── pod1.yaml
├── pod2.yaml
├── pv.yaml
└── storageclass.yaml

0 directories, 5 files

# EFS 스토리지클래스 생성 및 확인
[root@operator-host specs]# cat storageclass.yaml
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: efs-sc
provisioner: efs.csi.aws.com

[root@operator-host specs]# kubectl apply -f storageclass.yaml
storageclass.storage.k8s.io/efs-sc created

[root@operator-host specs]# kubectl get sc efs-sc
NAME     PROVISIONER       RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
efs-sc   efs.csi.aws.com   Delete          Immediate           false                  8s

# PV 생성 및 확인 : volumeHandle을 자신의 EFS 파일시스템ID로 변경
[root@operator-host specs]# EfsFsId=$(aws efs describe-file-systems --query "FileSystems[*].FileSystemId" --output text)
[root@operator-host specs]# echo $EfsFsId
fs-04bdbcbb996487caf
[root@operator-host specs]# sed -i "s/fs-4af69aab/$EfsFsId/g" pv.yaml
[root@operator-host specs]# cat pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: efs-pv
spec:
  capacity:
    storage: 5Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  storageClassName: efs-sc
  csi:
    driver: efs.csi.aws.com
    volumeHandle: fs-04bdbcbb996487caf

[root@operator-host specs]# kubectl apply -f pv.yaml
persistentvolume/efs-pv created

[root@operator-host specs]# kubectl get pv; kubectl describe pv
NAME     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
efs-pv   5Gi        RWX            Retain           Available           efs-sc         <unset>                          7s
Name:            efs-pv
Labels:          <none>
Annotations:     <none>
Finalizers:      [kubernetes.io/pv-protection]
StorageClass:    efs-sc
Status:          Available
Claim:
Reclaim Policy:  Retain
Access Modes:    RWX
VolumeMode:      Filesystem
Capacity:        5Gi
Node Affinity:   <none>
Message:
Source:
    Type:              CSI (a Container Storage Interface (CSI) volume source)
    Driver:            efs.csi.aws.com
    FSType:
    VolumeHandle:      fs-04bdbcbb996487caf
    ReadOnly:          false
    VolumeAttributes:  <none>
Events:                <none>

# PVC 생성 및 확인
[root@operator-host specs]# cat claim.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: efs-claim
spec:
  accessModes:
    - ReadWriteMany
  storageClassName: efs-sc
  resources:
    requests:
      storage: 5Gi

[root@operator-host specs]# kubectl apply -f claim.yaml
persistentvolumeclaim/efs-claim created

[root@operator-host specs]# kubectl get pvc
NAME        STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
efs-claim   Bound    efs-pv   5Gi        RWX            efs-sc         <unset>                 8s

# 파드 생성 및 연동 : 파드 내에 /data 데이터는 EFS를 사용
# 추후에 파드1,2가 각기 다른 노드에 배포되게 추가해두자!
[root@operator-host specs]# cat pod1.yaml pod2.yaml
apiVersion: v1
kind: Pod
metadata:
  name: app1
spec:
  containers:
  - name: app1
    image: busybox
    command: ["/bin/sh"]
    args: ["-c", "while true; do echo $(date -u) >> /data/out1.txt; sleep 5; done"]
    volumeMounts:
    - name: persistent-storage
      mountPath: /data
  volumes:
  - name: persistent-storage
    persistentVolumeClaim:
      claimName: efs-claim
apiVersion: v1
kind: Pod
metadata:
  name: app2
spec:
  containers:
  - name: app2
    image: busybox
    command: ["/bin/sh"]
    args: ["-c", "while true; do echo $(date -u) >> /data/out2.txt; sleep 5; done"]
    volumeMounts:
    - name: persistent-storage
      mountPath: /data
  volumes:
  - name: persistent-storage
    persistentVolumeClaim:
      claimName: efs-claim
[root@operator-host specs]# kubectl apply -f pod1.yaml,pod2.yaml
pod/app1 created
pod/app2 created

# 파드 정보 확인 : PV에 5Gi 와 파드 내에서 확인한 NFS4 볼륨 크리 8.0E의 차이는 무엇?
[root@operator-host specs]# kubectl get pods
NAME   READY   STATUS    RESTARTS   AGE
app1   1/1     Running   0          25s
app2   1/1     Running   0          25s
[root@operator-host specs]# kubectl exec -ti app1 -- sh -c "df -hT -t nfs4"
Filesystem           Type            Size      Used Available Use% Mounted on
127.0.0.1:/          nfs4            8.0E         0      8.0E   0% /data
[root@operator-host specs]# kubectl exec -ti app2 -- sh -c "df -hT -t nfs4"
Filesystem           Type            Size      Used Available Use% Mounted on
127.0.0.1:/          nfs4            8.0E         0      8.0E   0% /data

# 공유 저장소 저장 동작 확인
[root@operator-host specs]# tree /mnt/myefs
/mnt/myefs
├── memo.txt
├── out1.txt
└── out2.txt

0 directories, 3 files
[root@operator-host specs]# tail -f /mnt/myefs/out1.txt
Sat Feb 22 17:00:57 UTC 2025
Sat Feb 22 17:01:02 UTC 2025
Sat Feb 22 17:01:07 UTC 2025
Sat Feb 22 17:01:12 UTC 2025
Sat Feb 22 17:01:17 UTC 2025
Sat Feb 22 17:01:22 UTC 2025
Sat Feb 22 17:01:27 UTC 2025
Sat Feb 22 17:01:32 UTC 2025
Sat Feb 22 17:01:37 UTC 2025
Sat Feb 22 17:01:42 UTC 2025
Sat Feb 22 17:01:47 UTC 2025
Sat Feb 22 17:01:52 UTC 2025
^C
[root@operator-host specs]# tail -f /mnt/myefs/out2.txt
Sat Feb 22 17:01:12 UTC 2025
Sat Feb 22 17:01:17 UTC 2025
Sat Feb 22 17:01:22 UTC 2025
Sat Feb 22 17:01:27 UTC 2025
Sat Feb 22 17:01:32 UTC 2025
Sat Feb 22 17:01:37 UTC 2025
Sat Feb 22 17:01:42 UTC 2025
Sat Feb 22 17:01:47 UTC 2025
Sat Feb 22 17:01:52 UTC 2025
Sat Feb 22 17:01:57 UTC 2025
^C
[root@operator-host specs]# kubectl exec -ti app1 -- tail -f /data/out1.txt
Sat Feb 22 17:01:22 UTC 2025
Sat Feb 22 17:01:27 UTC 2025
Sat Feb 22 17:01:32 UTC 2025
Sat Feb 22 17:01:37 UTC 2025
Sat Feb 22 17:01:42 UTC 2025
Sat Feb 22 17:01:47 UTC 2025
Sat Feb 22 17:01:52 UTC 2025
Sat Feb 22 17:01:57 UTC 2025
Sat Feb 22 17:02:02 UTC 2025
Sat Feb 22 17:02:07 UTC 2025
^Ccommand terminated with exit code 130
[root@operator-host specs]# kubectl exec -ti app2 -- tail -f /data/out2.txt
Sat Feb 22 17:01:32 UTC 2025
Sat Feb 22 17:01:37 UTC 2025
Sat Feb 22 17:01:42 UTC 2025
Sat Feb 22 17:01:47 UTC 2025
Sat Feb 22 17:01:52 UTC 2025
Sat Feb 22 17:01:57 UTC 2025
Sat Feb 22 17:02:02 UTC 2025
Sat Feb 22 17:02:07 UTC 2025
Sat Feb 22 17:02:12 UTC 2025
Sat Feb 22 17:02:17 UTC 2025
^Ccommand terminated with exit code 130
  • 실습 완료 후 삭제
# 쿠버네티스 리소스 삭제
[root@operator-host specs]# kubectl delete pod app1 app2
pod "app1" deleted
pod "app2" deleted

[root@operator-host specs]# kubectl delete pvc efs-claim
persistentvolumeclaim "efs-claim" deleted

[root@operator-host specs]# kubectl delete pv efs-pv
persistentvolume "efs-pv" deleted

[root@operator-host specs]# kubectl delete sc efs-sc
storageclass.storage.k8s.io "efs-sc" deleted
  • EFS 파일시스템을 다수의 파드가 사용하게 설정 : Dynamic provisioning using EFS
# 모니터링
[yu3papa@iworks ~]$ watch 'kubectl get sc efs-sc; echo; kubectl get pv,pvc,pod'^C

# [운영 서버 EC2]
[yu3papa@iworks ~]$ ssh -i aews-yu3papa.pem ec2-user@15.164.244.197
Last login: Sat Feb 22 22:38:31 2025 from 221.149.127.57
   ,     #_
   ~\_  ####_        Amazon Linux 2
  ~~  \_#####\
  ~~     \###|       AL2 End of Life is 2026-06-30.
  ~~       \#/ ___
   ~~       V~' '->
    ~~~         /    A newer version of Amazon Linux is available!
      ~~._.   _/
         _/ _/       Amazon Linux 2023, GA and supported until 2028-03-15.
       _/m/'           https://aws.amazon.com/linux/amazon-linux-2023/

Last login: Sun Feb 23 01:57:44 KST 2025 on pts/1

# EFS 스토리지클래스 생성 및 확인
[root@operator-host ~]# curl -s -O https://raw.githubusercontent.com/kubernetes-sigs/aws-efs-csi-driver/master/examples/kubernetes/dynamic_provisioning/specs/storageclass.yaml

[root@operator-host ~]# cat storageclass.yaml
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: efs-sc
provisioner: efs.csi.aws.com
parameters:
  provisioningMode: efs-ap
  fileSystemId: fs-92107410
  directoryPerms: "700"
  gidRangeStart: "1000" # optional
  gidRangeEnd: "2000" # optional
  basePath: "/dynamic_provisioning" # optional
  subPathPattern: "${.PVC.namespace}/${.PVC.name}" # optional
  ensureUniqueDirectory: "true" # optional
  reuseAccessPoint: "false" # optional[root@operator-host ~]#

[root@operator-host ~]# EfsFsId=$(aws efs describe-file-systems --query "FileSystems[*].FileSystemId" --output text)

[root@operator-host ~]# sed -i "s/fs-92107410/$EfsFsId/g" storageclass.yaml

[root@operator-host ~]# kubectl apply -f storageclass.yaml
storageclass.storage.k8s.io/efs-sc created

[root@operator-host ~]# kubectl get sc efs-sc
NAME     PROVISIONER       RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
efs-sc   efs.csi.aws.com   Delete          Immediate           false                  12s

# PVC/파드 생성 및 확인
[root@operator-host ~]# curl -s -O https://raw.githubusercontent.com/kubernetes-sigs/aws-efs-csi-driver/master/examples/kubernetes/dynamic_provisioning/specs/pod.yaml

[root@operator-host ~]# cat pod.yaml
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: efs-claim
spec:
  accessModes:
    - ReadWriteMany
  storageClassName: efs-sc
  resources:
    requests:
      storage: 5Gi
---
apiVersion: v1
kind: Pod
metadata:
  name: efs-app
spec:
  containers:
    - name: app
      image: centos
      command: ["/bin/sh"]
      args: ["-c", "while true; do echo $(date -u) >> /data/out; sleep 5; done"]
      volumeMounts:
        - name: persistent-storage
          mountPath: /data
  volumes:
    - name: persistent-storage
      persistentVolumeClaim:
        claimName: efs-claim[root@operator-host ~]#

[root@operator-host ~]# kubectl apply -f pod.yaml
persistentvolumeclaim/efs-claim created
pod/efs-app created
[root@operator-host ~]# kubectl get pvc,pv,pod
NAME                              STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
persistentvolumeclaim/efs-claim   Bound    pvc-9899279b-2764-4935-8ee6-8b1985c61678   5Gi        RWX            efs-sc         <unset>                 17s

NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM               STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
persistentvolume/pvc-9899279b-2764-4935-8ee6-8b1985c61678   5Gi        RWX            Delete           Bound    default/efs-claim   efs-sc         <unset>                          16s

NAME          READY   STATUS    RESTARTS   AGE
pod/efs-app   1/1     Running   0          17s

# PVC/PV 생성 로그 확인
[root@operator-host ~]# kubectl logs  -n kube-system -l app=efs-csi-controller -c csi-provisioner -f
I0222 16:48:29.363008       1 reflector.go:368] Caches populated for *v1.PersistentVolumeClaim from k8s.io/client-go@v0.31.0/tools/cache/reflector.go:243
I0222 16:48:29.363282       1 reflector.go:368] Caches populated for *v1.StorageClass from k8s.io/client-go@v0.31.0/tools/cache/reflector.go:243
I0222 16:48:29.453866       1 controller.go:824] "Starting provisioner controller" component="efs.csi.aws.com_efs-csi-controller-64fc4bc65d-84bq4_9955ae9f-a6f6-47a2-8915-49480f88ebb3"
I0222 16:48:29.453911       1 volume_store.go:98] "Starting save volume queue"
I0222 16:48:29.456593       1 reflector.go:368] Caches populated for *v1.StorageClass from k8s.io/client-go@v0.31.0/tools/cache/reflector.go:243
I0222 16:48:29.457321       1 reflector.go:368] Caches populated for *v1.PersistentVolume from k8s.io/client-go@v0.31.0/tools/cache/reflector.go:243
I0222 16:48:29.555566       1 controller.go:873] "Started provisioner controller" component="efs.csi.aws.com_efs-csi-controller-64fc4bc65d-84bq4_9955ae9f-a6f6-47a2-8915-49480f88ebb3"
I0222 17:19:02.828324       1 event.go:389] "Event occurred" object="default/efs-claim" fieldPath="" kind="PersistentVolumeClaim" apiVersion="v1" type="Normal" reason="Provisioning" message="External provisioner is provisioning volume for claim \"default/efs-claim\""
I0222 17:19:03.122066       1 controller.go:955] successfully created PV pvc-9899279b-2764-4935-8ee6-8b1985c61678 for PVC efs-claim and csi volume name fs-04bdbcbb996487caf::fsap-0151b9bbfd7ee62b3
I0222 17:19:03.136203       1 event.go:389] "Event occurred" object="default/efs-claim" fieldPath="" kind="PersistentVolumeClaim" apiVersion="v1" type="Normal" reason="ProvisioningSucceeded" message="Successfully provisioned volume pvc-9899279b-2764-4935-8ee6-8b1985c61678"
W0222 16:48:30.436187       1 feature_gate.go:354] Setting GA feature gate Topology=true. It will be removed in a future release.
I0222 16:48:30.437080       1 feature_gate.go:387] feature gates: {map[Topology:true]}
I0222 16:48:30.437479       1 csi-provisioner.go:154] Version: v5.1.0
I0222 16:48:30.437572       1 csi-provisioner.go:177] Building kube configs for running in cluster...
I0222 16:48:30.441128       1 common.go:143] "Probing CSI driver for readiness"
I0222 16:48:30.448649       1 csi-provisioner.go:230] Detected CSI driver efs.csi.aws.com
I0222 16:48:30.452688       1 csi-provisioner.go:302] CSI driver does not support PUBLISH_UNPUBLISH_VOLUME, not watching VolumeAttachments
I0222 16:48:30.453202       1 controller.go:744] "Using saving PVs to API server in background"
I0222 16:48:30.453494       1 leaderelection.go:254] attempting to acquire leader lease kube-system/efs-csi-aws-com...
^C

# 파드 정보 확인
[root@operator-host ~]# kubectl exec -it efs-app -- sh -c "df -hT -t nfs4"
Filesystem     Type  Size  Used Avail Use% Mounted on
127.0.0.1:/    nfs4  8.0E     0  8.0E   0% /data

# 공유 저장소 저장 동작 확인
[root@operator-host ~]# tree /mnt/myefs
/mnt/myefs
├── dynamic_provisioning
│   └── default
│       └── efs-claim-3afddc0f-c72b-4d23-ab27-8302c45c8713
│           └── out
├── memo.txt
├── out1.txt
└── out2.txt

3 directories, 4 files
[root@operator-host ~]# kubectl exec efs-app -- bash -c "cat /data/out"
Sat Feb 22 17:19:13 UTC 2025
Sat Feb 22 17:19:18 UTC 2025
Sat Feb 22 17:19:23 UTC 2025
Sat Feb 22 17:19:28 UTC 2025
Sat Feb 22 17:19:33 UTC 2025
Sat Feb 22 17:19:38 UTC 2025
Sat Feb 22 17:19:43 UTC 2025
Sat Feb 22 17:19:48 UTC 2025
Sat Feb 22 17:19:53 UTC 2025
Sat Feb 22 17:19:58 UTC 2025
Sat Feb 22 17:20:03 UTC 2025
[root@operator-host ~]# kubectl exec efs-app -- bash -c "ls -l /data/out"
-rw-r--r--. 1 1000 1000 377 Feb 22 17:20 /data/out

[root@operator-host ~]# kubectl exec efs-app -- bash -c "stat /data/"
  File: /data/
  Size: 6144            Blocks: 8          IO Block: 1048576 directory
Device: 100007h/1048583d        Inode: 2689823224459023729  Links: 2
Access: (0700/drwx------)  Uid: ( 1000/ UNKNOWN)   Gid: ( 1000/ UNKNOWN)
Access: 2025-02-22 17:19:13.598000000 +0000
Modify: 2025-02-22 17:19:13.598000000 +0000
Change: 2025-02-22 17:19:13.598000000 +0000
 Birth: -

 

  • EFS → Access Point 확인
    • EFS Access Point는 EFS의 특정 부분을 격리하고, UID/GID를 강제하여 보안성을 높임.
    • 여러 팀, 여러 애플리케이션이 같은 EFS를 사용할 때 Access Point를 활용하면 보안과 관리가 용이.

 

  • 실습 완료 후 삭제
# 쿠버네티스 리소스 삭제
[root@operator-host ~]# kubectl delete -f pod.yaml
persistentvolumeclaim "efs-claim" deleted
pod "efs-app" deleted

[root@operator-host ~]# kubectl delete -f storageclass.yaml
storageclass.storage.k8s.io "efs-sc" deleted

 

6. AWS 리소스 삭제

  • Amazon EKS 클러스터 삭제(10분 정도 소요)
[yu3papa@iworks ~]$ eksctl delete cluster --name $CLUSTER_NAME

 

  • (클러스터 삭제 완료 확인 후) AWS CloudFormation 스택 삭제
[yu3papa@iworks ~]$ aws cloudformation delete-stack --stack-name myeks

 

  • EKS 배포 후 실습 편의를 위한 변수 설정 삭제
[yu3papa@iworks ~]$  vi ~/.bashrc