
IRSA(IAM Roles for Service Accounts)를 몇 년 넘게 굴려온 팀이라면 대체로 익숙해져 있다. 그런데 새 클러스터를 셋업하거나 멀티 리전으로 확장할 때마다 OIDC provider 등록하고, trust policy에 sub 조건 하드코딩하고, 클러스터 재생성할 때마다 IAM role 신뢰관계 다시 손보는 게 슬슬 지겨워진다.
EKS Pod Identity는 2023년 말 GA된 이후 계속 개선돼 왔고, 2026년 6월쯤 우리 팀도 신규 워크로드부터 하나씩 옮기기 시작했다. 아직 IRSA가 deprecated된 건 아니고 종료 일정도 없지만, 새 클러스터 만들 때마다 OIDC 신뢰 다시 세팅하는 걸 더는 하고 싶지 않았다. 이 글은 그 마이그레이션 과정에서 우리 팀이 실제로 밟은 단계와, 중간에 부딪혔던 것들을 정리한 가이드다.
Pod Identity가 IRSA와 뭐가 다른가
한 줄로 요약하면 "OIDC를 안 쓴다"이다. IRSA는 클러스터의 OIDC issuer를 IAM에 등록해두고, 파드 안에서 프로젝티드 서비스 어카운트 토큰(JWT)을 STS AssumeRoleWithWebIdentity에 넘겨 임시 자격증명을 받아오는 구조다. 그래서 IAM role trust policy에 클러스터별 OIDC provider ARN과 sub(namespace/serviceaccount) 조건을 박아둔다. 클러스터를 다시 만들면 OIDC issuer도 바뀌니 trust policy를 갈아엎어야 한다.
Pod Identity는 노드에 붙는 eks-pod-identity-agent DaemonSet이 통신 로컬 엔드포인트(http://169.254.170.23)를 열어두고, EKS 컨트롤 플레인이 서비스 어카운트-IAM role 연결을 관리한다. 파드 관점에서는 SDK가 컨테이너 자격증명 엔드포인트 환경변수(AWS_CONTAINER_CREDENTIALS_FULL_URI, AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE)를 읽고 그 엔드포인트에 요청하면 자격증명이 떨어진다. IAM role의 trust policy는 pods.eks.amazonaws.com 하나만 신뢰하면 끝이고, 어느 클러스터 어느 네임스페이스에 붙일지는 EKS API가 별도로 관리한다.
이 구조 덕분에 IAM role이 클러스터 간에 이식된다. 스테이징에서 잘 도는 role을 프로덕션에 그대로 갖다 붙일 수 있다. IRSA에서는 role trust policy에 클러스터별 OIDC ARN이 박혀 있어서 role을 클론해야 했는데, 그 지긋지긋한 걸 안 해도 된다.
시작 전 체크리스트
옮기기 전에 몇 가지 확인해두면 뒤에서 안 헤맨다.
첫째, EKS 버전. Pod Identity는 EKS 1.24 이상에서 지원한다. 우리는 1.29에서 시작했다.
둘째, Fargate 워크로드. Fargate 프로필로 도는 파드는 Pod Identity를 못 쓴다. 여전히 IRSA를 써야 한다. 우리는 이걸 뒤늦게 알아서, 이관 계획 세울 때 Fargate 워크로드 목록부터 뽑아 빼놓았다.
셋째, SDK 버전. 오래된 SDK는 컨테이너 자격증명 엔드포인트를 안 읽는다. 대략적인 컷라인은 다음과 같다.
- AWS SDK for Go v1.47.11 / v2 1.24.0 이상
- AWS SDK for Python (boto3) 1.34.41 이상
- AWS SDK for Java v1 1.12.746 / v2 2.25.63 이상
- AWS SDK for JavaScript v3 3.458.0 이상
컨테이너 이미지에 들어간 SDK 버전이 이보다 낮으면 자격증명을 못 받는다. 우리는 이관 시작하기 전에 각 서비스가 쓰는 base 이미지와 SDK 버전을 한 번 훑었다. 오래된 파이썬 서비스 두 개가 boto3 1.28 쓰고 있어서 그건 먼저 업그레이드부터 해야 했다.
1단계: 에이전트 설치
EKS Pod Identity Agent를 애드온으로 설치한다. eksctl 쓰면 한 줄이다.
eksctl create addon \
--name eks-pod-identity-agent \
--cluster my-cluster \
--region ap-northeast-2
콘솔이나 Terraform으로도 된다. 우리는 Terraform을 쓰기 때문에 aws_eks_addon 리소스로 관리한다.
resource "aws_eks_addon" "pod_identity_agent" {
cluster_name = aws_eks_cluster.this.name
addon_name = "eks-pod-identity-agent"
addon_version = "v1.3.4-eksbuild.1"
resolve_conflicts_on_create = "OVERWRITE"
resolve_conflicts_on_update = "PRESERVE"
}
설치 후 kube-system 네임스페이스에 DaemonSet이 뜬다. 노드마다 하나씩. 확인.
kubectl -n kube-system get ds eks-pod-identity-agent
주의할 점 하나. 에이전트 파드는 hostNetwork를 쓰고 특권이 좀 있는데, 이걸 막는 PSA(Pod Security Admission) 정책이 kube-system에 걸려 있으면 안 뜬다. 우리 팀은 kube-system은 원래 예외로 두고 있어서 문제가 없었지만, 만약 restricted 정책을 전역으로 걸어놨다면 예외 처리를 해줘야 한다.
2단계: IAM Role trust policy 수정
기존 IRSA용 role은 trust policy가 아래처럼 생겼을 것이다.
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:oidc-provider/oidc.eks.ap-northeast-2.amazonaws.com/id/ABC..."
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"oidc.eks.ap-northeast-2.amazonaws.com/id/ABC...:sub": "system:serviceaccount:app-ns:my-app"
}
}
}]
}
Pod Identity에서는 이걸 이렇게 바꾼다.
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"Service": "pods.eks.amazonaws.com"
},
"Action": [
"sts:AssumeRole",
"sts:TagSession"
]
}]
}
훨씬 깔끔하다. 클러스터 정보도, sub 조건도 없다. 대신 어느 파드가 이 role을 assume할 수 있는지는 EKS의 Pod Identity Association이 결정한다.
여기서 중요한 점: trust policy에 IRSA용 조건과 Pod Identity용 조건을 둘 다 넣어둘 수 있다. 그러면 마이그레이션 기간 동안 같은 role을 IRSA로도, Pod Identity로도 쓸 수 있다. 우리는 이 방식으로 하나씩 옮겼다.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:oidc-provider/oidc.eks.ap-northeast-2.amazonaws.com/id/ABC..."
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"oidc.eks.ap-northeast-2.amazonaws.com/id/ABC...:sub": "system:serviceaccount:app-ns:my-app"
}
}
},
{
"Effect": "Allow",
"Principal": {"Service": "pods.eks.amazonaws.com"},
"Action": ["sts:AssumeRole", "sts:TagSession"]
}
]
}
3단계: Pod Identity Association 생성
서비스 어카운트와 IAM role을 연결한다.
aws eks create-pod-identity-association \
--cluster-name my-cluster \
--namespace app-ns \
--service-account my-app \
--role-arn arn:aws:iam::123456789012:role/my-app-role \
--region ap-northeast-2
Terraform 리소스도 있다.
resource "aws_eks_pod_identity_association" "my_app" {
cluster_name = aws_eks_cluster.this.name
namespace = "app-ns"
service_account = "my-app"
role_arn = aws_iam_role.my_app.arn
}
4단계: 서비스 어카운트에서 IRSA 애노테이션 제거
이 부분이 가장 헷갈렸다. 같은 서비스 어카운트에 IRSA 애노테이션(eks.amazonaws.com/role-arn)이 남아 있으면서 Pod Identity Association도 만들어져 있으면 어떻게 될까.
문서상으로는 Pod Identity가 우선한다고 돼 있다. 하지만 실제로 우리는 파드 로그에서 자격증명 관련 이상한 워닝이 뜨는 걸 몇 번 봤다. 원인을 추적해보니 SDK 초기화 시점에 환경변수 두 종류(AWS_ROLE_ARN, AWS_WEB_IDENTITY_TOKEN_FILE vs AWS_CONTAINER_CREDENTIALS_FULL_URI)가 다 세팅돼 있으면 SDK 버전에 따라 어느 걸 먼저 시도할지가 애매해진다.
정석은 이렇다: Pod Identity로 완전히 이관됐다 싶으면 서비스 어카운트에서 eks.amazonaws.com/role-arn 애노테이션을 제거한다. 그리고 파드를 재시작한다. 이렇게 하면 IRSA 관련 환경변수 주입이 안 되고, 순수하게 Pod Identity만 쓰게 된다.
Helm 차트로 관리 중이라면 values에서 이 애노테이션 넣는 부분을 지우면 된다. Kustomize 오버레이라면 오버레이 쪽에서 지워준다.
5단계: 검증
파드에 들어가서 어느 자격증명 소스를 쓰는지 직접 확인한다.
kubectl -n app-ns exec -it my-app-xxx -- env | grep AWS_
Pod Identity가 잘 붙었으면 다음이 보인다.
AWS_CONTAINER_CREDENTIALS_FULL_URI=http://169.254.170.23/v1/credentials
AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE=/var/run/secrets/pods.eks.amazonaws.com/serviceaccount/eks-pod-identity-token
IRSA 관련 환경변수가 함께 있으면 마이그레이션이 아직 완전히 안 끝난 것이다.
STS로 실제 identity를 확인.
kubectl -n app-ns exec -it my-app-xxx -- aws sts get-caller-identity
Arn 필드에 원래 role이 나오면 성공. session name이 클러스터-네임스페이스-서비스어카운트 조합으로 예쁘게 찍힌다.
롤백 시나리오
이관 중에 문제가 생기면 Pod Identity Association만 삭제하면 된다. 서비스 어카운트에 원래 있던 IRSA 애노테이션이 아직 있다면(4단계를 안 지웠다면) 파드 재시작만으로 IRSA로 돌아간다. 4단계까지 진행한 상태라면 애노테이션을 다시 붙이고 재시작.
이런 안전망이 있으니 마이그레이션이 확실히 편해졌다. 우리는 서비스 하나씩 근무일 오전에 옮기고, 30분쯤 지켜본 뒤 다음 서비스로 넘어갔다.
우리 팀이 겪은 잔이슈
envoy sidecar가 자격증명을 못 받은 사건. Istio sidecar가 파드에 붙어 있는 경우, 앱 컨테이너가 뜨기 전에 sidecar가 먼저 뜨려고 하다가 Pod Identity Agent와의 통신이 안 되는 경우가 있었다. holdApplicationUntilProxyStarts를 true로 두면 오히려 상황이 이상해지는 케이스도 봤다. Istio 1.22+ 에서는 native sidecar container(initContainers의 restartPolicy: Always)를 쓰면 이 순서 문제가 사라진다. 우리는 결국 native sidecar로 전환하면서 해결했다.
커스텀 노드 AMI에서 에이전트가 안 뜬 케이스. 우리 팀은 Bottlerocket을 쓰는데 처음엔 문제가 없었다. 다만 다른 팀에서 EKS Optimized AMI를 커스텀해 쓰던 워크로드에서 에이전트가 crashloop 도는 걸 봤다. iptables 규칙 하나가 로컬 링크 주소(169.254.170.23)를 차단하고 있었다. 그 규칙을 걷어내니 정상 동작.
AWS SDK for Go v1의 세션 캐싱 이슈. 오래된 앱이 SDK for Go v1을 쓰면서 세션을 프로세스 시작 시 한 번만 만들고 재사용하는 코드가 있었다. IRSA에서는 그래도 그럭저럭 굴러갔는데(토큰 파일이 바뀌면 재로드), Pod Identity로 바꾸니 자격증명이 만료돼도 리프레시가 안 됐다. 원인은 SDK v1의 오래된 버전에서 컨테이너 자격증명 provider의 리프레시 로직이 살짝 다르게 동작하는 것. SDK v2로 업그레이드하는 게 정답이다.
다 옮기면 뭐가 남나
OIDC provider 정리. 클러스터에서 IRSA를 완전히 없앴다면 OIDC provider도 지울 수 있다. 다만 aws-load-balancer-controller, external-dns, cluster-autoscaler 같은 컨트롤러들이 아직 IRSA를 쓰고 있을 확률이 높다. 이런 애드온들도 Pod Identity를 지원하기 시작했으니 하나씩 옮겨보고, 다 정리된 다음에 OIDC provider를 지운다. 우리는 아직 OIDC provider를 못 지웠다. cluster-autoscaler 대신 Karpenter로 옮기는 별개 작업이 걸려 있어서 그 다음에나 정리 가능할 듯.
Terraform 코드 정리. IRSA용 role 정의에 assume_role_policy가 data.aws_iam_policy_document.irsa_trust로 지저분하게 얽혀 있던 것들이 다 사라진다. data로 OIDC provider ARN 룩업하던 것도 없어진다. 우리 IaC 저장소에서 이 정리 커밋 한 번이 550줄 정도 삭제로 잡혔다. 시각적으로도 시원했다.
세션 태그로 ABAC. Pod Identity는 자동으로 세션 태그(eks-cluster-name, kubernetes-namespace, kubernetes-service-account)를 붙여준다. 이걸로 하나의 IAM role을 여러 클러스터/네임스페이스에서 공유하면서 실제 접근 권한은 태그 기반으로 나눌 수 있다. 예를 들어 S3 버킷 접근 시 ${aws:PrincipalTag/kubernetes-namespace} 조건으로 네임스페이스별 prefix만 허용하는 식. 다만 이 패턴은 오남용하기 쉬워서 우리 팀은 아직 신중하게 파일럿 중이다.
마무리
한 문장으로 말하면, 지금 당장 급하게 옮길 이유는 없지만 새 클러스터부터는 무조건 Pod Identity로 시작하는 게 이득이다. IRSA도 여전히 잘 굴러가고, 하이브리드로 공존시키는 게 어렵지 않으니 스트레스 없이 진행할 수 있다. 우리 팀은 지금 60% 정도 옮겼고, 나머지 40%(주로 Fargate 워크로드와 몇 개 애드온)는 상황을 좀 더 지켜보고 있다.
혹시 이관 중에 다른 삽질 케이스 겪으신 분 있으면 댓글로 공유해주시면 좋겠다. 특히 GitOps 도구(ArgoCD, Flux) 자체의 IRSA를 Pod Identity로 옮긴 경험담이 있다면 궁금하다.
태그: EKS, PodIdentity, IRSA, AWS, Kubernetes, DevOps
'IT > AWS' 카테고리의 다른 글
| EKS Auto Mode 실전 도입 가이드 (0) | 2026.08.29 |
|---|---|
| EKS Pod Identity로 IRSA 걷어내다 만난 것들 (0) | 2026.08.25 |
| ALB 뒤에서 502가 뚝뚝 떨어지던 날의 회고 (0) | 2026.08.10 |
| Karpenter WhenEmptyOrUnderutilized 켰다가 밤새운 이야기 (0) | 2026.08.04 |
| Cluster Autoscaler에서 Karpenter로, 우리는 정말 이득이었나 (0) | 2026.07.25 |