티스토리 뷰
CloudNet@ 가시다님이 진행하는 AWS EKS Hands-on Study 내용 참고.
AWS EKS Upgrade workshop 내용으로 작성
Amazon EKS Upgrades: Strategies and Best Practices
This workshop covers 300-400 level content on everything about EKS Cluster upgrades. It leverages EKS Terraform Blueprints, ArgoCD, and more to guide customers through the entire EKS cluster upgrade process, including best-practices.
catalog.us-east-1.prod.workshops.aws
K8S Upgrade
☞ [K8S Docs] Release* : 1년에 3개의 마이너 버전 출시 → 최근 3개 버전 release branches(패치) 지원 - Link
- Download - Link
- Release Cycle - Link
- Patch Releases - Link
- Release Managers - Link
- Release Notes - Link
- Version Skew Policy - Link*
- kube-apiserver : HA apiserver 경우 newest 와 oldest 가 1개 마이너 버전 가능
- newest kube-apiserver is at 1.32
- other kube-apiserver instances are supported at 1.32 and 1.31
- kubelet/kube-proxy : kubelet 는 apiserver 보다 높을수 없음. 추가로 apiserver 보다 3개 older 가능
- kube-apiserver is at 1.32
- kubelet is supported at 1.32, 1.31, 1.30, and 1.29
- 만약 apiserver 가 HA로 1.32, 1.31 있다면 kubelet 는 1.32는 안되고, 1.31, 1.30, 1.29 가능
- kube-controller-manager, kube-scheduler, and cloud-controller-manager : skip
- kubectl : one minor version (older or newer) of kube-apiserver
- kube-apiserver is at 1.32
- kubectl is supported at 1.33, 1.32, and 1.31
- kube-apiserver : HA apiserver 경우 newest 와 oldest 가 1개 마이너 버전 가능
☞ 가상의 온프레미스 환경 K8S 에서 업그레이드 계획 짜기*
- 환경 소개 : k8s 1.28, CPx3대, DPx3대, HW LB 사용 중
- 각 구성요소와 버전
- Ubuntu 22.04 (kernel 5.15)
- Containerd 1.7.z
- K8S Version 1.28.2 - kubeadm, kubelet
- CNI : Cilium 1.4.z
- CSI : OpenEBS 3.y.z
- 애플리케이션 및 요구사항 확인 : 현황 조사 후 작성…
- 목표 버전 : K8S 1.32!
- 버전 호환성 검토
- K8S(kubelet, apiserver..) 1.32 요구 커널 버전 확인 : 예) user namespace 사용 시 커널 6.5 이상 필요 - Docs
- containerd 버전 조사 : 예) user namespace 에 대한 CRI 지원 시 containerd v2.0 및 runc v1.2 이상 필요 - Docs
- K8S 호환 버전 확인 - Docs
- CNI(Cilium) 요구 커널 버전 확인 : 예) BPF-based host routing 기능 필요 시 커널 5.10 이상 필요 - Docs
- CNI 이 지원하는 K8S 버전 확인 - Docs
- CSI : Skip…
- 애플리케이션 요구사항 검토…
- 업그레이드 방법 결정 : in-place vs blue-green
- Dev - Stg - Prd 별 각기 다른 방법 vs 모두 동일 방법
- 결정된 방법으로 업그레이드 계획 수립 : 예시) in-place 결정
4. 사전 준비
a. (옵션) 각 작업 별 상세 명령 실행 및 스크립트 작성, 작업 중단 실패 시 롤백 명령/스크립트 작성
b. 모니터링 설정
c. (1) ETCD 백업
d. (2) CNI(cilium) 업그레이드
5. CP 노드 순차 업그레이드 : 1.28 → 1.29 → 1.30
- 노드 1대씩 작업 진행 1.28 → 1.29 : 노드1 → 노드2 → 노드3
- (3) Ubuntu OS(kernel) 업그레이드 → 재부팅
- (4) containerd 업그레이드 → containerd 서비스 재시작
- (5) kubeadm 업그레이드 → kubelet/kubectl 업그레이드 설치 → kubelet 재시작
- 노드 1대씩 작업 진행 1.29 → 1.30 : 노드1 → 노드2 → 노드3
- (5) kubeadm 업그레이드 → kubelet/kubectl 업그레이드 설치 → kubelet 재시작
- 작업 완료 후 CP 노드 상태 확인
6. DP 노드 순차 업그레이드 : 1.28 → 1.29 → 1.30
- 노드 1대씩 작업 진행 1.28 → 1.29
- (6) 작업 노드 drain 설정 후 파드 Evicted 확인, 서비스 중단 여부 모니터링 확인
- (7) Ubuntu OS(kernel) 업그레이드 → 재부팅
- (8) containerd 업그레이드 → containerd 서비스 재시작
- (9) kubeadm 업그레이드 → kubelet 업그레이드 설치 → kubelet 재시작
- 노드 1대씩 작업 진행 1.29 → 1.30
- (9) kubeadm 업그레이드 → kubelet 업그레이드 설치 → kubelet 재시작
- 작업 완료 후 DP 노드 상태 확인 ⇒ (10) 작업 노드 uncordon 설정 후 다시 상태 확인
7. K8S 관련 전체적인 동작 1차 점검
- 애플리케이션 파드 동작 확인 등
8. CP 노드 순차 업그레이드 : 1.30 → 1.31 → 1.32
- 노드 1대씩 작업 진행 x 버전별 반복
- kubeadm 업그레이드 → kubelet/kubectl 업그레이드 설치 → kubelet 재시작
- 작업 완료 후 CP 노드 상태 확인
9. DP 노드 순차 업그레이드 : 1.30 → 1.31 → 1.32
- 노드 1대씩 작업 진행 x 버전별 반복
- 작업 노드 drain 설정 후 파드 Evicted 확인, 서비스 중단 여부 모니터링 확인
- kubeadm 업그레이드 → kubelet 업그레이드 설치 → kubelet 재시작
- 작업 완료 후 DP 노드 상태 확인 ⇒ 작업 노드 uncordon 설정 후 다시 상태 확인
10. K8S 관련 전체적인 동작 2차 점검
- 애플리케이션 파드 동작 확인 등
Amazon EKS Upgrades: Strategies and Best Practices
워크샵 원본 링크 https://catalog.us-east-1.prod.workshops.aws/workshops/693bdee4-bc31-41d5-841f-54e3e54f8f4a/en-US
Amazon EKS 클러스터 업그레이드 워크숍으로 Amazon EKS 클러스터 업그레이드를 계획하고 실행할 수 있는 모범 사례를 제공
In-Place, Blue/Green 등 다양한 클러스터 업그레이드 전략을 탐구하고 각 전략의 실행 세부 사항을 자세히 확인하고, EKS 테라폼 청사진, ArgoCD 등을 활용하여 전체 EKS 클러스터 업그레이드 프로세스를 안내
☞ Getting started : 실습 환경 정보 확인
- 실습 환경 배포 : 오레곤 리전(us-west-2), EC2(IDE-Server) 접속
- IDE-Server 는 AWS CloudFormation Stack 으로 배포되었고, AWS EKS는 Terraform 으로 배포되어 있음.
☞ IDE 정보 확인 : 버전(1.25.16), 노드(AL2, 커널 5.10.234, containerd 1.7.25) - IDE_Link , Self-Mng-Node , KrBlog
#
eksctl get cluster
NAME REGION EKSCTL CREATED
eksworkshop-eksctl us-west-2 False
eksctl get nodegroup --cluster $CLUSTER_NAME
CLUSTER NODEGROUP STATUS CREATED MIN SIZE MAX SIZE DESIRED CAPACITY INSTANCE TYPE IMAGE ID ASG NAME TYPE
eksworkshop-eksctl blue-mng-20250325023020754500000029 ACTIVE 2025-03-25T02:30:22Z 1 2 1 m5.large,m6a.large,m6i.large AL2_x86_64 eks-blue-mng-20250325023020754500000029-00cae591-7abe-6143-dd33-c720df2700b6 managed
eksworkshop-eksctl initial-2025032502302076080000002c ACTIVE 2025-03-25T02:30:22Z 2 10 2 m5.large,m6a.large,m6i.large AL2_x86_64 eks-initial-2025032502302076080000002c-30cae591-7ac2-1e9c-2415-19975314b08b managed
eksctl get addon --cluster $CLUSTER_NAME
NAME VERSION STATUS ISSUES IAMROLE UPDATE AVAILABLE CONFIGURATION VALUES
aws-ebs-csi-driver v1.41.0-eksbuild.1 ACTIVE 0 arn:aws:iam::xxxxxxx:role/eksworkshop-eksctl-ebs-csi-driver-2025032502294618580000001d
coredns v1.8.7-eksbuild.10 ACTIVE 0 v1.9.3-eksbuild.22,v1.9.3-eksbuild.21,v1.9.3-eksbuild.19,v1.9.3-eksbuild.17,v1.9.3-eksbuild.15,v1.9.3-eksbuild.11,v1.9.3-eksbuild.10,v1.9.3-eksbuild.9,v1.9.3-eksbuild.7,v1.9.3-eksbuild.6,v1.9.3-eksbuild.5,v1.9.3-eksbuild.3,v1.9.3-eksbuild.2
kube-proxy v1.25.16-eksbuild.8 ACTIVE 0
vpc-cni v1.19.3-eksbuild.1 ACTIVE 0
#
kubectl get node --label-columns=eks.amazonaws.com/capacityType,node.kubernetes.io/lifecycle,karpenter.sh/capacity-type,eks.amazonaws.com/compute-type
kubectl get node -L eks.amazonaws.com/nodegroup,karpenter.sh/nodepool
kubectl get nodepools
kubectl get nodeclaims -o yaml
kubectl get nodeclaims
kubectl get node --label-columns=node.kubernetes.io/instance-type,kubernetes.io/arch,kubernetes.io/os,topology.kubernetes.io/zone
#
kubectl cluster-info
kubectl get nodes -owide
#
kubectl get crd
#
helm list -A
#
kubectl get applications -n argocd
#
kubectl get pod -A
#
kubectl get pdb -A
#
kubectl get svc -n argocd argo-cd-argocd-server
#
kubectl get targetgroupbindings -n argocd
# 노드에 taint 정보 확인
kubectl get nodes -o custom-columns='NODE:.metadata.name,TAINTS:.spec.taints[*].key,VALUES:.spec.taints[*].value,EFFECTS:.spec.taints[*].effect'
kubectl get node -L eks.amazonaws.com/nodegroup,karpenter.sh/nodepool
# 노드 별 label 확인
kubectl get nodes -o json | jq '.items[] | {name: .metadata.name, labels: .metadata.labels}'
...
{
"name": "ip-10-0-12-8.us-west-2.compute.internal",
"labels": {
"beta.kubernetes.io/arch": "amd64",
"beta.kubernetes.io/instance-type": "m5.large",
"beta.kubernetes.io/os": "linux",
"eks.amazonaws.com/capacityType": "ON_DEMAND",
"eks.amazonaws.com/nodegroup": "blue-mng-20250330045659150100000029",
"eks.amazonaws.com/nodegroup-image": "ami-0078a0f78fafda978",
"eks.amazonaws.com/sourceLaunchTemplateId": "lt-04c7fa081d847fb0a",
"eks.amazonaws.com/sourceLaunchTemplateVersion": "1",
"failure-domain.beta.kubernetes.io/region": "us-west-2",
"failure-domain.beta.kubernetes.io/zone": "us-west-2a",
"k8s.io/cloud-provider-aws": "a94967527effcefb5f5829f529c0a1b9",
"kubernetes.io/arch": "amd64",
"kubernetes.io/hostname": "ip-10-0-12-8.us-west-2.compute.internal",
"kubernetes.io/os": "linux",
"node.kubernetes.io/instance-type": "m5.large",
"topology.ebs.csi.aws.com/zone": "us-west-2a",
"topology.kubernetes.io/region": "us-west-2",
"topology.kubernetes.io/zone": "us-west-2a",
"type": "OrdersMNG"
}
...
#
kubectl get sts -A
NAMESPACE NAME READY AGE
argocd argo-cd-argocd-application-controller 1/1 35h
catalog catalog-mysql 1/1 35h
rabbitmq rabbitmq 1/1 35h
#
kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
efs efs.csi.aws.com Delete Immediate true 35h
gp2 kubernetes.io/aws-ebs Delete WaitForFirstConsumer false 35h
gp3 (default) ebs.csi.aws.com Delete WaitForFirstConsumer true 35h 152m
kubectl describe sc efs
kubectl get pv,pvc -A
#
aws eks list-access-entries --cluster-name $CLUSTER_NAME
eksctl get iamidentitymapping --cluster $CLUSTER_NAME
kubectl describe cm -n kube-system aws-auth
# IRSA : karpenter, alb-controller, ebs-csi-driver, aws-efs-csi-driver 4곳에서 사용
eksctl get iamserviceaccount --cluster $CLUSTER_NAME
kubectl describe sa -A | grep role-arn
# pod identity 없음
eksctl get podidentityassociation --cluster $CLUSTER_NAME
# EKS API Endpoint
aws eks describe-cluster --name $CLUSTER_NAME | jq -r .cluster.endpoint | cut -d '/' -f 3
B852364D70EA6D62672481D278A15059.gr7.us-west-2.eks.amazonaws.com
dig +short $APIDNS
# OIDC
aws eks describe-cluster --name $CLUSTER_NAME --query cluster.identity.oidc.issuer --output text
https://oidc.eks.us-west-2.amazonaws.com/id/B852364D70EA6D62672481D278A15059
aws iam list-open-id-connect-providers | jq
☞ AWS 관리 콘솔 접속 : EKS, EC2, VPC 등 확인
- EKS - Compute
- EKS - Addon
- EKS - Upgrade version : 상위 1개 마이너 버전 업그레이드만 가능.
- EKS - Dashboard - Cluster insights : Deprecated APIs remove in Kubernetes 1.26 클릭 상세 확인
- EC2 : IDE-Server 제외한, (워커) 노드 6대 EC2 확인
- EC2 - LB : ArgoCD 외부 접속용 NLB 확인
- EC2 - ASG : 3개의 ASG 확인 - blug-mng 관리 노드는 AZ1 곳만 사용 중(us-west-2a)
☞ (추가) 실습 편리 설정 : kube-ops-view , krew , eks-node-view , k9s
- kube-ops-view 설치
# kube-ops-view
helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
helm repo update
helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --namespace kube-system
#
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
annotations:
service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip
service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
service.beta.kubernetes.io/aws-load-balancer-type: external
labels:
app.kubernetes.io/instance: kube-ops-view
app.kubernetes.io/name: kube-ops-view
name: kube-ops-view-nlb
namespace: kube-system
spec:
type: LoadBalancer
ports:
- name: http
port: 80
protocol: TCP
targetPort: 8080
selector:
app.kubernetes.io/instance: kube-ops-view
app.kubernetes.io/name: kube-ops-view
EOF
# kube-ops-view 접속 URL 확인 (1.5, 1.3 배율)
kubectl get svc -n kube-system kube-ops-view-nlb -o jsonpath='{.status.loadBalancer.ingress[0].hostname}' | awk '{ print "KUBE-OPS-VIEW URL = http://"$1"/#scale=1.5"}'
kubectl get svc -n kube-system kube-ops-view-nlb -o jsonpath='{.status.loadBalancer.ingress[0].hostname}' | awk '{ print "KUBE-OPS-VIEW URL = http://"$1"/#scale=1.3"}'
- krew 설치
# 설치
(
set -x; cd "$(mktemp -d)" &&
OS="$(uname | tr '[:upper:]' '[:lower:]')" &&
ARCH="$(uname -m | sed -e 's/x86_64/amd64/' -e 's/\(arm\)\(64\)\?.*/\1\2/' -e 's/aarch64$/arm64/')" &&
KREW="krew-${OS}_${ARCH}" &&
curl -fsSLO "https://github.com/kubernetes-sigs/krew/releases/latest/download/${KREW}.tar.gz" &&
tar zxvf "${KREW}.tar.gz" &&
./"${KREW}" install krew
)
# PATH
export PATH="${KREW_ROOT:-$HOME/.krew}/bin:$PATH"
# 플러그인 설치
kubectl krew install ctx ns df-pv get-all neat stern oomd whoami rbac-tool rolesum
kubectl krew list
#
kubectl df-pv
#
kubectl whoami --all
- eks-node-view
# 설치
wget -O eks-node-viewer https://github.com/awslabs/eks-node-viewer/releases/download/v0.7.1/eks-node-viewer_Linux_x86_64
chmod +x eks-node-viewer
sudo mv -v eks-node-viewer /usr/local/bin
# Standard usage : self-mng 노드는 가격 미출력됨.
eks-node-viewer
kubectl get node --label-columns=eks.amazonaws.com/capacityType,node.kubernetes.io/lifecycle,karpenter.sh/capacity-type,eks.amazonaws.com/compute-type
# Display both CPU and Memory Usage
eks-node-viewer --resources cpu,memory
eks-node-viewer --resources cpu,memory --extra-labels eks-node-viewer/node-age
# Display extra labels, i.e. AZ : node 에 labels 사용 가능
eks-node-viewer --extra-labels topology.kubernetes.io/zone
eks-node-viewer --extra-labels kubernetes.io/arch
# Sort by CPU usage in descending order
eks-node-viewer --node-sort=eks-node-viewer/node-cpu-usage=dsc
# Karenter nodes only
eks-node-viewer --node-selector "karpenter.sh/provisioner-name"
# Specify a particular AWS profile and region
AWS_PROFILE=myprofile AWS_REGION=us-west-2
Computed Labels : --extra-labels
# eks-node-viewer/node-age - Age of the node
eks-node-viewer --extra-labels eks-node-viewer/node-age
eks-node-viewer --extra-labels topology.kubernetes.io/zone,eks-node-viewer/node-age
# eks-node-viewer/node-ephemeral-storage-usage - Ephemeral Storage usage (requests)
eks-node-viewer --extra-labels eks-node-viewer/node-ephemeral-storage-usage
# eks-node-viewer/node-cpu-usage - CPU usage (requests)
eks-node-viewer --extra-labels eks-node-viewer/node-cpu-usage
# eks-node-viewer/node-memory-usage - Memory usage (requests)
eks-node-viewer --extra-labels eks-node-viewer/node-memory-usage
# eks-node-viewer/node-pods-usage - Pod usage (requests)
eks-node-viewer --extra-labels eks-node-viewer/node-pods-usage
- k9s
#
curl -sS https://webinstall.dev/k9s | bash
k9s version
#
k9s
☞ 실습 환경 배포한 테라폼 파일 확인 : 업그레이드 작업에서 사용 예정
# 파일 확인
ec2-user:~/environment:$ ls -lrt terraform/
total 40
-rw-r--r--. 1 ec2-user ec2-user 625 Aug 27 2024 vpc.tf
-rw-r--r--. 1 ec2-user ec2-user 542 Aug 27 2024 versions.tf
-rw-r--r--. 1 ec2-user ec2-user 353 Aug 27 2024 variables.tf
-rw-r--r--. 1 ec2-user ec2-user 282 Aug 27 2024 outputs.tf
-rw-r--r--. 1 ec2-user ec2-user 1291 Aug 27 2024 gitops-setup.tf
-rw-r--r--. 1 ec2-user ec2-user 4512 Feb 5 01:15 base.tf
-rw-r--r--. 1 ec2-user ec2-user 5953 Feb 13 19:55 addons.tf
-rw-r--r--. 1 ec2-user ec2-user 33 Mar 25 02:26 backend_override.tf
# 해당 경로에 tfstate 파일은 없다.
ec2-user:~/environment/terraform:$ terraform output
╷
│ Warning: No outputs found
#
aws s3 ls
2025-03-28 11:56:25 workshop-stack-tfstatebackendbucketf0fc9a9d-0ewwrmamh86n
aws s3 ls s3://workshop-stack-tfstatebackendbucketf0fc9a9d-0ewwrmamh86n
2025-03-28 12:18:23 940996 terraform.tfstate
# (옵션) terraform.tfstate 복사 후 IDE-Server 에서 terraform.tfstate 열어보기
aws s3 cp s3://workshop-stack-tfstatebackendbucketf0fc9a9d-wt2fbt9qjdpc/terraform.tfstate .
# backend_override.tf 수정
terraform {
backend "s3" {
bucket = "workshop-stack-tfstatebackendbucketf0fc9a9d-wt2fbt9qjdpc"
region = "us-west-2"
key = "terraform.tfstate"
}
}
# 확인
terraform state list
terraform output
configure_kubectl = "aws eks --region us-west-2 update-kubeconfig --name eksworkshop-eksctl"
- variables.tf : eks 버전(1.25), mng(1.25)
variable "cluster_version" {
description = "EKS cluster version."
type = string
default = "1.25"
}
variable "mng_cluster_version" {
description = "EKS cluster mng version."
type = string
default = "1.25"
}
variable "ami_id" {
description = "EKS AMI ID for node groups"
type = string
default = ""
}
- 모든 구성 요소는 ArgoCD를 사용하여 EKS 클러스터에 배포
☞ Argo CD CLI 확인
☞ Introduction : 소개
☞ K8S(+AWS EKS) Release Cycles
- Kubernetes 프로젝트는 새로운 기능, 최신 보안 패치 및 버그 수정을 통해 지속적으로 업데이트되고 있습니다. Kubernetes 버전의 의미론을 처음 접하는 경우 Semantic Versioning 용어를 따르며 일반적으로 x.y.z로 표현됩니다. 여기서 x는 주요 버전, y는 마이너 버전, z는 패치 버전입니다. 새로운 Kubernetes 마이너 버전(y)은 대략 4개월마다 출시되며, 모든 버전 >=v1.19는 12개월 동안 표준 지원을 제공하며, 한 번에 최소 3개의 마이너 버전을 지원합니다. Kubernetes 프로젝트는 최신 세 가지 마이너 버전에 대한 릴리스 브랜치를 유지하며, 최신 Kubernetes 릴리스와 관련된 자세한 정보는 여기에서 확인할 수 있습니다.
- Amazon Elastic Kubernetes Service(EKS)는 Kubernetes 프로젝트 릴리스 주기를 따르지만, 버전이 Amazon EKS에서 처음 제공된 후 14개월 동안 한 번에 4개의 마이너 버전에 대한 표준 지원을 제공합니다. 이는 업스트림 Kubernetes가 더 이상 Amazon EKS에서 제공되는 버전을 지원하지 않더라도 마찬가지입니다. Amazon EKS에서 지원되는 Kubernetes 버전에 적용되는 보안 패치를 백포트합니다.
- Amazon EKS Kubernetes 릴리스 캘린더에는 Amazon EKS에서 지원되는 각 Kubernetes 버전에 대한 중요한 릴리스 및 지원 날짜가 있습니다. 최신 EKS 버전의 릴리스가 해당 버전의 Kubernetes보다 몇 주 뒤처지는 이유가 궁금하다면, 이는 Amazon이 새로운 버전의 Kubernetes를 Amazon EKS에서 제공하기 전에 다른 AWS 서비스 및 도구와의 안정성과 호환성을 철저히 테스트하기 때문입니다. 새 버전이 얼마나 빨리 지원될지에 대한 구체적인 날짜나 SLA는 제공하지 않지만, Amazon EKS 팀은 업스트림 릴리스와 EKS 지원 착륙 사이의 격차를 줄이기 위해 노력하고 있습니다.
- [총 26개월 = 14개월 + 12개월] 표준 지원 외에도 Amazon EKS는 최근 확장 지원 기능(출시 발표)을 출시했습니다. 이제 모든 Kubernetes 버전 1.21 이상이 Amazon EKS에서 확장 지원을 받을 수 있습니다. 확장 지원은 표준 지원 종료 후 즉시 자동으로 시작되며 추가로 12개월 동안 계속되어 각 Kubernetes 마이너 버전에 대한 지원이 총 26개월로 늘어납니다. 확장 지원 기간이 끝나기 전에 클러스터가 업데이트되지 않으면 현재 지원되는 가장 오래된 확장 버전으로 자동 업그레이드됩니다. 자세한 내용은 Amazon EKS의 Kubernetes 버전 확장 지원을 참조하세요.
- 확장 지원 기간 동안 Kubernetes 버전을 실행하는 클러스터의 가격은 2024년 4월 1일부터 클러스터당 시간당 총 $0.60의 요금이 부과되며, 비용 표준 지원은 변경되지 않습니다(클러스터당 시간당 $0.10).
- 2024년 7월 23일, Amazon EKS는 Kubernetes 버전 정책을 발표했습니다(출시 발표). Kubernetes 버전 정책에 대한 Amazon EKS 제어를 통해 클러스터 관리자는 EKS 클러스터에 대한 표준 지원 동작 종료를 선택할 수 있습니다. 이러한 제어를 통해 어떤 클러스터가 확장 지원을 받아야 하는지와 Kubernetes 버전에 대한 표준 지원 종료 시 자동으로 업그레이드되어야 하는지 결정할 수 있습니다. 이 제어는 클러스터별로 실행되는 환경이나 애플리케이션에 따라 버전 업그레이드를 비즈니스 요구 사항과 균형 있게 조정할 수 있는 유연성을 제공합니다.
- supportType 속성을 사용하여 새 클러스터와 기존 클러스터 모두에 대한 버전 정책을 설정할 수 있습니다. 버전 지원 정책을 설정하는 데 사용할 수 있는 두 가지 옵션이 있습니다:
- 표준 — 표준 지원이 종료되면 EKS 클러스터가 자동으로 업그레이드될 수 있습니다. 이 설정에서는 연장 지원 비용이 발생하지 않지만, 표준 지원에서 EKS 클러스터는 다음 지원되는 Kubernetes 버전으로 자동으로 업그레이드됩니다.
- 확장 — Kubernetes 버전이 표준 지원 종료에 도달하면 EKS 클러스터가 확장 지원을 시작합니다. 이 설정에서는 확장 지원 요금이 부과됩니다. 클러스터를 표준 지원 Kubernetes 버전으로 업그레이드하여 확장 지원 요금이 발생하지 않도록 할 수 있습니다. 확장 지원에서 실행 중인 클러스터는 확장 지원 종료 시 자동으로 업그레이드할 수 있습니다.
- 이 동작은 EKS 콘솔과 CLI를 통해 쉽게 설정할 수 있습니다. 자세한 내용은 클러스터 업그레이드 정책을 참조하세요.
☞ Why Upgrade?
- Amazon EKS 버전을 업데이트하는 것은 Kubernetes 클러스터의 보안, 안정성, 성능 및 호환성을 유지하는 데 중요하며, 플랫폼에서 제공하는 최신 기능과 역량을 활용할 수 있도록 보장
- Kubernetes 버전은 제어 평면과 데이터 평면을 모두 포함합니다.
- AWS가 제어 평면을 관리하고 업그레이드하는 동안, 사용자(클러스터 소유자/고객)는 클러스터 제어 평면과 데이터 평면 모두에 대한 업그레이드를 시작할 책임을 집니다.
- 클러스터 업그레이드를 시작할 때, AWS는 제어 평면을 관리하며 데이터 평면의 업그레이드를 시작할 책임이 있습니다.
- 여기에는 자가 관리 노드 그룹, 관리 노드 그룹, Fargate 및 기타 애드온을 통해 프로비저닝된 작업자 노드가 포함됩니다.
- 작업자 노드가 Karpenter Controller를 통해 프로비저닝된 경우, 자동 노드 재활용 및 업그레이드를 위해 드리프트 또는 디스럽션 컨트롤러 기능(spec.expireAfter)을 활용할 수 있습니다.
- 또한 클러스터 업그레이드를 계획할 때 작업 부하의 애플리케이션 가용성을 보장해야 하며, 데이터 평면이 업그레이드되는 동안 작업 부하의 가용성을 보장하기 위해서는 적절한 PodDisruptionBudgets와 topologySpreadConstraint가 필수적입니다.
- Kubernetes 마이너 버전 외에도 Amazon EKS는 새로운 Kubernetes 컨트롤 플레인 설정을 가능하게 하고 보안 수정을 제공하기 위해 주기적으로 새로운 플랫폼 버전을 출시합니다.
- 각 Amazon EKS 마이너 버전은 하나 이상의 관련 플랫폼 버전을 가질 수 있습니다.
- 1.30과 같이 Amazon EKS에서 새로운 Kubernetes 마이너 버전을 사용할 수 있는 경우, 해당 Kubernetes 마이너 버전의 초기 플랫폼 버전은 eks.1부터 시작되며 새로운 버전이 출시될 때마다 플랫폼 버전이 증가합니다(eks.n+1).
- 아래 표는 이를 더 잘 시각화하는 데 도움이 될 수 있으며, Amazon EKS 플랫폼 버전에서 더 자세한 내용을 확인할 수 있습니다. - Docs
- 좋은 소식은 Amazon EKS가 해당 Kubernetes 마이너 버전에 대해 모든 기존 클러스터를 최신 Amazon EKS 플랫폼 버전으로 자동 업그레이드하며, 사용자 측에서 명시적인 조치가 필요하지 않다는 것입니다.
- 따라서 Kubernetes 업데이트 관점에서 볼 때, 안전하고 효율적인 EKS 환경을 위해서는 현재 마이너 버전으로 최신 상태를 유지하는 것이 매우 중요하며, 이는 Amazon EKS의 공유 책임 모델을 반영합니다. 이를 통해 클러스터가 최신 보안 패치 및 버그 수정을 실행하고 있는지 확인하여 보안 취약점의 위험을 줄일 수 있습니다. 또한 성능, 확장성 및 신뢰성을 향상시켜 애플리케이션과 고객에게 더 나은 서비스를 제공합니다.
☞ Amazon EKS Upgrades
- 아래는 인플레이스 클러스터 업그레이드의 고급 워크플로우이며, 다음 모듈에서 이에 대해 자세히 살펴보겠습니다.
- Amazon EKS 클러스터를 in-place upgrade 제자리에서 업그레이드하려면 다음과 같은 조치를 취해야 합니다:
- Kubernetes 및 EKS 릴리스 노트를 검토하세요. 업그레이드하기 전에 확인하세요
- 또한 업그레이드를 시작하기 전에 구현해야 할 중요한 정책, 도구 및 절차를 검토하는 섹션입니다.
- 클러스터 백업을 수행합니다. (선택 사항)
- AWS 콘솔이나 CLI를 사용하여 클러스터 제어 평면을 업그레이드합니다.
- add-on 기능 호환성을 검토하세요.
- 클러스터 데이터 플레인을 업그레이드합니다.
- Kubernetes 및 EKS 릴리스 노트를 검토하세요. 업그레이드하기 전에 확인하세요
- 위의 단계는 높은 수준의 시퀀스만 나타내며, API 사용 중단/수정, 버전 왜곡 검사 등과 같은 다른 검사가 있을 수 있습니다.
- 자세한 내용은 여기에서 확인할 수 있습니다. - Docs
- 또한, 아마존 EKS 클러스터 업그레이드에 대한 모범 사례 및 지침은 EKS 모범 사례 가이드의 클러스터 업그레이드 섹션을 참조하시기 바랍니다.
☞ Preparing for Cluster Upgrades : 업그레이드 전 준비
- 준비
- 클러스터 업그레이드를 시작하기 전에 다음 요구 사항을 확인하세요.
- Amazon EKS에서는 클러스터를 생성할 때 지정한 서브넷에서 사용 가능한 IP 주소를 최대 5개까지 필요로 합니다 .
- 클러스터의 AWS Identity and Access Management(IAM) 역할과 보안 그룹 이 AWS 계정에 있어야 합니다 .
- 비밀 암호화를 활성화하는 경우 클러스터 IAM 역할에 AWS Key Management Service(AWS KMS) 키 권한이 있어야 합니다 .
- 업그레이드 워크플로
- Amazon EKS 및 Kubernetes 버전에 대한 주요 업데이트 식별 Identify
- 사용 중단 정책 이해 및 적절한 매니페스트 리팩토링 Understand , Refactor
- 올바른 업그레이드 전략을 사용하여 EKS 제어 평면 및 데이터 평면 업데이트 Update
- 마지막으로 다운스트림 애드온 종속성 업그레이드
- 클러스터 업그레이드를 시작하기 전에 다음 요구 사항을 확인하세요.
☞ EKS Upgrade Insights
배경
- Amazon EKS가 제어 평면 업그레이드를 자동화하는 반면, 업그레이드로 인해 영향을 받을 가능성이 있는 리소스나 애플리케이션을 식별하는 것은 수동 프로세스였습니다.
- 여기에는 릴리스 노트를 검토하여 더 이상 사용되지 않거나 제거된 Kubernetes API를 확인한 다음 해당 API를 사용하는 애플리케이션을 검색하여 수정하는 작업이 포함됩니다.
EKS Upgrade Insights
- Amazon EKS 클러스터 인사이트는 Amazon EKS 및 Kubernetes 모범 사례를 따르는 데 도움이 되는 권장 사항을 제공합니다.
- 모든 Amazon EKS 클러스터는 Amazon EKS가 선별한 인사이트 목록에 대해 자동으로 반복적으로 검사를 받습니다.
- 이러한 인사이트 검사는 Amazon EKS에서 완전히 관리하며 모든 결과를 해결하는 방법에 대한 권장 사항을 제공합니다.
- Cluster 인사이트를 활용하면 최신 Kubernetes 버전으로 업그레이드하는 데 드는 노력을 최소화할 수 있습니다.
- 매일 클러스터 감사 로그를 스캔하여 사용되지 않는 리소스를 찾고 EKS 콘솔에 결과를 표시하거나 API 또는 CLI를 통해 프로그래밍 방식으로 검색할 수 있습니다.
- Amazon EKS는 Kubernetes 버전 업그레이드 준비와 관련된 insight만 출력.
- 클러스터 인사이트는 주기적으로 업데이트됩니다. 클러스터 인사이트를 수동으로 새로 고칠 수 없습니다. 클러스터 문제를 해결하면 클러스터 인사이트가 업데이트되는 데 시간이 걸립니다.
Each insight includes
- 권장 사항: 문제를 해결하기 위한 단계.
- 링크: 릴리스 노트, 블로그 게시물과 같은 추가 정보입니다.
- 리소스 목록: 심각도를 반영하는 상태(통과, 경고, 오류, 알 수 없음)가 있는 Kubernetes 리소스 유형(예: CronJobs)입니다.
- 오류: 다음 마이너 버전에서 API에 대한 호출이 제거되었습니다. 업그레이드 후 업그레이드가 실패합니다.
- 경고: 임박한 문제이지만 즉각적인 조치가 필요하지 않습니다(2개 이상 릴리스된 버전에서 지원 중단).
- 알 수 없음: 백엔드 처리 오류.
- 전체 상태: 인사이트의 모든 리소스 중에서 가장 높은 심각도 상태입니다. 이를 통해 업그레이드하기 전에 클러스터에 수정이 필요한지 빠르게 확인할 수 있습니다.
☞ EKS Upgrade Checklist
들어가며
- EKS 문서를 사용하여 업그레이드 체크리스트를 만듭니다. - **Docs***
- EKS Kubernetes 버전 설명서에는 각 버전에 대한 자세한 변경 사항 목록이 포함되어 있습니다. 각 업그레이드에 대한 체크리스트를 작성하세요.
- EKS 버전 업그레이드에 대한 구체적인 지침은 설명서를 참조하여 각 버전의 주요 변경 사항과 고려 사항을 확인하세요.
☞ Upgrading K8S Add-ons and Components with the API
- 주요한 요소 미리 확인 : 종종 주요 요소는 *-system 네임스페이스에 설치
- kubectl get ns | grep -e '-system' kube-system Active 4h50m # kubectl get-all -n kube-system ...
- 호환성을 위해 설명서를 확인하세요
- 식별된 각 구성 요소에 대해 해당 설명서를 참조하여 대상 Kubernetes 버전과의 호환성을 확인하세요.
- AWS Load Balancer Controller 설명서와 같은 리소스는 버전 호환성 세부 정보를 제공합니다.
- 일부 구성 요소는 진행하기 전에 업그레이드 또는 구성 변경이 필요할 수 있습니다.
- CoreDNS, kube-proxy, Container Network Interface(CNI) 플러그인 및 스토리지 드라이버와 같은 중요한 구성 요소에 세심한 주의를 기울이세요.
- 애드온 및 타사 도구 업그레이드
- 많은 클러스터는 Kubernetes API를 활용하여 유입 제어, 지속적인 전달 및 모니터링과 같은 기능을 제공하는 애드온 및 타사 도구에 의존합니다.
- EKS 클러스터를 업그레이드하려면 호환성을 유지하기 위해 이러한 도구를 업그레이드해야 합니다.
일반적인 추가 기능의 다음 예제와 관련 업그레이드 설명서를 참조하세요.
- Amazon VPC CNI: 각 클러스터 버전에 대한 Amazon VPC CNI 추가 기능의 권장 버전은 Kubernetes 자체 관리형 추가 기능에 대한 Amazon VPC CNI 플러그인 업데이트를 참조하세요. Amazon EKS 추가 기능으로 설치된 경우 한 번에 하나의 마이너 버전만 업그레이드할 수 있습니다.
- kube-proxy: Kubernetes kube-proxy 자체 관리형 추가 기능 업데이트를 참조하세요.
- CoreDNS: CoreDNS 자체 관리형 추가 기능 업데이트를 참조하세요.
- AWS Load Balancer 컨트롤러: AWS Load Balancer 컨트롤러는 배포한 EKS 버전과 호환되어야 합니다. 자세한 내용은 설치 안내서를 참조하세요.
- Amazon Elastic Block Store(Amazon EBS) 컨테이너 스토리지 인터페이스(CSI) 드라이버: 설치 및 업그레이드 정보는 Amazon EBS CSI 드라이버를 Amazon EKS 추가 기능으로 관리를 참조하세요.
- Amazon Elastic File System(Amazon EFS) 컨테이너 스토리지 인터페이스(CSI) 드라이버: 설치 및 업그레이드 정보는 Amazon EFS CSI 드라이버를 참조하세요.
- Kubernetes Metrics Server: 자세한 내용은 GitHub의 metrics-server를 참조하세요.
- Kubernetes 클러스터 오토스케일러: Kubernetes 클러스터 오토스케일러의 버전을 업그레이드하려면 배포에서 이미지의 버전을 변경합니다. Cluster Autoscaler는 Kubernetes 스케줄러와 긴밀하게 연결됩니다. 클러스터를 업그레이드할 때는 항상 업그레이드해야 합니다. GitHub 릴리스를 검토하여 Kubernetes 마이너 버전에 해당하는 최신 릴리스의 주소를 찾습니다.
- Karpenter: 설치 및 업그레이드 정보는 Karpenter 설명서를 참조하세요.
추가 팁 : Velero 와 같은 백업 툴로 클러스터의 주요 데이터를 미리 백업 해두기, 테스트 환경에서 충분히 사전 테스트 해보기
- EKS Cluster Upgrade High Availability (HA) Strategies
☞ 인플레이스 클러스터 업그레이드 vs 블루/그린 클러스터 업그레이드 비교
장점
- 여러 EKS 버전을 동시에 변경 가능(예: 1.23에서 1.25로)
- 이전 클러스터로 다시 전환 가능
- 최신 시스템(예: Terraform)으로 관리할 수 있는 새 클러스터를 만듭니다.
- 작업 부하를 개별적으로 마이그레이션할 수 있습니다.
단점
- API 엔드포인트 및 OIDC 변경으로 업데이트가 필요. (예: kubectl 및 CI/CD)
- 마이그레이션 중에 2개의 클러스터를 병렬로 실행해야 하므로 비용이 많이 들고 지역 용량이 제한될 수 있습니다.
- 작업 부하가 서로 의존하여 함께 마이그레이션되는 경우 **더 많은 조정(고려)**이 필요합니다.
- 로드 밸런서와 외부 DNS는 여러 클러스터에 쉽게 확장될 수 없습니다.
- 이 전략은 실행 가능하지만, 인플레이스 업그레이드보다 비용이 많이 들고 조정 및 워크로드 마이그레이션에 더 많은 시간이 필요합니다. 어떤 상황에서는 필요할 수 있으므로 신중하게 계획해야 합니다.
- 높은 수준의 자동화와 GitOps와 같은 선언적 시스템을 사용하면 이 접근 방식을 구현하기가 더 쉬울 수 있습니다. 그러나 데이터가 백업되고 새 클러스터로 마이그레이션되도록 하기 위해 상태 저장 워크로드에 대한 추가 예방 조치를 취해야 합니다.
☞ Choosing an Upgrade Strategy : 업그레이드 전략 선택 (In-Place vs Blue-Green)
- 적절한 업그레이드 전략이 없는 경우의 일반적인 문제
- 명확하게 정의된 EKS 업그레이드 전략이 없는 고객은 다음을 포함한 여러 가지 어려움에 직면할 수 있습니다.
- 계획 되지 않은 가동 중지: 적절한 계획 없이 업그레이드하면 예상치 못한 가동 중지가 발생하여 애플리케이션 가용성과 사용자 경험에 영향을 미칠 수 있습니다.
- 호환성 문제: 새로운 Kubernetes 버전과 애플리케이션, 애드온 및 도구의 호환성을 철저히 테스트하고 검증하지 못하면 손상 및 중단이 발생할 수 있습니다.
- 롤백의 어려움: 업그레이드가 실패하거나 문제가 발생할 경우, 잘 정의된 롤백 계획 없이 이전 버전으로 롤백하는 것은 복잡하고 시간이 많이 걸릴 수 있습니다.
- 보안 취약점: 업그레이드를 지연하면 클러스터가 알려진 보안 취약점에 노출되어 애플리케이션과 데이터가 위험에 노출될 수 있습니다.
- 놓친 기회: 정기적으로 업그레이드하지 않으면 새로운 기능, 성능 개선 및 최신 Kubernetes 버전에 도입된 최적화를 놓칠 수 있습니다.
- 이러한 과제를 해결하려면 잘 정의된 업그레이드 전략을 수립하는 것이 중요합니다. EKS 업그레이드를 계획할 때 고려해야 할 두 가지 주요 전략이 있습니다. 인플레이스 업그레이드와 블루그린 업그레이드입니다. 각 전략에는 고유한 장점, 단점 및 고려 사항이 있으며, 특정 요구 사항과 제약 조건에 따라 신중하게 평가해야 합니다.
- 이 모듈에서는 두 업그레이드 전략을 자세히 살펴보고 장단점을 논의합니다. 또한 특정 요구 사항과 제약 조건에 따라 가장 적합한 전략을 선택하는 방법에 대한 지침도 제공합니다. 이 모듈을 마치면 In-Place와 Blue-Green 업그레이드의 차이점을 명확하게 이해하고 EKS 클러스터 업그레이드를 계획할 때 정보에 입각한 결정을 내릴 수 있게 됩니다.
- In-place 업그레이드 전략 : 기존 EKS 클러스터를 최신 Kubernetes 버전으로 업그레이드
내부 업그레이드 프로세스에는 일반적으로 다음 단계가 포함됩니다.
- EKS 제어 평면을 대상 Kubernetes 버전으로 업그레이드합니다.
- 새로운 Kubernetes 버전과 일치하도록 작업자 노드 AMI를 업데이트합니다.
- 애플리케이션 가동 중지 시간을 최소화하기 위해 작업자 노드를 한 번에 하나씩 또는 소규모로 비우고 교체합니다.
- 새 버전과의 호환성을 보장하기 위해 모든 Kubernetes 매니페스트, 애드온 및 구성을 업데이트합니다.
- 철저한 테스트와 검증을 수행하여 애플리케이션과 서비스가 예상대로 작동하는지 확인합니다.
인플레이스 업그레이드의 장점
- VPC, 서브넷, 보안 그룹 등 기존 클러스터 리소스와 구성을 유지합니다.
- 동일한 클러스터 API 엔드포인트를 유지하므로 외부 통합 및 도구를 업데이트할 필요성이 최소화됩니다.
- 업그레이드 프로세스 중에 여러 클러스터를 관리하는 것에 비해 인프라 오버헤드가 덜 필요합니다.
- 클러스터 간에 상태 저장 애플리케이션과 지속형 데이터를 마이그레이션할 필요성을 최소화합니다.
인플레이스 업그레이드의 단점
- 업그레이드 과정에서 가동 중지 시간을 최소화하려면 신중한 계획과 조정이 필요합니다.
- 여러 Kubernetes 버전을 건너뛸 경우 연속적인 업그레이드가 여러 번 필요할 수 있으며, 업그레이드 프로세스가 길어질 수 있습니다.
- 업그레이드 중에 문제가 발생하면 롤백이 더 어렵고 시간이 많이 걸릴 수 있습니다. Control Plane이 업그레이드되면 롤백할 수 없습니다.
- 호환성을 보장하기 위해 모든 구성 요소와 종속성에 대한 철저한 테스트와 검증이 필요합니다.
☞ Blue-Green 업그레이드 전략 : 새 EKS 클러스터(addon, apps)를 만들고 트래픽을 이전 클러스터에서 새 클러스터로 점진적으로 전환
블루-그린 업그레이드 프로세스에는 일반적으로 다음 단계가 포함됩니다.
- 원하는 Kubernetes 버전 및 구성으로 새 EKS 클러스터(녹색)를 만듭니다.
- 새 클러스터에 애플리케이션, 애드온 및 구성을 배포합니다.
- 새 클러스터가 예상대로 작동하는지 확인하기 위해 철저한 테스트와 검증을 수행합니다.
- DNS 업데이트, 로드 밸런서 구성 또는 서비스 메시와 같은 기술을 사용하여 점진적으로 이전 클러스터(파란색)에서 새 클러스터(녹색)로 트래픽을 전환합니다.
- 새 클러스터를 면밀히 모니터링하여 트래픽을 처리하고 예상대로 성능이 발휘되는지 확인하세요.
- 모든 트래픽이 새 클러스터로 전환되면 이전 클러스터를 해제합니다.
블루-그린 업그레이드의 장점
- 새로운 클러스터를 프로덕션 트래픽으로 전환하기 전에 철저히 테스트할 수 있으므로, 보다 통제되고 안전한 업그레이드 프로세스가 가능합니다.
- 단일 업그레이드에서 여러 Kubernetes 버전을 건너뛸 수 있으므로 전체 업그레이드 시간과 노력이 줄어듭니다.
- 문제가 발생할 경우 트래픽을 이전 클러스터로 다시 전환하여 빠르고 쉬운 롤백 메커니즘을 제공합니다.
- 새 클러스터가 완전히 검증될 때까지 이전 클러스터가 계속 트래픽을 제공하므로 업그레이드 프로세스 동안 가동 중지 시간이 최소화됩니다.
블루-그린 업그레이드의 단점
- 두 개의 클러스터를 동시에 유지 관리해야 하므로 업그레이드 프로세스에 추가적인 인프라 리소스와 비용이 필요합니다.
- 클러스터 간 트래픽 이동에 대한 보다 복잡한 조정 및 관리가 필요합니다.
- CI/CD 파이프라인, 모니터링 시스템, 액세스 제어 등의 외부 통합을 업데이트하여 새 클러스터를 가리키도록 해야 합니다.
- 클러스터 간 데이터 마이그레이션이나 동기화가 필요한 상태 저장 애플리케이션의 경우 어려울 수 있습니다.
상태 저장 워크로드에 대한 고려 사항
- 상태 저장 워크로드에 대한 블루-그린 업그레이드를 수행할 때는 데이터 마이그레이션 및 동기화 프로세스를 신중하게 계획하고 실행하세요.
- Velero와 같은 도구를 사용하여 영구 데이터를 마이그레이션하고 클러스터 간에 데이터를 동기화 상태로 유지하며 빠른 롤백을 활성화하세요.
- 새 클러스터에서 영구 볼륨 프로비저닝을 구성하여 이전 클러스터의 스토리지 클래스 또는 프로비저너와 일치시키세요.
- 애플리케이션 설명서를 참조하고 애플리케이션 소유자와 협력하여 데이터 마이그레이션 및 동기화에 대한 특정 요구 사항을 이해하세요.
- Velero와 같은 도구를 적절히 계획하고 활용하는 것은 위험을 최소화하고 상태 저장 애플리케이션에 대한 원활한 업그레이드 프로세스를 보장하는 데 중요합니다.
☞ In-place 와 Blue-Green 업그레이드 전략 중 선택
전략을 선택할 때 고려해야 할 요소
- 가동 중지 허용 범위 : 업그레이드 프로세스 중에 애플리케이션과 서비스에 대한 허용 가능한 가동 중지 시간 수준을 고려하세요.
- 복잡성 업그레이드 : 애플리케이션 아키텍처, 종속성 및 상태 구성 요소의 복잡성을 평가합니다.
- Kubernetes 버전 차이 : 현재 Kubernetes 버전과 대상 버전 간의 차이와 애플리케이션 및 애드온의 호환성을 평가합니다.
- 리소스 제약 : 업그레이드 프로세스 중에 여러 클러스터를 유지하기 위한 사용 가능한 인프라 리소스와 예산을 고려하세요. 블루/그린과 유사한 카나리아 전략은 워크로드를 늘리는 동안 이전 클러스터를 확장하는 동안 새 클러스터를 확장하는 것을 제외하고는 이를 최소화합니다.
- 팀 전문성 : 여러 클러스터를 관리하고 트래픽 전환 전략을 구현하는 데 대한 팀의 전문성과 익숙함을 평가합니다.
☞ K8S Version Skew 를 이용한 Incremental In-place 업그레이드 전략
허용되는 최대 버전 스큐에 도달할 때까지 작업자 노드 업그레이드를 연기하면서 EKS 제어 평면을 점진적으로 업그레이드
업그레이드 프로세스:
- EKS 제어 평면을 다음 마이너 버전으로 업그레이드합니다. Kubernetes는 제어 평면이 작업자 노드보다 최대 2개의 마이너 버전(또는 Kubernetes 1.28부터 3개의 마이너 버전) 앞설 수 있는 버전 왜곡을 지원합니다.
- 제어 평면이 최대 허용 스큐를 초과하는 버전으로 업그레이드될 때까지 기존 버전에서 작업자 노드를 유지합니다. 예를 들어, 제어 평면을 1.21에서 시작하고 작업자 노드를 1.21에서 시작하는 경우 작업자 노드를 업그레이드하지 않고도 제어 평면을 1.22, 1.23 및 1.24로 업그레이드할 수 있습니다.
- 제어 평면이 허용되는 최대 스큐를 초과하는 버전(예: 작업자가 여전히 1.21을 사용하고 있는 경우 1.24)에 도달하면 제어 평면을 추가로 업그레이드하기 전에 지원되는 스큐 범위 내의 버전(예: 1.22 또는 1.23)으로 작업자 노드를 업그레이드합니다.
- 제어 평면과 작업자 노드 모두에서 원하는 Kubernetes 버전에 도달할 때까지 1~3단계를 반복합니다.
이상적인 사용 사례:
- 최신 Kubernetes 릴리스보다 여러 버전 뒤쳐져 있으며 점진적으로 따라잡고 싶어합니다.
- 작업자 노드에는 광범위한 테스트가 필요하고 빈번한 업그레이드를 어렵게 만드는 복잡하고 상태 저장형 워크로드가 있습니다.
- 작업자 노드의 중단을 최소화하면서 새로운 기능과 버그 수정에 액세스하기 위해 정기적으로 제어 평면 업그레이드를 수행하려고 합니다.
고려 사항:
- 워커 노드가 호환 버전으로 업그레이드될 때까지 일부 새로운 Kubernetes 기능, 성능 개선 및 버그 수정을 완전히 사용할 수 없을 수 있습니다.
- 여러 버전을 건너뛴 후 워커 노드를 업그레이드하는 경우 호환성을 보장하고 문제를 식별하기 위해 철저한 테스트가 중요합니다.
- 이 전략은 유연성을 제공하지만, 비호환성 위험을 최소화하고 클러스터 전체에서 일관된 기능 세트를 보장하기 위해 제어 평면과 작업자 노드 버전을 최대한 가깝게 유지하는 것이 좋습니다.
☞ In-place Cluster Upgrade : 1.25 → 1.26 : 총 1시간 (예상) 소요 이상 실습 포함
들어가며
사전 준비
- Kubernetes와 EKS의 릴리스 노트를 검토하세요.
- 추가 기능의 호환성을 검토하세요. Kubernetes 애드온과 사용자 정의 컨트롤러를 업그레이드하세요.
- 워크로드에서 더 이상 사용되지 않거나 삭제된 API 사용을 식별하고 수정합니다.
- 클러스터를 백업합니다(필요한 경우).
업그레이드 단계
- 클러스터 제어 평면 업그레이드
- Kubernetes 애드온 및 사용자 정의 컨트롤러를 업데이트하세요.
- 클러스터의 노드를 업그레이드하세요
필요한 경우 사용되지 않는 API를 제거하고 Kubernetes 매니페스트를 업데이트하여 적절한 리소스를 수정한 후, 위의 순서대로 클러스터를 업그레이드할 수 있습니다. 이러한 단계는 테스트 환경에서 완료하는 것이 가장 좋으므로 프로덕션 클러스터에서 업그레이드 작업을 시작하기 전에 클러스터 구성과 애플리케이션 매니페스트의 문제를 발견할 수 있습니다.
☞ [1. 제어부] Control Plane Upgrade 10분 소요 실습 포함
- Amazon EKS에서 클러스터를 실행하는 가장 큰 이점 중 하나는 클러스터 제어 평면의 업그레이드가 단일 완전 자동화된 작업이라는 것입니다. 업그레이드하는 동안 현재 제어 평면 구성 요소는 파란색/녹색 방식으로 업그레이드됩니다. 문제가 발생하면 업그레이드가 롤백되고 애플리케이션은 계속 작동하고 사용 가능합니다.
- 인플레이스 업그레이드는 다음으로 높은 Kubernetes 마이너 버전까지 점진적으로 진행되므로 현재 버전과 대상 버전 사이에 여러 릴리스가 있는 경우 순서대로 각 릴리스를 거쳐야 합니다. 다양한 요소를 고려하고 특정 요구 사항과 제약 조건에 가장 적합한 접근 방식을 평가하는 것이 중요합니다. 인플레이스 업그레이드 전략과 블루그린 업그레이드 전략 선택 참조.
제어 평면을 업그레이드하기 전의 기본 Amazon EKS 요구 사항입니다.
- Amazon EKS는 클러스터를 업데이트하기 위해 클러스터를 생성할 때 지정한 서브넷에서 최대 5개의 사용 가능한 IP 주소가 필요합니다. IP를 사용할 수 없는 경우 버전 업데이트를 수행하기 전에 "UpdateClusterConfiguration" API를 사용하여 클러스터 구성을 업데이트하여 새 클러스터 서브넷을 포함하거나 기존 VPC CIDR 블록의 IP 주소가 부족하면 추가 CIDR 블록을 연결하는 것을 고려하세요.
- 제어 평면 IAM 역할은 필요한 권한이 있는 계정에 있어야 합니다.
- 클러스터에 비밀 암호화가 활성화된 경우 클러스터 IAM 역할에 AWS Key Management Service(AWS KMS) 키를 사용할 수 있는 권한이 있는지 확인해야 합니다.
업그레이드를 시작하면 AWS가 EKS 제어 평면을 업그레이드하는 단계에 대한 자세한 분석은 다음과 같습니다.
- 업그레이드 사전 검사: 업그레이드를 시작하기 전에 고객은 EKS 업그레이드 인사이트를 활용하여 업그레이드 사전 검사를 수행하고, EKS 클러스터를 최신 버전의 Kubernetes로 업그레이드하는 데 영향을 줄 수 있는 문제에 대한 결과를 수정하기 위한 권장 사항을 사용할 수 있습니다.
- 제어 평면 구성 요소 업그레이드: 제어 평면 구성 요소에 대한 업그레이드는 블루/그린 방식으로 처리됩니다. 업그레이드 프로세스가 완료되면 제어 평면 API 서버 엔드포인트가 refreshed 됩니다. 전체 업그레이드 프로세스가 우아하게 처리됩니다.
- 이전 제어 평면 구성 요소 종료: 업그레이드가 완료되면 이전 제어 평면 구성 요소가 종료됩니다.
업그레이드는 블루/그린 방식으로 이루어지므로 EKS 업그레이드가 실패할 경우 중단을 최소화할 수 있습니다.
- 실패 감지: AWS는 업그레이드 프로세스를 지속적으로 모니터링하여 문제를 감지합니다. 제어 평면 업그레이드 중에 문제가 발생하면 프로세스가 즉시 중단됩니다.
- 새로운 제어 평면 구성 요소 종료: AWS는 업그레이드 중에 발생하는 새로운 제어 평면 구성 요소를 종료합니다.
- 롤백 후 평가: AWS는 업그레이드 실패의 이유를 평가하고 문제를 해결하는 방법에 대한 지침을 제공합니다. 고객은 업그레이드를 다시 시도하기 전에 문제를 해결하고 해결해야 합니다.
아래 방법 중 Terraform 을 이용한 방법을 진행하므로 나머지 방법은 실행하지 않음
방법 1 : 이 명령에서 대상 버전을 지정할 수 있지만 --version에 허용되는 값은 클러스터의 현재 버전 또는 한 버전 더 높은 버전입니다. 현재 두 개 이상의 Kubernetes 버전 업그레이드는 지원되지 않습니다.
eksctl upgrade cluster --name $EKS_CLUSTER_NAME --approve
방법 2 : AWS 관리 콘솔
방법 3 : 클러스터에 관리되는 노드 그룹이 연결되어 있는 경우, 클러스터를 새로운 Kubernetes 버전으로 업데이트하려면 모든 노드 그룹의 Kubernetes 버전이 클러스터의 Kubernetes 버전과 일치해야 합니다.
aws eks update-cluster-version --region ${AWS_REGION} --name $EKS_CLUSTER_NAME --kubernetes-version 1.26
{
"update": {
"id": "b5f0ba18-9a87-4450-b5a0-825e6e84496f",
"status": "InProgress",
"type": "VersionUpdate",
"params": [
{
"type": "Version",
"value": "1.26"
},
{
"type": "PlatformVersion",
"value": "eks.1"
}
],
[...]
"errors": []
}
}
# 다음 명령으로 클러스터 업데이트 상태를 모니터링할 수 있습니다. 이전 명령에서 반환한 클러스터 이름과 업데이트 ID를 사용합니다.
# Successful 상태가 표시되면 업데이트가 완료된 것입니다.
aws eks describe-update --region ${AWS_REGION} --name $EKS_CLUSTER_NAME --update-id b5f0ba18-9a87-4450-b5a0-825e6e84496f
방법 4 : Terraform
이 랩에서는 terraform을 사용하여 클러스터를 업그레이드합니다. EKS 클러스터는 이미 terraform을 통해 이 랩에 프로비저닝되었습니다. terraform init, plan 및 apply를 수행하여 리소스 상태를 새로 고칩니다.
#
cd ~/environment/terraform
terraform state list
# 모니터링을 위한 작업 추가
# 현재 버전 확인 : 파일로 저장해두기
kubectl get pods --all-namespaces -o jsonpath="{.items[*].spec.containers[*].image}" | tr -s '[[:space:]]' '\n' | sort | uniq -c > 1.25.txt
# IDE-Server 혹은 자신의 PC에서 반복 접속 해두자!
export UI_WEB=$(kubectl get svc -n ui ui-nlb -o jsonpath='{.status.loadBalancer.ingress[0].hostname}'/actuator/health/liveness)
curl -s $UI_WEB ; echo
{"status":"UP"}
# 반복 접속 1
UI_WEB=k8s-ui-uinlb-74fa280178-d399e6ef5e28fe82.elb.us-west-2.amazonaws.com/actuator/health/liveness
while true; do curl -s $UI_WEB ; date; sleep 1; echo; done
# 반복 접속 2 : aws cli 자격증명 설정 필요
aws eks describe-cluster --name $EKS_CLUSTER_NAME | egrep 'version|endpoint"|issuer|platformVersion'
"version": "1.25",
"endpoint": "https://A77BDC5EEBAE5EC887F1747B6AE965B3.gr7.us-west-2.eks.amazonaws.com",
"issuer": "https://oidc.eks.us-west-2.amazonaws.com/id/A77BDC5EEBAE5EC887F1747B6AE965B3"
"platformVersion": "eks.44",
# 반복 접속 2
while true; do curl -s $UI_WEB; date; aws eks describe-cluster --name eksworkshop-eksctl | egrep 'version|endpoint"|issuer|platformVersion'; echo ; sleep 2; echo; done
- cluster_version 을 1.25 -> 1.26 으로 변경
# 기본 정보
aws eks describe-cluster --name $EKS_CLUSTER_NAME | egrep 'version|endpoint"|issuer|platformVersion'
# Plan을 적용하면 Terraform이 어떤 변경을 하는지 볼 수 있습니다.
terraform plan
terraform plan -no-color > plan-output.txt # IDE에서 열어볼것
...
<= data "tls_certificate" "this" {
+ certificates = (known after apply)
+ id = (known after apply)
+ url = "https://oidc.eks.us-west-2.amazonaws.com/id/6081887A4A0000F3688517E06FDE08BA"
}
# module.eks.aws_iam_openid_connect_provider.oidc_provider[0] will be updated in-place
~ resource "aws_iam_openid_connect_provider" "oidc_provider" {
id = "arn:aws:iam::283052406981:oidc-provider/oidc.eks.us-west-2.amazonaws.com/id/6081887A4A0000F3688517E06FDE08BA"
tags = {
"Blueprint" = "eksworkshop-eksctl"
"GithubRepo" = "github.com/aws-ia/terraform-aws-eks-blueprints"
"Name" = "eksworkshop-eksctl-eks-irsa"
"karpenter.sh/discovery" = "eksworkshop-eksctl"
}
~ thumbprint_list = [
- "9e99a48a9960b14926bb7f3b02e22da2b0ab7280",
] -> (known after apply)
# (4 unchanged attributes hidden)
}
# module.eks_blueprints_addons.data.aws_eks_addon_version.this["aws-ebs-csi-driver"] will be read during apply
# (depends on a resource or a module with changes pending)
<= data "aws_eks_addon_version" "this" {
+ addon_name = "aws-ebs-csi-driver"
+ id = (known after apply)
+ kubernetes_version = "1.26"
+ most_recent = true
+ version = (known after apply)
}
# module.eks_blueprints_addons.aws_eks_addon.this["vpc-cni"] will be updated in-place
~ resource "aws_eks_addon" "this" {
~ addon_version = "v1.19.3-eksbuild.1" -> (known after apply)
id = "eksworkshop-eksctl:vpc-cni"
tags = {
"Blueprint" = "eksworkshop-eksctl"
"GithubRepo" = "github.com/aws-ia/terraform-aws-eks-blueprints"
}
# (11 unchanged attributes hidden)
# (1 unchanged block hidden)
}
# module.eks_blueprints_addons.aws_iam_role_policy_attachment.karpenter["AmazonEC2ContainerRegistryReadOnly"] must be replaced
-/+ resource "aws_iam_role_policy_attachment" "karpenter" {
~ id = "karpenter-eksworkshop-eksctl-20250330050128278900000037" -> (known after apply)
~ policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly" -> (known after apply) # forces replacement
# (1 unchanged attribute hidden)
}
# module.eks_blueprints_addons.module.aws_efs_csi_driver.data.aws_iam_policy_document.assume[0] will be read during apply
# (depends on a resource or a module with changes pending)
<= data "aws_iam_policy_document" "assume" {
+ id = (known after apply)
+ json = (known after apply)
+ minified_json = (known after apply)
+ statement {
+ actions = [
+ "sts:AssumeRoleWithWebIdentity",
]
+ effect = "Allow"
+ condition {
+ test = "StringEquals"
+ values = [
+ "sts.amazonaws.com",
]
+ variable = "oidc.eks.us-west-2.amazonaws.com/id/6081887A4A0000F3688517E06FDE08BA:aud"
}
+ condition {
+ test = "StringEquals"
+ values = [
+ "system:serviceaccount:kube-system:efs-csi-controller-sa",
]
+ variable = "oidc.eks.us-west-2.amazonaws.com/id/6081887A4A0000F3688517E06FDE08BA:sub"
}
+ principals {
+ identifiers = [
+ "arn:aws:iam::283052406981:oidc-provider/oidc.eks.us-west-2.amazonaws.com/id/6081887A4A0000F3688517E06FDE08BA",
]
+ type = "Federated"
}
}
+ statement {
+ actions = [
+ "sts:AssumeRoleWithWebIdentity",
]
+ effect = "Allow"
+ condition {
+ test = "StringEquals"
+ values = [
+ "sts.amazonaws.com",
]
+ variable = "oidc.eks.us-west-2.amazonaws.com/id/6081887A4A0000F3688517E06FDE08BA:aud"
}
+ condition {
+ test = "StringEquals"
+ values = [
+ "system:serviceaccount:kube-system:efs-csi-node-sa",
]
+ variable = "oidc.eks.us-west-2.amazonaws.com/id/6081887A4A0000F3688517E06FDE08BA:sub"
}
+ principals {
+ identifiers = [
+ "arn:aws:iam::283052406981:oidc-provider/oidc.eks.us-west-2.amazonaws.com/id/6081887A4A0000F3688517E06FDE08BA",
]
+ type = "Federated"
}
}
}
# 기본 정보
aws eks describe-cluster --name $EKS_CLUSTER_NAME | egrep 'version|endpoint"|issuer|platformVersion'
# 클러스터 버전을 변경하면 테라폼 계획에 표시된 것처럼, 관리 노드 그룹에 대한 특정 버전이나 AMI가 테라폼 파일에 정의되지 않은 경우,
# eks 클러스터 제어 평면과 관리 노드 그룹 및 애드온과 같은 관련 리소스를 업데이트하게 됩니다.
# 이 계획을 적용하여 제어 평면 버전을 업데이트해 보겠습니다.
terraform apply -auto-approve # 10분 소요
# eks control plane 1.26 업글 확인
aws eks describe-cluster --name $EKS_CLUSTER_NAME | jq
...
"version": "1.26",
# endpoint, issuer, platformVersion 동일 => IRSA 를 사용하는 4개의 App 동작에 문제 없음!
aws eks describe-cluster --name $EKS_CLUSTER_NAME | egrep 'version|endpoint"|issuer|platformVersion'
# 파드 컨테이너 이미지 버전 모두 동일
kubectl get pods --all-namespaces -o jsonpath="{.items[*].spec.containers[*].image}" | tr -s '[[:space:]]' '\n' | sort | uniq -c > 1.26.txt
kubectl get pods --all-namespaces -o jsonpath="{.items[*].spec.containers[*].image}" | tr -s '[[:space:]]' '\n' | sort | uniq -c
diff 1.26.txt 1.25.txt # 동일함
# 파드 AGE 정보 확인 : 재생성된 파드 없음!
kubectl get pod -A
...
☞ Control Plane Upgrade 후 상태 확인
Insight: EKS add-on version compatibility
Argo CD 에 ui app(HPA 리소스) 상태 확인
#
kubectl get hpa -n ui ui -o yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"autoscaling/v2beta2","kind":"HorizontalPodAutoscaler","metadata":{"annotations":{},"labels":{"argocd.argoproj.io/instance":"ui"},"name":"ui","namespace":"ui"},"spec":{"maxReplicas":4,"minReplicas":1,"scaleTargetRef":{"apiVersion":"apps/v1","kind":"Deployment","name":"ui"},"targetCPUUtilizationPercentage":80}}
creationTimestamp: "2025-03-30T05:07:00Z"
labels:
argocd.argoproj.io/instance: ui
...
kubectl get hpa -n ui ui -o json | jq -r '.metadata.annotations."kubectl.kubernetes.io/last-applied-configuration"' | jq
#
kubectl get hpa -n ui ui -o yaml | kubectl neat > hpa-ui.yaml
kubectl convert -f hpa-ui.yaml
☞ [2. 애드온] Upgrading EKS Addons
- EKS 업그레이드의 일부로 클러스터에 설치된 애드온을 업그레이드해야 합니다. 애드온은 K8s 애플리케이션에 지원 운영 기능을 제공하는 소프트웨어로, 네트워킹, 컴퓨팅 및 스토리지를 위해 클러스터가 기본 AWS 리소스와 상호 작용할 수 있도록 하는 관찰 에이전트 또는 Kubernetes 드라이버와 같은 소프트웨어가 포함됩니다. 애드온 소프트웨어는 일반적으로 Kubernetes 커뮤니티, AWS와 같은 클라우드 공급자 또는 타사 공급업체에서 빌드하고 유지 관리합니다.
- Amazon EKS 애드온을 사용하면 Amazon EKS 클러스터가 안전하고 안정적임을 지속적으로 보장하고 애드온을 설치, 구성 및 업데이트하는 데 필요한 작업량을 줄일 수 있습니다. Amazon EKS의 Amazon EKS 애드온 목록
- 이 섹션에서는 아래 추가 기능을 업그레이드합니다.
eksctl을 사용하여 EKS 1.25의 기존 애드온을 보려면 : 가능한 업그레이드 버전 정보 확인!
ec2-user:~/environment/terraform:$ eksctl get addon --cluster $CLUSTER_NAME
2025-04-01 10:02:03 [ℹ] Kubernetes version "1.26" in use by cluster "eksworkshop-eksctl"
2025-04-01 10:02:03 [ℹ] getting all addons
2025-04-01 10:02:04 [ℹ] 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
aws-ebs-csi-driver v1.41.0-eksbuild.1 ACTIVE 0 arn:aws:iam::xxxxxxxxxxxx:role/eksworkshop-eksctl-ebs-csi-driver-2025033004562460960000001d
coredns v1.8.7-eksbuild.10 ACTIVE 0 v1.9.3-eksbuild.22,v1.9.3-eksbuild.21,v1.9.3-eksbuild.19,v1.9.3-eksbuild.17,v1.9.3-eksbuild.15,v1.9.3-eksbuild.11,v1.9.3-eksbuild.10,v1.9.3-eksbuild.9,v1.9.3-eksbuild.7,v1.9.3-eksbuild.6,v1.9.3-eksbuild.5,v1.9.3-eksbuild.3,v1.9.3-eksbuild.2
kube-proxy v1.25.16-eksbuild.8 ACTIVE 0 v1.26.15-eksbuild.24,v1.26.15-eksbuild.19,v1.26.15-eksbuild.18,v1.26.15-eksbuild.14,v1.26.15-eksbuild.10,v1.26.15-eksbuild.5,v1.26.15-eksbuild.2,v1.26.13-eksbuild.2,v1.26.11-eksbuild.4,v1.26.11-eksbuild.1,v1.26.9-eksbuild.2,v1.26.7-eksbuild.2,v1.26.6-eksbuild.2,v1.26.6-eksbuild.1,v1.26.4-eksbuild.1,v1.26.2-eksbuild.1
vpc-cni v1.19.3-eksbuild.1 ACTIVE 0
terraform을 사용하여 CoreDNS , kube-proxy 및 VPC CNI 의 업그레이드를 살펴보겠습니다. 이를 위해 파일에서 CoreDNS, kube-proxy 및 VPC CNI의 버전을 업데이트하기만 하면 됩니다
/home/ec2-user/environment/terraform/addons.tf.
eks_addons = {
coredns = {
addon_version = "v1.8.7-eksbuild.10"
}
kube-proxy = {
addon_version = "v1.25.16-eksbuild.8"
}
vpc-cni = {
most_recent = true
}
aws-ebs-csi-driver = {
service_account_role_arn = module.ebs_csi_driver_irsa.iam_role_arn
}
}
Amazon EKS는 AWS Managed Addons에 대한 주어진 K8s 버전에 대한 호환되는 애드온 버전을 나열하는 API를 제공합니다. 다음 명령을 사용하여 1.26 호환되는 coredns애드온 버전을 찾을 수 있습니다.
#
kubectl get pods --all-namespaces -o jsonpath="{.items[*].spec.containers[*].image}" | tr -s '[[:space:]]' '\n' | sort | uniq -c
#
aws eks describe-addon-versions --addon-name coredns --kubernetes-version 1.26 --output table \
--query "addons[].addonVersions[:10].{Version:addonVersion,DefaultVersion:compatibilities[0].defaultVersion}"
#
aws eks describe-addon-versions --addon-name kube-proxy --kubernetes-version 1.26 --output table \
--query "addons[].addonVersions[:10].{Version:addonVersion,DefaultVersion:compatibilities[0].defaultVersion}"
- 참고로 나머지 Addon 인 VPC CNI, EBS CSI Driver 는 현재 최신 버전 사용 중임 - 콘솔에서 addon 에서 Edit 로 버전 정보 확인.
위의 출력에서 최신 버전을 선택하여 addons.tf아래에 표시된 대로 파일에 업데이트합니다.
/home/ec2-user/environment/terraform/addons.tf변경 사항을 저장 하고 적용합니다.
# 반복 접속 : 아래 coredns, kube-proxy addon 업그레이드 시 ui 무중단 통신 확인!
aws eks update-kubeconfig --name eksworkshop-eksctl # 자신의 집 PC일 경우
kubectl get pod -n kube-system -l k8s-app=kube-dns
kubectl get pod -n kube-system -l k8s-app=kube-proxy
kubectl get pod -n kube-system -l 'k8s-app in (kube-dns, kube-proxy)'
while true; do curl -s $UI_WEB ; date; kubectl get pod -n kube-system -l 'k8s-app in (kube-dns, kube-proxy)'; echo ; sleep 2; done
#
cd ~/environment/terraform/
terraform plan -no-color | tee addon.txt
...
# module.eks_blueprints_addons.aws_eks_addon.this["coredns"] will be updated in-place
~ resource "aws_eks_addon" "this" {
~ addon_version = "v1.8.7-eksbuild.10" -> "v1.9.3-eksbuild.22"
id = "eksworkshop-eksctl:coredns"
tags = {
"Blueprint" = "eksworkshop-eksctl"
"GithubRepo" = "github.com/aws-ia/terraform-aws-eks-blueprints"
}
# (11 unchanged attributes hidden)
# (1 unchanged block hidden)
}
# module.eks_blueprints_addons.aws_eks_addon.this["kube-proxy"] will be updated in-place
~ resource "aws_eks_addon" "this" {
~ addon_version = "v1.25.16-eksbuild.8" -> "v1.26.15-eksbuild.24"
id = "eksworkshop-eksctl:kube-proxy"
tags = {
"Blueprint" = "eksworkshop-eksctl"
"GithubRepo" = "github.com/aws-ia/terraform-aws-eks-blueprints"
}
...
# 1분 정도 이내로 롤링 업데이트 완료!
terraform apply -auto-approve
#
kubectl get pod -n kube-system -l 'k8s-app in (kube-dns, kube-proxy)'
NAME READY STATUS RESTARTS AGE
coredns-58cc4d964b-plmr8 1/1 Running 0 93s
coredns-58cc4d964b-w84cm 1/1 Running 0 93s
kube-proxy-2vxw9 1/1 Running 0 80s
kube-proxy-6vlfr 1/1 Running 0 88s
kube-proxy-mmtp6 1/1 Running 0 82s
kube-proxy-rcs6z 1/1 Running 0 93s
kube-proxy-t49sv 1/1 Running 0 85s
kube-proxy-z77mc 1/1 Running 0 90s
#
kubectl get pods --all-namespaces -o jsonpath="{.items[*].spec.containers[*].image}" | tr -s '[[:space:]]' '\n' | sort | uniq -c
2 602401143452.dkr.ecr.us-west-2.amazonaws.com/eks/coredns:v1.9.3-eksbuild.22
6 602401143452.dkr.ecr.us-west-2.amazonaws.com/eks/kube-proxy:v1.26.15-minimal-eksbuild.24
...
☞ [3.1 데이터부 - 관리형노드그룹] Upgrading EKS Managed Node groups** 총 30분 소요 이상 실습 포함
- 관리형 노드 그룹 : 업그레이드 전략 - 기존 EKS 관리 노드 그룹 업데이드(인플레이스) vs 새로운 EKS 관리 노드 그룹으로 마이그레이션(블루-그린)
- Amazon EKS 관리형 노드 그룹은 Amazon EKS 클러스터의 노드(Amazon EC2 인스턴스) 프로비저닝 및 수명 주기 관리를 자동화합니다.
- Amazon EKS 관리형 노드 그룹을 사용하면 Kubernetes 애플리케이션을 실행하기 위한 컴퓨팅 용량을 제공하는 Amazon EC2 인스턴스를 별도로 프로비저닝하거나 등록할 필요가 없습니다. 단일 작업으로 클러스터에 대한 노드를 생성, 자동 업데이트 또는 종료할 수 있습니다. 노드 업데이트 및 종료는 노드를 자동으로 비워 애플리케이션이 계속 사용 가능한 상태를 유지하도록 합니다.
- 모든 관리 노드는 Amazon EKS에서 관리하는 Amazon EC2 Auto Scaling 그룹의 일부로 프로비저닝됩니다. 인스턴스와 Auto Scaling 그룹을 포함한 모든 리소스는 AWS 계정 내에서 실행됩니다. 각 노드 그룹은 사용자가 정의한 여러 가용성 영역에서 실행됩니다.
- [3.1-1 데이터부 - 관리형노드그룹 : 인플레이스 업그레이드] In-Place Managed Node group Upgrade 20분 소요 실습 포함*
- 관리 노드 그룹 업그레이드도 완전히 자동화되어 점진적인 롤링 업데이트로 구현됩니다.
- 새 노드는 EC2 자동 스케일링 그룹에 프로비저닝되고 클러스터에 합류하며, as old nodes는 cordoned, drained, and removed
- 기본적으로 새 노드는최신 EKS 최적화AMI(Amazon Machine Image) 또는선택적으로 사용자 지정 AMI를 사용합니다.
- 참고로, 사용자 지정 AMI를 사용할 때는 업데이트된 이미지를 직접 만들어야 하므로 노드 그룹 업그레이드의 일환으로 업데이트된 Launch Template 버전이 필요합니다.
☞ 관리형 노드 그룹 업데이트를 시작하면 Amazon EKS가 자동으로 노드를 업데이트하여 아래 설명된 대로 4가지 단계를 완료합니다.
- 설정 단계:
- 노드 그룹과 연결된 자동 스케일링 그룹에 대한 새로운 Amazon EC2 launch template version을 생성합니다.
- The new launch template version은 업데이트에 target AMI 또는 custom launch template version for the update 를 사용합니다.
- Auto Scaling group을 최신 실행 템플릿 버전latest launch template version을 사용하도록 업데이트합니다.
- 노드 그룹에 대한 updateConfig 속성을 사용하여 병렬로 업그레이드할 최대 노드 수를 결정합니다.
- The maximum unavailable has a quota of 100 nodes. 기본값은 1개 노드입니다.
- 확장 단계: 확장 단계에는 다음 단계가 있습니다.
- 자동 크기 조정 그룹의 최대 크기와 원하는 크기를 더 큰 크기로 증가시킵니다. It increments the Auto Scaling Group's maximum size and desired size by the larger of either
- 자동 확장 그룹이 배포된 가용성 영역 수의 최대 2배. Up to twice the number of Availability Zones that the Auto Scaling group is deployed in.
- 업그레이드가 불가능 한 최대 한도입니다. The maximum unavailable of upgrade.
- 자동 확장 그룹을 확장한 후 최신 구성을 사용하는 노드가 노드 그룹에 있고 준비되었는지 확인합니다.
- 그런 다음 노드를 예약 불가능으로 표시하여 새 Pod를 예약하지 않도록 합니다. It then marks nodes as un-schedulable to avoid scheduling new Pods.
- 또한 노드를 node.kubernetes.io/exclude-from-external-load-balancers=true 로 레이블하여 노드를 종료하기 전에 로드 밸런서에서 노드를 제거합니다. It also labels nodes with node.kubernetes.io/exclude-from-external-load-balancers=true to remove the nodes from load balancers before terminating the nodes.
- 자동 크기 조정 그룹의 최대 크기와 원하는 크기를 더 큰 크기로 증가시킵니다. It increments the Auto Scaling Group's maximum size and desired size by the larger of either
- 업그레이드 단계:* 업그레이드 단계는 다음과 같은 단계로 구성됩니다.
- 노드 그룹에 대해 구성된 최대 사용 불가능 개수까지 업그레이드가 필요한 노드를 무작위로 선택합니다.
- It randomly selects a node that needs to be upgraded, up to the maximum unavailable configured for the node group.
- 노드에서 Pod를 비웁니다. Pod가 15분 이내에 노드를 떠나지 않고 강제 플래그가 없으면 업그레이드 단계는 PodEvictionFailure 오류
- It drains the Pods from the node. If the Pods don't leave the node within 15 minutes and there's no force flag, the upgrade phase fails with a PodEvictionFailure error. For this scenario, you can apply the force flag with the update-nodegroup-version request to delete the Pods.
- 로 실패합니다 . 이 시나리오에서는 update-nodegroup-version 요청과 함께 강제 플래그를 적용하여 Pod를 삭제할 수 있습니다.
- 모든 Pod가 퇴거된 후 노드를 봉쇄하고 60초 동안 기다립니다. 이는 서비스 컨트롤러가 이 노드에 새로운 요청을 보내지 않고 이 노드를 활성 노드 목록에서 제거하기 위해 수행됩니다.
- It cordons the node after every Pod is evicted and waits for 60 seconds. This is done so that the service controller doesn't send any new requests to this node and removes this node from its list of active nodes.
- cordoned 노드에 대한 자동 확장 그룹에 종료 요청을 보냅니다.
- It sends a termination request to the Auto Scaling Group for the cordoned node.
- 이전 버전의 실행 템플릿을 사용하여 배포된 노드 그룹에 노드가 없을 때까지 이전 업그레이드 단계를 반복합니다.
- It repeats the previous upgrade steps until there are no nodes in the node group that are deployed with the earlier version of the launch template.
- 노드 그룹에 대해 구성된 최대 사용 불가능 개수까지 업그레이드가 필요한 노드를 무작위로 선택합니다.
- 축소 단계: 축소 단계에서는 자동 크기 조정 그룹의 최대 크기와 원하는 크기를 하나씩 감소시켜 업데이트가 시작되기 전 값으로 돌아갑니다.
- 축소 단계는 자동 스케일링 그룹의 최대 크기와 원하는 크기를 하나씩 줄여 업데이트가 시작되기 전의 값으로 돌아갑니다.
아래 방법 중 Terraform 을 이용한 방법을 진행하므로 나머지 방법은 실행하지 않음
방법 1 : eksctl
# 우리는 사용 중인 AMI 유형의 Kubernetes 버전에 대한 최신 AMI 릴리스 버전으로 노드 그룹을 업데이트할 수 있습니다.
eksctl upgrade nodegroup --name=managed-ng-1 --cluster=$EKS_CLUSTER_NAME
# 노드 그룹이 클러스터의 Kubernetes 버전에서 이전 Kubernetes 버전인 경우, 다음을 통해 노드 그룹을 클러스터의 Kubernetes 버전과 일치하는 최신 AMI 릴리스 버전으로 업데이트할 수 있습니다.
eksctl upgrade nodegroup --name=managed-ng-1 --cluster=$EKS_CLUSTER_NAME --kubernetes-version=<control plane version>
# 최신 버전 대신 특정 AMI 릴리스 버전으로 업그레이드하려면 다음을 사용합니다.
eksctl upgrade nodegroup --name=managed-ng-1 --cluster=$EKS_CLUSTER_NAME --release-version=<specific AMI Release version>
방법 2 : AWS 관리 콘솔
- 업데이트할 노드 그룹이 포함된 클러스터를 선택하세요.
- 하나 이상의 노드 그룹에 사용 가능한 업데이트가 있는 경우 페이지 상단에 사용 가능한 업데이트를 알려주는 상자가 나타납니다. Compute 탭을 선택하면 사용 가능한 업데이트가 있는 노드 그룹의 Node groups 표에서 AMI 릴리스 버전 열에 Update now가 표시됩니다. 노드 그룹을 업데이트하려면 Update now를 선택합니다 .
- 노드 그룹 버전 업데이트 대화 상자에서 다음 옵션을 활성화하거나 비활성화합니다.
- 노드 그룹 버전 업데이트 – 사용자 지정 AMI를 배포했거나 Amazon EKS 최적화 AMI가 현재 클러스터의 최신 버전인 경우 이 옵션을 사용할 수 없습니다.
- 출시 템플릿 버전 변경: 노드 그룹이 사용자 지정 출시 템플릿 없이 배포된 경우 이 옵션을 사용할 수 없습니다.
- 업데이트 전략의 경우 다음 옵션 중 하나를 선택하세요.
- 롤링 업데이트 - 이 옵션은 클러스터의 Pod 중단 예산을 존중합니다. Amazon EKS가 이 노드 그룹에서 실행 중인 Pod를 우아하게 비울 수 없게 하는 Pod 중단 예산 문제가 있는 경우 업데이트가 실패합니다.
- 강제 업데이트 - 이 옵션은 Pod 중단 예산을 존중하지 않습니다. Pod 중단 예산 문제와 관계없이 노드 재시작을 강제로 발생시켜 업데이트가 발생합니다.
- 업데이트를 선택하세요.
방법 3 : Terraform , 해당 방법으로 실제 업그레이드 실행! 20분 소요 실습 포함
이제 우리는 마지막 랩에서 eks 클러스터 제어 평면을 버전 1.26으로 업그레이드했습니다. 다음 단계로, 우리는 워커 노드를 업그레이드해야 합니다. 이 랩에서 우리는 이 클러스터에 대해 프로비저닝된 관리 노드 그룹을 업그레이드할 것입니다. 우리는 접두사가 있는 두 개의 관리 노드 그룹을 가지고 있습니다:
- initial-<random_identifier>
eks 모듈인 eks_managed_node_groups 아래의 base.tf 에서 두 관리 노드 그룹의 구성을 볼 수 있습니다.
#
cat base.tf
...
eks_managed_node_group_defaults = {
cluster_version = var.mng_cluster_version
}
eks_managed_node_groups = { # 버전 명시가 없으면, 상단 default 버전 사용 -> variables.tf 확인
initial = {
instance_types = ["m5.large", "m6a.large", "m6i.large"]
min_size = 2
max_size = 10
desired_size = 2
update_config = {
max_unavailable_percentage = 35
}
}
blue-mng={
instance_types = ["m5.large", "m6a.large", "m6i.large"]
cluster_version = "1.25"
min_size = 1
max_size = 2
desired_size = 1
update_config = {
max_unavailable_percentage = 35
}
labels = {
type = "OrdersMNG"
}
subnet_ids = [module.vpc.private_subnets[0]]
taints = [
{
key = "dedicated"
value = "OrdersApp"
effect = "NO_SCHEDULE"
}
]
}
관리 노드 그룹 업데이트를 수행할 때 다음 시나리오를 보여드리겠습니다.
- 특정 AMI ID 사용(사용자 정의 AMI와 같음) Using specific AMI ID (like custom AMIs)
- 지정된 K8s 버전에 대한 기본 AMI 사용 Using default AMI for given K8s version
이 실험실의 전제 조건으로 먼저 사용자 지정 AMI를 사용하여 신규 관리 노드 그룹을 생성해 보겠습니다.
이를 위해 kubernetes 버전 1.25의 ami_id를 가져오고 variable.tf 파일의 변수 ami_id를 결과 값으로 대체해 보겠습니다.
#
aws ec2 describe-instances --query "Reservations[*].Instances[*].[Tags[?Key=='Name'].Value | [0], ImageId]" --filters "Name=tag:Blueprint,Values=eksworkshop-eksctl" --output table
#
kubectl get node --label-columns=eks.amazonaws.com/capacityType,node.kubernetes.io/lifecycle,karpenter.sh/capacity-type,eks.amazonaws.com/compute-type
#
kubectl get node -L eks.amazonaws.com/nodegroup,karpenter.sh/nodepool
#
kubectl get node -L eks.amazonaws.com/nodegroup-image | grep ami
#
aws ssm get-parameter --name /aws/service/eks/optimized-ami/1.25/amazon-linux-2/recommended/image_id \
--region $AWS_REGION --query "Parameter.Value" --output text
ami-0078a0f78fafda978
아래와 같이 variable.tf 파일에서 ami_id를 업데이트합니다.
base.tf사용자 정의 관리 노드 그룹을 프로비저닝하려면 다음 코드를 추가합니다 .
Terraform Plan을 실행하고 적용합니다.
# 모니터링
aws autoscaling describe-auto-scaling-groups --query 'AutoScalingGroups[*].AutoScalingGroupName' --output json | jq
kubectl get node -L eks.amazonaws.com/nodegroup
kubectl get node -L eks.amazonaws.com/nodegroup-image | grep ami
while true; do aws autoscaling describe-auto-scaling-groups --query 'AutoScalingGroups[*].AutoScalingGroupName' --output json | jq; echo ; kubectl get node -L eks.amazonaws.com/nodegroup; echo; date ; echo ; kubectl get node -L eks.amazonaws.com/nodegroup-image | grep ami; echo; sleep 1; echo; done
# 2분 정도 소요
terraform plan && terraform apply -auto-approve
#
kubectl get node -L eks.amazonaws.com/nodegroup,eks.amazonaws.com/nodegroup-image | grep custom
ip-10-0-17-102.us-west-2.compute.internal Ready <none> 11m v1.25.16-eks-59bf375 custom-20250401115802906700000007 ami-0078a0f78fafda978
while true; do aws autoscaling describe-auto-scaling-groups --query 'AutoScalingGroups[*].AutoScalingGroupName' --output json | jq; echo ; kubectl get node -L eks.amazonaws.com/nodegroup; echo; date ; echo ; kubectl get node -L eks.amazonaws.com/nodegroup-image | grep ami; echo; sleep 1; echo; done
[
"default-selfmng-2025033004565918770000002d",
"eks-blue-mng-20250330045659150100000029-8acaf2b4-811c-50cf-013b-30455bda3569",
"eks-custom-20250401115802906700000007-decaf89b-9c6f-65a4-7203-838eedd66cde",
"eks-initial-2025033004565915530000002b-accaf2b4-8120-f752-1283-10d13c3160e4"
]
#
kubectl describe node ip-10-0-44-132.us-west-2.compute.internal
...
Labels: beta.kubernetes.io/arch=amd64
beta.kubernetes.io/instance-type=t3.medium
beta.kubernetes.io/os=linux
eks.amazonaws.com/capacityType=ON_DEMAND
eks.amazonaws.com/nodegroup=custom-20250401115802906700000007
eks.amazonaws.com/nodegroup-image=ami-0078a0f78fafda978
eks.amazonaws.com/sourceLaunchTemplateId=lt-08c23b16b23e7cff6
eks.amazonaws.com/sourceLaunchTemplateVersion=1
failure-domain.beta.kubernetes.io/region=us-west-2
failure-domain.beta.kubernetes.io/zone=us-west-2b
...
kubectl get pod -A -owide | grep 10-0-44-132
kube-system aws-node-m97fw 2/2 Running 0 13m 10.0.17.102 ip-10-0-17-102.us-west-2.compute.internal <none> <none>
kube-system ebs-csi-node-br4fr 3/3 Running 0 13m 10.0.24.242 ip-10-0-17-102.us-west-2.compute.internal <none> <none>
kube-system efs-csi-node-tnczq 3/3 Running 0 13m 10.0.17.102 ip-10-0-17-102.us-west-2.compute.internal <none> <none>
kube-system kube-proxy-8jvwj 1/1 Running 0 13m 10.0.17.102 ip-10-0-17-102.us-west-2.compute.internal <none> <none>
초기 관리 노드그룹에는 정의된 클러스터 버전이 없으며 기본적으로 eks_managed_node_group_defaults에 정의된 클러스터 버전을 사용하도록 설정됩니다.
`eks_managed_node_group_defaults` 클러스터_버전의 값은 `variable.tf` 의 변수 `mng_cluster_version`에 정의되어 있습니다.
`variable.tf` 에서 변수 `mng_cluster_version`을 1.25에서 1.26으로 변경하고 테라폼을 실행하면 됩니다.
#
terraform plan -no-color > plan-output.txt
# plan-output.txt 내용 확인
이 계획에 따라 initial managed node group은 1.26 버전으로 업그레이드되지만, custom managed nodegroup 을 프로비저닝하는 동안 특정 AMI를 정의했기 때문에 업그레이드 되지 않습니다.
따라서 커스텀 관리 노드 그룹과 같이 커스텀 AMI로 구성된 관리 노드 그룹이 있는 경우 업그레이드된 Kubernetes 버전에 커스텀 AMI를 제공하여 관리 노드 그룹을 업그레이드할 수 있습니다.
Lets upgrade the initial and custom managed node groups to latest cluster version.
kubernetes 버전 1.26에 대해 ami_id를 검색한 후 variable.tf 에서 다음과 같은 변경 사항을 적용합니다.
#
aws ssm get-parameter --name /aws/service/eks/optimized-ami/1.26/amazon-linux-2/recommended/image_id \
--region $AWS_REGION --query "Parameter.Value" --output text
ami-086414611b43bb691
mng_cluster_version 변수를 에서 . 1.25 -> 1.26 으로 변경합니다.
ami_id 변수를 ami-0078a0f78fafda978에서 ami-086414611b43bb691로 변경합니다.
# 모니터링
while true; do aws autoscaling describe-auto-scaling-groups --query 'AutoScalingGroups[*].AutoScalingGroupName' --output json | jq; echo ; kubectl get node -L eks.amazonaws.com/nodegroup; echo; date ; echo ; kubectl get node -L eks.amazonaws.com/nodegroup-image | grep ami; echo; sleep 1; echo; done
# 20분 소요 : UI 웹과 liveness 반복 접속도 해보자, 노드에 labels 와 taint 변화도 모니터링 해보자.
terraform plan && terraform apply -auto-approve
...
완료되면 Amazon EKS 콘솔에서 initial and custom manage node groups의 변경 사항을 확인할 수 있습니다.
초기 및 맞춤형 관리 노드그룹 kubernetes 버전이 1.26임을 알 수 있습니다.
custom 은 실습 동작 확인했으니, 다음 실습을 위해서 제거하자.
다음 변경 사항을 적용 base.tf하고 terraform plan과 terraform apply를 실행합니다.
terraform plan && terraform apply -auto-approve
☞ [3.1-2 데이터부 - 관리형노드그룹 : 블루-그린 업그레이드] 10분 소요 실습 포함*
관리 노드 그룹을 업데이트하기 위해 Blue / Green 전략을 사용할 수도 있습니다.
우리는 eks 제어 평면 버전을 업데이트한 후 새로운 관리 노드 그룹을 생성함으로써 이를 수행합니다.
지난 실험[3.1-1]에서는 Blue-mng 관리 노드 그룹을 업데이트하지 않았습니다.
Blue-mng 관리 노드 그룹의 구성을 보면, 이 노드 그룹에 할당된 노드들이 특정 테인트와 라벨을 가지고 있으며, 하나의 특정 가용성 구역에 할당되도록 구성되어 있음을 알 수 있습니다.
상태 저장 워크로드를 위한 관리 노드 그룹은 가용 영역(AZ)별로 프로비저닝되어야 합니다. Managed node groups for stateful workloads should be provisioned per availability zone (AZ).
이 실험실에서는 stateful 워크로드의 마이그레이션을 시연하고 있습니다.
Blue-mng 관리 노드 그룹의 노드에서 실행되는 my-sql 포드로 구성된 Orders 애플리케이션 포드가 있습니다.
우리는 blue-mng와 유사한 구성을 가진 새로운 관리 노드 그룹 green-mng을 만들 것입니다.
Green-mng는 eks_managed_node_group_defaults에 제공된 클러스터 버전(즉, 1.26)과 Blue-mng과 동일한 가용성 영역에서 프로비저닝됩니다.
그 후에, 우리는 blue-mng을 삭제할 것입니다.
블루밍 노드가 자동으로 코드화되고 배수되며, 주문 앱 포드가 그린밍에서 프로비저닝되는 것을 볼 수 있습니다.
다음 Terraform 구성에서 찾을 수 base.tf있습니다 blue-mng.
#
terraform state show 'module.vpc.aws_subnet.private[0]'
terraform state show 'module.vpc.aws_subnet.private[1]'
terraform state show 'modle.vpc.aws_subnet.private[2]'
우리는 eks 제어 평면 버전을 업데이트한 후 새로운 관리 노드 그룹을 생성함으로써 이를 수행합니다.
Blue-mng 관리 노드 그룹의 구성을 보면, 이 노드 그룹에 할당된 노드들이 특정 테인트와 라벨을 가지고 있으며, 하나의 특정 가용성 구역에 할당되도록 구성되어 있음을 알 수 있습니다.
상태 저장 워크로드를 위한 관리 노드 그룹은 가용 영역(AZ)별로 프로비저닝되어야 합니다.
Managed node groups for stateful workloads should be provisioned per availability zone (AZ).Blue-mng 관리 노드 그룹의 노드에서 실행되는 my-sql 포드로 구성된 Orders 애플리케이션 포드가 있습니다.
Green-mng는 eks_managed_node_group_defaults에 제공된 클러스터 버전(즉, 1.26)과 Blue-mng과 동일한 가용성 영역에서 프로비저닝됩니다.블루밍 노드가 자동으로 코드화되고 배수되며, 주문 앱 포드가 그린밍에서 프로비저닝되는 것을 볼 수 있습니다.
Lets get the node provisioned for this node group.
#
kubectl get nodes -l type=OrdersMNG
NAME STATUS ROLES AGE VERSION
ip-10-0-12-8.us-west-2.compute.internal Ready <none> 2d10h v1.25.16-eks-59bf375
# Check if the node have specific taints applied to it.
ec2-user:~/environment:$ kubectl get nodes -l type=OrdersMNG -o jsonpath="{range .items[*]}{.metadata.name} {.spec.taints[?(@.effect=='NoSchedule')]}{\"\n\"}{end}"
ip-10-0-12-8.us-west-2.compute.internal {"effect":"NoSchedule","key":"dedicated","value":"OrdersApp"}
# Check which pods are running on this node.
# Non-terminated Pods: in above output, kube-system pods and orders app pods running on this node.
kubectl describe node -l type=OrdersMNG
...
Labels: beta.kubernetes.io/arch=amd64
...
type=OrdersMNG
Taints: dedicated=OrdersApp:NoSchedule
...
Non-terminated Pods: (6 in total)
Namespace Name CPU Requests CPU Limits Memory Requests Memory Limits Age
--------- ---- ------------ ---------- --------------- ------------- ---
kube-system aws-node-vhw7j 50m (2%) 0 (0%) 0 (0%) 0 (0%) 2d9h
kube-system ebs-csi-node-844zk 30m (1%) 0 (0%) 120Mi (1%) 768Mi (10%) 2d9h
kube-system efs-csi-node-nd2nd 0 (0%) 0 (0%) 0 (0%) 0 (0%) 2d9h
kube-system kube-proxy-2vxw9 100m (5%) 0 (0%) 0 (0%) 0 (0%) 3h49m
orders orders-5b97745747-zcjn2 250m (12%) 0 (0%) 1Gi (14%) 1Gi (14%) 2d9h
orders orders-mysql-b9b997d9d-dqcgt 0 (0%) 0 (0%) 0 (0%) 0 (0%) 2d9h
...
#
kubectl get pvc -n orders
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
order-mysql-pvc Bound pvc-12482437-7b10-429a-840f-a2048739e67a 4Gi RWO gp3 2d9h
#
cat eks-gitops-repo/apps/orders/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: orders
...
spec:
nodeSelector:
type: OrdersMNG
tolerations:
- key: "dedicated"
operator: "Equal"
value: "OrdersApp"
effect: "NoSchedule"
cat eks-gitops-repo/apps/orders/deployment-mysql.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: orders-mysql
...
spec:
nodeSelector:
type: OrdersMNG
tolerations:
- key: "dedicated"
operator: "Equal"
value: "OrdersApp"
effect: "NoSchedule"
Now Lets create a new managed node group green-mng by adding below code in base.tf file under eks module, eks_managed_node_groups
blue-mng={
instance_types = ["m5.large", "m6a.large", "m6i.large"]
cluster_version = "1.25"
min_size = 1
max_size = 2
desired_size = 1
update_config = {
max_unavailable_percentage = 35
}
labels = {
type = "OrdersMNG"
}
subnet_ids = [module.vpc.private_subnets[0]]
taints = [
{
key = "dedicated"
value = "OrdersApp"
effect = "NO_SCHEDULE"
}
]
}
green-mng={
instance_types = ["m5.large", "m6a.large", "m6i.large"]
subnet_ids = [module.vpc.private_subnets[0]]
min_size = 1
max_size = 2
desired_size = 1
update_config = {
max_unavailable_percentage = 35
}
labels = {
type = "OrdersMNG"
}
taints = [
{
key = "dedicated"
value = "OrdersApp"
effect = "NO_SCHEDULE"
}
]
}
}
# 모니터링
while true; do aws autoscaling describe-auto-scaling-groups --query 'AutoScalingGroups[*].AutoScalingGroupName' --output json | jq; echo ; kubectl get node -L eks.amazonaws.com/nodegroup; echo; date ; echo ; kubectl get node -L eks.amazonaws.com/nodegroup-image | grep ami; echo; sleep 1; echo; done
# 3분 소요
terraform plan && terraform apply -auto-approve
You will see green-mng will be created with latest control plane version in Amazon EKS console .
# Lets get the nodes with labels type=OrdersMNG.
kubectl get node -l type=OrdersMNG -o wide
kubectl get node -l type=OrdersMNG -L topology.kubernetes.io/zone
# Check if this node have same taints applied to it.
kubectl get nodes -l type=OrdersMNG -o jsonpath="{range .items[*]}{.metadata.name} {.spec.taints[?(@.effect=='NoSchedule')]}{\"\n\"}{end}"
동일한 레이블과 테인트를 가진 두 개의 노드가 있습니다. 블루-mng 관리 노드 그룹 노드는 버전 v1.25.16-eks-ae9a62a에 있고 그린-mng 관리 노드 그룹 노드는 버전 v1.26.15-eks-ae9a62a에 있습니다.
# Blue-mng과 Green-mng 관리 그룹 이름을 각각 BLUE_MNG 변수와 GREEN_MNG 변수로 내보냅니다.
export BLUE_MNG=$(aws eks list-nodegroups --cluster-name eksworkshop-eksctl | jq -c .[] | jq -r 'to_entries[] | select( .value| test("blue-mng*")) | .value')
echo $BLUE_MNG
blue-mng-20250330045659150100000029
export GREEN_MNG=$(aws eks list-nodegroups --cluster-name eksworkshop-eksctl | jq -c .[] | jq -r 'to_entries[] | select( .value| test("green-mng*")) | .value')
echo $GREEN_MNG
green-mng-20250401153250959800000007
Blue-mng 관리 노드 그룹을 삭제합니다. It will automatically cordon and drain the blue managed node group nodes and app pods will get scheduled on to green managed node group nodes.
클러스터 업그레이드 준비 실험실에서 주문 시 PodDisruptionBudget(PDB)을 생성했다면, 배포가 1개의 복제본으로 설정되고 PDB가 minAvailable=1로 설정될 때 노드 그룹 삭제 프로세스가 차단됩니다. 따라서 이 문제를 해결하기 위해 복제본을 2개로 늘립니다.
#
kubectl get pdb -n orders
NAME MIN AVAILABLE MAX UNAVAILABLE ALLOWED DISRUPTIONS AGE
orders-pdb 1 N/A 0 15h
#
cd ~/environment/eks-gitops-repo/
sed -i 's/replicas: 1/replicas: 2/' apps/orders/deployment.yaml
git add apps/orders/deployment.yaml
git commit -m "Increase orders replicas 2"
git push
# sync 를 해도 HPA로 무시로 인해 파드는 1개 유지 상태
argocd app sync orders
# apps 설정에 deploy replicas 무시 설정되어 있으니, 아래처럼 직접 증가해둘것 (이미 위에서 코드상에는 2로 변경)
# (실행 과정 중) orders 파드가 다른 노드로 옮겨감.. 실행 전에 replicas=2로 실행해두어서 좀 더 안정성 확보
kubectl scale deploy -n orders orders --replicas 2
#
kubectl get deploy -n orders
kubectl get pod -n orders
Make the following changes in base.tf and run terraform plan and apply
#
kubectl get node -l type=OrdersMNG
kubectl get pod -n orders -l app.kubernetes.io/component=service -owide
kubectl get deploy -n orders orders
kubectl get pdb -n orders
aws autoscaling describe-auto-scaling-groups --query 'AutoScalingGroups[*].AutoScalingGroupName' --output json | jq
while true; do kubectl get node -l type=OrdersMNG; echo ; kubectl get pod -n orders -l app.kubernetes.io/component=service -owide ; echo ; kubectl get deploy -n orders orders; echo ; kubectl get pdb -n orders; echo ; aws autoscaling describe-auto-scaling-groups --query 'AutoScalingGroups[*].AutoScalingGroupName' --output json | jq; echo ; date ; done
# 10분 정도 소요
cd ~/environment/terraform/
terraform plan && terraform apply -auto-approve
# 신규(버전) 노드 확인
kubectl get node -l eks.amazonaws.com/nodegroup=$GREEN_MNG
NAME STATUS ROLES AGE VERSION
ip-10-0-10-196.us-west-2.compute.internal Ready <none> 45m v1.26.15-eks-59bf375
aws eks list-nodegroups --cluster-name eksworkshop-eksctl
{
"nodegroups": [
"green-mng-20250325235742179300000007",
"initial-2025032502302076080000002c"
]
}
# 기존 노드 스케줄 중지
Wed Mar 26 00:32:43 UTC 2025
NAME STATUS ROLES AGE VERSION
ip-10-0-10-196.us-west-2.compute.internal Ready <none> 33m v1.26.15-eks-59bf375
ip-10-0-3-199.us-west-2.compute.internal Ready,SchedulingDisabled <none> 22h v1.25.16-eks-59bf375
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
orders-5b97745747-mxg72 1/1 Running 2 (21h ago) 21h 10.0.5.219 ip-10-0-3-199.us-west-2.compute.internal <none> <none>
NAME READY UP-TO-DATE AVAILABLE AGE
orders 1/1 1 1 21h
NAME MIN AVAILABLE MAX UNAVAILABLE ALLOWED DISRUPTIONS AGE
orders-pdb 1 N/A 0 15h
# orders 파드가 다른 노드로 옮겨감.. 실행 전에 replicas=2로 실행해두었으면 더 좋았을듯..
## kubectl scale deploy -n orders orders --replicas 1
Wed Mar 26 00:32:47 UTC 2025
NAME STATUS ROLES AGE VERSION
ip-10-0-10-196.us-west-2.compute.internal Ready <none> 34m v1.26.15-eks-59bf375
ip-10-0-3-199.us-west-2.compute.internal Ready,SchedulingDisabled <none> 22h v1.25.16-eks-59bf375
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
orders-5b97745747-wr9bf 0/1 ContainerCreating 0 2s <none> ip-10-0-10-196.us-west-2.compute.internal <none> <none>
NAME READY UP-TO-DATE AVAILABLE AGE
orders 0/1 1 0 21h
NAME MIN AVAILABLE MAX UNAVAILABLE ALLOWED DISRUPTIONS AGE
orders-pdb 1 N/A 0 15h
# 신규 노드로 옮겨간 orders 파드가 정상 상태가 되자, 기존 노드는 종료 실행으로 NotReady 상태
Wed Mar 26 00:33:28 UTC 2025
NAME STATUS ROLES AGE VERSION
ip-10-0-10-196.us-west-2.compute.internal Ready <none> 34m v1.26.15-eks-59bf375
ip-10-0-3-199.us-west-2.compute.internal NotReady,SchedulingDisabled <none> 22h v1.25.16-eks-59bf375
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
orders-5b97745747-wr9bf 1/1 Running 0 44s 10.0.8.253 ip-10-0-10-196.us-west-2.compute.internal <none> <none>
NAME READY UP-TO-DATE AVAILABLE AGE
orders 1/1 1 1 21h
NAME MIN AVAILABLE MAX UNAVAILABLE ALLOWED DISRUPTIONS AGE
orders-pdb 1 N/A 0 15h
# 기존 노드 그룹 삭제됨
Wed Mar 26 00:33:34 UTC 2025
NAME STATUS ROLES AGE VERSION
ip-10-0-10-196.us-west-2.compute.internal Ready <none> 34m v1.26.15-eks-59bf375
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
orders-5b97745747-wr9bf 1/1 Running 0 50s 10.0.8.253 ip-10-0-10-196.us-west-2.compute.internal <none> <none>
NAME READY UP-TO-DATE AVAILABLE AGE
orders 1/1 1 1 21h
NAME MIN AVAILABLE MAX UNAVAILABLE ALLOWED DISRUPTIONS AGE
orders-pdb 1 N/A 0 15h
[
"default-selfmng-2025032502302075740000002b",
"eks-green-mng-20250325235742179300000007-8acae7de-c8e8-3c1d-f093-acddb3156142",
"eks-initial-2025032502302076080000002c-30cae591-7ac2-1e9c-2415-19975314b08b"
]
# 이벤트 로그 확인
kubectl get events --sort-by='.lastTimestamp' --watch
4m55s Normal NodeNotSchedulable node/ip-10-0-3-199.us-west-2.compute.internal Node ip-10-0-3-199.us-west-2.compute.internal status is now: NodeNotSchedulable
0s Normal NodeNotReady node/ip-10-0-3-199.us-west-2.compute.internal Node ip-10-0-3-199.us-west-2.compute.internal status is now: NodeNotReady
0s Normal DeletingNode node/ip-10-0-3-199.us-west-2.compute.internal Deleting node ip-10-0-3-199.us-west-2.compute.internal because it does not exist in the cloud provider
0s Normal RemovingNode node/ip-10-0-3-199.us-west-2.compute.internal Node ip-10-0-3-199.us-west-2.compute.internal event: Removing Node ip-10-0-3-199.us-west-2.compute.internal from Controller
☞ [3.2 데이터부 - 카펜터노드] Upgrading Karpenter managed nodes** 10분 소요 (예상) 실습 포함
Karpenter는 집계된 CPU, 메모리, 볼륨 요청 및 기타 Kubernetes 스케줄링 제약(예: 친화도 및 포드 토폴로지 확산 제약)을 기반으로 스케줄링 불가능한 포드에 대응하여 적절한 크기의 노드를 제공하는 오픈 소스 클러스터 오토스케일러로, 인프라 관리를 간소화합니다.
또한 Karpenter는 노드 그룹 및 Amazon EC2 자동 확장 그룹과 같은 외부 인프라와 용량 관리를 조정하지 않기 때문에 운영 프로세스에 대한 다른 관점을 도입하여 작업자 노드 구성 요소와 운영 체제를 최신 보안 패치 및 기능으로 최신 상태로 유지합니다.
AWS는 새로운 Kubernetes 버전뿐만 아니라 패치 및 CVE(일반 취약점 및 노출)를 위한 AMI를 출시합니다. 다양한 Amazon EKS에 최적화된 AMI 중에서 선택할 수 있습니다. 또는 자신만의 맞춤형 AMI를 사용할 수도 있습니다.
현재 Karpenter에서는 EC2NodeClass 리소스가 amiFamily 값 AL2, AL2023, Bottlerocket, Ubuntu, Windows2019, Windows2022 및 Custom을 지원합니다. amiFamily of Custom을 선택하면 어떤 사용자 지정 AMI를 사용할지 Karpenter에 알려주는 amiSelectorTerms를 지정해야 합니다.
Karpenter는 Karpenter를 통해 프로비저닝된 Kubernetes 작업자 노드를 패치하는 데 Drift 또는 타이머(expireAfter)와 같은 기능을 사용합니다.
Drift
Karpenter는 롤링 배포 후 Drift를 사용하여 Kubernetes 노드를 업그레이드합니다.
Karpenter를 통해 프로비저닝된 Kubernetes 노드가 원하는 사양에서 벗어난 경우, Karpenter는 먼저 새로운 노드를 프로비저닝하고 이전 노드에서 포드를 제거한 다음 종료합니다.
노드가 디프로비전됨에 따라 노드는 새로운 포드 스케줄링을 방지하기 위해 코드화되고 Kubernetes Eviction API를 사용하여 포드가 퇴거됩니다.
EC2NodeClass에서는 amiFamily가 필수 필드이며, 자신의 AMI 값인 EKS 최적화 AMI를 사용할 수 있습니다.
AMI의 드리프트는 두 가지 동작이 있으며, 아래에 자세히 설명되어 있습니다.
Drift with specified AMI values 지정된 AMI 값을 사용한 드리프트:
일관성을 위해 애플리케이션 환경을 통해 AMI의 promotion를 제어하는 이 접근 방식을 고려할 수 있습니다.
EC2NodeClass에서 NodePool의 AMI를 변경하거나 다른 EC2NodeClass를 NodePool과 연결하면, Karpenter는 기존 작업자 노드가 원하는 설정에서 벗어났음을 감지합니다.
AMI는 AMI ID, AMI 이름 또는 amiSelectorTerms를 사용하여 특정 태그로 명시적으로 지정할 수 있습니다.
여러 AMI가 기준을 충족하면 최신 AMI가 선택됩니다.
고객은 EC2NodeClass의 상태 필드에 있는 AMI 값에서 EC2NodeClass가 발견한 AMI를 추적할 수 있습니다.
상태를 얻는 한 가지 방법은 EC2NodeClass에서 kubectl 설명을 실행하는 것입니다. 특정 시나리오에서는 EC2NodeClass에 의해 이전 AMI와 새로운 AMI가 모두 발견되면, 이전 AMI를 가진 실행 중인 노드들이 드리프트되고, 디프로비전되며, 새로운 AMI를 가진 작업자 노드로 대체됩니다. 새로운 노드들은 새로운 AMI를 사용하여 프로비저닝됩니다.
- Drift with Amazon EKS optimized AMIs Amazon EKS 최적화 AMI를 사용한 드리프트:
EC2NodeClass에 amiSelectorTerms가 지정되지 않은 경우, Karpenter는 Amazon EKS에 최적화된 AMI에 대해 게시된 SSM 매개변수를 모니터링합니다.
AMiFamily 필드에 AL2, AL2023, Bottlerocket, Ubuntu, Windows2019 또는 Windows2022의 값을 지정하여 Karpenter에 어떤 Amazon EKS에 최적화된 AMI를 사용해야 하는지 알려줄 수 있습니다.
Karpenter는 실행 중인 EKS 버전 클러스터를 위해 지정된 최신 Amazon EKS 최적화 AMI로 노드를 프로비저닝합니다. Karpenter는 Kubernetes 클러스터 버전의 새로운 AMI가 언제 출시되는지 감지하고 기존 노드를 드리프트합니다. 상태 필드에서 EC2NodeClass AMI 값은 새로 발견된 AMI를 반영합니다.
이러한 노드는 프로비저닝이 해제되고 최신 AMI를 가진 작업자 노드로 대체됩니다. 이 접근 방식을 사용하면 이전 AMI를 가진 노드는 자동으로 재활용됩니다(예: 새로운 AMI가 있을 때 또는 Kubernetes 제어 평면 업그레이드 후).
이전 접근 방식인 amiSelectorTerms를 사용하면 노드가 업그레이드될 때 더 많은 제어 권한을 가질 수 있습니다.
TTL (expireAfter) to automatically delete nodes from the cluster 클러스터에서 노드를 자동으로 삭제
프로비저닝된 노드에서 ttl을 사용하여 워크로드 포드가 없거나 만료 시간에 도달한 노드를 삭제할 시기를 설정할 수 있습니다. 노드 만료는 업그레이드 수단으로 사용할 수 있으므로 노드가 폐기되고 업데이트된 버전으로 대체될 수 있습니다.
Karpenter는 NodePool의 spec.druption.expireAfter 값에 따라 노드가 만료된 것으로 표시하고 설정된 시간(초)이 지나면 노드를 중단합니다.
보안 문제로 인해 노드 만료를 사용하여 주기적으로 노드를 재활용할 수 있습니다.
kubectl get nodepools default -o jsonpath={.spec.disruption} | jq
{
"budgets": [
{
"nodes": "10%"
}
],
"consolidationPolicy": "WhenUnderutilized",
"expireAfter": "Never"
}
Disruption Budgets
이러한 karpenter 중단을 제어하려는 특정 시나리오가 있습니다. 예를 들어:
- 미션 크리티컬 워크로드를 실행 중인데 자발적인 중단으로 인해 노드를 재활용하고 싶지 않을 때.
- 100개의 드리프트 또는 만료가 한 번에 정렬되어 모든 활성 노드를 함께 회전하는 시나리오의 경우. 이러한 시나리오에서는 중단되는 활성 노드의 수와 중단 시점을 제어해야 합니다.
우리는 NodePool의 spec.disruption.budgets를 통해 Karpenter disruption 를 rate limit 할 수 있습니다.
Disruption Budgets 을 사용하면 새로운 AMI를 가진 노드로의 업그레이드 속도를 늦추거나(일정을 사용하여) 선택된 날짜와 시간 동안에만 업그레이드가 이루어지도록 할 수 있습니다.
이렇게 하면 나쁜 AMI가 배포되는 것을 방지할 수는 없지만, 노드가 업그레이드되는 시기를 제어할 수 있지만, 롤아웃 문제에 더 많은 시간을 대응할 수 있습니다.
Disruption Budgets은 노드가 중단될 수 있는 시기와 정도를 제한합니다. 노드(한 번에 중단될 수 있는 노드의 비율 또는 수)와 일정(노드 중단으로 인한 특정 시간 제외)에 따라 중단을 방지할 수 있습니다.
정의되지 않으면 Karpenter는 노드가 있는 **budgets 10%**로 기본 설정됩니다.
budgets은 어떤 이유로든 적극적으로 삭제되는 노드를 고려하며, 만료, 드리프트, 공백 및 통합을 통해 자발적으로 노드를 방해하지 못하도록 Karpenter를 차단합니다.
예시)
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
name: default
spec: # This is not a complete NodePool Spec.
disruption:
consolidationPolicy: WhenEmpty
expireAfter: 720h # 30 * 24h = 720h
budgets:
- nodes: 0
이 budgets으로 카르펜터는 이 노드풀을 통해 프로비저닝된 노드가 자발적인 중단으로 간주되는 것을 방지할 수 있습니다.
This budget allow karpenter to disrupt active nodes one at a time. 한번에 1대씩 노드만 중단.
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
name: default
spec: # This is not a complete NodePool Spec.
disruption:
consolidationPolicy: WhenEmpty
expireAfter: 720h # 30 * 24h = 720h
budgets:
- nodes: "1"
이러한 예산으로 인해 카르펜터는 평일 영업 시간 동안 노드의 프로비저닝을 해제할 수 없습니다. 그외 시간에 10개의 노드만 동시에 프로비저닝을 해제할 수 있습니다
With these budgets, karpenter is not allowed to de-provision any node on weekdays during business hours. Every other time, only allow 10 nodes to be de-provisioned simultaneously
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
name: default
spec: # This is not a complete NodePool Spec.
disruption:
consolidationPolicy: WhenEmpty
expireAfter: 720h # 30 * 24h = 720h
budgets:
- schedule: "0 9 * * mon-fri"
duration: 8h
nodes: 0
- nodes: 10
As prerequisite for this lab, lets clone codecommit repo eks-gitops-rep (if you haven't already)
# In this lab, we have already installed karpenter and applied default NodePool and EC2NodeClass on the cluster.
kubectl describe nodepool
Name: default
Namespace:
Labels: argocd.argoproj.io/instance=karpenter
Annotations: karpenter.sh/nodepool-hash: 12028663807258658692
karpenter.sh/nodepool-hash-version: v2
API Version: karpenter.sh/v1beta1
Kind: NodePool
Metadata:
Creation Timestamp: 2025-03-25T02:40:15Z
Generation: 1
Resource Version: 6130
UID: 7e081e92-bcfb-4e88-a611-026349b02dd9
Spec:
Disruption:
Budgets:
Nodes: 10%
Consolidation Policy: WhenUnderutilized
Expire After: Never
Limits:
Cpu: 100
Template:
Metadata:
Labels:
Env: dev
Team: checkout
Spec:
Node Class Ref:
Name: default
Requirements:
Key: karpenter.k8s.aws/instance-family
Operator: In
Values:
c5
m5
m6i
m6a
r4
c4
Key: kubernetes.io/arch
Operator: In
Values:
amd64
Key: karpenter.sh/capacity-type
Operator: In
Values:
on-demand
spot
Key: kubernetes.io/os
Operator: In
Values:
linux
Taints:
Effect: NoSchedule
Key: dedicated
Value: CheckoutApp
Status:
Resources:
Attachable - Volumes - Aws - Ebs: 25
Cpu: 4
Ephemeral - Storage: 20959212Ki
Memory: 7766420Ki
Pods: 58
Events: <none>
기본 노드풀 및 ec2 nod 클래스의 출력에서 다음을 확인할 수 있습니다:
- 이 노드풀을 사용하면 Spec.Druption.Budgets에 따라 중단 시 10% 노드가 재활용되는 기본 예산이 적용됩니다.
- 이 노드풀에 대해 카르펜터를 통해 프로비저닝된 노드에는 Team: 체크아웃 라벨이 Spec.Template.Metadata.Labels에 따라 표시되며, 이들 노드에는 Spec.Template.Spec.Taints에 따라 태그가 적용됩니다.
- 이 노드풀은 Spec.Template.Spec.NodeClass Ref에 따라 기본 EC2NodeClass를 사용하고 있습니다.
- 이 기본 EC2NodeClass 사양을 사용하여 노드풀은 아미드 ami-03db5eb 228232c943을 사용하여 노드를 프로비저닝하고 있습니다.
node selector label team=checkou와 default nodepool을 통해 프로비저닝된 노드에 tolerations to tolerate taints를 가진 체크아웃 앱 포드를 프로비저닝했습니다.
Checkout 앱은 포드에 지속적인 볼륨이 부착된 stateful 애플리케이션입니다.
이 실험실에서는 상태 저장 워크로드를 실행하는 카펜터를 통해 프로비저닝된 노드를 업그레이드하는 방법을 시연할 것입니다.
이를 위해 kubernetes 버전 1.26용으로 빌드된 AMI ID를 가진 eks-gitops-repo/apps/karpenter 폴더의 기본 eks-ec2nc.yaml 파일에서 AMI ID를 변경하고 eks-gitops-repo의 변경 사항을 푸시할 것입니다.
Argo-CD를 통해 변경 사항이 동기화되면 Karpenter 컨트롤러는 노드가 드리프트되었음을 자동으로 감지하여 드리프트된 노드를 최신 구성의 노드로 프로비저닝하는 데 방해가 될 것입니다.
논의한 바와 같이, 중단되는 노드의 수를 제어하거나 특정 시간에만 노드를 중단시키는 시나리오가 있습니다.
이 실험실에서는 Karpenter가 다른 노드를 프로비저닝하여 예정에 없던 체크아웃 포드를 실행할 수 있도록 체크아웃 앱을 확장할 것입니다. 그
런 다음 한 번에 하나씩 노드를 중단시키기 위한 중단 예산을 정의합니다.
Karpenter 컨트롤러는 한 번에 하나씩 드리프트를 통해 노드를 중단합니다.
이 노드풀에 대해 카펜터를 통해 노드를 프로비저닝해 보겠습니다
#
aws ec2 describe-instances --query "Reservations[*].Instances[*].[Tags[?Key=='Name'].Value | [0], ImageId]" --filters "Name=tag:Blueprint,Values=eksworkshop-eksctl" --output table
# 기본 노드풀을 통해 프로비저닝된 노드가 버전 v1.25.16-eks-59bf375에 있는 것을 확인할 수 있습니다.
kubectl get nodes -l team=checkout
NAME STATUS ROLES AGE VERSION
ip-10-0-39-95.us-west-2.compute.internal Ready <none> 24h v1.25.16-eks-59bf375
# Check taints applied on the nodes.
kubectl get nodes -l team=checkout -o jsonpath="{range .items[*]}{.metadata.name} {.spec.taints}{\"\n\"}{end}"
kubectl get nodes -l team=checkout -o jsonpath="{range .items[*]}{.metadata.name} {.spec.taints[?(@.effect=='NoSchedule')]}{\"\n\"}{end}"
ip-10-0-39-95.us-west-2.compute.internal {"effect":"NoSchedule","key":"dedicated","value":"CheckoutApp"}
#
kubectl get pods -n checkout -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
checkout-558f7777c-fs8dk 1/1 Running 0 24h 10.0.39.105 ip-10-0-39-95.us-west-2.compute.internal <none> <none>
checkout-redis-f54bf7cb5-whk4r 1/1 Running 0 24h 10.0.41.7 ip-10-0-39-95.us-west-2.compute.internal <none> <none>
# 모니터링
kubectl get nodeclaim
kubectl get nodes -l team=checkout -o jsonpath="{range .items[*]}{.metadata.name} {.spec.taints}{\"\n\"}{end}"
kubectl get pods -n checkout -o wide
while true; do kubectl get nodeclaim; echo ; kubectl get nodes -l team=checkout; echo ; kubectl get nodes -l team=checkout -o jsonpath="{range .items[*]}{.metadata.name} {.spec.taints}{\"\n\"}{end}"; echo ; kubectl get pods -n checkout -o wide; echo ; date; sleep 1; echo; done
결제 앱 포드가 카펜터에서 프로비저닝한 노드에서 실행되고 있는 것을 볼 수 있습니다.
체크아웃 애플리케이션을 확장해 보겠습니다. 이를 위해 아래와 같이 eks-gitops-repo/apps/checkout 폴더의 deposition.yaml 파일에서 복제본을 1에서 10으로 변경합니다.
변경이 완료되면 저장소에 변경 사항을 커밋하고 푸시한 후 Argo-cd가 변경 사항을 동기화할 때까지 기다립니다.
#
while true; do kubectl get nodeclaim; echo ; kubectl get nodes -l team=checkout; echo ; kubectl get nodes -l team=checkout -o jsonpath="{range .items[*]}{.metadata.name} {.spec.taints}{\"\n\"}{end}"; echo ; kubectl get pods -n checkout -o wide; echo ; date; sleep 1; echo; done
#
cd ~/environment/eks-gitops-repo
git add apps/checkout/deployment.yaml
git commit -m "scale checkout app"
git push --set-upstream origin main
# You can force the sync using ArgoCD console or following command:
argocd app sync checkout
# LIVE(k8s)에 직접 scale 실행
kubectl scale deploy checkout -n checkout --replicas 10
# 현재는 1.25.16 2대 사용 중 확인
# Karpenter will scale looking at the aggregate resource requirements of unscheduled pods. We will see we have now two nodes provisioned via karpenter.
kubectl get nodes -l team=checkout
NAME STATUS ROLES AGE VERSION
ip-10-0-24-213.us-west-2.compute.internal Ready <none> 62s v1.25.16-eks-59bf375
ip-10-0-39-95.us-west-2.compute.internal Ready <none> 24h v1.25.16-eks-59bf375
# Lets get the ami id for the AMI build for kubernetes version 1.26.
aws ssm get-parameter --name /aws/service/eks/optimized-ami/1.26/amazon-linux-2/recommended/image_id \
--region ${AWS_REGION} --query "Parameter.Value" --output text
ami-086414611b43bb691
[파일 수정] We are making following changes in the default-ec2nc.yaml and default-np.yaml in eks-gitops-repo/app/karpenter folder as shown below.
우리는 spec.amiSelectorTerms의 AMI ID를 default-ec2nc.yaml파일의 위 출력으로 대체할 것입니다 We will add below code snippet under spec.disruption to add disruption budgets to disrupt all nodes at once.
budgets:
- nodes: "1"
Changes in default-ec2nc.yaml file.
kubectl get ec2nodeclass default -o yaml | grep 'id: ami-' | uniq
- id: ami-0ee947a6f4880da75
Changes in default-np.yaml file
kubectl get nodepool default -o yaml
...
spec:
disruption:
budgets:
- nodes: "1"
consolidationPolicy: WhenUnderutilized
expireAfter: Never
limits:
...
To apply these changes we will commit and push these changes to the repository.
#
while true; do kubectl get nodeclaim; echo ; kubectl get nodes -l team=checkout; echo ; kubectl get nodes -l team=checkout -o jsonpath="{range .items[*]}{.metadata.name} {.spec.taints}{\"\n\"}{end}"; echo ; kubectl get pods -n checkout -o wide; echo ; date; sleep 1; echo; done
# 10분 소요 (예상) 실습 포함
cd ~/environment/eks-gitops-repo
git add apps/karpenter/default-ec2nc.yaml apps/karpenter/default-np.yaml
git commit -m "disruption changes"
git push --set-upstream origin main
argocd app sync karpenter
# Once Argo CD sync the karpenter app, we can see the disruption event in karpenter controller logs. It will then provision new nodes with kubernetes version 1.26 and delete the old nodes.
kubectl -n karpenter logs deployment/karpenter -c controller --tail=33 -f
혹은
kubectl stern -n karpenter deployment/karpenter -c controller
# Lets get the ami id for the AMI build for kubernetes version 1.26.
aws ssm get-parameter --name /aws/service/eks/optimized-ami/1.26/amazon-linux-2/recommended/image_id \
--region ${AWS_REGION} --query "Parameter.Value" --output text
ami-086414611b43bb691
카르펜터 컨트롤러 로그에서 노드 ip-10-0-11-205.us -west-2.compute.internal과 ip-10-0-47-152.us -west-2.compute.internal이 하나씩 표류하는 것을 볼 수 있습니다.
새 노드를 가져와서 이 노드에서 체크아웃 앱 포드가 실행되고 있는지 확인해 보겠습니다. 이제 노드가 v1.26.15-eks-1552ad0 버전에 있는지 확인할 수 있습니다.
# 과정 중 상태
Wed Mar 26 03:52:39 UTC 2025
NAME TYPE ZONE NODE READY AGE
default-m4bv4 m6a.large us-west-2b ip-10-0-24-213.us-west-2.compute.internal True 15m
default-rpl9w c5.xlarge us-west-2c ip-10-0-39-95.us-west-2.compute.internal True 25h
default-xxf6m c5.xlarge us-west-2c ip-10-0-36-63.us-west-2.compute.internal True 45s
ip-10-0-24-213.us-west-2.compute.internal [{"effect":"NoSchedule","key":"dedicated","value":"CheckoutApp"}]
ip-10-0-36-63.us-west-2.compute.internal [{"effect":"NoSchedule","key":"dedicated","value":"CheckoutApp"}]
ip-10-0-39-95.us-west-2.compute.internal [{"effect":"NoSchedule","key":"dedicated","value":"CheckoutApp"},
>> {"effect":"NoSchedule","key":"karpenter.sh/disruption","value":"disrupting"}]
NAME STATUS ROLES AGE VERSION
ip-10-0-24-213.us-west-2.compute.internal Ready <none> 14m v1.25.16-eks-59bf375
ip-10-0-36-63.us-west-2.compute.internal Ready <none> 22s v1.26.15-eks-59bf375
ip-10-0-39-95.us-west-2.compute.internal Ready <none> 25h v1.25.16-eks-59bf375
#
Wed Mar 26 03:54:15 UTC 2025
NAME TYPE ZONE NODE READY AGE
default-rngwl m6a.large us-west-2b ip-10-0-21-135.us-west-2.compute.internal True 79s
default-xxf6m c5.xlarge us-west-2c ip-10-0-36-63.us-west-2.compute.internal True 2m22s
ip-10-0-21-135.us-west-2.compute.internal [{"effect":"NoSchedule","key":"dedicated","value":"CheckoutApp"}]
ip-10-0-36-63.us-west-2.compute.internal [{"effect":"NoSchedule","key":"dedicated","value":"CheckoutApp"}]
NAME STATUS ROLES AGE VERSION
ip-10-0-21-135.us-west-2.compute.internal Ready <none> 55s v1.26.15-eks-59bf375
ip-10-0-36-63.us-west-2.compute.internal Ready <none> 119s v1.26.15-eks-59bf375
# 카펜터 동작 로그 확인
kubectl -n karpenter logs deployment/karpenter -c controller --tail=33 -f
혹은
kubectl stern -n karpenter deployment/karpenter -c controller
#
kubectl get nodes -l team=checkout
# 파드 신규 노드로 이전 완료
kubectl get pods -n checkout -o wide
...
카르펜터 컨트롤러 로그에서 노드 ip-10-0-11-205.us -west-2.compute.internal과 ip-10-0-47-152.us -west-2.compute.internal이 하나씩 표류하는 것을 볼 수 있습니다.
새 노드를 가져와서 이 노드에서 체크아웃 앱 포드가 실행되고 있는지 확인해 보겠습니다. 이제 노드가 v1.26.15-eks-1552ad0 버전에 있는지 확인할 수 있습니다.
# 과정 중 상태
Wed Mar 26 03:52:39 UTC 2025
NAME TYPE ZONE NODE READY AGE
default-m4bv4 m6a.large us-west-2b ip-10-0-24-213.us-west-2.compute.internal True 15m
default-rpl9w c5.xlarge us-west-2c ip-10-0-39-95.us-west-2.compute.internal True 25h
default-xxf6m c5.xlarge us-west-2c ip-10-0-36-63.us-west-2.compute.internal True 45s
ip-10-0-24-213.us-west-2.compute.internal [{"effect":"NoSchedule","key":"dedicated","value":"CheckoutApp"}]
ip-10-0-36-63.us-west-2.compute.internal [{"effect":"NoSchedule","key":"dedicated","value":"CheckoutApp"}]
ip-10-0-39-95.us-west-2.compute.internal [{"effect":"NoSchedule","key":"dedicated","value":"CheckoutApp"},
>> {"effect":"NoSchedule","key":"karpenter.sh/disruption","value":"disrupting"}]
NAME STATUS ROLES AGE VERSION
ip-10-0-24-213.us-west-2.compute.internal Ready <none> 14m v1.25.16-eks-59bf375
ip-10-0-36-63.us-west-2.compute.internal Ready <none> 22s v1.26.15-eks-59bf375
ip-10-0-39-95.us-west-2.compute.internal Ready <none> 25h v1.25.16-eks-59bf375
#
Wed Mar 26 03:54:15 UTC 2025
NAME TYPE ZONE NODE READY AGE
default-rngwl m6a.large us-west-2b ip-10-0-21-135.us-west-2.compute.internal True 79s
default-xxf6m c5.xlarge us-west-2c ip-10-0-36-63.us-west-2.compute.internal True 2m22s
ip-10-0-21-135.us-west-2.compute.internal [{"effect":"NoSchedule","key":"dedicated","value":"CheckoutApp"}]
ip-10-0-36-63.us-west-2.compute.internal [{"effect":"NoSchedule","key":"dedicated","value":"CheckoutApp"}]
NAME STATUS ROLES AGE VERSION
ip-10-0-21-135.us-west-2.compute.internal Ready <none> 55s v1.26.15-eks-59bf375
ip-10-0-36-63.us-west-2.compute.internal Ready <none> 119s v1.26.15-eks-59bf375
# 카펜터 동작 로그 확인
kubectl -n karpenter logs deployment/karpenter -c controller --tail=33 -f
혹은
kubectl stern -n karpenter deployment/karpenter -c controller
#
kubectl get nodes -l team=checkout
# 파드 신규 노드로 이전 완료
kubectl get pods -n checkout -o wide
...
☞ [3.3 데이터부 - 셀프노드] Upgrading EKS Self-managed Nodes 10분 소요 (예상) 실습 포함
기본 정보 확인
# Lets explore the self-managed nodes.
kubectl get nodes --show-labels | grep self-managed
ip-10-0-29-22.us-west-2.compute.internal Ready <none> 2d12h v1.25.16-eks-59bf375 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/instance-type=m5.large,beta.kubernetes.io/os=linux,failure-domain.beta.kubernetes.io/region=us-west-2,failure-domain.beta.kubernetes.io/zone=us-west-2b,k8s.io/cloud-provider-aws=a94967527effcefb5f5829f529c0a1b9,kubernetes.io/arch=amd64,kubernetes.io/hostname=ip-10-0-29-22.us-west-2.compute.internal,kubernetes.io/os=linux,node.kubernetes.io/instance-type=m5.large,node.kubernetes.io/lifecycle=self-managed,team=carts,topology.ebs.csi.aws.com/zone=us-west-2b,topology.kubernetes.io/region=us-west-2,topology.kubernetes.io/zone=us-west-2b
ip-10-0-4-238.us-west-2.compute.internal Ready <none> 2d12h v1.25.16-eks-59bf375 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/instance-type=m5.large,beta.kubernetes.io/os=linux,failure-domain.beta.kubernetes.io/region=us-west-2,failure-domain.beta.kubernetes.io/zone=us-west-2a,k8s.io/cloud-provider-aws=a94967527effcefb5f5829f529c0a1b9,kubernetes.io/arch=amd64,kubernetes.io/hostname=ip-10-0-4-238.us-west-2.compute.internal,kubernetes.io/os=linux,node.kubernetes.io/instance-type=m5.large,node.kubernetes.io/lifecycle=self-managed,team=carts,topology.ebs.csi.aws.com/zone=us-west-2a,topology.kubernetes.io/region=us-west-2,topology.kubernetes.io/zone=us-west-2a
# Verify if the pods are scheduled on self-managed nodes.
kubectl get pods -n carts -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
carts-7ddbc698d8-zts9k 1/1 Running 0 30h 10.0.31.72 ip-10-0-26-119.us-west-2.compute.internal <none> <none>
carts-dynamodb-6594f86bb9-8pzk5 1/1 Running 0 30h 10.0.30.122 ip-10-0-26-119.us-west-2.compute.internal <none> <none>
# lets perform in-place upgrade on the self-managed nodes.
자가 관리 노드를 업그레이드하려면 실행 템플릿에서 AMI를 업데이트해야 합니다.
이 워크숍에서는 테라폼을 사용하여 자가 관리 노드를 생성했습니다.
이를 통해 /home/ec2-user/Environment/terraform/base.tf 파일에서 AMI를 업데이트하고 변경 사항을 적용할 수 있습니다.
하지만 먼저 Kubernetes 버전 1.26에 해당하는 최신 AMI를 확인해 보겠습니다.
aws ssm get-parameter --name /aws/service/eks/optimized-ami/1.26/amazon-linux-2/recommended/image_id --region $AWS_REGION --query "Parameter.Value" --output text
ami-086414611b43bb691
Update /home/ec2-user/environment/terraform/base.tf file with the AMI obtained from the previous command.
self_managed_node_groups = {
self-managed-group = {
instance_type = "m5.large"
.
.
.
# Additional configurations
ami_id = "ami-086414611b43bb691" # Replaced the latest AMI ID for EKS 1.26
subnet_ids = module.vpc.private_subnets
.
.
.
launch_template_use_name_prefix = true
}
}
Apply terraform changes.
#
while true; do kubectl get nodes -l node.kubernetes.io/lifecycle=self-managed; echo ; aws ec2 describe-instances --query "Reservations[*].Instances[*].[Tags[?Key=='Name'].Value | [0], ImageId]" --filters "Name=tag:Name,Values=default-selfmng" --output table; echo ; date; sleep 1; echo; done
#
cd ~/environment/terraform/
terraform plan && terraform apply -auto-approve
Once it is completed. Now validate the nodes with the below command to make sure the version is v1.26:
신규 버전 EC2 1대가 Ready되고 나서 5분 Refresh 시간 이후에 기존 버전 EC2 1대를 종료 실행하면서, 동시에 추가 신규 버전 EC2 1대 생성.
#
kubectl get nodes -l node.kubernetes.io/lifecycle=self-managed
NAME STATUS ROLES AGE VERSION
ip-10-0-19-69.us-west-2.compute.internal Ready <none> 23m v1.26.15-eks-1552ad0
ip-10-0-46-19.us-west-2.compute.internal Ready <none> 28m v1.26.15-eks-1552ad0
#
aws ec2 describe-instances --query "Reservations[*].Instances[*].[Tags[?Key=='Name'].Value | [0], ImageId]" --filters "Name=tag:Name,Values=default-selfmng" --output table
...
☞ [3.4 데이터부 - 파게이트노드] Upgrading AWS Fargate Nodes
Fargate는 컨테이너에 필요에 따라 적절한 크기의 컴퓨팅 용량을 제공하는 기술입니다.
Fargate를 사용하면 컨테이너를 실행하기 위해 가상 머신 그룹을 직접 프로비저닝, 구성 또는 확장할 필요가 없습니다.
서버 유형을 선택하거나 노드 그룹을 확장할 시기를 결정하거나 클러스터 패킹을 최적화할 필요도 없습니다.
Fargate에서 실행되는 기준을 충족하는 Pod를 시작하면 클러스터에서 실행 중인 Fargate 컨트롤러가 Pod를 Fargate로 인식, 업데이트 및 예약합니다.
AWS Fargate 노드를 업그레이드하려면 K8s 배포를 다시 시작하여 새로운 포드가 최신 Kubernetes 버전에서 자동으로 예약되도록 할 수 있습니다.
기본 정보 확인
#
kubectl get pods -n assets -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
assets-7ccc84cb4d-qhnjg 1/1 Running 0 2d12h 10.0.38.16 fargate-ip-10-0-38-16.us-west-2.compute.internal <none> <none>
# Now, lets describe the node to see its version.
kubectl get node $(kubectl get pods -n assets -o jsonpath='{.items[0].spec.nodeName}') -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
fargate-ip-10-0-38-16.us-west-2.compute.internal Ready <none> 2d12h v1.25.16-eks-2d5f260 10.0.38.16 <none> Amazon Linux 2 5.10.234-225.910.amzn2.x86_64 containerd://1.7.25
디플로이먼트 재시작 Restart 으로 신규 버전 마이그레이션!
# 디플로이먼트 재시작 Restart
kubectl rollout restart deployment assets -n assets
# Lets wait for the new pods to become ready.
kubectl wait --for=condition=Ready pods --all -n assets --timeout=180s
# Once the new pods reach ready state, check the version of the new fargate node.
kubectl get pods -n assets -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
assets-5ddf8b7c99-tb5vq 1/1 Running 0 3m 10.0.31.81 fargate-ip-10-0-31-81.us-west-2.compute.internal <none> <none>
kubectl get node $(kubectl get pods -n assets -o jsonpath='{.items[0].spec.nodeName}') -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
fargate-ip-10-0-31-81.us-west-2.compute.internal Ready <none> 2m29s v1.26.15-eks-2d5f260 10.0.31.81 <none> Amazon Linux 2 5.10.234-225.910.amzn2.x86_64 containerd://1.7.25
☞ [3. 데이터부] 제어부와 동일한 데이터부 버전 업그레이드 최종 확인
#
kubectl get node
NAME STATUS ROLES AGE VERSION
fargate-ip-10-0-31-81.us-west-2.compute.internal Ready <none> 4h27m v1.26.15-eks-2d5f260
ip-10-0-11-240.us-west-2.compute.internal Ready <none> 7h30m v1.26.15-eks-59bf375
ip-10-0-14-139.us-west-2.compute.internal Ready <none> 2m45s v1.26.15-eks-59bf375
ip-10-0-15-12.us-west-2.compute.internal Ready <none> 6h43m v1.26.15-eks-59bf375
ip-10-0-18-51.us-west-2.compute.internal Ready <none> 98s v1.26.15-eks-59bf375
ip-10-0-20-131.us-west-2.compute.internal Ready <none> 4h28m v1.26.15-eks-59bf375
ip-10-0-32-184.us-west-2.compute.internal Ready <none> 4h33m v1.26.15-eks-59bf375
ip-10-0-45-86.us-west-2.compute.internal Ready <none> 7h31m v1.26.15-eks-59bf375
#
aws ec2 describe-instances --query "Reservations[*].Instances[*].[Tags[?Key=='Name'].Value | [0], ImageId]" --output table
#
kubectl get node --label-columns=eks.amazonaws.com/capacityType,node.kubernetes.io/lifecycle,karpenter.sh/capacity-type,eks.amazonaws.com/compute-type
kubectl get node -L eks.amazonaws.com/nodegroup,karpenter.sh/nodepool
kubectl get node --label-columns=node.kubernetes.io/instance-type,kubernetes.io/arch,kubernetes.io/os,topology.kubernetes.io/zone
[AWS EKS CI/CD 추천 영상] GS리테일 Amazon EKS A-A 무중단 업그레이드(업무 시간 진행) - Youtube
'AEWS study' 카테고리의 다른 글
11주차 - ML Infra(GPU) on EKS (0) | 2025.04.15 |
---|---|
10주차 - K8s 시크릿 관리 Update (0) | 2025.04.09 |
8주차 - K8S CI/CD - #2 (0) | 2025.03.29 |
8주차 - K8S CI/CD - #1 (0) | 2025.03.28 |
7주차 - EKS Mode/Nodes - #2 (0) | 2025.03.22 |