본 글은 AWS EKS Workshop의 Troubleshooting > ALB Controller 모듈을 기반으로 작성되었습니다. 환경 구성부터 3가지 트러블슈팅 시나리오까지 전체 실습 과정을 다룹니다. 서종호(가시다)님의 AWS EKS Workshop Study(AEWS) 5주차 학습 내용을 기반으로 합니다.
1. 실습 개요
EKS 환경에서 AWS Load Balancer Controller를 통해 ALB를 프로비저닝할 때 실무에서 자주 마주치는 문제들을 의도적으로 재현하고, 이를 단계별로 진단·해결하는 실습이다.
실습에서 다루는 3가지 트러블슈팅 시나리오는 다음과 같다.
| # | 시나리오 | 핵심 원인 |
|---|---|---|
| 1 | ALB가 생성되지 않음 | Public Subnet에 kubernetes.io/role/elb 태그 누락 |
| 2 | IAM 권한 오류로 ALB 생성 실패 | LB Controller의 IAM Role에 필요한 정책 미부착 |
| 3 | ALB는 생성되었으나 503 오류 | Ingress의 Service 이름 오류 + Service Selector 불일치 |
2. 환경 구성
EKS Workshop은 크게 두 가지 방식으로 환경을 구성할 수 있다.
- AWS 이벤트 참가자: 제공된 계정을 통해 바로 시작
- 개인 AWS 계정 사용자: CloudFormation → IDE 생성 → eksctl로 클러스터 구축
여기서는 개인 계정에서 진행하는 방법을 기준으로 설명한다.
2.1. IDE 환경 생성 (CloudFormation)
아래 리전별 Quick-create 링크를 통해 IDE(VS Code Server가 설치된 EC2)를 생성한다.
- us-west-2: Launch Stack
- eu-west-1: Launch Stack
- ap-southeast-1: Launch Stack
⚠️ IDE EC2 인스턴스에는 IAM Role 생성 등 광범위한 IAM 권한이 부여된다. 생성 전에 CloudFormation 템플릿의 IAM 권한을 검토하자.
진행 순서:
- 링크 클릭 후 하단의 IAM 리소스 생성 승인 체크박스를 선택
- Create stack 클릭 (약 5분 소요)
- 스택 생성 완료 후 Outputs 탭에서
IdeUrl과IdePasswordSecret확인

IdePasswordSecretURL로 이동하여 Retrieve 버튼 클릭 → 비밀번호 복사IdeUrl로 접속하여 비밀번호 입력 → IDE 진입

2.2. EKS 클러스터 생성 (eksctl)
IDE 터미널에서 아래 명령어를 실행한다.
export EKS_CLUSTER_NAME=eks-workshop
curl -fsSL https://raw.githubusercontent.com/aws-samples/eks-workshop-v2/stable/cluster/eksctl/cluster.yaml | \
envsubst | eksctl create cluster -f -
클러스터 생성에는 약 20분이 소요된다. 생성되는 주요 리소스는 다음과 같다.
- 3개 AZ에 걸친 VPC (CIDR:
10.42.0.0/16) - IAM OIDC Provider
- Managed Node Group (
m5.large× 3대) - VPC CNI (Prefix Delegation 활성화)
참고로 eksctl이 사용하는 클러스터 설정 파일의 주요 내용은 아래와 같다.
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
name: ${EKS_CLUSTER_NAME}
region: ${AWS_REGION}
version: "1.33"
iam:
withOIDC: true
vpc:
cidr: 10.42.0.0/16
clusterEndpoints:
privateAccess: true
publicAccess: true
managedNodeGroups:
- name: default
desiredCapacity: 3
minSize: 3
maxSize: 6
instanceType: m5.large
privateNetworking: true
addons:
- name: vpc-cni
configurationValues: '{"env":{"ENABLE_PREFIX_DELEGATION":"true", ...}}'
2.3. 트러블슈팅 랩 환경 준비
클러스터가 준비되면 아래 명령어로 ALB 트러블슈팅 랩 환경을 배포한다.
prepare-environment troubleshooting/alb
이 스크립트는 다음 리소스를 배포한다.
- 샘플 UI 애플리케이션 (Deployment + Service)
- Ingress 리소스 (ALB 사용)
- AWS Load Balancer Controller (의도적인 오류 포함)
- IAM Role/Policy (의도적인 권한 누락 포함)
💡 prepare-environment 실행 후 몇 분 기다려야 모든 리소스가 배포 완료된다.
3. 실습 환경 확인
랩 환경이 준비되면, 먼저 Service와 Ingress가 정상적으로 생성되었는지 확인한다.
kubectl get svc -n ui
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ui ClusterIP 172.20.224.112 <none> 80/TCP 12d
kubectl get ingress -n ui
NAME CLASS HOSTS ADDRESS PORTS AGE
ui alb * 80 11m
ADDRESS 필드가 비어 있다. 정상적이라면 ALB DNS 이름이 표시되어야 한다.
ALB가 실제로 생성되지 않았는지 AWS CLI로도 확인해보자.
aws elbv2 describe-load-balancers \
--query 'LoadBalancers[?contains(LoadBalancerName, `k8s-ui-ui`) == `true`]'
[]
ALB가 생성되지 않은 것이 확인되었다. 이제 원인을 파악해보자.
4. 시나리오 1: ALB가 생성되지 않는 문제 — Subnet 태그 누락
4.1. 현상 확인
Pod 자체는 정상 동작 중이다.
kubectl get pod -n ui
NAME READY STATUS RESTARTS AGE
ui-68495c748c-jkh2z 1/1 Running 0 85s
Ingress를 describe하여 이벤트를 확인한다.
kubectl describe ingress/ui -n ui

