← ALL ENTRIES

ECS 트러블슈팅


📋 요약 개요 ECS Fargate 셋업·운영에서 마주친 함정과 해결책 종합. 셋업은 ECS Fargate 셋업 가이드, 인프라 현황은 운영 인프라 현황, 배포/롤백은 ECS 배포·롤백. 인덱스: ECS Fargate 인프라 MOC

IAM / OIDC 인증

증상원인해결
Could not load credentialsSecret 미등록 또는 이름 오타Repository Secret AWS_DEPLOY_ROLE_ARN 등록
Source Account ID is neededSecret에 Role Name만 (전체 ARN X)전체 ARN으로 업데이트
is not authorized ... sts:AssumeRoleWithWebIdentityTrust Policy sub 조건과 저장소명 불일치repo:OWNER/REPO:* 확인
Audience 입력 “잘못된 값”한글 입력기/번역 자동 변환영문 모드 + 드롭다운 선택
인라인 정책 안 박힘RegisterTaskDefinition 권한 누락ECS Deploy 인라인 정책 추가

ECS Task 시작 실패

에러원인해결
log group does not existCloudWatch Log Group 미생성aws logs create-log-group
unable to assume the role 'ecsTaskRole-...'Task Role 신뢰 정책 잘못”Elastic Container Service Task” 사용 사례로 재생성
CannotPullContainerErrorECR 권한 또는 ARM 호환성Task Execution Role + 이미지 platform 확인
Essential container exited (exit 1)앱 시작 실패CloudWatch Logs 에러 메시지 확인
ResourceInitializationError: ... pull secretsTask Execution Role 권한 부족AmazonECSTaskExecutionRolePolicy 연결 확인
exit code 143 (SIGTERM)헬스체크 3회 실패 후 ECS가 보낸 정상 종료 신호 (원인 자체 X)CloudWatch 로그 첫 30줄에서 진짜 시작 실패 원인 찾기
exit code 137 (SIGKILL)OOM 또는 강제 종료Task Memory 증설 또는 누수 점검

🚨 위험 Next.js standalone UNHEALTHY인데 로그엔 Ready in 0ms 원인: node:alpine의 entrypoint sh가 HOSTNAME 자동변수를 ENI hostname(ip-10-x-x-x)으로 set → standalone server가 ENI IP에만 bind → localhost:3011 헬스체크가 다른 인터페이스라 도달 X → 3회 실패 → SIGTERM(exit 143). Dockerfile의 ENV HOSTNAME=0.0.0.0도 덮어써짐. 해결: Task Definition environment{"name":"HOSTNAME","value":"0.0.0.0"} 명시(runtime env가 image ENV + entrypoint 자동변수를 덮음). 영구 처치는 Dockerfile에 ENTRYPOINT [] 추가해 alpine entrypoint 우회.

Target Group Unhealthy

증상원인해결
Target.Timeout보안그룹 차단 또는 앱 응답 XTask SG 인바운드 (ALB SG → 포트)
initial 영원히Service 등록 못 함Subnet/SG/네트워크 설정 확인
Health checks failed헬스체크 경로 응답 X 또는 늦음경로/포트/grace period 확인
정상 200인데 unhealthy응답 헤더 Content-Type 등헬스체크 success code 확인

보안그룹 (가장 자주 빠뜨림)

인터넷 → ALB (alb-sg: 443/80 from 0.0.0.0/0)  ✅
ALB  → Task (task-sg: <PORT> from alb-sg)     ← 자주 빠짐
Task → RDS  (rds-sg: 3306 from task-sg)       ← 자주 빠짐

⚠️ 주의 포트 0으로 만들면 Timeout 정확한 포트(BE 8082, FE 3011, RDS 3306) 필수.

ALB Listener Rule

증상원인해결
기본 도메인 → 다른 서비스로 감priority 잘못 / Host 조건 누락priority 낮게, Host 정확히
503 Service Temporarily UnavailableTarget Group 비어있음/unhealthyService 띄우기 / 헬스 확인
HTTPS 인증서 오류ACM 와일드카드 부적합*.example.comapp.example.com 커버, example.com은 별도

RDS

증상원인해결
Communications link failure (앱)RDS 보안그룹 차단Task SG → 3306 인바운드
Unknown database 'myapp'초기 DB 이름 안 박음mysql 접속해서 CREATE DATABASE
본인 PC에서 timeout퍼블릭 액세스 OFF임시 ON → 작업 → OFF
failover 깨짐인스턴스 endpoint 사용반드시 클러스터 endpoint
자동 선택이 r 클래스 ($300+)콘솔 기본값t4g.medium로 다운사이즈
# RDS 점검 시 임시 퍼블릭 ON → 작업 후 OFF (필수)
aws rds modify-db-instance --db-instance-identifier myapp-prod-aurora-instance-1 \
  --publicly-accessible --apply-immediately --region ap-northeast-2
