ES /docs

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#

  1. 2026-06-24 22:23:18 KST — JobManager가 SQS message(fef9e7ba-...)에서 job 1149392 처리 시작. runTask 첫 시도 — 적합 instance 없음, task size: 0 종료.
  2. 2026-06-24 22:23:29 KSTrunTask 재진입. describeContainerInstances 가 instance e8a48cda...status: ACTIVE, running_tasks: 0, pending_tasks: 0 으로 보고 → 가드 통과 후 startTask 호출. ECS 응답 failures: [{ reason: "DRAINING" }] 으로 error log 기록 (본 cluster 발생 지점).
  3. 2026-06-24 22:23:41 KST — 재시도. 남은 instance는 메모리 991 MiB · pending 1 뿐이라 가드(runningTasks==0 && pendingTasks==0)에 막혀 task size: 0.
  4. 2026-06-24 22:24:02 KST — 다시 재시도. instance 14d82840... (ACTIVE, mem 126943 MiB, pending 0) 선택, startTask 성공. task size: 1.
  5. 2026-06-24 22:24:03 KSTupdateTaskIdToJob 가 task ARN 70c29c87... 를 job 1149392 에 매핑하며 정상 처리 완료.

Error Log#

Datadog Logs

text
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#

runTaskdescribeContainerInstances 응답에서 status == 'ACTIVE' 인 instance만 골라 startTask 를 호출한다. 그러나 describe 호출과 startTask 호출 사이에는 수십~수백 ms 의 시간 차가 존재하고, 이 사이에 scale_in.py (또는 ASG lifecycle hook) 가 동일 instance를 DRAINING 으로 전환할 수 있다. ECS는 DRAINING instance 에 새 task를 배치하지 않으므로 data.failuresreason: "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:155if (tasks.length > 0) break; 이후 다음 instance로 진행 / runTask 자체가 SQS retry 로 다시 호출됨

runTask 내부의 instance 선택 가드:

applications/agents/packages/cupix-tesla-compute-agent/src/model/qmAws.ts:140-160typescript
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 로 기록한다:

applications/agents/packages/cupix-tesla-compute-agent/src/model/qmAws.ts:191-215typescript
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 실제 동작: describeContainerInstancesACTIVE 를 반환했으므로 코드의 입장에서는 task 배치가 성공하리라 기대한다. 그러나 ECS state 는 비동기로 변경되며, 두 API 호출 사이의 race 가 존재한다. 그 결과 ECS 는 정상적으로 failures.reason = DRAINING 응답을 돌려주며, 시스템은 다음 instance / SQS retry 로 자체 복구된다 — 즉 외부 dependency 의 정상 동작 범위 내이지 코드 버그가 아니다.

Log Evidence#

Datadog query (재현):

text
service:cupixworks-any-compute-agent "1149392"

핵심 로그 (시간순):

text
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):

text
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-203data.failures 처리 logger.errorlogger.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 으로 그룹화하기 어렵다.

장기 개선 (재발 방지)#

  • StartTaskRunTask 마이그레이션. RunTask 는 ECS placement strategy 가 instance 선택까지 담당해 클라이언트 코드의 TOCTOU race 자체가 사라진다. service note (memory/services/cupixworks-any-compute-agent.md) 에 이미 평가 중이라고 기록됨. cupix-infrastructure repo 의 capacity provider / ASG 설정 변경이 동반된다.
  • scale_in.py Lambda 와 compute-agent 간의 lifecycle 조율: ASG terminating:wait lifecycle hook + agent side 의 dry-run 검증을 도입하면 ECS describe/start 사이 race 창을 줄일 수 있다 (Option B).

Monitoring#

  • DRAINING/AGENT failure rate (proof-of-fix; log-level 강등 후 warn 으로 옮긴 다음 사용):

    text
    sum:logs.hits\{service:cupixworks-any-compute-agent,status:error,@message:*DRAINING*\}.as_count()
    
  • 재시도 후 자체 복구 비율 (DRAINING 직후 동일 job 의 task size > 0 비율):

    text
    sum:logs.hits\{service:cupixworks-any-compute-agent,@message:*"task size: 1"*\}.as_count()
    
  • ECS cluster scale-in 빈도 (DRAINING 시점과 상관관계 확인):

    text
    avg: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 형태를 그대로 답습 가능.