IRSA vs Pod Identity, 우리 팀이 결국 Pod Identity로 간 이유
EKS 쓰다 보면 언젠가 한 번은 부딪히는 질문. 파드가 AWS 리소스에 접근할 때 IAM을 어떻게 붙일 것인가. 예전에는 답이 하나였다. IRSA. 근데 몇 년 전 Pod Identity가 나오고, 올해 초 우리 팀은 결국 이걸로 옮겼다. 왜 옮겼는지, 뭐가 좋고 뭐가 애매한지 정리해본다.
IRSA는 그동안 잘 써왔는데
솔직히 IRSA에 큰 불만은 없었다. OIDC provider 만들어놓고, ServiceAccount에 어노테이션 붙이고, IAM 역할의 trust policy에 그 SA를 넣어주면 끝. 몇 년째 잘 굴러왔다.
문제는 클러스터가 늘어나면서 시작됐다. 우리 팀은 dev/staging/prod로 클러스터가 세 개, 여기에 데이터 팀이 요청한 별도 클러스터 하나가 더 붙어서 총 네 개다. 각 클러스터마다 OIDC provider가 따로 있고, 같은 역할을 여러 클러스터에서 쓰려면 trust policy에 각 클러스터의 OIDC issuer URL을 다 넣어줘야 한다.
이게 은근히 귀찮다. 새 클러스터 하나 띄울 때마다 기존 역할들의 trust policy를 다 뒤져서 새 issuer를 추가해야 하고, 어떤 역할이 어떤 클러스터에서 쓰이는지 추적하기도 애매하다. Terraform으로 관리하고는 있는데, 팀원이 실수로 trust policy 하나를 빼먹어서 파드가 인증에 실패한 일도 몇 번 있었다.
Pod Identity로 옮긴 결정적 이유
Pod Identity의 핵심은 OIDC를 안 쓴다는 점이다. 대신 클러스터 내에 eks-pod-identity-agent라는 애드온이 돌고, 얘가 노드 위의 파드에게 임시 자격증명을 주입한다. 신뢰 관계는 pods.eks.amazonaws.com이라는 서비스 프린시펄 하나로 통일된다.
이게 뭘 의미하냐면, 역할을 여러 클러스터에서 재사용할 때 trust policy를 건드릴 일이 없어진다. 새 클러스터에 배포? 그냥 그 클러스터에서 aws eks create-pod-identity-association 한 번 해주면 끝이다. 역할 쪽은 아무것도 안 바꿔도 된다.
세션 태그도 자동으로 붙는다. eks-cluster-name, kubernetes-namespace, kubernetes-service-account, kubernetes-pod-name. ABAC 정책을 쓰고 싶으면 이 태그들로 조건을 걸 수 있다. IRSA 시절에는 이걸 하려면 자체적으로 세션 태그를 주입하는 로직이 필요했는데, Pod Identity는 그냥 알아서 붙여준다.
실제로 옮기면서 겪은 것들
거창하게 "마이그레이션 프로젝트" 이렇게 잡지 않았다. IRSA와 Pod Identity는 동시에 공존할 수 있으니까, 새 워크로드부터 Pod Identity로 배포하고 기존 건 그대로 뒀다. AWS도 IRSA 지원 중단 얘기는 안 하고 있으니 급할 이유가 없다.
그래도 세 가지는 미리 확인해야 했다.
첫째, 클러스터 버전. 1.24 이상이면 되지만 실무에서는 1.29 이상이 편하다. 그 이전 버전에서는 애드온 설치가 좀 애매한 경우가 있었다. 우리는 그때 1.30이라 문제없었다.
둘째, Fargate. Fargate 워크로드는 여전히 IRSA를 써야 한다. Pod Identity 에이전트가 노드에 데몬셋으로 뜨는 구조인데, Fargate는 노드 개념이 없으니 방법이 없다. 우리 팀은 Fargate를 거의 안 써서 문제가 안 됐는데, Fargate 비중이 높은 팀이라면 하이브리드 구성을 감수해야 한다.
셋째, Terraform 리팩터링. IRSA는 assume_role_policy에 OIDC 조건을 넣는 방식이고, Pod Identity는 aws_eks_pod_identity_association 리소스와 pods.eks.amazonaws.com 프린시펄 기반 trust policy가 필요하다. 모듈을 새로 짜야 했고, 우리는 두 방식을 다 지원하는 헬퍼 모듈을 만들어서 점진적으로 넘어가고 있다.
resource "aws_iam_role" "app" {
name = "app-role"
assume_role_policy = jsonencode({
Statement = [{
Effect = "Allow"
Principal = { Service = "pods.eks.amazonaws.com" }
Action = ["sts:AssumeRole", "sts:TagSession"]
}]
})
}
resource "aws_eks_pod_identity_association" "app" {
cluster_name = "prod-eks"
namespace = "app-ns"
service_account = "app-sa"
role_arn = aws_iam_role.app.arn
}
trust policy가 이렇게 짧아진다. OIDC issuer URL 같은 클러스터별 값이 사라지니, 역할 자체는 재사용 가능한 모듈로 관리하기 훨씬 편해졌다.
그럼에도 애매한 것들
완벽하지는 않다. 몇 가지 걸리는 점.
Pod Identity 에이전트는 결국 노드에 데몬셋으로 도는 컴포넌트다. 이게 죽으면 파드의 자격증명 발급이 막힌다. 프로덕션에서 애드온의 헬스 모니터링을 추가해야 했다. IRSA는 kube-apiserver의 프로젝티드 토큰과 STS만 있으면 되니 이런 종속성이 없었다.
또 하나. 파드 시작 직후 아주 짧은 순간, 에이전트가 자격증명 준비를 마치기 전에 애플리케이션이 AWS API를 호출하면 실패할 수 있다. AWS SDK 대부분은 자동 재시도가 있어서 실무에서 크게 문제 된 적은 없는데, 초기화 로직이 빡빡한 애플리케이션은 한 번 짚어봐야 한다.
그리고 로컬 개발. IRSA는 로컬에서 AWS_WEB_IDENTITY_TOKEN_FILE 같은 걸 흉내 낼 수 있었는데, Pod Identity의 credential provider 프로토콜은 로컬에서 재현하기가 더 번거롭다. 결국 로컬 개발용은 별도 IAM 유저나 SSO 프로파일을 쓰는 걸로 정리했다.
결론
새 워크로드는 Pod Identity, 기존은 그대로. 급하게 다 옮길 필요는 없다. 다만 신규 팀이나 신규 클러스터로 시작한다면 Pod Identity를 기본값으로 잡아두는 걸 추천한다. 몇 년 뒤 클러스터가 늘어났을 때 예전 우리 팀처럼 trust policy 지옥을 겪지 않으려면.
Fargate 많이 쓰는 팀이라면 좀 더 지켜봐도 될 것 같다. AWS가 언젠가 Fargate 지원을 추가할 수도 있고, 아닐 수도 있고. 아직 로드맵에 확실한 게 없다.
혹시 이미 옮긴 팀들, 로컬 개발 환경은 어떻게 정리했는지 궁금하다. 우리는 아직 좀 어색하다.