Published on

EKS IRSA로 Karpenter 권한 오류 해결하기

Authors

서버리스처럼 노드를 자동으로 늘려주는 Karpenter는 운영 난이도를 크게 낮춰주지만, 초기 셋업에서 가장 흔하게 부딪히는 문제가 IAM 권한 오류입니다. 특히 EKS에서는 Pod가 AWS API를 호출할 때 IRSA(IAM Roles for Service Accounts)를 제대로 구성하지 않으면 Karpenter 컨트롤러가 AccessDenied로 멈추거나, 노드는 만들어졌는데 조인하지 못하는 등 애매한 장애로 이어집니다.

이 글은 “Karpenter 권한 오류”를 IRSA 관점에서 빠르게 분해하고, 어떤 로그가 어떤 설정 누락을 의미하는지, 그리고 최소한의 변경으로 정상화하는 절차를 코드 중심으로 정리합니다.

운영 중 네트워크/Ingress 타임아웃 이슈와 함께 발생하면 원인 파악이 더 어려워지는데, 그런 경우에는 별도로 Kubernetes CrashLoopBackOff 12가지 원인·해결 같은 체크리스트도 같이 참고하면 진단 속도가 올라갑니다.

문제 증상: Karpenter 권한 오류의 전형적인 형태

Karpenter 권한 오류는 크게 두 층위에서 발생합니다.

1) 컨트롤러 Pod가 AWS API를 못 호출함

예를 들어 Karpenter 컨트롤러가 EC2, IAM, SSM, Pricing 같은 API를 호출해야 하는데, IRSA가 없거나 잘못되면 다음과 같은 로그가 나옵니다.

  • AccessDeniedException / UnauthorizedOperation
  • NoCredentialProviders 또는 failed to refresh cached credentials
  • sts:AssumeRoleWithWebIdentity 실패

이 경우는 거의 항상 “ServiceAccount 어노테이션” 또는 “IAM Role trust policy의 OIDC 조건”이 잘못된 케이스입니다.

2) 노드는 떴는데 클러스터에 조인하지 못함

컨트롤러 권한은 맞는데, 노드 인스턴스 프로파일(Instance Profile)이나 aws-auth 매핑이 불완전하면 다음 문제가 생깁니다.

  • EC2 인스턴스는 생성되지만 NotReady로 남음
  • kubelet이 API 서버 인증 실패

이 글의 초점은 IRSA로 컨트롤러 권한 오류를 해결하는 것이지만, 마지막에 노드 조인 단계에서 같이 점검할 포인트도 정리합니다.

IRSA 동작 원리(필수 개념만)

IRSA는 “Kubernetes ServiceAccount”와 “AWS IAM Role”을 OIDC로 연결합니다.

  • EKS는 OIDC Provider를 제공
  • Pod는 ServiceAccount 토큰을 마운트
  • AWS SDK는 해당 토큰으로 sts:AssumeRoleWithWebIdentity 호출
  • 성공하면 임시 자격 증명을 발급받아 AWS API 호출

즉, IRSA에서 핵심은 다음 두 가지가 정확히 맞아야 합니다.

  1. ServiceAccount에 eks.amazonaws.com/role-arn 어노테이션이 있어야 함
  2. IAM Role의 trust policy에서 sub 조건이 system:serviceaccount:네임스페이스:서비스어카운트명과 일치해야 함

1단계: EKS OIDC Provider 연결 확인

먼저 클러스터에 OIDC Provider가 연결되어 있어야 합니다.

eksctl을 쓴다면 다음으로 확인 및 생성이 가능합니다.

eksctl utils associate-iam-oidc-provider \
  --cluster my-eks \
  --region ap-northeast-2 \
  --approve

OIDC issuer URL은 아래로 확인합니다.

aws eks describe-cluster \
  --name my-eks \
  --region ap-northeast-2 \
  --query "cluster.identity.oidc.issuer" \
  --output text

여기서 나온 URL이 IAM의 OIDC Provider 목록에 존재해야 합니다.

2단계: Karpenter ServiceAccount에 Role ARN 어노테이션

Karpenter 설치 방식(Helm, YAML)에 따라 ServiceAccount 이름이 다를 수 있습니다. 대표적으로 karpenter 네임스페이스의 karpenter ServiceAccount를 사용합니다.

다음은 ServiceAccount 예시입니다.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: karpenter
  namespace: karpenter
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/KarpenterControllerRole-my-eks

이미 설치되어 있다면 패치로도 가능합니다.

kubectl -n karpenter annotate serviceaccount karpenter \
  eks.amazonaws.com/role-arn=arn:aws:iam::123456789012:role/KarpenterControllerRole-my-eks \
  --overwrite