핵심 에러: kubernetes.io/role/elb 태그가 설정된 서브넷을 찾을 수 없다는 것이다.
4.2. 원인 분석
AWS Load Balancer Controller는 ALB를 생성할 때 Subnet Auto Discovery 메커니즘을 사용한다.
- Internet-facing ALB:
kubernetes.io/role/elb=1태그가 있는 Public Subnet 필요 - Internal ALB:
kubernetes.io/role/internal-elb=1태그가 있는 Private Subnet 필요
현재 환경에서는 Public Subnet에 해당 태그가 누락되어 있다.
4.3. 해결 과정
Step 1: 클러스터의 서브넷 목록 확인
aws ec2 describe-subnets \
--filters "Name=tag:alpha.eksctl.io/cluster-name,Values=${EKS_CLUSTER_NAME}" \
--query 'Subnets[].SubnetId[]'
[
"subnet-xxxxxxxxxxxxxxxxx",
"subnet-xxxxxxxxxxxxxxxxx",
"subnet-xxxxxxxxxxxxxxxxx",
"subnet-xxxxxxxxxxxxxxxxx",
"subnet-xxxxxxxxxxxxxxxxx",
"subnet-xxxxxxxxxxxxxxxxx"
]
Step 2: Public Subnet 식별
각 서브넷의 라우팅 테이블을 확인하여 IGW(igw-xxx)로의 0.0.0.0/0 경로가 있는 서브넷이 Public Subnet이다.
for subnet_id in $(aws ec2 describe-subnets \
--filters "Name=tag:alpha.eksctl.io/cluster-name,Values=${EKS_CLUSTER_NAME}" \
--query 'Subnets[].SubnetId[]' --output text); do
echo "Subnet: ${subnet_id}"
aws ec2 describe-route-tables \
--filters "Name=association.subnet-id,Values=${subnet_id}" \
--query 'RouteTables[].Routes[].[DestinationCidrBlock,GatewayId]' --output text
done
Subnet: subnet-xxxxxxxxxxxxxxxxx
10.42.0.0/16 local
0.0.0.0/0 igw-xxxxxxxxxxxxxxxxx ← Public Subnet (IGW 경로 존재)
Subnet: subnet-yyyyyyyyyyyyyyyyy
10.42.0.0/16 local
0.0.0.0/0 nat-xxxxxxxxxxxxxxxxx ← Private Subnet (NAT Gateway)
...
Step 3: 현재 ELB 태그 상태 확인
aws ec2 describe-subnets \
--filters 'Name=tag:kubernetes.io/role/elb,Values=1' \
--query 'Subnets[].SubnetId'
[]
태그가 설정된 서브넷이 하나도 없는 것이 확인되었다.
Step 4: Public Subnet에 태그 추가
aws ec2 create-tags \
--resources $PUBLIC_SUBNET_1 $PUBLIC_SUBNET_2 $PUBLIC_SUBNET_3 \
--tags 'Key="kubernetes.io/role/elb",Value=1'
Step 5: 태그 적용 확인
aws ec2 describe-subnets \
--filters 'Name=tag:kubernetes.io/role/elb,Values=1' \
--query 'Subnets[].SubnetId'
[
"subnet-xxxxxxxxxxxxxxxxx",
"subnet-xxxxxxxxxxxxxxxxx",
"subnet-xxxxxxxxxxxxxxxxx"
]
Step 6: Load Balancer Controller 재시작
kubectl -n kube-system rollout restart deploy aws-load-balancer-controller
deployment.apps/aws-load-balancer-controller restarted
Step 7: 결과 확인
kubectl describe ingress/ui -n ui

