[CloudNeta] EKS 워크샵 스터디 (7) - EKS 클러스터 업그레이드 Part 2 - terraform-aws-eks-blueprints 기반 버전 업그레이드
이번 게시글에서는 EKS 워크샵 스터디 제 7주차 내용을 작성합니다.
이 글은 2부로 나누어집니다.
들어가며
Part 1에서는 AWS 워크샵 카탈로그의 시나리오(EKS Cluster Upgrades)를 따라 콘솔과 CLI 위주로 컨트롤 플레인·노드 그룹·애드온을 단계적으로 끌어올리며 인플레이스 업그레이드의 흐름을 익혔습니다. 워크샵은 운영자가 어떤 순서로 무엇을 점검해야 하는지를 가르치는 데 초점이 맞춰져 있어, 클러스터 업그레이드를 처음 다뤄보는 단계에서 매우 유용한 출발점입니다.
다만 실제 운영 환경에서는 손으로 콘솔·CLI를 조작하기보다는 인프라를 코드로 관리(IaC)하는 흐름이 더 일반적입니다. Part 2에서는 aws-ia/terraform-aws-eks-blueprints 저장소에서 제공하는 모듈과 패턴을 활용해, Part 1에서 학습한 업그레이드 절차를 테라폼 코드 변경 한 줄로 재현하는 과정을 정리합니다.
terraform-aws-eks-blueprints 개요
terraform-aws-eks-blueprints는 AWS가 메인테이닝하는 EKS용 테라폼 패턴 모음입니다. 공식 가이드에서는 이 저장소를 다음과 같이 설명합니다.
Amazon EKS Blueprints for Terraform has been designed to be a "blueprint" - a starting point that you can copy/paste/modify to create your own EKS environment.
즉, 단일 모듈이 아닌 참조 가능한 패턴 카탈로그라는 점이 핵심입니다. 핵심 빌딩 블록은 다음 세 가지로 정리할 수 있습니다.
- terraform-aws-modules/terraform-aws-eks: EKS 클러스터, 노드 그룹, IRSA 등을 선언적으로 정의하는 커뮤니티 모듈입니다. 사실상 EKS Blueprints의 기반이 됩니다.
- aws-ia/terraform-aws-eks-blueprints-addons: AWS Load Balancer Controller, Karpenter, EBS CSI 등 자주 쓰는 애드온의 설치/구성 패턴을 모아둔 모듈입니다.
patterns/디렉터리: 실제 사용 시나리오별(EKS Auto Mode, Karpenter, Fargate, GitOps 등) 완성된 예제 모음입니다. 업그레이드 작업의 출발점으로 가장 적합한 자원입니다.
이번 글에서는 patterns/ 하위에 있는 가장 단순한 매니지드 노드 그룹 예제를 베이스로, 클러스터 버전을 한 단계 끌어올리는 과정을 따라가 봅니다.
사전 준비 사항
작업에 앞서 다음을 준비합니다.
- AWS CLI v2,
kubectl,helm,terraform(>=1.5) 또는tofu설치 - 업그레이드 대상 EKS가 위치한 계정/리전에 대한 권한
- 백업이 필요한 리소스(컨피그맵, PVC, 클러스터 외부 통합 설정 등) 식별
- AWS EKS 업그레이드 인사이트 또는 kubent, pluto로 사용 중지 API 사전 점검
# Deprecated API 사용 여부 점검
kubent --target-version 1.31
# EKS 업그레이드 인사이트 조회
aws eks list-insights --cluster-name <cluster-name> \
--filter "categories=UPGRADE_READINESS"
업그레이드 인사이트는 컨트롤 플레인·노드·애드온 단위로 막힐 만한 이슈를 미리 알려주므로, 테라폼 변경을 적용하기 전에 반드시 통과시키는 것이 좋습니다.
베이스 패턴 가져오기
patterns/ 하위에서 우리가 다룰 예제를 그대로 가져옵니다. 워크샵 시나리오와 가장 비슷한 구성을 위해 매니지드 노드 그룹 기반 예제를 사용합니다.
git clone https://github.com/aws-ia/terraform-aws-eks-blueprints.git
cd terraform-aws-eks-blueprints/patterns/eks-cluster-with-managed-nodegroup
해당 디렉터리에는 보통 다음과 같은 파일들이 자리합니다.
.
├── main.tf
├── outputs.tf
├── providers.tf
├── variables.tf
└── versions.tf
main.tf의 핵심은 EKS 모듈 호출부입니다.
module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "~> 20.0"
cluster_name = local.name
cluster_version = "1.30"
cluster_endpoint_public_access = true
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnets
eks_managed_node_groups = {
default = {
instance_types = ["m5.large"]
min_size = 2
max_size = 5
desired_size = 3
}
}
cluster_addons = {
coredns = {}
kube-proxy = {}
vpc-cni = {}
aws-ebs-csi-driver = {}
}
tags = local.tags
}
업그레이드의 핵심 변수는 단 하나, cluster_version입니다. 단, 컨트롤 플레인 버전을 올린다고 해서 노드 그룹의 AMI나 애드온 버전이 자동으로 따라오는 것은 아니므로, 함께 점검할 포인트들이 있습니다.
업그레이드 전 현재 상태 캡처
변경 전후 비교를 명확히 하기 위해 현재 상태를 한 번 정리합니다.
# 컨트롤 플레인 버전
aws eks describe-cluster --name $CLUSTER_NAME \
--query 'cluster.{version:version,platformVersion:platformVersion}'
# 노드 그룹의 AMI/쿠버네티스 버전
aws eks describe-nodegroup \
--cluster-name $CLUSTER_NAME \
--nodegroup-name default \
--query 'nodegroup.{version:version,releaseVersion:releaseVersion,amiType:amiType}'
# 애드온 버전
aws eks list-addons --cluster-name $CLUSTER_NAME --output text \
| xargs -n1 -I{} aws eks describe-addon \
--cluster-name $CLUSTER_NAME --addon-name {} \
--query 'addon.{name:addonName,version:addonVersion}'
kubectl로도 노드 상태를 확인해 둡니다.
kubectl get nodes -o wide
kubectl get pods -A -o wide | grep -v Running
이 결과는 업그레이드 후 동일하게 다시 실행해, 워크로드가 정상적으로 따라 올라왔는지 비교할 때 사용합니다.
컨트롤 플레인 업그레이드
cluster_version만 한 단계 올려 적용합니다. EKS는 마이너 버전을 한 번에 한 단계씩만 끌어올릴 수 있다는 제약이 있으므로, 1.30 → 1.32처럼 두 단계를 건너뛰려 한다면 1.30 → 1.31 → 1.32 순서로 두 번 적용해야 합니다.
module "eks" {
# ...
- cluster_version = "1.30"
+ cluster_version = "1.31"
# ...
}
플랜과 적용은 평소처럼 진행합니다.
terraform plan -out tfplan
terraform apply tfplan
terraform plan 결과는 사람 눈으로 보되, 맹신은 금물
변경 대상이 컨트롤 플레인으로 한정되는지(노드 그룹·애드온이 함께 교체되지 않는지)를 plan 출력에서 확인합니다. 의도하지 않은 강제 재생성(replace) 항목이 섞이면 노드 풀이 통째로 교체되며 워크로드가 한꺼번에 흔들리므로, ~ update in-place 외의 변경은 모두 의심하고 들여다보세요.
동시에, plan 결과를 100% 신뢰하지는 마세요. AMI/애드온 latest 조회, data 소스 리프레시, (known after apply)로 잡힌 값들은 실제 apply 시점에 resolve되면서 plan에 안 보였던 변경이 따라붙는 경우가 있습니다. 가능하면 AMI/애드온/차트 버전을 명시적으로 핀해 두고, 운영 환경에서는 terraform apply tfplan 형태로 plan 파일을 그대로 적용해 plan-apply 사이의 드리프트를 좁혀 둡니다.
컨트롤 플레인 업그레이드는 EKS 측에서 평균 30~40분 정도 걸립니다. 진행 상황은 다음 명령으로 추적할 수 있습니다.
aws eks describe-update --name $CLUSTER_NAME --update-id <id>
매니지드 노드 그룹 업그레이드
컨트롤 플레인이 올라가도 워커 노드는 여전히 이전 마이너 버전입니다. 매니지드 노드 그룹은 다음 두 가지 방식으로 끌어올릴 수 있습니다.
cluster_version만 따라가도록 두기: 노드 그룹의version을 별도로 명시하지 않으면 클러스터 버전과 동일하게 맞춰집니다.- 노드 그룹 단위로 명시적으로 버전을 지정하기: 컨트롤 플레인 업그레이드와 노드 롤링을 분리해 운영합니다.
운영 환경에서는 후자를 권장합니다. 컨트롤 플레인이 안정화된 뒤 별도 PR로 노드 롤링을 진행할 수 있어, 변경 단위가 명확하고 롤백도 수월합니다.
eks_managed_node_groups = {
default = {
instance_types = ["m5.large"]
min_size = 2
max_size = 5
desired_size = 3
# 컨트롤 플레인 업그레이드 후, 별도 변경으로 명시
ami_type = "AL2023_x86_64_STANDARD"
# release_version 또는 cluster_version 명시 가능
}
}
terraform apply 시 노드 그룹은 기본 업그레이드 전략에 따라 롤링 교체됩니다. PDB가 부적절하게 설정되어 있다면 노드 드레인이 멈추므로, 사전에 워크로드별 PDB를 점검합니다.
kubectl get pdb -A
애드온 업그레이드
컨트롤 플레인 마이너 버전이 올라가면 호환되는 애드온 버전 범위도 함께 이동합니다. 애드온 버전을 명시적으로 핀(pin)해 둔 상태라면 같이 끌어올려야 합니다.
호환 가능한 최신 버전은 다음 명령으로 조회할 수 있습니다.
aws eks describe-addon-versions \
--kubernetes-version 1.31 \
--addon-name coredns \
--query 'addons[].addonVersions[].addonVersion' \
--output text | tr '\t' '\n' | sort -V | tail -n 5
블록을 다음과 같이 손봐줍니다.
cluster_addons = {
coredns = {
addon_version = "v1.11.3-eksbuild.2"
resolve_conflicts_on_update = "PRESERVE"
}
kube-proxy = {
addon_version = "v1.31.2-eksbuild.3"
}
vpc-cni = {
addon_version = "v1.19.1-eksbuild.1"
}
aws-ebs-csi-driver = {
addon_version = "v1.37.0-eksbuild.1"
}
}
resolve_conflicts_on_update = "PRESERVE"는 운영 클러스터의 기본값
사용자가 직접 수정한 컨피그(예: CoreDNS Corefile 커스텀 항목, VPC CNI 환경변수)를 EKS가 덮어쓰지 않도록 막아줍니다. OVERWRITE는 새 클러스터를 베이스라인으로 되돌릴 때만 의도적으로 선택합니다.
검증
업그레이드 후에는 단순히 terraform apply가 성공했는지를 넘어, 클러스터가 실제로 정상 동작하는지를 확인해야 합니다.
# 컨트롤 플레인/노드 버전 일관성
kubectl version --short
kubectl get nodes -o wide
# kube-system 컴포넌트가 새 버전 이미지로 떠 있는지
kubectl -n kube-system get pods -o wide
kubectl -n kube-system get ds aws-node -o jsonpath='{.spec.template.spec.containers[0].image}'
# 워크로드가 모두 Running인지
kubectl get pods -A --field-selector=status.phase!=Running
kubectl get nodes에서 노드 버전이 컨트롤 플레인과 일치하는지, kube-system의 핵심 파드 이미지 태그가 새 버전을 가리키는지 두 가지를 반드시 확인합니다. 다음으로 애플리케이션 측에서 다음을 점검합니다.
- HPA가 정상적으로 메트릭을 받아오는지 (
kubectl get hpa -A) - 인그레스/로드 밸런서가 새 노드를 타겟으로 인식하는지
- 모니터링 대시보드(Prometheus/Grafana, CloudWatch)에서 에러율·지연시간 회귀가 없는지
운영 관점에서의 팁
실제로 운영 클러스터를 끌어올릴 때 chunk 단위로 PR을 쪼개는 편이 안전합니다.
- PR #1, 사전 점검:
kubent,pluto, EKS 업그레이드 인사이트 결과를 PR 설명에 첨부합니다. 이 PR은 코드 변경이 아니더라도 의사결정 기록으로 남깁니다. - PR #2, 컨트롤 플레인 업그레이드:
cluster_version만 한 단계 변경합니다. 적용 후 컨트롤 플레인 안정화를 확인합니다. - PR #3, 노드 그룹 업그레이드:
ami_type/release_version를 명시적으로 끌어올리고 롤링합니다. - PR #4, 애드온 업그레이드: 애드온 버전을 새 마이너에 맞게 핀합니다.
이렇게 나누면 어느 단계에서 회귀가 발생했는지를 곧바로 분리 식별할 수 있고, 롤백 시에도 되돌려야 할 범위가 작아집니다. 또한 멀티 클러스터(스테이징 → 프로덕션) 전략과도 자연스럽게 결합됩니다. 동일한 PR 시퀀스를 스테이징 → 프로덕션 순으로 흘려 보내면 됩니다.
"컨트롤 플레인 + 노드 + 애드온"을 한 PR에 묶지 마세요. 각 단계는 적용 시간과 회귀 패턴이 모두 다릅니다. 단계를 분리해 두면 회귀가 났을 때 어느 변경이 원인인지를 즉시 가리킬 수 있고, 롤백 범위도 한 단계로 좁혀집니다. 이 분리가 IaC 위에서 업그레이드를 운영할 때 얻는 가장 큰 실용적 이득입니다.
마치며
이번 7주차에서는 EKS 클러스터 업그레이드를 두 가지 관점에서 살펴봤습니다. Part 1에서는 워크샵 시나리오를 따라가며 컨트롤 플레인·노드 그룹·애드온을 단계적으로 끌어올리는 정공법을 익혔고, Part 2에서는 같은 절차를 terraform-aws-eks-blueprints 패턴 위에서 코드 변경으로 재현했습니다.
핵심은 결국 "클러스터 업그레이드는 한 번의 큰 이벤트가 아니라, 컨트롤 플레인 → 노드 → 애드온의 작은 단계가 모인 시퀀스"라는 점입니다. 각 단계를 PR로 분리하고, 사전 점검과 사후 검증을 명시적인 체크리스트로 박아두면 IaC의 강점인 되돌릴 수 있는 변경으로 운영할 수 있습니다. 이번 스터디에서 다룬 패턴이 실제 운영 클러스터의 마이너 버전 업그레이드를 계획할 때 좋은 출발점이 되기를 바랍니다.