ES /docs

CloudFront origin connection failure — 504 Gateway Timeout

RCA: [CupixAerialMap] preprocess fail - CloudFront 504 Gateway Timeout

Overview#

What Happened#

2026-04-23 15:28~16:04 KST 사이에 aerial-map-service의 preprocess 및 postprocess Fargate 태스크가 Cupix API 호출 시 CloudFront로부터 504 Gateway Timeout 응답을 받아 실패했다. 이 클러스터는 preprocess fail 1건을 포함하며, 같은 시간대에 postprocess fail도 6건 발생했다 (별도 fingerprint).

Quick Facts#

Field Value
exception.message 504 Gateway Timeout ERROR - Generated by cloudfront (CloudFront) HTTP3 Server
top_frame src/preprocess/index.ts:175
runtime Node.js 20 on ECS Fargate (2048 MB)
env production, us-west-2

Timeline#

  1. 2026-04-23 15:28:21 KST — 최초 postprocess 504 에러 발생 (aerial map ID 불확인)
  2. 2026-04-23 15:49:57 KST — aerial map 334의 preprocess 성공 (정상 요청 확인)
  3. 2026-04-23 15:51:19 KST — aerial map 334의 preprocess fail (504 Gateway Timeout) - 이 클러스터의 대표 에러
  4. 2026-04-23 15:52:17 KST — preprocess-check Lambda가 Fargate 태스크 실패 감지 (ExitCode: 1, aerial map 332)
  5. 2026-04-23 15:58:34~16:04:47 KST — postprocess에서도 동일 504 에러 반복 발생
  6. 2026-04-23 16:02:05 KST — aerial map 333의 postprocess-check 성공 (정상 요청 확인)

Error Log#

