AWS

IRSA에서 EKS Pod Identity로 옮기는 법

gfrog 2026. 4. 27. 20:43
반응형

작년 KubeCon Salt Lake City 끝나고 팀에서 한참 얘기가 나왔던 게 EKS Pod Identity였다. 우리 팀은 그동안 IRSA(IAM Roles for Service Accounts)를 잘 쓰고 있었는데, 클러스터를 4개 운영하다 보니 OIDC provider를 클러스터마다 다 등록하고, 신뢰 정책에 sub 조건을 박아놓는 방식이 점점 귀찮아졌다. 멀티 클러스터 환경에서 같은 워크로드에 같은 권한을 주려면 클러스터마다 trust policy를 다르게 써야 했고, 새 클러스터를 띄울 때마다 이걸 반복했다.

그래서 최근 2주에 걸쳐 dev → staging 클러스터를 차례로 Pod Identity로 옮겼다. prod는 다음 주 작업 예정이다. 이 글은 그 작업을 정리한 가이드다. 이미 운영 중인 IRSA 환경을 비파괴적으로 옮기는 데 초점을 맞췄다.

무엇이 달라지나

IRSA는 ServiceAccount에 IAM role ARN을 annotation으로 박고, Pod이 토큰을 받아서 STS의 AssumeRoleWithWebIdentity로 자격 증명을 교환하는 구조다. OIDC provider가 가운데 끼어 있다.

Pod Identity는 노드에 데몬셋으로 도는 Pod Identity Agent가 자격 증명을 발급하고, IAM에 직접 등록된 association이 ServiceAccount-ARN-Role을 묶어준다. 클러스터별 OIDC provider가 필요 없다. 같은 IAM role을 여러 클러스터에서 그대로 재사용할 수 있다는 게 가장 큰 차이다.

세션 태그도 자동으로 붙는다. eks-cluster-arn, eks-cluster-name, kubernetes-namespace, kubernetes-service-account, kubernetes-pod-name 다섯 개가 STS 세션에 태깅돼서 들어온다. 이걸 ABAC에 쓸 수 있다.

한 가지 알아둘 것: Fargate 워크로드는 여전히 IRSA를 써야 한다. EC2 노드 위 Pod에서만 Pod Identity가 동작한다. 그래서 같은 클러스터에서 Fargate Pod은 IRSA, EC2 Pod은 Pod Identity로 섞어 쓰는 구성도 가능하다. 우리 팀은 Fargate를 안 써서 그냥 전환했다.

사전 준비

EKS 클러스터 버전이 1.24 이상이어야 한다. 1.29 이상을 권장한다. 우리 dev 클러스터는 1.30이라 문제없었지만, 1.27쯤 도는 팀이 있다면 컨트롤 플레인 업그레이드부터 잡아야 한다.

Pod Identity Agent를 EKS add-on으로 설치한다. eksctl 기준:

eksctl create addon \
  --cluster my-cluster \
  --name eks-pod-identity-agent \
  --version latest

Terraform이라면 aws_eks_addon 리소스로 동일하게 추가할 수 있다.

resource "aws_eks_addon" "pod_identity_agent" {
  cluster_name = aws_eks_cluster.this.name
  addon_name   = "eks-pod-identity-agent"
}

설치되면 kube-system 네임스페이스에 eks-pod-identity-agent 데몬셋이 뜬다. 노드 수만큼 Pod이 떠 있어야 한다.

kubectl get ds -n kube-system eks-pod-identity-agent

마이그레이션 순서

비파괴적으로 가는 게 핵심이다. IRSA와 Pod Identity가 같은 ServiceAccount에 동시에 적용돼 있으면 Pod Identity가 우선한다(공식 문서 기준). 그래서 IRSA를 남겨둔 채 Pod Identity association만 추가하고, 검증 끝나면 IRSA annotation을 떼는 방식이 안전하다.

1) 현재 IRSA 인벤토리

ServiceAccount의 eks.amazonaws.com/role-arn annotation을 다 뽑는다.

kubectl get sa -A -o json | jq -r '
  .items[]
  | select(.metadata.annotations["eks.amazonaws.com/role-arn"])
  | "\(.metadata.namespace) \(.metadata.name) \(.metadata.annotations["eks.amazonaws.com/role-arn"])"
'

출력 예:

external-dns external-dns arn:aws:iam::1234:role/external-dns-prod
cert-manager cert-manager arn:aws:iam::1234:role/cert-manager-prod
argocd       argocd-server arn:aws:iam::1234:role/argocd-prod

이 목록이 전환 대상이다. 우리 staging 클러스터는 17개였다.

2) IAM role의 신뢰 정책 수정

Pod Identity는 신뢰 주체가 OIDC provider가 아니라 pods.eks.amazonaws.com이다. 기존 role의 trust policy를 둘 다 받아들이도록 바꾸면, 한 role이 IRSA와 Pod Identity 둘 다에서 동작한다. 이게 비파괴적 전환의 핵심이다.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::1234:oidc-provider/oidc.eks.ap-northeast-2.amazonaws.com/id/ABCDE..."
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "oidc.eks.ap-northeast-2.amazonaws.com/id/ABCDE...:sub": "system:serviceaccount:external-dns:external-dns"
        }
      }
    },
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "pods.eks.amazonaws.com"
      },
      "Action": [
        "sts:AssumeRole",
        "sts:TagSession"
      ]
    }
  ]
}

sts:TagSession을 빼먹으면 association은 만들어져도 Pod에서 자격 증명을 못 받는다. 첫날에 이거 때문에 30분 헤맸다.

3) Pod Identity association 생성

aws eks create-pod-identity-association \
  --cluster-name my-cluster \
  --namespace external-dns \
  --service-account external-dns \
  --role-arn arn:aws:iam::1234:role/external-dns-prod

