이번 실습에서는 Karpenter 노드가 2대 있는 상태에서 nodes: "1" budget을 걸어서
한 대씩 순차적으로 교체 되는 것을 직접 확인합니다.
budget이 없으면 2대가 동시에 교체되어 짧은 시간 동안 서비스 가용성이 떨어질 수 있기 때문입니다.
사전 준비 : 현재 Karpenter 환경 확인
업그레이드 전에 현재 NodePool과 EC2NodeClass 설정을 파악합니다.
Step1. NodePool 확인 (핵심 항목 출력)
$ kubectl describe nodepool
Spec:
Disruption:
Budgets:
Nodes: 10% # 기본 budget: 전체의 10%만 동시 교체
Consolidation Policy: WhenEmpty
Limits:
Cpu: 100
Template:
Metadata:
Labels:
Env: dev
Team: checkout # 이 NodePool로 뜬 노드에는 team=checkout 레이블이 붙음
Spec:
Expire After: 720h # 30일 TTL: 노드가 30일 지나면 자동 교체 대상
Node Class Ref:
Name: default # 아래 확인할 EC2NodeClass 참조
Taints:
Key: dedicated
Value: CheckoutApp
Effect: NoSchedule # checkout toleration 없는 파드는 이 노드에 스케줄링 불가
team=checkout 레이블로 Karpenter가 프로비저닝한 노드를 쉽게 필터링할 수 있습니다.
dedicated=CheckoutApp:NoSchedule Taint로 checkout 앱 파드만 이 노드에 뜨도록 격리되어 있습니다.
Step2. EC2NodeClass 확인
$ kubectl describe ec2nodeclass
Spec:
Ami Family: AL2023
Ami Selector Terms:
Id: ami-0f676a166352f02ab # 현재 1.30용 AMI가 직접 지정되어 있음
Role: karpenter-eksworkshop-eksctl
Security Group Selector Terms:
Tags:
karpenter.sh/discovery: eksworkshop-eksctl
Subnet Selector Terms:
Tags:
karpenter.sh/discovery: eksworkshop-eksctl
AMI ID가 직접 지정(id: ami-0f676a166352f02ab)되어 있습니다. 이 AMI가 1.30 기준입니다.
이 값을 1.31용 AMI ID로 바꾸는 순간 Karpenter가 Drift를 감지합니다.
Step3. 현재 Karpenter 노드 확인
$ kubectl get nodes -l team=checkout
NAME STATUS ROLES AGE VERSION
ip-10-0-2-174.us-west-2.compute.internal Ready <none> 9h v1.30.14-eks-f69f56f
현재 Karpenter 노드는 1대이고 v1.30입니다.
# 이 노드에 어떤 Taint가 걸려 있는지 확인
$ kubectl get nodes -l team=checkout \
-o jsonpath="{range .items[*]}{.metadata.name} {.spec.taints[?(@.effect=='NoSchedule')]}{\"\n\"}{end}"
ip-10-0-2-174.us-west-2.compute.internal {"effect":"NoSchedule","key":"dedicated","value":"CheckoutApp"}
dedicated=CheckoutApp:NoSchedule Taint가 확인됩니다.
checkout 파드는 이 toleration을 가지고 있어서 이 노드에 뜰 수 있습니다.
Step4. checkout 파드 현황 확인
$ kubectl get pods -n checkout -o wide
NAME READY STATUS AGE NODE
checkout-9c674566c-rvqcd 1/1 Running 9h ip-10-0-2-174...
checkout-redis-97ff8589d-6b9kf 1/1 Running 9h ip-10-0-2-174...
checkout 앱과 checkout-redis가 Karpenter 노드에서 돌고 있습니다.
Node Provisioning via Karpenter
Karpenter를 통해 1.31 버전의 노드를 올리고, 기존 1.30 버전의 노드를 삭제하는 실습을 해보겠습니다.
Step1. Karpenter 노드 2대 확보 : checkout 파드 10개로 증설
Disruption Budget 시연을 하려면 Karpenter 노드가 최소 2대 이상 있어야 합니다.
노드 1대일 때 budget을 걸어봤자 1대 교체하고 끝이라 순차 교체 효과를 눈으로 볼 수 없기 때문입니다.
checkout 파드를 10개로 늘리면 기존 1대 노드의 리소스만으로는 10개 파드의 요청을 감당할 수 없고,
Karpenter가 자동으로 추가 노드를 프로비저닝합니다.
모니터링을 먼저 켜둡니다.
아래 명령은 1초마다 NodeClaim 목록, team=checkout 노드 목록, 해당 노드의 Taint, checkout 파드 배치 상태를 한 번에 출력합니다. 파드가 늘어나면서 Karpenter가 새 NodeClaim을 생성하고 노드가 추가되는 과정을 실시간으로 확인할 수 있습니다.
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
done
$ kubectl get nodes -l team=checkout
NAME STATUS ROLES AGE VERSION
ip-10-0-2-174.us-west-2.compute.internal Ready <none> 9h v1.30.14-eks-f69f56f
ip-10-0-8-63.us-west-2.compute.internal Ready <none> 2m40s v1.30.14-eks-f69f56f
새로 뜬 노드도 EC2NodeClass에 지정된 현재 AMI(1.30용)로 프로비저닝됐기 때문에 v1.30입니다.
이제 2대가 준비됐습니다.
Step2. 1.31용 AMI ID 확인
현재 EC2NodeClass에 어떤 AMI가 지정되어 있는지 먼저 확인합니다.
kubectl get ec2nodeclass default -o yaml | grep 'id: ami-' | uniq
- id: ami-0f676a166352f02ab # 현재 1.30용 AMI
# 첫 번째 노드 교체 시작
{"message":"disrupting nodeclaim(s) via replace, terminating 1 nodes","reason":"drifted"}
{"message":"created nodeclaim","NodePool":{"name":"default"},"NodeClaim":{"name":"default-j882g"}}
{"message":"launched nodeclaim","instance-type":"c4.large","zone":"us-west-2c"}
{"message":"registered nodeclaim","Node":{"name":"ip-10-0-38-70..."}}
{"message":"initialized nodeclaim","Node":{"name":"ip-10-0-38-70..."}}
# 기존 노드 disruption taint 추가 후 삭제
{"message":"tainted node","Node":{"name":"ip-10-0-8-63..."},"taint.Key":"karpenter.sh/disruption"}
{"message":"deleted node","Node":{"name":"ip-10-0-8-63..."}}
{"message":"deleted nodeclaim","NodeClaim":{"name":"default-6swc4"}}
# 첫 번째 교체 완료 후 두 번째 노드 교체 시작
{"message":"disrupting nodeclaim(s) via replace, terminating 1 nodes","reason":"drifted"}
{"message":"created nodeclaim","NodePool":{"name":"default"},"NodeClaim":{"name":"default-ct7nn"}}
...
{"message":"tainted node","Node":{"name":"ip-10-0-2-174..."},"taint.Key":"karpenter.sh/disruption"}
{"message":"deleted node","Node":{"name":"ip-10-0-2-174..."}}
{"message":"deleted nodeclaim","NodeClaim":{"name":"default-q9tgw"}}
로그에서 "reason":"drifted"가 교체 트리거가 Drift임을 나타냅니다.
karpenter.sh/disruption taint는 Karpenter가 교체 대상 노드에 붙이는 taint로,
이 taint가 붙는 순간 해당 노드에 새 파드가 스케줄링되지 않고 기존 파드 eviction이 시작됩니다.
노드 상태 변화도 별도 터미널에서 모니터링합니다.
while true; do
kubectl get nodeclaim
echo
kubectl get nodes -l team=checkout
echo
kubectl get pods -n checkout -o wide
echo; date; sleep 1
done
이 명령이 각각 보여주는 것은 다음과 같습니다.
kubectl get nodeclaim은 Karpenter가 관리하는 NodeClaim 목록입니다. 교체 과정에서 새 NodeClaim이 생성되고 이전 NodeClaim이 삭제되는 시점을 볼 수 있습니다.
kubectl get nodes -l team=checkout은 team=checkout 레이블이 붙은 노드만 필터링해서 버전과 AGE를 보여줍니다. 새 노드가 추가되고 구 노드가 사라지는 흐름이 여기서 보입니다.
kubectl get pods -n checkout -o wide는 파드가 어느 노드에서 실행 중인지 확인합니다. 교체 과정에서 파드가 신규 노드로 재스케줄링되는 것을 볼 수 있습니다.
업그레이드 결과 확인
모든 교체가 완료된 후 노드 버전을 확인합니다.
$ kubectl get nodes -l team=checkout
NAME STATUS ROLES AGE VERSION
ip-10-0-12-207.us-west-2.compute.internal Ready <none> 4m5s v1.31.14-eks-ecaa3a6
ip-10-0-38-70.us-west-2.compute.internal Ready <none> 5m48s v1.31.14-eks-ecaa3a6
두 노드 모두 v1.31로 교체됐습니다. AGE가 짧은 것이 방금 새로 프로비저닝된 노드라는 증거입니다.
checkout 파드도 새 노드에서 정상 실행 중인지 확인합니다.
kubectl get pods -n checkout -o wide
10개의 checkout 파드와 checkout-redis가 두 새 노드에 분산되어 Running 상태인 것을 확인합니다.
Managed Node Group과의 업그레이드 비교
이번 실습까지 진행하면서 MNG와 Karpenter의 업그레이드 방식 차이가 명확해졌습니다.
항목
Managed Node Group
Karpenter
노드 관리 주체
AWS (ASG 기반)
Karpenter 컨트롤러
업그레이드 트리거
mng_cluster_version 변수 또는 ami_id 변경
EC2NodeClass AMI 변경 → Drift 감지
롤링 제어
max_unavailable_percentage
Disruption Budget (nodes, schedule)
교체 단위
ASG가 인스턴스 교체
Karpenter가 직접 EC2 프로비저닝/삭제
상태 확인
kubectl get nodes
kubectl get nodeclaim + kubectl get nodes
긴 글 읽어주셔서 감사합니다.
다음 섹션에서는 Self-managed node group과 Fargate 업그레이드로 찾아뵙겠습니다.