QMAws::startTask | ECS failures - job_id: 36870, task_definition: cupix-capture-3d-reconstruction-pr
RCA: QMAws::startTask | ECS failures - job_id: 36870, task_definition: cupix-capture-3d-reconstruction-pr
Error Log#
QMAws::startTask | ECS failures - job_id: 36870, task_definition: cupix-capture-3d-reconstruction-production, cluster: cupix-tesla-ece, container_instance: arn:aws:ecs:us-west-2:619071347432:container-instance/cupix-tesla-ece/76fb48183fcf4d3db2340035c803b68f, failures: [{"arn":"arn:aws:ecs:us-west-2:619071347432:container-instance/76fb48183fcf4d3db2340035c803b68f","reason":"DRAINING"}]
Impact#
- Service:
cupixvista-any-compute-agent - Team: feelitallprod
- 발생 횟수: 1
- 최초 발생: 2026-04-11T16:12:20.341Z
- 최근 발생: 2026-04-11T16:12:20.341Z
Root Cause Summary#
ECS container instance 76fb48183fcf4d3db2340035c803b68f가 클러스터 cupix-tesla-ece에서 DRAINING 상태로 전환되는 중에 QMAws::startTask가 해당 인스턴스에 태스크를 배치하려 시도하여 실패하였다. listContainerInstances API는 status: 'ACTIVE' 필터로 인스턴스를 조회하지만, 조회 시점과 실제 startTask 호출 시점 사이에 인스턴스 상태가 DRAINING으로 변경되는 race condition이 발생했다. describeContainerInstances가 반환한 데이터에서도 해당 인스턴스가 status: ACTIVE로 보고되었으나, ECS StartTask API 호출 시점에는 이미 드레이닝이 시작된 상태였다. Job 36870은 SQS 재시도를 통해 4번째 시도에서 다른 인스턴스(fdd25ca08f9c4c8b90cfa877a1d67d58)에 성공적으로 배치되어 자동 복구되었다.
Technical Analysis#
Code Path#
- Entry point:
applications/agents/packages/cupix-tesla-compute-agent/src/manager/job.manager.ts— SQS 메시지 수신 후QMAws::runTask호출 QMAws::runTask(qmAws.ts:117): 인스턴스 목록 조회 및 태스크 배치 오케스트레이션QMAws::listContainerInstances(qmAws.ts:238):status: 'ACTIVE'필터로 컨테이너 인스턴스 조회QMAws::describeContainerInstances(qmAws.ts:262): 인스턴스 상세 정보(리소스, 상태) 조회- Failure point:
QMAws::startTask(qmAws.ts:170): ECS StartTask API 호출 시 DRAINING 실패
인스턴스 필터링 로직 — listContainerInstances는 ECS API에 status: 'ACTIVE' 필터를 전달하지만, 이 결과는 조회 시점의 스냅샷이다:
// qmAws.ts:240-244
const params: AWS.ECS.ListContainerInstancesRequest = {
cluster: awsClusterName,
status: 'ACTIVE',
filter: `attribute:ecs.instance-type == ${instanceType} and runningTasksCount == 0`,
maxResults: 10
};
상태 검증 로직 — runTask에서 describeContainerInstances 결과를 기반으로 재확인하지만, 이 역시 stale data일 수 있다:
// qmAws.ts:149-158
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 failures가 발생해도 에러를 로깅만 하고, promise를 reject하지 않고 빈 배열 또는 tasks 배열로 resolve한다. 실패한 인스턴스에 대한 재시도나 다음 인스턴스 선택이 이 메서드 내에서는 발생하지 않는다:
// qmAws.ts:196-213
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 실제 동작:
- 기대:
listContainerInstances가 ACTIVE 인스턴스만 반환하므로,startTask호출 시 태스크가 정상 배치됨 - 실제: 조회와 배치 사이의 시간 차이(약 200ms) 동안 인스턴스가 DRAINING으로 전환되어 ECS가 배치를 거부함.
runTask의 for 루프(qmAws.ts:137)는 사용 가능한 인스턴스가 하나뿐이면 다음 인스턴스를 시도할 수 없으며, 빈 tasks 배열을 반환한다.
Log Evidence#
사용한 Datadog 쿼리:
service:cupixvista-any-compute-agent status:error @environment:production "QMAws::startTask"
service:cupixvista-any-compute-agent "36870"
service:cupixvista-any-compute-agent "QMAws" OR "startTask"
Job 36870 타임라인 (4회 시도):
| 시각 (UTC) | 이벤트 | 결과 |
|---|---|---|
| 16:10:48.288 | SQS 메시지 수신, job 생성 | 1차 시도 시작 |
| 16:10:48.408 | runTask 시작 (g6.4xlarge) |
— |
| 16:10:48.709 | runTask 종료, task size: 0 |
사용 가능한 인스턴스 없음 |
| 16:12:19.947 | SQS 재전달 (2차 시도) | — |
| 16:12:20.075 | runTask 시작 |
— |
| 16:12:20.144 | containerInstances size: 1 | 인스턴스 1개 발견 |
| 16:12:20.145 | 인스턴스 76fb4818... status: ACTIVE, memory: 62019, cpu: 16384 |
ACTIVE로 보고됨 |
| 16:12:20.341 | startTask ECS failures — reason: DRAINING |
에러 발생 |
| 16:12:20.342 | runTask 종료, task size: 0 |
배치 실패 |
| 16:13:52.500 | SQS 재전달 (3차 시도) | — |
| 16:13:52.862 | runTask 종료, task size: 0 |
인스턴스 없음 |
| 16:15:35.440 | SQS 재전달 (4차 시도) | — |
| 16:15:35.625 | 인스턴스 fdd25ca0... status: ACTIVE |
새로운 인스턴스 발견 |
| 16:15:35.824 | runTask 종료, task size: 1 |
성공 |
| 16:15:35.982 | task ARN 할당, SQS 메시지 삭제 | 작업 완료 |
핵심 로그 (인스턴스가 ACTIVE로 보고되었으나 실제로는 DRAINING):
[2026-04-11T16:12:20.145Z] [INFO] QMAws::runTask | job_id: 36870, container_instance: arn:aws:ecs:us-west-2:619071347432:container-instance/cupix-tesla-ece/76fb48183fcf4d3db2340035c803b68f, status: ACTIVE, available_memory_mib: 62019, available_cpu: 16384, running_tasks: 0, pending_tasks: 0
[2026-04-11T16:12:20.341Z] [ERROR] QMAws::startTask | ECS failures - job_id: 36870, task_definition: cupix-capture-3d-reconstruction-production, cluster: cupix-tesla-ece, container_instance: arn:aws:ecs:us-west-2:619071347432:container-instance/cupix-tesla-ece/76fb48183fcf4d3db2340035c803b68f, failures: [{"arn":"arn:aws:ecs:us-west-2:619071347432:container-instance/76fb48183fcf4d3db2340035c803b68f","reason":"DRAINING"}]
4차 시도 성공 로그 (다른 인스턴스):
[2026-04-11T16:15:35.625Z] [INFO] QMAws::runTask | job_id: 36870, container_instance: arn:aws:ecs:us-west-2:619071347432:container-instance/cupix-tesla-ece/fdd25ca08f9c4c8b90cfa877a1d67d58, status: ACTIVE, available_memory_mib: 62019, available_cpu: 16384, running_tasks: 0, pending_tasks: 0
[2026-04-11T16:15:35.824Z] [INFO] QMAws::runTask | end - job id: 36870, task size: 1
동일 시간대 다른 서비스에서 DRAINING 관련 로그: 없음 (이 인스턴스에만 해당하는 격리된 이벤트)
Fix Recommendation#
즉시 조치 (Critical)#
없음. 이 에러는 단일 발생이며 SQS 기반 재시도로 자동 복구되었다. 사용자 영향(약 5분 지연)은 최소한이었다.
단기 개선 (1주 이내)#
startTask 실패 시 다음 인스턴스로 재시도 로직 추가 — qmAws.ts:154-155
현재 startTask가 ECS failures를 반환하면(tasks 배열이 비어있으면) runTask의 for 루프가 break 없이 다음 반복으로 넘어간다. 그러나 listContainerInstances가 단일 인스턴스만 반환하면 재시도할 대상이 없다. startTask가 DRAINING 등 배치 실패를 반환할 때, 해당 인스턴스를 제외하고 listContainerInstances를 재호출하거나, DRAINING 실패를 감지하면 해당 인스턴스 ARN을 블랙리스트에 추가하여 즉시 다른 인스턴스를 시도하는 로직이 도움이 될 수 있다.
에러 레벨 조정 고려 — qmAws.ts:197
DRAINING은 일시적이고 자동 복구되는 인프라 이벤트이므로, logger.error 대신 logger.warn으로 변경하여 불필요한 에러 알림을 줄이는 것을 고려할 수 있다.
장기 개선 (재발 방지)#
ECS Capacity Provider 전략 도입 검토 — 현재 startTask API는 특정 인스턴스를 지정하여 태스크를 배치하므로 DRAINING race condition에 취약하다. ECS runTask API와 Capacity Provider를 사용하면 ECS가 자체적으로 유효한 인스턴스를 선택하므로 DRAINING 인스턴스를 자동으로 회피할 수 있다. 다만 이는 현재 아키텍처의 인스턴스 타입 기반 라우팅 로직과의 호환성을 검토해야 한다.
Monitoring#
- DRAINING 관련 에러 빈도 추적:
service:cupixvista-any-compute-agent status:error "DRAINING"
- ECS 태스크 배치 실패율 모니터링:
service:cupixvista-any-compute-agent "ECS failures"
- Job 재시도 횟수가 비정상적으로 높은 경우 알림:
service:cupixvista-any-compute-agent "runTask | end" "task size: 0"
Risk Assessment#
- Risk level: low
- 예상 복잡도: trivial
- 단일 발생이며 SQS 재시도로 자동 복구됨. 인스턴스 DRAINING은 ECS AutoScaling 또는 인프라 유지보수 시 정상적으로 발생하는 이벤트이며, 현재 재시도 메커니즘이 이를 처리하고 있음. 다만 코드 레벨에서 DRAINING 인스턴스를 사전에 회피하면 불필요한 재시도와 지연을 줄일 수 있다.