QMAws#runTask GPU 리소스 경합 — 카운터 기반 검증 부족
RCA: QMAws::startTask | ECS failures - DRAINING (job_id 1149392)
Overview#
What Happened#
2026-06-24 22:23 KST에 cupixworks-any-compute-agent 가 ECS cluster cupix-tesla-ece (us-west-2)의 container instance e8a48cda... 위로 skat-master ARM task를 StartTask 호출한 결과, ECS가 해당 instance의 상태를 DRAINING 으로 보고하며 1건의 task 배치 실패를 기록했다. 동일 job 1149392 는 11초 후(22:23:41)와 33초 후(22:24:02) 재시도에서 다른 instance를 선택해 정상 시작되었고, 사용자/data pipeline 영향은 없었다.
Quick Facts#
| Field | Value |
|---|---|
| exception.class | QMAws::startTask ECS failure (reason: DRAINING) |
| exception.message | ECS failures - job_id: 1149392, task_definition: cupix-skat-master-production-arm |
| top_frame | applications/agents/packages/cupix-tesla-compute-agent/src/model/qmAws.ts:197 |
| runtime | Node.js (compute-agent) |
| env | production, us-west-2 |
| cluster | cupix-tesla-ece |
| container_instance | e8a48cdadab747bcac6cda204c841846 |
| job_id | 1149392 (Capture create_capture, capture_id 720328) |
Affected Teams#
| Team / Domain | Error Count | Impact |
|---|---|---|
| gad (capture pipeline) | 1 | 없음 — 11~33초 내 다른 instance에서 SQS 재시도로 자동 복구 (task size: 1 at 22:24:02) |
Timeline#
- 2026-06-24 22:23:18 KST — JobManager가 SQS message(
fef9e7ba-...)에서 job 1149392 처리 시작.runTask첫 시도 — 적합 instance 없음,task size: 0종료. - 2026-06-24 22:23:29 KST —
runTask재진입.describeContainerInstances가 instancee8a48cda...를status: ACTIVE, running_tasks: 0, pending_tasks: 0으로 보고 → 가드 통과 후startTask호출. ECS 응답failures: [{ reason: "DRAINING" }]으로 error log 기록 (본 cluster 발생 지점). - 2026-06-24 22:23:41 KST — 재시도. 남은 instance는 메모리 991 MiB · pending 1 뿐이라 가드(runningTasks==0 && pendingTasks==0)에 막혀
task size: 0. - 2026-06-24 22:24:02 KST — 다시 재시도. instance
14d82840...(ACTIVE, mem 126943 MiB, pending 0) 선택,startTask성공.task size: 1. - 2026-06-24 22:24:03 KST —
updateTaskIdToJob가 task ARN70c29c87...를 job 1149392 에 매핑하며 정상 처리 완료.
Error Log#
QMAws::startTask | ECS failures - job_id: 1149392, task_definition: cupix-skat-master-production-arm, cluster: cupix-tesla-ece, container_instance: arn:aws:ecs:us-west-2:002596530511:container-instance/cupix-tesla-ece/e8a48cdadab747bcac6cda204c841846, failures: [{"arn":"arn:aws:ecs:us-west-2:002596530511:container-instance/e8a48cdadab747bcac6cda204c841846","reason":"DRAINING"}]
Impact#
- Service:
cupixworks-any-compute-agent - Team: gad
- 발생 횟수: 1 (이 cluster) / 최근 7일 동일 reason DRAINING 8건
- 최초 발생: 2026-06-24 22:23 KST
- 최근 발생: 2026-06-24 22:23 KST
- 사용자 영향: 없음 — 동일 job이 두 번의 재시도 끝에 22:24:02 KST에 다른 instance로 정상 task 배치됨.
Root Cause Summary#
runTask 는 describeContainerInstances 응답에서 status == 'ACTIVE' 인 instance만 골라 startTask 를 호출한다. 그러나 describe 호출과 startTask 호출 사이에는 수십~수백 ms 의 시간 차가 존재하고, 이 사이에 scale_in.py (또는 ASG lifecycle hook) 가 동일 instance를 DRAINING 으로 전환할 수 있다. ECS는 DRAINING instance 에 새 task를 배치하지 않으므로 data.failures 에 reason: "DRAINING" 을 응답으로 반환하고, 코드는 이를 logger.error 로 기록한다 (qmAws.ts:197-203). 즉, 본질은 분산 시스템 상의 TOCTOU(time-of-check vs time-of-use) race 이며, runTask 루프가 다음 instance로 fallback 하여 SQS 재시도로 즉시 자체 복구되는 expected operational condition 이다. 동일 패턴의 AGENT failure 는 episode c6afd4dc(2026-04-21) 에서 이미 warn 으로 강등된 바 있다.
Technical Analysis#
Code Path#
- Entry point:
applications/agents/packages/cupix-tesla-compute-agent/src/manager/job.manager.ts(SQS message 수신 후runTask호출) - 후보 instance 필터:
qmAws.ts:149-152 - Failure point (error log emission):
qmAws.ts:191-204 - Fallback:
qmAws.ts:155—if (tasks.length > 0) break;이후 다음 instance로 진행 /runTask자체가 SQS retry 로 다시 호출됨
runTask 내부의 instance 선택 가드:
logger.info('QMAws::runTask | job_id: %d, container_instance: %s, status: %s, available_memory_mib: %d, available_cpu: %d, running_tasks: %d, pending_tasks: %d',
job.id,
containerInstance.containerInstanceArn,
containerInstance.status,
memoryResource?.integerValue || 0,
cpuResource?.integerValue || 0,
containerInstance.runningTasksCount,
containerInstance.pendingTasksCount
);
if (containerInstance.status == 'ACTIVE'
&& containerInstance.runningTasksCount == 0
&& containerInstance.pendingTasksCount == 0
) {
try {
tasks = await this.startTask(awsClusterName, containerInstance.containerInstanceArn as string, job);
if (tasks.length > 0) break;
} catch (error) {
return;
}
}
startTask 의 ECS 응답 처리 — data.failures 가 비어 있지 않을 때 logger.error 로 기록한다:
this.ecs.startTask(params, (err, data) => {
if (err) {
logger.error('QMAws::startTask | end - %s', err.message);
reject(err);
} else {
if (data.failures && data.failures.length > 0) {
logger.error('QMAws::startTask | ECS failures - job_id: %d, task_definition: %s, cluster: %s, container_instance: %s, failures: %s',
job.id,
taskDefinition,
awsClusterName,
containerInstanceArn,
JSON.stringify(data.failures.map(f => ({ arn: f.arn, reason: f.reason, detail: f.detail })))
);
}
const tasks = data.tasks;
if (tasks == undefined) {
logger.info('QMAws::startTask | end - task empty');
resolve([]);
} else {
logger.debug('QMAws::startTask | end - task size: %d', tasks.length);
resolve(tasks);
}
}
});
기대 동작 vs 실제 동작: describeContainerInstances 가 ACTIVE 를 반환했으므로 코드의 입장에서는 task 배치가 성공하리라 기대한다. 그러나 ECS state 는 비동기로 변경되며, 두 API 호출 사이의 race 가 존재한다. 그 결과 ECS 는 정상적으로 failures.reason = DRAINING 응답을 돌려주며, 시스템은 다음 instance / SQS retry 로 자체 복구된다 — 즉 외부 dependency 의 정상 동작 범위 내이지 코드 버그가 아니다.
Log Evidence#
Datadog query (재현):
service:cupixworks-any-compute-agent "1149392"
핵심 로그 (시간순):
2026-06-24 22:23:18 info JobManager::createJobFromMessages | message id: fef9e7ba-01e8-4bce-a5ec-0f13afea231f, body: {"job":{"id":1149392},"session":{"id":10682851,...},"cpu_architecture":"arm","launch_mode":"CUPIXWORKS"}
2026-06-24 22:23:18 info QMAws::runTask | begin - job id: 1149392, task def: cupix-skat-master-production-arm, instance_type: m8g.8xlarge
2026-06-24 22:23:18 info QMAws::runTask | end - job id: 1149392, task size: 0
2026-06-24 22:23:29 info QMAws::runTask | job_id: 1149392, container_instance: .../e8a48cdadab747bcac6cda204c841846, status: ACTIVE, available_memory_mib: 126943, available_cpu: 32768, running_tasks: 0, pending_tasks: 0
2026-06-24 22:23:29 error QMAws::startTask | ECS failures - job_id: 1149392, ... failures: [{"arn":".../e8a48cdadab747bcac6cda204c841846","reason":"DRAINING"}]
2026-06-24 22:23:41 info QMAws::runTask | job_id: 1149392, container_instance: .../de1cc8ca8fc3400eb1ece642d52c54cd, status: ACTIVE, available_memory_mib: 991, available_cpu: 32768, running_tasks: 0, pending_tasks: 1
2026-06-24 22:23:41 info QMAws::runTask | end - job id: 1149392, task size: 0
2026-06-24 22:24:02 info QMAws::runTask | job_id: 1149392, container_instance: .../14d828408c78432090d935cf9166f626, status: ACTIVE, available_memory_mib: 126943, available_cpu: 32768, running_tasks: 0, pending_tasks: 0
2026-06-24 22:24:03 info QMAws::runTask | end - job id: 1149392, task size: 1
2026-06-24 22:24:03 info QMJob::updateTaskIdToJob | end - job id: 1149392, task arn: .../70c29c872c2e4af7a4823f50f99493fd
최근 7일 DRAINING 발생 분포 (Datadog service:cupixworks-any-compute-agent status:error "DRAINING" over now-7d):
2026-06-18 05:03 KST job 1134267 pano-postprocessor cupix-tesla-ece
2026-06-18 16:09 KST job 207240 3d-reconstruction cupix-tesla-ece-au (2건)
2026-06-19 01:03 KST job 1137146 pano-postprocessor cupix-tesla-ece
2026-06-19 17:33 KST job 1139875 3d-reconstruction cupix-tesla-ece
2026-06-23 17:33 KST job 1146586 pano-postprocessor cupix-tesla-ece (2건)
2026-06-24 22:23 KST job 1149392 skat-master-arm cupix-tesla-ece ← 본 cluster
→ 다양한 task_definition / cluster (us-west-2, ap-southeast-2) 에서 비슷한 빈도(≈일 1건)로 재현되는 만성적 race 신호. 모든 케이스에서 후속 SQS retry 가 성공한 흔적이 있어 사용자 영향 보고는 없다.
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | TOCTOU race: describeContainerInstances 가 ACTIVE 로 본 직후 ASG/scale_in 이 instance를 DRAINING 으로 전환 → 즉시 SQS retry/다음 instance로 자체 복구 |
qmAws.ts:149-154 가 ACTIVE 만 통과시키는데 ECS는 DRAINING 응답. 동일 job 1149392 가 11/33초 후 다른 instance로 정상 시작 (task size: 1 at 22:24:02). 동일 패턴이 us-west-2 / au 양 region에서 7일간 8건 재현. |
없음 | Confirmed |
| H2 | 코드 버그 — instance 상태 가드가 누락 | qmAws.ts:149 에 status == 'ACTIVE' 가드 존재. ASG 의 비동기 상태 변화는 클라이언트 코드로 막을 수 없는 분산 시스템 race 임. |
코드는 가드 후에 호출하고 있으며, 실패 시 다음 instance 로 fallback 한다. | Rejected |
| H3 | ECS Agent 연결 문제 (reason: AGENT) — episode c6afd4dc 와 동일 원인 | 동일 file/log message 패턴, 동일 service. | failures 의 reason 필드가 DRAINING (AGENT 아님). |
Rejected |
| H4 | task_definition / IAM / 권한 오류로 task가 영구적으로 시작 불가 | ECS error log 발생. | 30~40초 후 동일 task_definition 으로 다른 instance 에서 성공 (22:24:02). 영구 실패 아님. | Rejected |
| H5 | 외부 의존성 outage (AWS us-west-2 ECS) | startTask 가 ECS 응답을 받음. | status-board for-cluster: active: null. Datadog 로그 상 동일 cluster (cupix-tesla-ece) 의 다른 startTask 는 정상. |
Rejected |
Fix Recommendation#
즉시 조치 (Critical)#
본 cluster 단독으로는 코드 변경이 필요하지 않다. SQS retry 로 자체 복구되며 사용자 영향이 없는 expected operational condition 이다. 그러나 매일 1건꼴로 error 레벨 alert noise 가 발생하므로 다음 단기 개선과 함께 처리하는 것을 권장.
단기 개선 (1주 이내)#
applications/agents/packages/cupix-tesla-compute-agent/src/model/qmAws.ts:197-203의data.failures처리logger.error→logger.warn강등. AGENT failure 와 동일 근거(self-recovery via SQS retry, no user impact). Episode c6afd4dc (2026-04-21, PR merged) 의 패턴을 그대로 따른다.- 메시지에
failures[].reason을 prefix 로 노출해 (e.g.ECS failures (DRAINING) - ...) Datadog facet/필터링과 alerting 분기에 활용. 현재는 JSON.stringify 결과 안에 reason 이 묻혀 있어 Datadog 에서 facet 으로 그룹화하기 어렵다.
장기 개선 (재발 방지)#
StartTask→RunTask마이그레이션. RunTask 는 ECS placement strategy 가 instance 선택까지 담당해 클라이언트 코드의 TOCTOU race 자체가 사라진다. service note (memory/services/cupixworks-any-compute-agent.md) 에 이미 평가 중이라고 기록됨.cupix-infrastructurerepo 의 capacity provider / ASG 설정 변경이 동반된다.scale_in.pyLambda 와 compute-agent 간의 lifecycle 조율: ASGterminating:waitlifecycle hook + agent side 의dry-run검증을 도입하면 ECS describe/start 사이 race 창을 줄일 수 있다 (Option B).
Monitoring#
-
DRAINING/AGENT failure rate (proof-of-fix; log-level 강등 후 warn 으로 옮긴 다음 사용):
textsum:logs.hits\{service:cupixworks-any-compute-agent,status:error,@message:*DRAINING*\}.as_count() -
재시도 후 자체 복구 비율 (DRAINING 직후 동일 job 의 task size > 0 비율):
textsum:logs.hits\{service:cupixworks-any-compute-agent,@message:*"task size: 1"*\}.as_count() -
ECS cluster scale-in 빈도 (DRAINING 시점과 상관관계 확인):
textavg:aws.ecs.container_instance_count\{clustername:cupix-tesla-ece\}
알림 임계: DRAINING warn 이 1시간 내 5건 이상 발생 시 Slack 경고 (현재는 일 1건 수준이므로 5건은 lifecycle 이슈 신호).
Risk Assessment#
- Risk level: low — self-recovering, no user impact, 7일간 8건의 만성 noise.
- 예상 복잡도: trivial (log-level 강등) — episode c6afd4dc 의 절차/PR 형태를 그대로 답습 가능.