aws rds modify-db-instance --db-instance-identifier myapp-prod-aurora-instance-1 \
  --no-publicly-accessible --apply-immediately --region ap-northeast-2

# 마스터 암호 리셋 (1Password + GitHub Secret PROD_DB_PASSWORD 업데이트 필수)
NEW_PASSWORD=$(openssl rand -base64 24 | tr -d '/+=' | cut -c1-20)
aws rds modify-db-cluster --db-cluster-identifier myapp-prod-aurora \
  --master-user-password "$NEW_PASSWORD" --apply-immediately --region ap-northeast-2

NAT Gateway / EIP

증상원인해결
VPC 마법사 NAT 생성 실패회사 공유 계정 EIP 한도 초과미사용 EIP 해제 또는 quota 증가
외부 API 호출 timeoutNAT 없음 또는 라우팅 오류NAT GW 라우팅 테이블 확인
BizTalk IP 화이트리스트NAT EIP 확인describe-nat-gateways의 PublicIp

GHA Workflow

증상원인해결
service ... is MISSING (첫 dispatch)정상 (Service 아직 없음)ECS Service 콘솔 생성
Waiter has timed outService stability 못 됨Task 진단 (위 항목)
빌드 느림 (15~20분)x86 러너에서 QEMU로 ARM 에뮬레이션runs-on: ubuntu-24.04-arm + setup-qemu-action 제거 → 네이티브 ARM 3~5분, private repo 분당 비용도 저렴
빌드 220분+ / QEMU hangQEMU 에뮬레이션 stall캔슬 + ARM 네이티브 러너로 전환
Multi-arch (amd64+arm64) 필요ARM 러너에선 amd64가 에뮬레이션단일 러너에 setup-qemu-action 유지 또는 matrix로 러너 분리

진단 종합 스크립트

/tmp/diagnose.shbash /tmp/diagnose.sh myapp-prod myapp-be:

#!/bin/bash
CLUSTER=$1; SERVICE=$2; REGION=ap-northeast-2

echo "=== Service Status ==="
aws ecs describe-services --cluster $CLUSTER --services $SERVICE --region $REGION \
  --query 'services[0].[desiredCount,runningCount,pendingCount,deployments[0].rolloutState]'

echo "=== Recent Events ==="
aws ecs describe-services --cluster $CLUSTER --services $SERVICE --region $REGION \
  --query 'services[0].events[:5].message'

echo "=== STOPPED Tasks ==="
STOPPED=$(aws ecs list-tasks --cluster $CLUSTER --service-name $SERVICE \
  --desired-status STOPPED --region $REGION --max-results 3 --query 'taskArns' --output text)
if [ -n "$STOPPED" ]; then
  aws ecs describe-tasks --cluster $CLUSTER --tasks $STOPPED --region $REGION \
    --query 'tasks[*].[stoppedReason,containers[0].reason,containers[0].exitCode]' --output table
fi

echo "=== Network Config ==="
aws ecs describe-services --cluster $CLUSTER --services $SERVICE --region $REGION \
  --query 'services[0].networkConfiguration.awsvpcConfiguration'

echo "=== Recent Logs ==="
aws logs tail /ecs/${CLUSTER}/${SERVICE} --region $REGION --since 5m 2>/dev/null | tail -30

배포 후 Service unhealthy 빠른 점검

# Service 상태 + 이벤트
aws ecs describe-services --cluster myapp-prod --services <SERVICE> --region ap-northeast-2 \
  --query 'services[0].[desiredCount,runningCount,events[:5]]'

# STOPPED Task 이유
STOPPED=$(aws ecs list-tasks --cluster myapp-prod --service-name <SERVICE> \
  --desired-status STOPPED --region ap-northeast-2 --max-results 3 --query 'taskArns' --output text)
aws ecs describe-tasks --cluster myapp-prod --tasks $STOPPED --region ap-northeast-2 \
  --query 'tasks[*].[stoppedReason,containers[0].reason,containers[0].exitCode]'

# Target Group health
TG_ARN=$(aws elbv2 describe-target-groups --names <TG_NAME> --region ap-northeast-2 \
  --query 'TargetGroups[0].TargetGroupArn' --output text)
aws elbv2 describe-target-health --target-group-arn $TG_ARN --region ap-northeast-2 \
  --query 'TargetHealthDescriptions[*].[Target.Id,TargetHealth.State,TargetHealth.Description]'

참고