Published on

Terraform로 EKS 업그레이드 후 aws-auth 꼬임으로 노드 Join 실패 해결

Authors

서론

Terraform로 EKS를 운영하다 보면, 클러스터 버전 업그레이드(또는 모듈 업그레이드) 이후 갑자기 노드가 Ready로 올라오지 않고 NodeGroup이 계속 NotReady/Unhealthy로 남는 상황을 만날 수 있습니다. 이때 원인이 네트워크나 AMI가 아니라 kube-system/aws-auth ConfigMap 꼬임인 경우가 매우 흔합니다.

특히 다음 조건이 겹치면 재현 확률이 높습니다.

  • Terraform EKS 모듈 버전 변경과 함께 EKS 버전을 업그레이드
  • Managed Node Group/자체 ASG 혼재
  • IRSA, access entry, aws-auth 관리 방식이 과거/현재가 섞임
  • aws-auth를 Terraform이 관리하지만, 중간에 수동 수정이 들어감

이 글에서는 “왜 Join이 실패하는지”를 로그/이벤트로 빠르게 확인하고, 꼬인 aws-auth안전하게 복구한 뒤, Terraform에서 재발 방지까지 하는 실전 절차를 다룹니다.

증상: 노드는 뜨는데 클러스터에 안 붙는다

대표적으로 아래 현상이 함께 나타납니다.

  • EC2 인스턴스(노드)는 정상 기동, EKS 콘솔에서도 NodeGroup 인스턴스는 생성됨
  • Kubernetes에서는 노드가 보이지 않거나, 잠깐 보였다가 사라짐
  • NodeGroup 상태가 CREATE_FAILED 또는 NodeCreationFailure

확인 포인트:

kubectl get nodes
kubectl -n kube-system get cm aws-auth -o yaml
kubectl get events -A --sort-by=.metadata.creationTimestamp | tail -n 50

노드 Join 실패는 결국 “노드가 API 서버에 인증/인가를 못 받는다”로 귀결되는 경우가 많고, 그 핵심이 aws-authmapRoles/mapUsers 매핑입니다.

원인: aws-auth ConfigMap이 왜 꼬이나?

EKS에서 워커 노드(kubelet)는 노드 IAM Role을 통해 AWS IAM 인증을 받고, Kubernetes RBAC으로 매핑되어야 노드로 승인됩니다. 이 매핑을 전통적으로 담당하던 것이 kube-system/aws-auth ConfigMap입니다.

Terraform 업그레이드 이후 꼬임이 발생하는 대표 패턴은 다음과 같습니다.

1) mapRoles에서 노드 Role이 누락/덮어쓰기

Terraform이 aws-auth를 “전체 리소스”로 관리하는데, 중간에 다른 경로(예: eksctl, 콘솔, 수동 kubectl edit)로 수정되면 다음 apply에서 덮어쓰기가 발생합니다.

  • 기존 노드 Role 매핑이 사라짐
  • 새로운 노드그룹 Role이 추가되지 않음
  • system:bootstrappers, system:nodes 그룹이 빠짐

2) rolearn 문자열이 미세하게 바뀜(리네이밍/재생성)

모듈 업그레이드로 IAM Role 이름/경로가 바뀌거나, Launch Template/NodeGroup 재생성 과정에서 Role을 교체하면 aws-authrolearn이 이전 값을 가리키게 됩니다.

3) EKS Access Entry(새 권한 모델)와 혼용

최근 EKS는 Access Entry 기반 권한 부여가 확장되었지만, 노드 Join은 여전히 aws-auth에 의존하는 구성들이 많습니다(특히 기존 클러스터). 팀 내에서 “이제 aws-auth 안 써도 된다”는 오해로 관리가 느슨해지면, 업그레이드 타이밍에 문제가 터집니다.

진단: 진짜 aws-auth 문제인지 빠르게 판별하기

1) NodeGroup 이벤트/상태 확인

aws eks describe-nodegroup \
  --cluster-name <CLUSTER> \
  --nodegroup-name <NG> \
  --query 'nodegroup.health.issues'

Unauthorized/AccessDenied류가 보이거나, 노드가 계속 교체되는데 Join이 안 되면 aws-auth 가능성이 큽니다.

2) kubelet 부트스트랩 로그(EC2) 확인

노드 인스턴스에서(SSM 또는 SSH):

sudo journalctl -u kubelet -n 200 --no-pager
sudo cat /var/log/cloud-init-output.log | tail -n 200

다음 유형의 메시지가 힌트가 됩니다.

  • API 서버 연결은 되는데 인증/인가 실패
  • node "ip-..." is forbidden

3) aws-auth에 노드 Role이 있는지 확인

kubectl -n kube-system get cm aws-auth -o jsonpath='{.data.mapRoles}'

노드 Role이 있어야 하는 최소 형태는 대개 아래처럼 생깁니다.

  • system:bootstrappers
  • system:nodes
  • username 패턴: system:node:{{EC2PrivateDNSName}}

해결: aws-auth를 올바른 상태로 복구하기

1) 현재 aws-auth 백업

kubectl -n kube-system get cm aws-auth -o yaml > aws-auth.backup.yaml

2) 노드 Role ARN 식별

Managed Node Group의 노드 Role:

aws eks describe-nodegroup \
  --cluster-name <CLUSTER> \
  --nodegroup-name <NG> \
  --query 'nodegroup.nodeRole' \
  --output text

여러 NodeGroup이면 각각 확인합니다.

3) aws-auth에 mapRoles 추가/수정

아래는 가장 흔한 정상 예시입니다. rolearn만 본인 값으로 바꿉니다.

