ES /docs

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#

Datadog Logs

text
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' 필터를 전달하지만, 이 결과는 조회 시점의 스냅샷이다:

typescript
// 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일 수 있다:

typescript
// 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한다. 실패한 인스턴스에 대한 재시도나 다음 인스턴스 선택이 이 메서드 내에서는 발생하지 않는다:

typescript
// 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 쿼리:

text
service:cupixvista-any-compute-agent status:error @environment:production "QMAws::startTask"
text
service:cupixvista-any-compute-agent "36870"
text
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):

text
[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
text
[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차 시도 성공 로그 (다른 인스턴스):

text
[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
text
[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 관련 에러 빈도 추적:
text
service:cupixvista-any-compute-agent status:error "DRAINING"
  • ECS 태스크 배치 실패율 모니터링:
text
service:cupixvista-any-compute-agent "ECS failures"
  • Job 재시도 횟수가 비정상적으로 높은 경우 알림:
text
service:cupixvista-any-compute-agent "runTask | end" "task size: 0"

Risk Assessment#

  • Risk level: low
  • 예상 복잡도: trivial
  • 단일 발생이며 SQS 재시도로 자동 복구됨. 인스턴스 DRAINING은 ECS AutoScaling 또는 인프라 유지보수 시 정상적으로 발생하는 이벤트이며, 현재 재시도 메커니즘이 이를 처리하고 있음. 다만 코드 레벨에서 DRAINING 인스턴스를 사전에 회피하면 불필요한 재시도와 지연을 줄일 수 있다.