서브넷 문제는 해결되었지만, 이번에는 IAM 권한 오류가 발생했다. 다음 시나리오로 넘어가자.
5. 시나리오 2: IAM 권한 부족으로 ALB 생성 실패
5.1. 현상 확인
Ingress 이벤트에서 AccessDenied 에러가 발생한다.
Warning FailedDeployModel 68s ingress Failed deploy model due to AccessDenied:
User: arn:aws:sts::xxxxxxxxxxxx:assumed-role/alb-controller-xxx/...
is not authorized to perform: elasticloadbalancing:CreateLoadBalancer
status code: 403
5.2. 원인 분석
AWS Load Balancer Controller는 IRSA(IAM Roles for Service Accounts) 를 통해 AWS API를 호출한다. 컨트롤러의 ServiceAccount에 연결된 IAM Role에 필요한 권한이 부족한 상태이다.
ServiceAccount를 확인해보자.
kubectl get serviceaccounts -n kube-system \
-l app.kubernetes.io/name=aws-load-balancer-controller -o yaml
apiVersion: v1
items:
- apiVersion: v1
kind: ServiceAccount
metadata:
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::xxxxxxxxxxxx:role/alb-controller-xxx
name: aws-load-balancer-controller-sa
namespace: kube-system
eks.amazonaws.com/role-arn 어노테이션에 지정된 IAM Role에 elasticloadbalancing:CreateLoadBalancer 등의 권한이 없는 것이다.
컨트롤러 로그에서도 동일한 에러를 확인할 수 있다.
kubectl logs -n kube-system -l app.kubernetes.io/name=aws-load-balancer-controller
{"level":"error","ts":"...","msg":"Reconciler error","controller":"ingress",
"error":"AccessDenied: ... is not authorized to perform:
elasticloadbalancing:CreateLoadBalancer ..."}
5.3. 해결 과정
Step 1: 올바른 IAM Policy 부착
워크샵에서는 올바른 정책이 미리 생성되어 있으므로, 환경 변수를 사용하여 교체한다.
# 올바른 정책 부착
aws iam attach-role-policy \
--role-name ${LOAD_BALANCER_CONTROLLER_ROLE_NAME} \
--policy-arn ${LOAD_BALANCER_CONTROLLER_POLICY_ARN_FIX}
Step 2: 잘못된 IAM Policy 제거
# 잘못된 정책 분리
aws iam detach-role-policy \
--role-name ${LOAD_BALANCER_CONTROLLER_ROLE_NAME} \
--policy-arn ${LOAD_BALANCER_CONTROLLER_POLICY_ARN_ISSUE}
💡 실무에서는 공식 문서에서 제공하는 IAM Policy JSON을 기반으로 정책을 구성해야 한다.
Step 3: Load Balancer Controller 재시작
kubectl -n kube-system rollout restart deploy aws-load-balancer-controller
deployment.apps/aws-load-balancer-controller restarted
kubectl -n kube-system wait --for=condition=available \
deployment/aws-load-balancer-controller
Step 4: ALB 생성 확인
ALB 생성에는 몇 분이 소요될 수 있다. Ingress의 ADDRESS 필드를 확인한다.
kubectl get ingress -n ui ui \
-o jsonpath="{.status.loadBalancer.ingress[*].hostname}{'\n'}"
k8s-ui-ui-5ddc3ba496-1208241872.us-west-2.elb.amazonaws.com
ALB가 성공적으로 생성되었다! 하지만 해당 URL로 접속하면...

"Backend service does not exist" 에러가 발생한다. 다음 시나리오에서 이 문제를 해결해보자.
6. 시나리오 3: Service Endpoint 미등록 — Selector 불일치
6.1. 현상 확인
ALB는 생성되었으나 브라우저에서 접속하면 503 에러 또는 "Backend service does not exist" 메시지가 표시된다.
6.2. 원인 분석 — Ingress Backend Service 이름 불일치
Ingress 설정을 확인해보자.
kubectl get ingress/ui -n ui -o yaml
spec:
ingressClassName: alb
rules:
- http:
paths:
- backend:
service:
name: service-ui # ← 문제: 존재하지 않는 Service 이름
port:
number: 80
path: /
pathType: Prefix
실제 Service 이름은 ui인데, Ingress에서는 service-ui를 참조하고 있다.
Ingress 수정
kubectl apply -k ~/environment/eks-workshop/modules/troubleshooting/alb/creating-alb/fix_ingress
수정된 Ingress 설정:
spec:
ingressClassName: alb
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: ui # ← 올바른 Service 이름
port:
number: 80
하지만 Ingress를 수정한 후에도 여전히 503 에러가 발생한다.

6.3. 원인 분석 — Service Selector 불일치
Service의 Endpoint를 확인해보자.
kubectl -n ui get endpoints ui
NAME ENDPOINTS AGE
ui <none> 13d
Endpoint가 비어 있다. Service가 어떤 Pod도 선택하지 못하고 있다는 의미이다.
Deployment의 Pod 라벨과 Service의 Selector를 비교해보자.
Deployment의 Pod 라벨:
kubectl -n ui get deploy/ui -o yaml
spec:
template:
metadata:
labels:
app.kubernetes.io/component: service
app.kubernetes.io/instance: ui
app.kubernetes.io/name: ui # ← Pod 라벨
Service의 Selector:
kubectl -n ui get svc ui -o yaml
spec:
selector:
app.kubernetes.io/component: service
app.kubernetes.io/instance: ui
app.kubernetes.io/name: ui-app # ← Selector (불일치!)
| 항목 | Pod Label | Service Selector | 일치 여부 |
|---|---|---|---|
app.kubernetes.io/component |
service |
service |
✅ |
app.kubernetes.io/instance |
ui |
ui |
✅ |
app.kubernetes.io/name |
ui |
ui-app |
❌ |
app.kubernetes.io/name의 값이 다르기 때문에 Service가 Pod를 찾지 못하는 것이다.
6.4. 해결 과정
Service Selector를 수정한다.
kubectl apply -k ~/environment/eks-workshop/modules/troubleshooting/alb/creating-alb/fix_ui
💡 수동으로 수정할 수도 있다:
6.5. 최종 확인
Endpoint가 정상적으로 등록되었는지 확인한다.
kubectl -n ui get endpoints ui
NAME ENDPOINTS AGE
ui 10.42.x.x:8080 13d
이제 브라우저에서 ALB URL로 접속하면 UI 애플리케이션이 정상적으로 표시된다.

7. 정리
AWS Load Balancer Controller의 동작 원리
- Controller: Kubernetes API Server의 Ingress 이벤트를 감시하며, 조건에 맞는 Ingress가 감지되면 대응하는 AWS 리소스를 생성한다.
- ALB: 각 Ingress 리소스에 대해 하나의 ALB가 생성된다.
internet-facing/internal스키마를 어노테이션으로 지정한다. - Target Group: Ingress에 정의된 각 Kubernetes Service에 대해 생성된다. IP 모드(
target-type: ip)를 사용하면 Pod IP로 직접 등록된다. - Listener: Ingress 어노테이션에 지정된 포트별로 생성되며, 기본은 80/443이다.
- Rules: Ingress의 Path 규칙에 따라 트래픽을 적절한 Target Group으로 라우팅한다.
이번 실습에서 배운 트러블슈팅 체크리스트
Subnet 관련
- Public Subnet →
kubernetes.io/role/elb=1태그 필수 - Private Subnet →
kubernetes.io/role/internal-elb=1태그 필수 - 서브넷이 올바른 Route Table에 연결되어 있는지 확인
IAM 관련
- LB Controller의 ServiceAccount에 IRSA가 올바르게 설정되어 있는지 확인
- IAM Role에 ELB 관련 권한(
elasticloadbalancing:*,ec2:Describe*,iam:CreateServiceLinkedRole등)이 포함되어 있는지 확인 - 컨트롤러 로그에서
AccessDenied에러 확인
Service/Ingress 관련
- Ingress의
backend.service.name이 실제 Service 이름과 일치하는지 확인 - Service의
selector가 Pod의labels와 정확히 일치하는지 확인 kubectl get endpoints로 Endpoint 등록 여부 확인
유용한 트러블슈팅 명령어 모음
# Ingress 이벤트 확인 (첫 번째로 확인해야 할 곳)
kubectl describe ingress/<name> -n <namespace>
# LB Controller 로그 확인
kubectl logs -n kube-system -l app.kubernetes.io/name=aws-load-balancer-controller
# Service Endpoint 확인
kubectl get endpoints <service-name> -n <namespace>
# Pod 라벨 확인
kubectl get pods -n <namespace> --show-labels
# ServiceAccount의 IAM Role 확인
kubectl get sa -n kube-system <sa-name> -o yaml
# ALB 존재 여부 확인
aws elbv2 describe-load-balancers \
--query 'LoadBalancers[?contains(LoadBalancerName, `k8s-<namespace>`) == `true`]'
# Subnet 태그 확인
aws ec2 describe-subnets \
--filters 'Name=tag:kubernetes.io/role/elb,Values=1' \
--query 'Subnets[].SubnetId'
8. 리소스 정리
실습이 끝나면 비용 발생을 방지하기 위해 리소스를 정리한다.
# 1. 랩 환경 제거
delete-environment
# 2. EKS 클러스터 삭제
eksctl delete cluster $EKS_CLUSTER_NAME --wait
# 3. CloudFormation 스택(IDE) 삭제
# AWS Console → CloudFormation → eks-workshop-ide 스택 삭제