apiVersion: v1
kind: ConfigMap
metadata:
  name: aws-auth
  namespace: kube-system
data:
  mapRoles: |
    - rolearn: arn:aws:iam::<ACCOUNT_ID>:role/<NODE_INSTANCE_ROLE>
      username: system:node:{{EC2PrivateDNSName}}
      groups:
        - system:bootstrappers
        - system:nodes

적용:

kubectl apply -f aws-auth.fixed.yaml

여기서 중요한 점:

  • mapRoles는 YAML “문자열 블록” 안에 또 YAML 리스트가 들어가는 구조라 인덴트가 조금만 틀려도 깨집니다.
  • 여러 Role을 넣을 경우 - rolearn: 항목을 같은 레벨로 나열해야 합니다.

4) 노드 재기동(또는 NodeGroup 롤링)

aws-auth를 고쳐도 이미 실패 상태인 노드가 계속 재시도하다가 꼬일 수 있어, 다음 중 하나를 권장합니다.

  • NodeGroup 인스턴스 수를 0으로 내렸다가 다시 올리기(가능하면)
  • ASG 인스턴스 교체(terminate)
  • Managed Node Group 업데이트로 롤링

정상화 확인:

kubectl get nodes -o wide
kubectl -n kube-system get pods -o wide

이후 CoreDNS 등 필수 파드가 연쇄적으로 문제를 일으키는 경우도 있는데, 그건 별도 트러블슈팅이 필요합니다. (참고: AWS EKS CoreDNS CrashLoopBackOff와 DNS 타임아웃 해결)

Terraform에서 재발 방지: aws-auth를 “한 군데서만” 관리하기

가장 위험한 운영 패턴은 aws-auth를 Terraform이 관리하면서도, 운영자가 급할 때 kubectl edit로 고치는 것입니다. 업그레이드/배포 타이밍에 누군가의 변경이 덮어써지며 장애가 재발합니다.

선택지 A) Terraform로 aws-auth를 일관되게 관리

terraform-aws-modules/eks를 쓴다면(버전에 따라 다르지만) 보통 아래처럼 aws_auth_roles 또는 manage_aws_auth_configmap 계열 옵션을 사용합니다.

예시(개념 코드):

module "eks" {
  source  = "terraform-aws-modules/eks/aws"
  version = "~> 20.0"

  cluster_name    = var.cluster_name
  cluster_version = var.cluster_version

  manage_aws_auth_configmap = true

  aws_auth_roles = [
    {
      rolearn  = aws_iam_role.node_role.arn
      username = "system:node:{{EC2PrivateDNSName}}"
      groups   = ["system:bootstrappers", "system:nodes"]
    }
  ]
}

핵심 원칙:

  • 노드 Role(모든 NodeGroup/ASG)의 ARN이 항상 aws_auth_roles에 포함되게 만들기
  • 수동 수정 금지(필요하면 Terraform 변수/코드로 반영)

선택지 B) Terraform은 aws-auth를 건드리지 않고, 별도 GitOps로 관리

클러스터 내부 리소스는 Argo CD/Flux 같은 GitOps로 관리하고, Terraform은 인프라까지만 책임지는 방식도 좋습니다. 단, 이 경우에도 “단일 소스 오브 트루스” 원칙을 지켜야 합니다.

업그레이드 시 체크리스트(장애 예방)

1) 업그레이드 전 aws-auth 스냅샷

kubectl -n kube-system get cm aws-auth -o yaml > aws-auth.pre-upgrade.yaml

2) NodeGroup별 nodeRole 목록화

aws eks list-nodegroups --cluster-name <CLUSTER> --output text
# 각 노드그룹에 대해
aws eks describe-nodegroup --cluster-name <CLUSTER> --nodegroup-name <NG> --query 'nodegroup.nodeRole' --output text

3) 업그레이드 직후 “노드 Join”을 가장 먼저 확인

kubectl get nodes
kubectl -n kube-system get pods

노드가 안 붙으면 애플리케이션 증상(CrashLoopBackOff, ImagePullBackOff 등)만 보고 헤매기 쉽습니다. 예를 들어 노드가 붙지 않아 CNI/코어 파드가 불안정하면, 이미지 풀도 연쇄적으로 실패할 수 있습니다. 이미지 인증/IRSA 이슈와 구분이 필요합니다. (참고: Kubernetes ImagePullBackOff 401 - ECR·IRSA·imagePullSecrets)

실전 팁: aws-auth YAML 사고를 줄이는 방법

  • kubectl edit 대신 파일 기반으로 kubectl apply를 사용해 변경 이력을 남기기
  • mapRoles 블록을 수정할 때는 YAML lint 또는 최소한 kubectl apply --dry-run=client -f ...로 문법 검증
kubectl apply --dry-run=client -f aws-auth.fixed.yaml
  • Terraform을 쓴다면 plan에서 aws-auth 변경(diff)을 항상 리뷰하고, 노드 Role 항목이 사라지는지 체크

결론

Terraform로 EKS 업그레이드 후 노드 Join이 실패한다면, 네트워크/CNI/AMI를 의심하기 전에 kube-system/aws-auth부터 확인하는 것이 시간 대비 효율이 가장 좋습니다. 대부분의 케이스는 노드 IAM Role 매핑 누락 또는 덮어쓰기로 설명되며, mapRoles에 올바른 rolearn, username, system:bootstrappers/system:nodes 그룹을 복구하면 빠르게 정상화됩니다.

마지막으로, aws-auth는 “누가 최종적으로 관리하는지”를 팀 차원에서 합의하고(Terraform 또는 GitOps), 업그레이드 전후로 스냅샷/검증 절차를 넣으면 같은 장애를 반복하지 않을 수 있습니다.