여기서 자주 하는 실수는 “네임스페이스가 다름” 또는 “ServiceAccount 이름이 다름”입니다. Helm values에서 serviceAccount.name이 바뀌어 있으면 trust policy의 sub도 같이 바뀌어야 합니다.

3단계: IAM Role trust policy(가장 많이 틀리는 부분)

Karpenter 컨트롤러용 IAM Role에는 반드시 sts:AssumeRoleWithWebIdentity가 허용되어야 하고, 조건에 sub가 정확히 들어가야 합니다.

다음 trust policy 예시는 핵심만 담은 형태입니다. 본문에서 부등호 기호를 피하기 위해 JSON은 코드 블록으로 제공합니다.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::123456789012:oidc-provider/oidc.eks.ap-northeast-2.amazonaws.com/id/EXAMPLED539D4633E53DE1B716D3041E"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "oidc.eks.ap-northeast-2.amazonaws.com/id/EXAMPLED539D4633E53DE1B716D3041E:aud": "sts.amazonaws.com",
          "oidc.eks.ap-northeast-2.amazonaws.com/id/EXAMPLED539D4633E53DE1B716D3041E:sub": "system:serviceaccount:karpenter:karpenter"
        }
      }
    }
  ]
}

체크 포인트는 다음과 같습니다.

  • Federated ARN의 OIDC Provider 경로가 클러스터 issuer와 정확히 일치
  • audsts.amazonaws.com
  • subsystem:serviceaccount:karpenter:karpenter

sub가 한 글자라도 다르면 AccessDenied로 실패합니다.

4단계: Karpenter Controller Policy(권한) 구성

trust policy가 “누가 역할을 Assume할 수 있는지”라면, permission policy는 “Assume한 뒤 무엇을 할 수 있는지”입니다.

Karpenter는 대략 다음 권한이 필요합니다.

  • EC2: 런치 템플릿/인스턴스 생성, 태그, 서브넷/보안그룹 조회
  • IAM: 인스턴스 프로파일 조회/패스
  • SSM: AMI 파라미터 조회(설정에 따라)
  • EKS: 클러스터 정보 조회(설정에 따라)

아래는 예시 스니펫입니다. 실제 운영에서는 Karpenter 공식 문서의 최신 정책을 기반으로 클러스터 태그 조건을 강제하는 것을 권장합니다.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "EC2ReadAndRun",
      "Effect": "Allow",
      "Action": [
        "ec2:DescribeInstances",
        "ec2:DescribeInstanceTypes",
        "ec2:DescribeSubnets",
        "ec2:DescribeSecurityGroups",
        "ec2:DescribeAvailabilityZones",
        "ec2:DescribeImages",
        "ec2:RunInstances",
        "ec2:CreateLaunchTemplate",
        "ec2:CreateFleet",
        "ec2:CreateTags",
        "ec2:TerminateInstances"
      ],
      "Resource": "*"
    },
    {
      "Sid": "AllowPassNodeRole",
      "Effect": "Allow",
      "Action": "iam:PassRole",
      "Resource": "arn:aws:iam::123456789012:role/KarpenterNodeRole-my-eks"
    }
  ]
}

여기서 iam:PassRole이 빠지면 컨트롤러는 인스턴스를 만들면서 노드 역할을 연결하지 못해 실패합니다.

5단계: 실제로 IRSA가 적용됐는지 검증

설정이 맞는지 확인할 때는 “Pod 내부에서 AWS 호출이 되는지”가 가장 확실합니다.

ServiceAccount 어노테이션 확인

kubectl -n karpenter get sa karpenter -o yaml

출력에서 eks.amazonaws.com/role-arn이 있는지 확인합니다.

Pod 환경에서 STS 호출 확인

Karpenter 컨트롤러 Pod에 들어가 AWS CLI가 없다면 디버그 Pod를 띄우는 방식이 편합니다.

kubectl -n karpenter run irsa-debug \
  --image=amazon/aws-cli:2.15.0 \
  --serviceaccount=karpenter \
  --command -- sleep 3600

그 다음 STS 호출로 현재 자격 증명이 무엇인지 확인합니다.

kubectl -n karpenter exec -it irsa-debug -- aws sts get-caller-identity

여기서 Arnassumed-role/KarpenterControllerRole 형태로 나오면 IRSA는 정상입니다. 만약 에러가 난다면 trust policy, OIDC Provider, ServiceAccount 어노테이션 중 하나가 틀린 것입니다.

6단계: 자주 발생하는 실수와 빠른 해결