Terraform:

resource "aws_eks_pod_identity_association" "external_dns" {
  cluster_name    = aws_eks_cluster.this.name
  namespace       = "external-dns"
  service_account = "external-dns"
  role_arn        = aws_iam_role.external_dns.arn
}

Helm으로 관리되는 워크로드면 이걸 chart values에 박지 말고, 별도 IaC 모듈로 빼는 걸 추천한다. ServiceAccount 생성과 association 생성은 다른 라이프사이클이다.

4) Pod 재시작 후 검증

association은 Pod이 새로 뜰 때 적용된다. 기존 Pod은 영향받지 않는다.

kubectl rollout restart deployment/external-dns -n external-dns

Pod에 들어가서 자격 증명이 어디서 오는지 확인한다.

kubectl exec -it deploy/external-dns -n external-dns -- env | grep -i aws

IRSA였으면 AWS_ROLE_ARN, AWS_WEB_IDENTITY_TOKEN_FILE 환경변수가 있다. Pod Identity로 전환되면 AWS_CONTAINER_CREDENTIALS_FULL_URI, AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE이 보인다. 이게 바뀌었으면 전환 성공이다.

CloudTrail에서도 확인할 수 있다. AssumeRole 이벤트의 userIdentity.sessionContext에 위에서 말한 세션 태그가 들어 있으면 Pod Identity 경유로 발급된 것이다.

5) IRSA annotation 제거

검증이 끝나면 ServiceAccount의 annotation을 떼고, IAM role의 trust policy에서 Federated 부분을 지운다.

kubectl annotate sa external-dns -n external-dns eks.amazonaws.com/role-arn-

여기까지 끝내고 며칠 운영해보면서 권한 에러 없는지 본 다음, 마지막으로 OIDC provider를 정리한다. OIDC provider는 다른 워크로드(예: GitHub Actions OIDC)에서 같이 쓰고 있을 수 있으니 함부로 지우면 안 된다. 우리 팀은 GitHub Actions가 같은 OIDC provider를 쓰고 있어서 결국 안 지웠다.

ABAC로 권한 정리하기

마이그레이션 자체보다 이쪽이 더 재밌다. 세션 태그 덕분에 클러스터/네임스페이스 단위로 권한을 한 번에 묶을 수 있다.

예를 들어 staging 클러스터의 argocd 네임스페이스에서만 특정 S3 버킷을 읽게 하려면, role 하나에 ABAC 정책을 붙이고 모든 ArgoCD 인스턴스가 그 role을 공유하면 된다.

{
  "Effect": "Allow",
  "Action": "s3:GetObject",
  "Resource": "arn:aws:s3:::config-${aws:PrincipalTag/eks-cluster-name}/*",
  "Condition": {
    "StringEquals": {
      "aws:PrincipalTag/kubernetes-namespace": "argocd"
    }
  }
}

클러스터 이름이 staging-apne2면 자동으로 s3://config-staging-apne2/*만 접근 가능해진다. 새 클러스터 띄울 때 IAM 정책을 추가로 안 만들어도 되는 게 핵심이다.

다만 이걸 너무 일찍 도입하면 정책이 복잡해져서 디버깅이 어려워진다. 우리 팀은 일단 1:1 매핑으로 옮긴 뒤, 분기별로 ABAC 통합 가능한 role들만 골라서 점진적으로 합칠 계획이다.

마이그레이션 중 만난 함정

sts:TagSession 빠뜨린 거 외에도 몇 가지 더 있다.

첫째, Pod Identity Agent가 노드에 안 떠 있는 경우. 새로 추가한 노드 그룹에 데몬셋 toleration이 안 맞아서 일부 노드만 agent가 누락됐다. 그 노드에 스케줄링된 Pod은 association이 있어도 자격 증명을 못 받았다. 데몬셋 toleration을 클러스터 노드의 모든 taint를 커버하도록 손봤다.

둘째, IRSA에서는 동작하는데 Pod Identity에서는 안 되는 SDK 버전이 가끔 있다. AWS SDK가 AWS_CONTAINER_CREDENTIALS_FULL_URI를 지원해야 하는데, 오래된 SDK(파이썬 boto3 1.26 미만 등)는 이걸 못 읽는다. 우리는 한 곳에서 발견했고 SDK 업그레이드로 해결했다. 마이그레이션 전에 사용 중인 SDK 버전을 한번 확인해두는 게 좋다.

셋째, 같은 ServiceAccount에 IRSA annotation과 Pod Identity association이 동시에 있을 때 우선순위. 공식 문서는 Pod Identity가 우선이라고 하는데, 실제로 SDK가 어느 자격 증명을 먼저 시도하는지는 SDK 구현마다 미묘하게 다르다. 안전하게 가려면 검증 후 IRSA annotation을 명시적으로 제거하는 게 좋다.

그래서 옮길 가치가 있나

OIDC provider 등록과 trust policy 관리에서 자유로워지는 것만으로도 멀티 클러스터 환경에선 충분히 옮길 만하다. 단일 클러스터에서 IRSA로 잘 굴러가는 환경이라면 굳이 서두를 필요는 없다. AWS도 IRSA deprecation 일정을 발표한 적은 없다.

우리 팀에선 ABAC 가능성이 결정적이었다. 클러스터 30개 분량의 권한 정책을 IAM 차원에서 통합할 수 있다는 건 운영 측면에서 큰 차이다.

prod 마이그레이션 끝나면 후속 글로 ABAC 통합 작업기를 써볼 생각이다. 혹시 이미 통합 운영 중인 분이 있다면 어떻게 정책을 구조화했는지 댓글로 좀 알려주시면 좋겠다.

반응형