batch_pull! error on batch arn:aws:ecs:us-west-2:002596530511:task/cupix-tesla-ece/f08cf04288c144f38
RCA: batch_pull! AccessDenied on ecs:DescribeTasks
Overview#
What Happened#
2026-07-13 19:02 KST 부터 cupixworks-migration-worker (tesla EB migrationworker role) 의 AwsTask.batch_pull! cron job 이 ecs:DescribeTasks API 호출에서 AWS AccessDenied 를 반환하면서 74분간 988건의 에러를 발생시켰다. tesla-api-production-5nod IAM user 의 identity-based policy 가 새 ECS long ARN 포맷(task/<cluster>/<taskId>)의 task 리소스와 매칭되지 않아 ecs:DescribeTasks 권한이 거부되었다.
Quick Facts#
| Field | Value |
|---|---|
| exception.class | AwsTask (message-only, exception class not captured) |
| 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:87 |
| env | production, region tag ap-southeast-1, ECS cluster in us-west-2 |
| iam_user | arn:aws:iam::002596530511:user/system/tesla-api-production-5nod |
| ecs_cluster | cupix-tesla-ece (account 002596530511, region us-west-2) |
Affected Teams#
| Team / Domain | Error Count | Impact |
|---|---|---|
| cupixworks-migration-worker (tesla EB migrationworker) | 988 | ECS task status polling 중단 — AwsTask 레코드의 last_status/desired_status 가 갱신되지 않아 downstream job callback 및 stopped 처리 지연 |
Timeline#
- 2026-07-13 19:02 KST —
AwsTask.batch_pull!첫AccessDenied발생 (first_seen) - 2026-07-13 19:02 ~ 20:16 KST — 988건 반복 (cron 이 배치별로 계속 재시도)
- 2026-07-13 20:16 KST — 클러스터 마지막 관측 (
last_seen)
Error Log#
batch_pull! error on batch arn:aws:ecs:us-west-2:002596530511:task/cupix-tesla-ece/f08cf04288c144f384c0a517fc3ec322,arn:aws:ecs:us-west-2:002596530511:task/cupix-tesla-ece/522564c7ebb14f7a8ce1ba152e821400,arn:aws:ecs:us-west-2:002596530511:task/cupix-tesla-ece/ed4c0e4d621e47e89a0d92f933a76d10 - 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-migration-worker - 발생 횟수: 988
- 최초 발생: 2026-07-13 19:02 KST
- 최근 발생: 2026-07-13 20:16 KST
batch_pull! 은 4개 스코프(blank/pending/running/stopping) 에 대해 주기적으로 실행되어 ECS task 의 최신 상태를 DB (AwsTask) 에 반영한다. 이 호출이 실패하면:
AwsTask레코드의last_status가 stale 하게 남는다.PRE_RUNNING_STATUSES(PROVISIONING,PENDING,ACTIVATING) 에서 실제로는RUNNING/STOPPED로 전이된 task 들이 DB 상 갱신되지 않음.after_save에 걸린job.run_task_stopped_callbacks(task_id)가 트리거되지 않아 상위 job 파이프라인의 완료 처리가 지연될 수 있다.
Root Cause Summary#
tesla-api-production-5nod IAM user 에 연결된 api_ecs_policy (Terraform: cupix-service/api-eb/main.tf:769-801) 는 ecs:* 액션을 arn:aws:ecs:*:*:task/* 등의 resource-scoped ARN 목록으로 제한하고 있다. AWS ECS 는 2020년 이후 task ARN 포맷을 old (task/<taskId>) 에서 long (task/<clusterName>/<taskId>) 으로 전환했고, 실제 호출되는 ARN 은 arn:aws:ecs:us-west-2:002596530511:task/cupix-tesla-ece/<taskId> (long 포맷) 이다. IAM ARN wildcard * 는 / 를 매칭하지 않으므로 task/* 패턴은 long ARN 을 매칭하지 못하고, ecs:DescribeTasks 호출이 어느 Statement 에도 인가되지 않아 AWS 가 AccessDenied 로 응답한다. 에러 메시지의 on resource: * 는 매칭되는 정책이 하나도 없을 때 나타나는 AWS 의 표준 응답 문구다. 이 문제가 이번 시점에 대량 발생한 이유는 로그만으로는 특정할 수 없으며(코드/Terraform 커밋 이력 상 관련 변경 없음, 최근 6bfe7d3f/030403df 은 무관), 계정 단위 ECS long ARN 강제 전환·SCP·최근 배포된 새 task 들이 처음으로 long-format ARN 을 저장하기 시작한 데 따른 것 중 하나로 추정된다 (uncertain -- needs verification against AWS account settings and recent deploys).
Technical Analysis#
Code Path#
Cron entry → Cupix::Cron::AwsTask.pull_* → AwsTask.batch_pull!(tasks) → Aws::ECS::Client#describe_tasks → AccessDenied → rescue StandardError 로그.
- Entry point:
lib/cupix/cron/aws_task.rb:3-31 - Failure point:
app/models/aws_task.rb:55-59(describe_tasks call),app/models/aws_task.rb:86-88(rescue/log)
Cron entry:
def self.pull_blank
tasks = ::AwsTask.blank.to_a
::AwsTask.batch_pull!(tasks)
# ...
end
def self.pull_pending
tasks = ::AwsTask.pending.to_a
::AwsTask.batch_pull!(tasks)
# ...
end
def self.pull_all
tasks = ::AwsTask.pullable.to_a
::AwsTask.batch_pull!(tasks)
# ...
end
배치 호출 지점:
def self.batch_pull!(tasks)
tasks = tasks.to_a
return if tasks.empty?
ecs = tasks.first.ecs_client
cluster_name = $AWS[:ecs][:cluster_name]
task_ids = tasks.filter_map(&:task_id)
tasks_by_task_id = tasks.index_by(&:task_id)
task_ids.each_slice(BATCH_SIZE) do |batch_ids|
resp = ecs.describe_tasks({
tasks: batch_ids,
cluster: cluster_name,
include: ['TAGS']
})
# ...
rescue StandardError => e
Cupix::Logger.error("batch_pull! error on batch #{batch_ids.first(3).join(',')} - #{e.message}", class: name, function: __method__)
end
end
task_id 필드에 저장된 값은 full ARN 형식이며, describe_tasks 는 이를 그대로 AWS 에 전달한다. AWS 는 호출자 IAM user 의 policy 를 조회해 각 task ARN 에 대해 매칭되는 Statement 가 있는지 확인한 뒤, 없으면 위 에러를 던진다.
문제의 IAM policy (실제 정책은 AWS 계정에 있으나 Terraform 정의는 아래와 같음):
resource "aws_iam_policy" "api_ecs_policy" {
# ...
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "HandleECS",
Effect = "Allow",
Action = [
"ecs:*"
],
Resource = [
local.resource_ecs, # cluster/<name>
"arn:aws:ecs:*:*:task/*", # 새 long-format task ARN 미매칭
"arn:aws:ecs:*:*:container-instance/*",
"arn:aws:ecs:*:*:service/*",
"arn:aws:ecs:*:*:task-set/*/*/*",
"arn:aws:ecs:*:*:task-definition/*:*"
]
}
]
})
}
기대 동작: ecs:DescribeTasks 가 arn:aws:ecs:us-west-2:002596530511:task/cupix-tesla-ece/<taskId> 리소스에 대해 허용되어야 함.
실제 동작: task/* 는 task/<taskId> (segment 1개) 만 매칭하고 task/<cluster>/<taskId> (segment 2개) 를 매칭하지 못하므로, describe_tasks 호출이 모든 대상 task 에 대해 거부됨. AWS 는 매칭되는 Statement 가 없을 때 on resource: * 를 반환한다.
Log Evidence#
Datadog query:
service:cupixworks-migration-worker status:error "batch_pull!"
Sample logs (최근 3시간, 5건 확인):
{
"timestamp": "2026-07-13 20:16:23 KST",
"status": "error",
"class": "AwsTask",
"function": "batch_pull!",
"message": "batch_pull! error on batch arn:aws:ecs:us-west-2:002596530511:task/cupix-tesla-ece/48e9afa19f0a4682a1d662f21d4eec79,arn:aws:ecs:us-west-2:002596530511:task/cupix-tesla-ece/d1b13a496a7848e09c8d1ad0d1c85a34,arn:aws:ecs:us-west-2:002596530511:task/cupix-tesla-ece/3454fc8384d64a86834a75295c23a3a2 - 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"
}
- 관측된 모든 task ARN 은 long-format (
task/cupix-tesla-ece/<hex>) 이다. - 호출자는 IAM user
tesla-api-production-5nod—cupix-infrastructure/cupix-service/api-eb/main.tf:642-647의aws_iam_user "api_user"(name =tesla-api-${var.service_env}-${random_string}) 로 생성되는 EB API 인스턴스용 access key 다. - 상태 보드상 동일 서비스에서 2026-07-10, 2026-07-13 04:07 UTC 에도 root_cause_types=
unknown로 분류된 별개 클러스터가 있었으나 fingerprint 가 달라 직접 상관관계는 확인되지 않는다 (uncertain -- needs verification).
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | IAM policy api_ecs_policy 의 task/* resource pattern 이 long ARN (task/<cluster>/<taskId>) 을 매칭하지 못해 ecs:DescribeTasks 가 거부 |
Terraform cupix-infrastructure/cupix-service/api-eb/main.tf:791-794 의 arn:aws:ecs:*:*:task/*; 실제 호출 ARN 은 task/cupix-tesla-ece/<taskId> (long); AWS IAM wildcard * 는 / 미매칭; 에러 문구 no identity-based policy allows the ecs:DescribeTasks action |
— | Confirmed |
| H2 | 특정 리전(ap-southeast-1) 에서 ECS 엔드포인트가 다르게 동작 |
클러스터 tag regions: [ap-southeast-1] |
실제 호출 ARN 및 클러스터는 us-west-2 (cupix-tesla-ece); AwsTask#ecs_client 도 ENV['CUPIX_REGION'] || 'us-west-2' 로 고정 (app/models/aws_task.rb:91-93) |
Rejected |
| H3 | 코드 변경으로 인증되지 않은 새 API 호출이 추가됨 | — | git log -L 769,801:cupix-service/api-eb/main.tf 은 whitespace-only 변경(030403df); AwsTask.batch_pull! 및 cron 코드 최근 변경 없음 |
Rejected |
| H4 | Aws 자격 증명이 만료/rotate 되어 다른 user 로 전환 | — | 에러 메시지는 AccessDenied (인증 성공 후 인가 실패) 이지 InvalidClientTokenId/SignatureDoesNotMatch 가 아님; user ARN 도 정상 tesla-api-production-5nod 로 표시 |
Rejected |
| H5 | Trigger 는 계정/리전 단위 ECS long-ARN 강제 활성화 또는 SCP 변경 | 그 이전에는 same policy 로 정상 동작; 문제가 특정 시각(19:02 KST)부터 대량 발생; long-format ARN 이 정책 매칭을 실패시키는 것과 일관 | AWS 계정 설정/CloudTrail 미조회 — 로그만으로는 미확정 | Inconclusive (uncertain -- needs verification via CloudTrail PutAccountSetting / SCP 변경 이력) |
Fix Recommendation#
즉시 조치 (Critical)#
- 파일:
cupix-infrastructure/cupix-service/api-eb/main.tf:791-798(api_ecs_policy의HandleECSstatement 의Resource배열) - 접근: long-format task ARN 을 매칭하도록 wildcard 패턴을 넓히고, list/describe 계열 API 는 resource-level 제한이 아예 불가능한 경우가 있으므로 이를 분리한다.
arn:aws:ecs:*:*:task/*를arn:aws:ecs:*:*:task/*/*로 변경 (또는 두 패턴을 병기)- 관련 리소스인
container-instance/*,service/*도 필요 시*/*로 확장 검토 (AWS long ARN 정책 동일하게 적용됨) - 근거: AWS 문서 Amazon ECS resource IDs 에 따르면 long ARN 은 cluster segment 를 포함하므로 policy resource 도
task/*/*패턴이 필요.
- 선행 확인: AWS 콘솔의
ECS → Account Settings에서 이 계정의taskLongArnFormat이enabled인지, 언제 전환됐는지 CloudTrail (PutAccountSetting) 로 확인 — H5 검증 및 다른 계정 대비 rollout 순서 판단에 필요. - rollback 안전성: 정책을 더 넓게 만들 뿐 축소가 없으므로 rollback 은 이 커밋을 revert 하면 즉시 이전 상태로 돌아간다.
단기 개선 (1주 이내)#
AwsTask.batch_pull!의rescue StandardError를 세분화:Aws::ECS::Errors::AccessDeniedException은error로 유지하되, transient network/ThrottlingException은warn로 다운그레이드하고, 반복 발생 시 반복 로그가 스팸이 되지 않도록 batch 단위로 dedup 한다 (app/models/aws_task.rb:86-88). 근거: 이번 사고는 4분 창에 988건이 로그를 도배해 alert 노이즈가 심했다.describe_tasks결과가 지속 실패할 때AwsTask스코프(blank/pending/running/stopping) 별 처리 지연을 감지할 수 있는 최소 metric (성공/실패 카운트) 을 추가한다.
장기 개선 (재발 방지)#
- IAM policy 의
ecs:*action 을 그대로 두는 대신, migration/worker 가 실제로 사용하는 action 집합만 나열하고 resource wildcard 대신Condition: { StringEquals: { "ecs:cluster": "arn:aws:ecs:...:cluster/cupix-tesla-ece" } }로 좁히는 것을 고려.DescribeTasks,StopTask등은 이 조건 키를 지원한다. 이렇게 하면 ARN 포맷 변화에 대한 취약성이 사라진다. - Terraform 정책 변경에 대한
awspec/iamlive기반의 규칙 검증(예: long ARN 매칭 여부) 을 CI 에서 실행. - 계정-레벨 ARN 포맷 전환 이벤트를 Datadog monitor 로 잡을 수 있도록 CloudTrail
PutAccountSetting을 forwarding.
Monitoring#
Datadog 쿼리 (release dashboard timeseries widget 용, writing-datadog-monitoring-queries 가이드에 따라 aggregation 함수만 사용):
batch_pull!AccessDenied 재발 감지:
service:cupixworks-migration-worker status:error @class:AwsTask @function:batch_pull!
- ECS 인가 실패 전반 (다른 코드 경로 포함):
service:cupixworks-migration-worker status:error "not authorized to perform: ecs:"
- 성공 카운트 (fetched 로그) — 실패 대비 정상성 확인:
service:cupixworks-migration-worker status:info @class:AwsTask "Task fetched"
메트릭 알림 후보:
- 5분 rolling window 에서
batch_pull!error rate > 10/min 시 P2 알림. Task fetchedinfo 로그가 15분간 0 건이면 cron 이 완전히 멈춘 것으로 P1 알림.
Risk Assessment#
- Risk level: medium — IAM policy 확장은 blast radius 가 큰 리소스이므로 반드시 staging → production 순으로 배포하고 CloudTrail
Deny이벤트를 배포 후 30분간 모니터링해야 한다. 다만 이번 변경은 wildcard 확장(축소 아님)이라 기존 기능을 잃을 위험은 없다. - 예상 복잡도: standard — Terraform 1줄 수정 + plan/apply, 이후 dev/stage/production 순차 적용. 코드 변경은 최소.