- Published on
EKS에서 AWS SDK NoCredentialProviders 해결 가이드
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버리스/컨테이너 환경에서 AWS SDK가 NoCredentialProviders: no valid providers in chain(또는 언어별로 유사한 메시지)로 터지는 순간은 대개 “코드 문제”가 아니라 “런타임에서 자격 증명 공급 체인이 끊긴 문제”입니다. 특히 EKS에서는 IRSA(IAM Roles for Service Accounts), 노드 인스턴스 프로파일, STS 엔드포인트/네트워크, 환경 변수 오염, SDK 버전이 얽히면서 재현이 쉽습니다.
이 글은 EKS에서 NoCredentialProviders를 가장 빠르게 원인 분리 → 재현 → 수정하는 실전 체크리스트입니다.
> 함께 보면 좋은 글: EKS에서 네트워크/컨트롤플레인 이슈로 진단이 꼬일 때는 Kubernetes apiserver i/o timeout 원인과 해결도 참고하세요.
1) NoCredentialProviders가 의미하는 것: “체인에서 아무 것도 못 찾음”
AWS SDK는 보통 아래 순서(언어/버전별 약간 차이)로 크리덴셜을 찾습니다.
- 환경 변수:
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_SESSION_TOKEN - 공유 설정/크리덴셜 파일:
~/.aws/credentials,~/.aws/config - 웹 아이덴티티(IRSA):
AWS_ROLE_ARN,AWS_WEB_IDENTITY_TOKEN_FILE - EC2/ECS 메타데이터: IMDS(노드 인스턴스 프로파일) 또는 ECS task role
EKS에서 의도한 정답은 대부분 IRSA입니다. 그런데 IRSA가 깨지면 SDK는 다음 후보(노드 IMDS)로 넘어가거나, 아예 아무 것도 못 찾아 NoCredentialProviders로 종료합니다.
대표 증상 로그
- Java v1:
NoCredentialProviders: no valid providers in chain+Unable to load credentials from any of the providers... - boto3:
botocore.exceptions.NoCredentialsError: Unable to locate credentials - Go v1:
NoCredentialProviders: no valid providers in chain. Deprecated.
2) 가장 먼저 확인할 것: “이 파드가 어떤 방식으로 AWS에 접근해야 하나?”
아래 중 하나로 설계를 확정해야 합니다.
- 권장(대부분): IRSA로 파드 단위 IAM Role 부여
- 예외(레거시/간단): 노드 인스턴스 프로파일로 접근(보안상 비권장)
- 로컬/배치: 정적 키(가능하면 금지)
이 글은 IRSA를 기준으로 설명하고, 필요 시 노드 IMDS 방식도 보조로 다룹니다.
3) IRSA 진단: ServiceAccount, 토큰, 환경 변수를 한 번에 보기
IRSA가 정상이라면 파드 안에는 보통 다음이 존재합니다.
AWS_ROLE_ARNAWS_WEB_IDENTITY_TOKEN_FILE(예:/var/run/secrets/eks.amazonaws.com/serviceaccount/token)- 해당 경로에 토큰 파일이 실제로 존재
(A) ServiceAccount에 role-arn annotation이 붙었는지
kubectl -n <ns> get sa <sa-name> -o yaml
확인 포인트:
metadata.annotations.eks.amazonaws.com/role-arn: arn:aws:iam::<acct>:role/<role>
(B) 파드에 IRSA 관련 환경 변수가 주입됐는지
kubectl -n <ns> exec -it <pod> -- sh -lc 'env | egrep "AWS_(ROLE_ARN|WEB_IDENTITY_TOKEN_FILE|REGION|DEFAULT_REGION)"'
정상 예:
AWS_ROLE_ARN=arn:aws:iam::...:role/...AWS_WEB_IDENTITY_TOKEN_FILE=/var/run/secrets/eks.amazonaws.com/serviceaccount/token
(C) 토큰 파일이 실제로 있는지/읽을 수 있는지
kubectl -n <ns> exec -it <pod> -- sh -lc 'ls -l $AWS_WEB_IDENTITY_TOKEN_FILE && head -c 30 $AWS_WEB_IDENTITY_TOKEN_FILE && echo'
- 파일이 없으면: SA 토큰 마운트/Projected volume 문제 또는 IRSA 주입 실패
4) IRSA에서 가장 흔한 원인 6가지와 해결
원인 1) ServiceAccount를 지정하지 않았다 (기본 SA 사용)
Deployment/Pod spec에서 serviceAccountName이 누락되면 기본 SA를 씁니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
spec:
template:
spec:
serviceAccountName: app-sa
containers:
- name: app
image: ...
- 해결: 의도한 SA를 명시하고 롤 어노테이션을 그 SA에 부여
원인 2) OIDC Provider 미연결 또는 잘못된 issuer
IRSA는 EKS 클러스터의 OIDC issuer와 IAM OIDC provider가 정확히 매칭되어야 합니다.
확인(클러스터 issuer):
aws eks describe-cluster --name <cluster> --query "cluster.identity.oidc.issuer" --output text
IAM에 provider가 있는지 확인:
aws iam list-open-id-connect-providers
- 해결:
eksctl utils associate-iam-oidc-provider --cluster <cluster> --approve또는 Terraform/CloudFormation으로 OIDC provider 생성
원인 3) IAM Role trust policy의 sub/aud 조건이 틀림
가장 많이 틀리는 부분이 sub입니다. system:serviceaccount:<ns>:<sa>가 정확해야 합니다.
예시(trust policy):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::<acct>:oidc-provider/oidc.eks.<region>.amazonaws.com/id/<oidc-id>"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"oidc.eks.<region>.amazonaws.com/id/<oidc-id>:aud": "sts.amazonaws.com",
"oidc.eks.<region>.amazonaws.com/id/<oidc-id>:sub": "system:serviceaccount:<ns>:<sa>"
}
}
}
]
}
- 해결: namespace/sa 이름 변경 후 trust policy가 업데이트 안 된 경우가 흔합니다.
원인 4) SDK가 IRSA를 지원하지 않는 버전(특히 구버전)
- Java AWS SDK v1의 아주 오래된 버전, 일부 서드파티 라이브러리 내장 SDK 등은 web identity를 제대로 못 읽습니다.
- 해결: SDK 버전 업 또는 credential provider chain에 WebIdentity provider 포함
Java (AWS SDK v2) 예시
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
import software.amazon.awssdk.services.s3.S3Client;
S3Client s3 = S3Client.builder()
.credentialsProvider(DefaultCredentialsProvider.create())
.build();
- IRSA 환경에서는
DefaultCredentialsProvider가WebIdentityTokenFileCredentialsProvider를 자동 사용합니다.
Python(boto3) 예시
import boto3
s3 = boto3.client("s3")
print(s3.list_buckets())
- 별도 설정 없이도 IRSA 환경 변수가 있으면 STS AssumeRoleWithWebIdentity로 동작합니다.
원인 5) AWS_EC2_METADATA_DISABLED=true + IRSA도 깨짐
보안 강화로 IMDS를 꺼둔 환경에서 IRSA까지 깨지면, SDK는 마지막 탈출구(IMDS)도 못 써서 바로 NoCredentialProviders가 납니다.
파드 환경 확인:
kubectl -n <ns> exec -it <pod> -- sh -lc 'env | grep AWS_EC2_METADATA_DISABLED || true'
- 해결: IRSA를 정상화하는 것이 정답이며, 임시로 IMDS에 의존하려면 해당 설정을 재검토(권장하지 않음)
원인 6) STS 엔드포인트 접근 불가(프라이빗 서브넷 + VPC 엔드포인트 없음)
IRSA는 결국 STS 호출이 필요합니다.
- NAT 없이 프라이빗 서브넷만 사용
- egress가 막힘(NetworkPolicy/보안그룹/라우팅)
- STS VPC Endpoint가 없음
이 경우 로그는 NoCredentialProviders가 아니라 RequestError, i/o timeout, connection refused 등으로 보일 수도 있지만, 라이브러리에 따라 “자격증명 획득 실패”로 뭉개져 NoCredentialProviders로 보이기도 합니다.
확인(파드에서 STS 호출):
kubectl -n <ns> exec -it <pod> -- sh -lc 'apk add --no-cache curl >/dev/null 2>&1 || true; curl -sS https://sts.amazonaws.com/ -o /dev/null -w "%{http_code}\n"'
- 해결: NAT 구성 또는
com.amazonaws.<region>.stsInterface VPC Endpoint 생성(+ 프라이빗 DNS)
5) 노드 인스턴스 프로파일(IMDS)로 접근하는 경우의 함정
IRSA를 쓰지 않고 노드 IAM Role에 권한을 넣는 구조라면, 파드에서 IMDS(169.254.169.254)에 접근 가능해야 합니다.
하지만 최근에는 보안 정책으로 IMDS hop limit, iptables, CNI 설정에 따라 파드에서 IMDS 접근이 막히기도 합니다.
확인:
kubectl -n <ns> exec -it <pod> -- sh -lc 'curl -sS --connect-timeout 1 http://169.254.169.254/latest/meta-data/iam/security-credentials/ || echo IMDS_BLOCKED'
IMDS_BLOCKED면 노드 role 방식은 실패합니다.- 해결: IRSA로 전환하는 것이 장기적으로 안전합니다.
6) 빠른 해결 루트: IRSA를 “정석대로” 다시 구성하기
여기서는 eksctl 없이도 이해 가능한 형태로 핵심 리소스만 정리합니다.
(1) IAM Policy/Role 생성
S3 읽기 예시 정책:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:ListBucket"],
"Resource": ["arn:aws:s3:::my-bucket"]
}
]
}
Role은 앞서 소개한 trust policy(AssumeRoleWithWebIdentity + sub/aud 조건)로 생성합니다.
(2) Kubernetes ServiceAccount에 role-arn annotation
apiVersion: v1
kind: ServiceAccount
metadata:
name: app-sa
namespace: app
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::<acct>:role/app-irsa-role
(3) Deployment에서 serviceAccountName 지정
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
namespace: app
spec:
replicas: 2
selector:
matchLabels:
app: app
template:
metadata:
labels:
app: app
spec:
serviceAccountName: app-sa
containers:
- name: app
image: public.ecr.aws/docker/library/python:3.12-slim
command: ["python", "-c"]
args:
- |
import boto3
print(boto3.client('sts').get_caller_identity())
배포 후 get_caller_identity 결과의 ARN이 IRSA Role로 찍히면 성공입니다.
7) 디버깅을 더 빠르게: STS로 “내가 누구인지”부터 확인
애플리케이션이 S3/DynamoDB 등에서 실패할 때, 먼저 STS로 자격 증명 자체가 유효한지 확인하면 원인 분리가 빨라집니다.
kubectl -n app logs deploy/app
# 또는
kubectl -n app exec -it deploy/app -- python - <<'PY'
import boto3
print(boto3.client('sts').get_caller_identity())
PY
- 여기서도 실패하면: 권한 문제가 아니라 크리덴셜 획득/네트워크/IRSA 설정 문제
- 여기서 성공하고 서비스 호출이 실패하면: IAM policy 권한 부족 문제
8) 운영 중 자주 겪는 “꼬임” 포인트
(A) 롤/SA를 바꿨는데 파드가 계속 예전 권한을 씀
- 파드는 생성 시점의 SA/토큰을 기반으로 동작합니다.
- 해결: Deployment rollout restart
kubectl -n app rollout restart deploy/app
(B) 클러스터/테라폼 변경으로 IRSA가 갑자기 깨짐
OIDC provider, issuer, role trust policy가 드리프트 나면 증상이 한꺼번에 터집니다. 인프라 변경이 잦다면 상태 꼬임도 같이 점검하세요: Terraform EKS 상태 꼬임으로 apply 무한 반복 끊기
(C) 네트워크가 불안정해 STS가 간헐 실패
간헐 실패는 “자격 증명 없음”처럼 보이기도 합니다. CoreDNS는 정상인데 외부/엔드포인트가 간헐 실패하는 케이스도 있어요: EKS에서 CoreDNS 정상인데 DNS가 간헐 실패할 때
9) 최종 체크리스트(이 순서대로 보면 빨리 끝남)
- 이 파드는 IRSA를 써야 하는가? 노드 role을 써야 하는가?
serviceAccountName이 의도한 SA인가?- SA에
eks.amazonaws.com/role-arn이 붙었나? - 파드 env에
AWS_ROLE_ARN,AWS_WEB_IDENTITY_TOKEN_FILE가 있나? - 토큰 파일이 존재하고 읽히나?
- IAM Role trust policy의
sub/aud가 정확한가? - 파드에서 STS 호출이 가능한가(NAT/VPC Endpoint/DNS)?
- SDK 버전이 WebIdentity(=IRSA)를 지원하나?
결론
EKS에서 NoCredentialProviders는 “AWS 권한이 없다”가 아니라 AWS SDK가 자격 증명을 얻을 경로를 하나도 찾지 못했다는 뜻입니다. 따라서 해결도 권한(policy)부터 보기보다,
- IRSA 주입(서비스어카운트/토큰/환경변수)
- IAM OIDC/trust policy
- STS 네트워크 경로
- SDK 버전/프로바이더 체인
을 순서대로 분리하면 대부분 10~20분 내에 원인을 특정할 수 있습니다.
원하시면 사용 중인 언어(Java/Go/Python/Node), SDK 버전, 그리고 현재 SA/Deployment YAML 일부를 기준으로 “어디서 체인이 끊기는지”를 로그와 함께 더 구체적으로 짚어드릴게요.