실수 1) ServiceAccount 이름이 Helm 설정과 다름

Helm으로 설치할 때 serviceAccount.createserviceAccount.name을 커스터마이징하면, trust policy의 sub도 반드시 동일하게 바꿔야 합니다.

  • 기대 sub: system:serviceaccount:karpenter:karpenter
  • 실제 sub: system:serviceaccount:karpenter:karpenter-sa 같은 형태

해결은 둘 중 하나입니다.

  • trust policy의 sub를 실제 ServiceAccount로 수정
  • ServiceAccount 이름을 trust policy에 맞게 되돌림

실수 2) OIDC issuer가 다른 클러스터의 Provider를 가리킴

여러 클러스터를 운영하면 IAM에 OIDC Provider가 여러 개 생깁니다. Federated ARN의 Provider ID가 다른 클러스터 것이라면 100퍼센트 실패합니다.

해결은 describe-cluster로 issuer를 다시 확인하고, 정확한 Provider ARN으로 trust policy를 수정하는 것입니다.

실수 3) Karpenter Controller Role은 맞는데 권한 정책이 부족

AssumeRoleWithWebIdentity는 성공하지만, ec2:RunInstancesiam:PassRole에서 막히는 케이스입니다.

이때는 Karpenter 로그에 AccessDenied가 어떤 액션에서 났는지 찍히므로, 해당 액션을 정책에 추가하거나 리소스 범위를 조정해야 합니다.

실수 4) 노드가 안 붙는 문제를 IRSA로만 해결하려고 함

Karpenter 컨트롤러 IRSA가 정상이어도, 노드가 조인하려면 다음이 필요합니다.

  • 노드 IAM Role이 AmazonEKSWorkerNodePolicy, AmazonEKS_CNI_Policy, AmazonEC2ContainerRegistryReadOnly 등을 포함
  • aws-auth ConfigMap에 노드 Role 매핑(또는 Access Entry 방식)

노드가 생성되는데 NotReady로 남는다면 이 영역을 먼저 의심하세요.

7단계: Karpenter 리소스 측면에서 권한과 연결되는 설정

IRSA로 컨트롤러 권한을 해결했는데도 프로비저닝이 안 되면, Karpenter가 선택할 서브넷/보안그룹을 못 찾는 경우가 많습니다. 이때는 태그 기반 디스커버리가 핵심입니다.

대표적으로 다음 태그가 필요합니다.

  • 서브넷: karpenter.sh/discovery: my-eks
  • 보안그룹: karpenter.sh/discovery: my-eks

그리고 Karpenter의 NodeClass 혹은 EC2NodeClass에서 selector를 쓰는 경우, 권한은 맞아도 대상 리소스가 0개라서 스케줄링이 멈출 수 있습니다.

운영 팁: 장애를 줄이는 로그 관측 포인트

  • Karpenter 컨트롤러 로그에서 AssumeRoleWithWebIdentity 실패 여부
  • CloudTrail에서 AssumeRoleWithWebIdentity 이벤트와 실패 사유
  • EC2 이벤트에서 RunInstances 실패 사유

권한 문제는 네트워크 장애처럼 보이는 경우도 있습니다. 예를 들어 Pod DNS는 되는데 HTTPS만 실패하는 환경에서는 STS 호출이 타임아웃으로 보일 수 있어 혼동됩니다. 그런 경우 네트워크 측면 점검은 EKS Pod DNS는 되는데 HTTPS만 실패할 때 점검도 같이 확인하는 것이 좋습니다.

정리: IRSA로 Karpenter 권한 오류를 끝내는 체크리스트

아래 4가지만 정확히 맞추면 Karpenter 컨트롤러 권한 오류의 대부분은 해결됩니다.

  1. EKS OIDC Provider가 IAM에 연결되어 있음
  2. Karpenter ServiceAccount에 eks.amazonaws.com/role-arn 어노테이션이 있음
  3. IAM Role trust policy의 subsystem:serviceaccount:네임스페이스:서비스어카운트명과 정확히 일치
  4. Controller permission policy에 ec2:* 필요한 범위와 iam:PassRole이 포함

이후에도 노드가 조인하지 못한다면, 그것은 IRSA보다는 노드 Role 정책과 aws-auth 매핑 문제일 가능성이 큽니다.

권한 오류는 한 번 잡아두면 재발하지 않게 “정답 구성을 코드로 고정”하는 것이 가장 중요합니다. Terraform이나 CloudFormation으로 OIDC, Role, Policy, ServiceAccount 어노테이션까지 선언적으로 관리하면, Karpenter 도입 이후 운영 안정성이 확실히 올라갑니다.