- Published on
EKS IRSA로 Karpenter 권한 오류 해결하기
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버리스처럼 노드를 자동으로 늘려주는 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/UnauthorizedOperationNoCredentialProviders또는failed to refresh cached credentialssts: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에서 핵심은 다음 두 가지가 정확히 맞아야 합니다.
- ServiceAccount에
eks.amazonaws.com/role-arn어노테이션이 있어야 함 - 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"
}
}
}
]
}
체크 포인트는 다음과 같습니다.
FederatedARN의 OIDC Provider 경로가 클러스터 issuer와 정확히 일치aud가sts.amazonaws.comsub가system: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
여기서 Arn이 assumed-role/KarpenterControllerRole 형태로 나오면 IRSA는 정상입니다. 만약 에러가 난다면 trust policy, OIDC Provider, ServiceAccount 어노테이션 중 하나가 틀린 것입니다.
6단계: 자주 발생하는 실수와 빠른 해결
실수 1) ServiceAccount 이름이 Helm 설정과 다름
Helm으로 설치할 때 serviceAccount.create나 serviceAccount.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:RunInstances나 iam:PassRole에서 막히는 케이스입니다.
이때는 Karpenter 로그에 AccessDenied가 어떤 액션에서 났는지 찍히므로, 해당 액션을 정책에 추가하거나 리소스 범위를 조정해야 합니다.
실수 4) 노드가 안 붙는 문제를 IRSA로만 해결하려고 함
Karpenter 컨트롤러 IRSA가 정상이어도, 노드가 조인하려면 다음이 필요합니다.
- 노드 IAM Role이
AmazonEKSWorkerNodePolicy,AmazonEKS_CNI_Policy,AmazonEC2ContainerRegistryReadOnly등을 포함 aws-authConfigMap에 노드 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 컨트롤러 권한 오류의 대부분은 해결됩니다.
- EKS OIDC Provider가 IAM에 연결되어 있음
- Karpenter ServiceAccount에
eks.amazonaws.com/role-arn어노테이션이 있음 - IAM Role trust policy의
sub가system:serviceaccount:네임스페이스:서비스어카운트명과 정확히 일치 - Controller permission policy에
ec2:*필요한 범위와iam:PassRole이 포함
이후에도 노드가 조인하지 못한다면, 그것은 IRSA보다는 노드 Role 정책과 aws-auth 매핑 문제일 가능성이 큽니다.
권한 오류는 한 번 잡아두면 재발하지 않게 “정답 구성을 코드로 고정”하는 것이 가장 중요합니다. Terraform이나 CloudFormation으로 OIDC, Role, Policy, ServiceAccount 어노테이션까지 선언적으로 관리하면, Karpenter 도입 이후 운영 안정성이 확실히 올라갑니다.