[Datadog Logs](https://app.datadoghq.com/logs?query=service%3Aaerial-map-service%20status%3Aerror%20%40environment%3Aproduction%20%22%5BCupixAerialMap%5D%20preprocess%20fail%20-%20error%3A(%7B%7D)%20%2F%20message%3A(%3C!DOCTYPE%20HTML%20PUBLIC%20%5C-%2F%2FW3C%2F%2FDTD%20HTML%204%22&from_ts=1776923460000&to_ts=1776930720000&live=false)

text
[CupixAerialMap] preprocess fail - error:({}) / message:("<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<HTML><HEAD><META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
<TITLE>ERROR: The request could not be satisfied</TITLE>
</HEAD><BODY>
<H1>504 Gateway Timeout ERROR</H1>
<H2>The request could not be satisfied.</H2>
<HR noshade size="1px">
We can't connect to the server for this app or website at this time. There might be too much traffic or a configuration error. Try again later, or contact the app or website owner.
<BR clear="all">
If you provide content to customers through CloudFront, you can find steps to troubleshoot and help prevent this error by reviewing the CloudFront documentation.
<BR clear="all">
<HR noshade size="1px">
<PRE>
Generated by cloudfront (CloudFront) HTTP3 Server
Request ID: PMhS5RtFCDBDI0Vx1w78L2K-1ODzBQZ_wtx8tVSjyH8L5pXekuyhMA==
</PRE>
</BODY></HTML>")

Impact#

  • Service: aerial-map-service
  • 발생 횟수: 1 (이 클러스터, preprocess fail), 동일 시간대 postprocess fail 6건 추가 발생
  • 최초 발생: 2026-04-23T06:51:20.076Z
  • 최근 발생: 2026-04-23T06:51:20.076Z

Root Cause Summary#

aerial-map-service의 Fargate 태스크(preprocess/postprocess)가 Cupix API(CUPIX_ENDPOINT)에 HTTP 요청을 보낼 때, 해당 API 앞단의 CloudFront distribution이 origin 서버에 연결하지 못해 504 Gateway Timeout을 반환했다. 이 HTML 에러 응답이 axios에 의해 Error 객체로 변환되어 catch 블록에서 error.message로 로깅되었다. 504는 서버 에러이므로 axios retry interceptor가 3회 재시도를 수행했으나, CloudFront origin 장애가 지속되어 모든 재시도가 실패했다. 이는 aerial-map-service 코드 결함이 아니라, CloudFront-origin 간 일시적 연결 장애 (인프라 이슈)로 인한 transient failure이다.

Technical Analysis#

Code Path#

  • Entry point: src/preprocess/index.ts:168 — IIFE에서 app(input) 호출
  • app() 함수 내부에서 CupixApi 인스턴스 생성 후 여러 API 호출 수행 (src/preprocess/index.ts:39-45)
  • CupixApi의 모든 HTTP 요청은 커스텀 axios 인스턴스를 사용 (src/common/axios.ts:8-10)
  • Failure point: src/preprocess/index.ts:175 — catch 블록에서 에러 로깅
src/preprocess/index.ts:168-179typescript
(async () => {
  try {
    await app(input);
    logger.info(`[CupixAerialMap] preprocess success`);
    await sleep(30000); // sleep for log push
    process.exit(0);
  } catch (error: any) {
    logger.error(`[CupixAerialMap] preprocess fail - error:(${JSON.stringify(error)}) / message:(${error.message})`);
    await sleep(30000); // sleep for log push
    process.exit(1);
  }
})();

app() 내부에서 cupixApi.saveProcessingState() 등 API 호출 시 CloudFront 504를 수신하면, axios가 error.response.data에 HTML 응답을 담아 에러를 throw한다. 에러가 AerialMapError 인스턴스가 아니므로 else 분기로 진입하여 AMB710 (Preprocess Fail) 코드로 저장된다.

src/preprocess/index.ts:144-153typescript
  } catch (error: any) {
    if (error instanceof AerialMapError) {
      await cupixApi.saveError(aerialMapId, error.code, error.reason);
    } else {
      logger.error(`[CupixAerialMap] preprocess fail ${error.message}`);
      await cupixApi.saveError(aerialMapId, ERROR_CODE['AMB710'].code, ERROR_CODE['AMB710'].reason);
    }

    throw error;
  }

axios retry interceptor는 500번대 에러에 대해 최대 3회 재시도를 수행하나, CloudFront 장애가 지속되어 모든 재시도가 실패했다:

src/common/axios.ts:1-33typescript
const RETRY_CODES = ['ECONNRESET', 'ETIMEDOUT', 'ECONNABORTED', 'EAI_AGAIN'];
const MAX_RETRIES = 3;
const BASE_DELAY_MS = 1000;
const REQUEST_TIMEOUT_MS = 60000;

const axios = _axios.create({
  timeout: REQUEST_TIMEOUT_MS,
});

axios.interceptors.response.use(undefined, async (error) => {
  const config = error.config;
  if (!config) throw error;

  config.__retryCount = config.__retryCount || 0;

  const retryable = RETRY_CODES.includes(error.code) || (error.response?.status && error.response.status >= 500);

  if (!retryable || config.__retryCount >= MAX_RETRIES) {
    throw error;
  }
  // ... exponential backoff retry
});

error.response.status >= 500 조건에 504가 포함되어 retry가 트리거되었으나, 총 재시도 시간(1s + 2s + 4s = ~7초)으로는 CloudFront origin 장애 회복에 충분하지 않았다.

Log Evidence#

사용한 Datadog 쿼리:

text
service:aerial-map-service status:error
text
service:aerial-map-service "preprocess"
text
service:aerial-map-service status:error "504 Gateway Timeout"

15:49:57 KST에 aerial map 334가 정상 preprocess 완료 후, 15:51:19 KST에 같은 aerial map의 후속 요청 시 504가 발생했다:

text
[2026-04-23 15:49:57 KST] [info] [CupixAerialMap] preprocess success
[2026-04-23 15:49:57 KST] [info] [CupixAerialMap] preprocess time: 45277ms
[2026-04-23 15:51:19 KST] [error] [CupixAerialMap] preprocess fail "<!DOCTYPE HTML...504 Gateway Timeout ERROR..."
[2026-04-23 15:51:20 KST] [error] [CupixAerialMap] preprocess fail - error:({}) / message:("...504 Gateway Timeout...")

preprocess-check Lambda가 Fargate 태스크 실패를 감지한 로그 (aerial map 332, ExitCode 1):

text
[2026-04-23 15:52:17 KST] [info] [CupixAerialMap] preprocess-check - event({"Error":"States.TaskFailed","Cause":"...ExitCode\":1...StopCode\":\"EssentialContainerExited\"..."})

동시간대 정상 처리된 요청도 존재 — aerial map 333이 16:02에 성공적으로 postprocess 완료:

text
[2026-04-23 16:02:05 KST] [info] postprocess-check - event({"result":"success","aerialMap":{"id":333,...}})
[2026-04-23 16:02:08 KST] [info] cupix-process-success-production-vdco - pix4d process finish: success

cupixworks-api 서비스에서 동시간대 aerial_map 관련 에러는 0건 — API 서버 자체의 에러가 아닌 CloudFront 레이어의 문제임을 확인:

text
service:cupixworks-api status:error "aerial_map" — 0 results (2026-04-23 06:20~07:10 UTC)

3일간(04/2004/23) 504 Gateway Timeout 에러 검색 결과, 모든 5건이 2026-04-23 15:2816:04 KST에 집중되어 있어 일시적 burst 패턴을 보인다.

Hypotheses Considered#

# Hypothesis Evidence for Evidence against Verdict
H1 CloudFront-origin 간 일시적 연결 장애로 504 발생 에러 메시지가 CloudFront가 생성한 504 HTML 페이지; 같은 시간대 preprocess/postprocess 모두 동일 패턴; API 서버 자체 에러 0건; 일부 요청은 같은 시간대에 성공 Confirmed
H2 Cupix API 서버 과부하/다운으로 인한 에러 504는 origin 연결 실패 시 발생 가능 cupixworks-api 서비스에 동시간대 에러 로그 0건; aerial map 333이 16:02에 정상 처리됨; 504가 API 서버가 아닌 CloudFront에서 생성됨 Rejected
H3 aerial-map-service의 axios timeout(60초) 초과 REQUEST_TIMEOUT_MS=60000 설정 있음 에러 메시지가 ETIMEDOUT이 아닌 CloudFront HTML 504 응답; axios timeout과 CloudFront 504는 다른 에러 패턴 Rejected
H4 Pix4D API 호출 실패 Pix4dApi도 같은 axios 인스턴스 사용 에러 메시지에 Pix4D 관련 내용 없음; CloudFront는 Cupix API 앞단에 위치; Pix4D API는 별도 endpoint 사용 Rejected

Fix Recommendation#

즉시 조치 (Critical)#

  • 조치 불필요: 이 에러는 CloudFront 인프라의 일시적 장애로 인한 transient failure이다. aerial-map-service 코드 결함이 아니므로 코드 수정 대상이 아니다.

단기 개선 (1주 이내)#

  • src/common/axios.ts:5MAX_RETRIES를 3에서 5로 증가하고, BASE_DELAY_MS에 jitter를 추가하여 CloudFront 장애 시 회복 가능성을 높인다.
  • src/preprocess/index.ts:175src/postprocess/index.ts:179JSON.stringify(error){}를 출력하는 문제가 있다. Error 객체는 JSON.stringify에서 빈 객체가 되므로, error.response?.status, error.response?.data 등 유용한 정보를 별도로 로깅하도록 변경해야 한다.

장기 개선 (재발 방지)#

  • CloudFront origin failover 설정을 검토하여 primary origin 장애 시 secondary origin으로 자동 전환되도록 구성한다.
  • aerial-map-service에서 Cupix API 호출 실패 시 Step Functions 레벨에서 재시도하는 retry policy를 추가한다. 현재는 Fargate 태스크가 ExitCode 1로 종료되면 즉시 실패로 처리된다.

Monitoring#

  • CloudFront 504 에러율 모니터링 추가:
text
service:aerial-map-service status:error "504 Gateway Timeout"
  • Cupix API origin의 health check 및 latency 메트릭을 CloudWatch에서 모니터링
  • aerial-map-service의 preprocess/postprocess 실패율 추적:
text
service:aerial-map-service status:error ("preprocess fail" OR "postprocess fail")

Risk Assessment#

  • Risk level: low
  • 예상 복잡도: trivial — CloudFront 인프라의 transient failure로, 코드 수정 없이도 자연 해소됨. 로깅 개선과 retry 강화는 선택적 개선 사항.