batch_pull! error on batch arn:aws:ecs:us-west-2:002596530511:task/cupix-tesla-ece/ab866da276af40d4b
RCA: batch_pull! error — ecs:DescribeTasks IAM authorization denied
Overview#
What Happened#
2026-07-13 16:06 KST 부터 약 40분간 cupixworks-worker 서비스에서 AwsTask.batch_pull! 이 377회 연속 실패했다. 원인은 IAM 사용자 tesla-api-production-5nod 가 ecs:DescribeTasks 권한을 가지고 있지 않아 AWS ECS API 호출이 AccessDeniedException 으로 거부된 것이다. 동일한 권한 오류가 최소 2026-06-29 부터 존재해 왔고 (Datadog retention 상한), 2분마다 실행되는 cron (Cupix::Cron::AwsTask.pull_pending, pull_blank) 이 pending/blank 상태인 AwsTask 레코드를 반복 조회하면서 그 시점에 큐잉된 대량의 task 를 배치로 처리하는 순간마다 스파이크가 발생한다.
Quick Facts#
| Field | Value |
|---|---|
| exception.class | Aws::ECS::Errors::AccessDeniedException (rescued as StandardError) |
| exception.message | User: arn:aws:iam::002596530511:user/system/tesla-api-production-5nod is not authorized to perform: ecs:DescribeTasks on resource: * because no identity-based policy allows the ecs:DescribeTasks action |
| top_frame | app/models/aws_task.rb:55 (ecs.describe_tasks) |
| runtime | Rails / Sidekiq (cupixworks-worker) |
| env | production, ECS cluster cupix-tesla-ece in us-west-2 |
| iam_user | arn:aws:iam::002596530511:user/system/tesla-api-production-5nod |
Affected Teams#
| Team / Domain | Error Count | Impact |
|---|---|---|
| cupixworks-worker (ECS task lifecycle tracking) | 377 | AwsTask 레코드의 last_status/desired_status 가 실제 ECS task 상태와 동기화되지 않음. Job.run_task_stopped_callbacks 가 트리거되지 않아 stopped task 에 대한 후속 처리(콜백)가 지연될 위험 |
Timeline#
- 2026-06-29 20:21 KST —
PullTaskWorker::perform | error on 9836 ... not authorized to perform: ecs:DescribeTasks— 동일 권한 오류 최초 관측 (Datadog 14일 retention 상한이라 실제 시작 시점은 더 이를 수 있음). - 2026-07-13 16:03 KST —
PullTaskWorker::perform | error on 10256(개별 워커 경로에서도 동일 오류) 재확인. - 2026-07-13 16:06:30 KST —
AwsTask.batch_pull!배치 경로에서 오류 스파이크 시작 (cluster first_seen). - 2026-07-13 16:46:18 KST — 스파이크 종료 (cluster last_seen). 40분 동안 377건.
- 2026-07-13 16:34–16:46 KST — 상태 보드에
cupixworks-worker service degraded인시던트 자동 생성 (id2026-07-13-svc-cupixworks-worker--unknown-2, 3 clusters 그룹핑).
Error Log#
batch_pull! error on batch arn:aws:ecs:us-west-2:002596530511:task/cupix-tesla-ece/ab866da276af40d4bf8db1f485e4c09a,arn:aws:ecs:us-west-2:002596530511:task/cupix-tesla-ece/16e50e1224b04b01bd504689163f1a5b,arn:aws:ecs:us-west-2:002596530511:task/cupix-tesla-ece/eca61fa88eed4831b37fd91b608bee2a - User: arn:aws:iam::002596530511:user/system/tesla-api-production-5nod is not authorized to perform: ecs:DescribeTasks on resource: * because no identity-based policy allows the ecs:DescribeTasks action
Impact#
- Service:
cupixworks-worker - 발생 횟수: 377
- 최초 발생: 2026-07-13 16:06 KST
- 최근 발생: 2026-07-13 16:46 KST
- 부수 영향: 각 배치의
describe_tasks호출이 실패하므로AwsTask의last_status가 갱신되지 않는다.after_save :task_stopped?콜백이 발화하지 않아, 실제로 STOPPED 로 전이한 ECS task 에 대해job.run_task_stopped_callbacks(task_id)가 지연된다. 다만Cupix::Cron::AwsTask.pull_blank는blankscope(신규 생성 직후 상태) 만 대상으로 하고pull_pending은 pending scope 만 다루므로, 각 배치가 실패해도 대상 task 는 계속 스캔 대상에 남아 다음 cron 주기에 다시 시도된다 — 즉 데이터 손실이 아닌 반복 실패 상태다.
Root Cause Summary#
AwsTask.batch_pull! 는 Aws::ECS::Client#describe_tasks 를 호출하여 배치로 ECS task 상태를 조회한다. 프로덕션 IAM principal tesla-api-production-5nod 의 identity-based policy 에 ecs:DescribeTasks 가 포함되어 있지 않아 AWS 가 요청을 거부한다. 이 권한 결함은 최소 2026-06-29 부터 계속 존재해 왔으며(직접 관측 가능한 retention 상한), 애플리케이션 코드는 변경되지 않았다 (app/models/aws_task.rb 마지막 수정은 2026-05-12, 커밋 f9b7cdd3a). 07-13 의 스파이크는 pending/blank scope 에 다수의 AwsTask 레코드가 축적된 시점에 2분 주기 cron 이 100개씩(BATCH_SIZE = 100) 배치로 조회하여 각 slice 마다 rescue 로그가 남은 결과다.
Technical Analysis#
Code Path#
- Entry point:
config/schedule.rb:29-35— cronevery 2.minutes로Cupix::Cron::AwsTask.pull_pending/pull_blank실행 - Call:
lib/cupix/cron/aws_task.rb:21-25→::AwsTask.batch_pull!(tasks) - Failure point:
app/models/aws_task.rb:54-59—ecs.describe_tasks(...)가Aws::ECS::Errors::AccessDeniedException을 raise →rescue StandardError(line 86-88) 에서error레벨로 로그 - Alternate path:
PullTaskWorker#perform(app/workers/pull_task_worker.rb:5-15) 도AwsTask#pull!→fetch!→ 동일한describe_tasks호출을 하여 같은 예외를 rescue 로 기록한다.
task_ids.each_slice(BATCH_SIZE) do |batch_ids|
resp = ecs.describe_tasks({
tasks: batch_ids,
cluster: cluster_name,
include: ['TAGS']
})
found_task_ids = Set.new
resp.tasks.each do |resp_task|
resp_json = resp_task.as_json
record = tasks_by_task_id[resp_json['task_id']]
next unless record
# ...
end
(batch_ids - found_task_ids.to_a).each do |missing_id|
# ...
end
rescue StandardError => e
Cupix::Logger.error("batch_pull! error on batch #{batch_ids.first(3).join(',')} - #{e.message}", class: name, function: __method__)
end
def ecs_client
Aws::ECS::Client.new region: ENV['CUPIX_REGION'] || 'us-west-2'
end
every 2.minutes do
# runner 'Cupix::Cron::AwsTask.pull_all'
runner 'Cupix::Cron::AwsTask.pull_pending'
runner 'Cupix::Cron::AwsTask.pull_blank'
runner 'Cupix::Cron::CopyRequest.run_waiting_copy_reqeusts'
runner 'Cupix::Cron::SiteinsightsService.publish_polling_events_by_schedule'
end
기대 동작: describe_tasks 가 성공적으로 응답을 반환하고 각 AwsTask 레코드의 last_status/desired_status/stopped_at 이 실제 ECS 상태와 일치하도록 갱신되어야 한다.
실제 동작: AWS 가 IAM 정책 부재로 AccessDeniedException 을 반환한다. 각 100개 slice 당 한 번의 rescue 로그가 남고, 상태 동기화는 이 cron 주기 동안 수행되지 않는다.
Log Evidence#
Datadog query used:
service:cupixworks-worker @class:AwsTask "not authorized"
첫 3건 (07-13 스파이크 초반):
2026-07-13 16:06:30 KST error
batch_pull! error on batch arn:aws:ecs:us-west-2:002596530511:task/cupix-tesla-ece/ab866da276af40d4bf8db1f485e4c09a,arn:aws:ecs:us-west-2:002596530511:task/cupix-tesla-ece/16e50e1224b04b01bd504689163f1a5b,arn:aws:ecs:us-west-2:002596530511:task/cupix-tesla-ece/eca61fa88eed4831b37fd91b608bee2a - User: arn:aws:iam::002596530511:user/system/tesla-api-production-5nod is not authorized to perform: ecs:DescribeTasks on resource: * because no identity-based policy allows the ecs:DescribeTasks action
@class=AwsTask @function=batch_pull!
동일 principal 로 개별 워커 경로에서도 실패:
2026-07-13 16:03:22 KST error
PullTaskWorker::perform | error on 10256 - User: arn:aws:iam::002596530511:user/system/tesla-api-production-5nod is not authorized to perform: ecs:DescribeTasks on resource: * because no identity-based policy allows the ecs:DescribeTasks action
@class=PullTaskWorker @function=perform
만성적 발생 여부 확인 (retention 상한 근처 샘플):
service:cupixworks-worker "not authorized to perform: ecs:DescribeTasks"
2026-06-29 20:21:58 KST error
PullTaskWorker::perform | error on 9836 - User: arn:aws:iam::002596530511:user/system/tesla-api-production-5nod is not authorized to perform: ecs:DescribeTasks on resource: * because no identity-based policy allows the ecs:DescribeTasks action
2026-06-29 부터 2026-07-13 까지 동일 오류가 지속적으로 관측되므로 이번 스파이크는 신규 회귀가 아니라 만성 IAM 결함이 특정 순간 배치 볼륨과 겹쳐 표면화된 것이다.
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | 프로덕션 IAM principal tesla-api-production-5nod 에 ecs:DescribeTasks 권한이 부여되지 않은 정책 결함 |
AWS 응답 메시지가 명시적으로 "no identity-based policy allows the ecs:DescribeTasks action" 이라고 밝힘; 동일 principal/동일 permission 오류가 최소 2026-06-29 부터 07-13 까지 지속됨 | — | Confirmed |
| H2 | 최근 코드 변경이 describe_tasks 호출을 새로 추가하여 발생 |
app/models/aws_task.rb 마지막 수정은 2026-05-12 (f9b7cdd3a); describe_tasks 호출부(line 55, 118)는 그보다 오래됨 |
2026-06-29 부터 동일 오류 존재 → 07-13 이 신규 회귀가 아님 | Rejected |
| H3 | 크로스 리전 호출 문제 (worker 가 다른 리전에서 실행 중) | 클러스터 frontmatter 의 regions: ap-southeast-1 과 리소스 리전 us-west-2 가 다름 |
AWS IAM 은 리전 관계없이 policy attach 여부만 검사; 에러 메시지가 "no identity-based policy" 라고 명시하여 리전이 아닌 policy 부재가 원인임을 명확히 함. ecs_client (line 91-93) 도 us-west-2 로 명시 |
Rejected |
| H4 | AWS 측 rate limiting / throttling | 과거 커밋 5a7d5a8d5 ("Rate exceeded") 존재 |
이번 오류 메시지는 Rate exceeded 가 아니라 not authorized — 완전히 다른 예외 |
Rejected |
Infrastructure Configuration (cupix-infrastructure)#
Revision 1 에서 실제 인프라 저장소를 조사한 결과, tesla-api-production-5nod IAM 사용자와 그 정책은 cupix-infrastructure 저장소에서 Terraform 으로 관리되고 있음을 확인했다.
IAM user 정의 위치: cupix-service/api-eb/main.tf:642-651
resource "aws_iam_user" "api_user" {
name = "tesla-api-${var.service_env}-${random_string.random.result}"
path = "/system/"
tags = local.tags
}
resource "aws_iam_access_key" "api_user" {
user = aws_iam_user.api_user.name
}
random_string.random (line 636-640, length = 4) 이 5nod 접미사를 생성한다 → 사용자 이름 tesla-api-production-5nod 와 path /system/ 이 에러 메시지의 principal ARN arn:aws:iam::002596530511:user/system/tesla-api-production-5nod 와 정확히 일치한다.
ECS 관련 정책 정의 위치: cupix-service/api-eb/main.tf:769-809
resource "aws_iam_policy" "api_ecs_policy" {
name = "${var.tenant}-tesla-${var.service_env}-ecs-policy-${random_string.random.result}"
path = "/tesla/"
description = "${var.tenant} tesla ${var.service_env} ecs policy by terraform"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "IamPassRole",
Effect = "Allow",
Action = "iam:PassRole",
Resource = [
"arn:aws:iam::*:role/ecsTaskExecutionRole"
]
},
{
Sid = "HandleECS",
Effect = "Allow",
Action = [
"ecs:*"
],
Resource = [
local.resource_ecs,
"arn:aws:ecs:*:*:task/*",
"arn:aws:ecs:*:*:container-instance/*",
"arn:aws:ecs:*:*:service/*",
"arn:aws:ecs:*:*:task-set/*/*/*",
"arn:aws:ecs:*:*:task-definition/*:*"
]
}
]
})
tags = local.tags
}
resource "aws_iam_user_policy_attachment" "api_ecs_attach" {
user = aws_iam_user.api_user.name
policy_arn = aws_iam_policy.api_ecs_policy.arn
}
여기서 local.resource_ecs = "arn:aws:ecs:*:*:cluster/${var.ece_cluster_name}" (line 74) 이고, 프로덕션 (cupix-aws2/production/terragrunt.hcl:218) 은 ecs_cluster_name = "cupix-tesla-ece" 로 설정되어 있다.
핵심 발견 — Terraform 정의 vs. 실제 배포 상태 불일치 (drift 의심):
Terraform 코드상 이 정책은 ecs:* (와일드카드) 를 arn:aws:ecs:*:*:task/* 를 포함한 다양한 리소스에 부여하고 있어 ecs:DescribeTasks 가 허용되어야 한다. api_ecs_policy 는 2021-07-14 (TSLA-3027, commit 13658353) 부터 이 형태를 유지하고 있고, 그 이후 최근 변경은 TSLA-11560 (030403df, style-only whitespace 정렬) 뿐이다 — 실질적 정책 변경 없음.
그러나 프로덕션에서 AWS 가 반환하는 오류 메시지 no identity-based policy allows the ecs:DescribeTasks action 은 실제 IAM 평가 시점에 매칭되는 Allow 문이 없다는 뜻이다. Terraform 정의와 배포된 실제 정책 사이에 drift 가 발생했을 가능성이 매우 높다:
api_ecs_policy리소스가 손으로 삭제되었거나api_ecs_attach(user → policy attachment) 가 손으로 해제되었거나- 프로덕션 stack 이 최신 Terraform 을 apply 하지 못한 채 IAM 사용자만 잔존 (drift)
셋 다 코드 자체 결함이 아니라 applied state 확인이 필요한 운영 이슈이다.
참고 — 관련 정책들 (같은 파일 내):
| Policy resource | Path | Actions |
|---|---|---|
api_default_policy (line 653) |
/tesla/ |
Lambda invoke, SES, STS, Cognito, SSM, StepFunctions |
api_cognito_policy (line 740) |
/tesla/ |
Cross-account sts:AssumeRole |
api_ecs_policy (line 769) |
/tesla/ |
ecs:* — 이번 이슈 대상 |
api_s3_policy (line 811) |
/tesla/ |
s3:* (다양한 버킷) |
api_sqs_policy (line 865) |
/tesla/ |
SQS SendMessage |
즉 ECS 권한은 별도 파일이 아니라 같은 api-eb/main.tf 안의 api_ecs_policy 하나로 관리된다. Datadog IAM 지침이나 별도 module 이 아니라 이 파일이 유일한 소스임을 확인했다.
Fix Recommendation#
즉시 조치 (Critical)#
-
인프라(IAM policy) drift 확인 및 재-apply — 코드 변경 아님. Terraform 코드는 이미
ecs:*(withtask/*) 를 부여하도록 정의되어 있다 (cupix-infrastructure/cupix-service/api-eb/main.tf:769-809). 따라서 신규 정책을 작성할 필요는 없다. 대신:- 인프라 팀에
cupix-aws2/production(account002596530511, regionus-west-2) 의 실제 IAM 사용자 정책 목록을 확인 요청:기대 출력에bashaws iam list-attached-user-policies --user-name tesla-api-production-5nod \ --profile cupix-aws2-productioncupix-tesla-production-ecs-policy-5nod(또는 유사한 이름, random suffix5nod) 가 포함되어야 한다. 누락 시 drift 이므로terragrunt apply로 재-apply 하면 해결된다. - 정책은 존재하지만 attachment 만 없는 경우:
로bash
aws iam list-policies --scope Local --path-prefix /tesla/api_ecs_policy존재를 확인 후aws_iam_user_policy_attachment.api_ecs_attach만 재적용한다. - 정책 자체가 삭제된 경우, 해당 리소스만 targeted apply:
(모듈 경로는 실제 terragrunt tree 에 맞춰 조정)bash
cd cupix-aws2/production terragrunt apply \ -target='module.tesla.module.api_eb.aws_iam_policy.api_ecs_policy' \ -target='module.tesla.module.api_eb.aws_iam_user_policy_attachment.api_ecs_attach'
- 인프라 팀에
-
최소권한 검토는 별개 이슈: 현재 코드는
ecs:*라는 매우 넓은 액션을 부여한다. 이번 오류 복구와 무관하게, 장기적으로는 실제로 tesla 가 호출하는 액션 (DescribeTasks,RunTask,StopTask,ListTasks,RegisterTaskDefinition등) 만 열거하도록 정책을 좁히는 리팩터링을 별도 티켓으로 검토 가능.
단기 개선 (1주 이내)#
app/models/aws_task.rb:86-88의rescue StandardError를Aws::ECS::Errors::AccessDeniedException와 그 외 예외로 분기해 IAM 결함은Cupix::Logger.warn대신error로 유지하되, 스팸을 줄이기 위해 배치 실패 시 첫 발생만error, 이후 반복은warn으로 downgrade 하는 rate-limited 로깅을 검토. 다만 IAM 이슈는 조용해지면 알아채기 어려우므로 downgrade 대신 아래 모니터링을 우선 도입한다.PullTaskWorker(app/workers/pull_task_worker.rb:13-15) 의 rescue 도AccessDeniedException을 별도 로그 필드로 태깅하여 Datadog facet 으로 필터링 가능하게 할 것.
장기 개선 (재발 방지)#
- IAM policy drift 감지: Terraform state 와 실제 배포된 policy 를 주기적으로 비교하는
awspec/iam-lint등 검증 파이프라인을 CI 에 추가. - ECS 접근이 필요한 새 코드 경로 (
describe_tasks,stop_task,list_tasks등) 도입 시 인프라 팀 리뷰 필수화 (repo-level CODEOWNERS 또는 PR 템플릿 체크리스트). AwsTask동기화 실패가 실제 job 콜백 지연으로 이어지는지 관찰하기 위해Job.run_task_stopped_callbacks호출 지연 시간을 메트릭화.
Monitoring#
- Datadog 알림:
ecs:DescribeTasksAccessDenied 관련 error 가 5분 window 에 10건 이상이면 페이지. 다음 timeseries 쿼리를 dashboard widget 에 사용:
sum:trace.rack.request.errors{service:cupixworks-worker,resource_name:aws_task.batch_pull}.as_count()
logs("service:cupixworks-worker @class:AwsTask \"not authorized to perform: ecs:DescribeTasks\"").index("*").rollup("count").by("host").rollup("count", "5m")
PullTaskWorker경로:
logs("service:cupixworks-worker @class:PullTaskWorker \"not authorized\"").index("*").rollup("count").by("environment").rollup("count", "5m")
- ECS API 4xx 비율 (인프라 측):
sum:aws.ecs.api.4xx_error{account_id:002596530511,region:us-west-2}.as_count()
각 쿼리는 writing-datadog-monitoring-queries 가이드에 맞춰 monitor-only 문법(| stats, threshold suffix) 을 사용하지 않았다.
Risk Assessment#
- Risk level: medium — 실제 데이터 파괴는 없으나 ECS task 상태 동기화가 지속 실패하여
run_task_stopped_callbacks트리거가 지연될 수 있고,PullTaskWorker큐에 재시도(retry: 1) 트래픽이 누적된다. 로그 노이즈로 다른 실질 오류가 묻힐 위험도 존재. - 예상 복잡도: standard — 인프라(IAM policy) 변경 위주. 애플리케이션 코드 변경은 선택적(로깅 개선)이며 breaking change 없음. 인프라 저장소 위치 확인 및 리뷰가 필요하므로 소규모 크로스팀 조율이 필요하다.
Revision History#
Revision 1#
Feedback: cupix-infra repo 에서 어떻게 설정되어있는건지 확인좀
판정:
| 피드백 항목 | 판정 | 근거 |
|---|---|---|
cupix-infra (실제 저장소명 cupix-infrastructure) 에서 IAM 정책이 어떻게 설정되어 있는지 확인 |
수용 | cupix-infrastructure/cupix-service/api-eb/main.tf:642-651 에 IAM user tesla-api-${service_env}-${random_string} (path /system/) 정의 확인 — 프로덕션 service_env=production, random_string=5nod 로 에러의 principal ARN 과 일치. 같은 파일 line 769-809 에 api_ecs_policy (Action ecs:*, Resource arn:aws:ecs:*:*:task/* 포함) 정의, line 806-809 에 user attachment 확인. 프로덕션 cluster 이름은 cupix-aws2/production/terragrunt.hcl:218 에서 ecs_cluster_name = "cupix-tesla-ece" 로 설정 — 에러 로그의 task ARN 클러스터 이름과 일치. Terraform 정의상 정책은 ecs:DescribeTasks 를 허용해야 하지만 AWS 응답은 "no identity-based policy allows..." — Terraform 정의와 배포된 실제 IAM 상태 간 drift 가능성이 매우 높다는 결론 반영. git log develop -L 로 확인 시 api_ecs_policy 는 2021-07-14 (commit 13658353, TSLA-3027) 이후 style-only whitespace 정렬 (030403df) 외에는 변경 없음 — 최근 코드 회귀 아님. |
변경 사항:
## Infrastructure Configuration (cupix-infrastructure)섹션을 신규 추가하여 IAM user/policy Terraform 정의 위치,random_string접미사 매커니즘, prod cluster 이름 매핑, drift 가설을 코드 참조와 함께 명시.## Fix Recommendation→### 즉시 조치 (Critical)를 재작성. Revision 0 은 "저장소 위치 미확인, 새 정책 작성 필요"로 서술했지만, 조사 결과 정책은 이미 Terraform 에 있으므로 조치를 (1) drift 확인 명령, (2) attachment 재적용, (3) targeted terragrunt apply 세 단계로 구체화. 최소권한 검토는 별개 이슈로 분리.
추가 조사 내용:
- 탐색한 저장소:
/home/ec2-user/repos/cupix-infrastructure - 참조한 파일:
cupix-service/api-eb/main.tf,cupix-service/api-eb/outputs.tf,cupix-service/main.tf,cupix-aws2/production/terragrunt.hcl git log develop -L '/api_ecs_policy/,/^}$/:cupix-service/api-eb/main.tf'로 정책 이력 확인 (Rev 이래 실질 변경 없음)- 프로덕션 이외에도 stage/qa/dev/au/eu/sg/jp/ca 리전별
ecs_cluster_name값 확인 (cupix-tesla-ece-{env},cupix-tesla-ece-{region}) — 같은 module 인스턴스가 리전마다 별도의 IAM user/policy 를 생성함을 확인