ES /docs

failed to generate thumbnail - error: 503 Service Unavailable

RCA: failed to generate thumbnail - error: 503 Service Unavailable

Overview#

What Happened#

2026-04-20 09:08~10:53 UTC 사이에 cupixworks-workerCupix::ThumbnailService가 AWS API Gateway 기반 thumbnail Lambda를 호출할 때 503 Service Unavailable 응답을 받아 69건의 에러가 발생했다. 주로 eu-central-1 리전(63건)에서 발생했으며, me-central-1 S3 버킷의 리소스를 대상으로 한 thumbnail 생성 요청이 실패했다. RestClient.post 호출에 재시도 로직이 없어 일시적인 503 응답이 곧바로 에러로 전환되었다.

Quick Facts#

Field Value
exception.class Cupix::Errors::System (SYS20000)
exception.message failed to generate thumbnail - error: 503 Service Unavailable
top_frame app/services/cupix/thumbnail_service.rb:59
deploy 72acbce4-cupixworks (2026-04-16)
env production (eu-central-1, us-west-2)

Affected Teams#

Team / Domain Error Count Impact
cupixworks-worker (eu-central-1) 63 me-central-1 소스 pano의 thumbnail 생성 실패
cupixworks-worker (us-west-2) 6 me-central-1 소스 pano의 thumbnail 생성 실패

Timeline#

  1. 07:53 UTC — me-central-1 소스 thumbnail 503 에러 최초 발생 (확장 시간대 분석)
  2. 09:08 UTC — 클러스터 감지 시간대 내 최초 에러
  3. 09:12~10:05 UTC — 약 50분간 에러 소강
  4. 10:20~10:40 UTC — 에러 최대 집중 구간 (분당 ~2건)
  5. 10:53 UTC — 클러스터 감지 시간대 내 마지막 에러
  6. ~11:13 UTC — 확장 시간대에서 에러 종료 (총 ~105건)

Error Log#

Datadog Logs

text
failed to generate thumbnail - error: 503 Service Unavailable

Impact#

  • Service: cupixworks-worker
  • 발생 횟수: 69 (감지 시간대), ~105 (확장 시간대)
  • 최초 발생: 2026-04-20T09:08:25.101Z
  • 최근 발생: 2026-04-20T10:53:45.901Z
  • 영향 범위: 69개 고유 pano의 thumbnail 미생성. 모두 me-central-1 S3 버킷 소스. eu-central-1 worker 63건, us-west-2 worker 6건. 같은 시간대에 다른 리전(apse2 등) 소스의 thumbnail은 정상 생성됨 — 간헐적 장애.

Root Cause Summary#

AWS API Gateway 뒤의 thumbnail Lambda 서비스가 간헐적으로 503 Service Unavailable을 반환했고, Cupix::ThumbnailService.generate_thumbnail!RestClient.post를 직접 호출하면서 재시도 로직이 전혀 없어 일시적인 503 응답이 곧바로 Cupix::Errors::System (SYS20000)으로 변환되었다. Sidekiq worker의 retry: 1 설정이 있지만, change_thumbnail_job 메서드가 rescue StandardError => false로 에러를 삼키기 때문에 Sidekiq 레벨 재시도도 발동되지 않았다. 결과적으로 me-central-1 소스 리소스 69건의 thumbnail이 생성되지 못했다.

Technical Analysis#

Code Path#

1. Worker 진입점: ThumbnailChangeWorker::Pano.perform(entity_id)entity.change_thumbnail_job을 호출한다.

app/workers/thumbnail_change_worker.rb:1-8ruby
class ThumbnailChangeWorker
  include Sidekiq::Worker
  sidekiq_options queue: :default, retry: 1

  def perform(entity_id = nil)
    raise NotImplementedError
  end
end

2. Thumbnailable concern: change_thumbnail_jobchange_thumbnail_job!을 호출하되, 모든 StandardError를 잡아서 false를 반환한다. 이로 인해 Sidekiq retry가 트리거되지 않는다.

app/models/concerns/thumbnailable.rb:104-110ruby
def change_thumbnail_job
  change_thumbnail_job!
rescue StandardError
  false
else
  true
end

3. change_thumbnail_job!: S3 소스/타겟 정보를 구성하여 Cupix::ThumbnailService.generate_thumbnail을 호출한다.

app/models/concerns/thumbnailable.rb:78-96ruby
def change_thumbnail_job!
  Cupix::Logger.info('Start Thumbnail Change', class: self.class.name, function: __method__,
    module: 'Thumbnailable', model: { id: id })
  resource_s3_object = self.resource.object
  s3_client_options = Cupix::StorageService.s3_client_options(storage_option: self.resource.storage_option)

  Cupix::ThumbnailService.generate_thumbnail(
    source: {
      bucket: resource_s3_object.bucket_name,
      key: resource_s3_object.key,
      s3_bucket_region: self.resource.storage_option.s3_bucket_region,
      s3_client_options: s3_client_options
    },
    target: {
      bucket: self.fog_directory,
      key: thumbnail_settings[:thumbnail_path],
      s3_bucket_region: self.fog_credentials[:region]
    },
    size: thumb_size
  )
end

4. ThumbnailService (에러 발생 지점): generate_thumbnailgenerate_thumbnail!을 호출하고, 에러 발생 시 로깅 후 false를 반환한다.

app/services/cupix/thumbnail_service.rb:16-24ruby
def generate_thumbnail(source: nil, target: nil, size: [300, 200])
  generate_thumbnail!(source: source, target: target, size: size)
rescue Cupix::Errors::System => e
  Cupix::Logger.error(e.reason, class: self.name, function: __method__,
    thumbnail_source: source, thumbnail_target: target, width: size[0], height: size[1])
  false
else
  true
end

5. Failure point — RestClient.post 직접 호출 (재시도 없음):

app/services/cupix/thumbnail_service.rb:47-60ruby
begin
  width, height = size
  body = {
    source: source,
    target: target,
    width: width,
    height: height
  }
  Cupix::Logger.info('Thumbnail Requested', class: self.name, function: __method__,
    thumbnail_source: source, thumbnail_target: target, width: width, height: height)
  response = RestClient.post(Cupix::Thumbnail.service_url, body.to_json, _header)
  Cupix::Logger.info('Thumbnail Generated', class: self.name, function: __method__,
    thumbnail_source: source, thumbnail_target: target, width: width, height: height)
rescue RestClient::Exception => e
  raise Cupix::Errors::System.new(code: 'SYS20000',
    reason: "failed to generate thumbnail - error: #{e.message}")
end

RestClient.post가 503을 받으면 RestClient::ServiceUnavailable 예외가 발생하고, 이를 Cupix::Errors::System으로 감싸서 재발생시킨다. 재시도 없이 즉시 실패한다.

6. Thumbnail service URL routing: Cupix::Thumbnail.service_url에는 mece1 (me-central-1) 전용 endpoint가 없다. eu-central-1 worker에서는 euce1 endpoint로, us-west-2 worker에서는 default endpoint로 라우팅된다.

lib/cupix/thumbnail.rb:32-45ruby
when 'production'
  case Cupix::Tesla.region_code
  when 'euce1'
    'https://xntqdgslbc.execute-api.eu-central-1.amazonaws.com/api/v1/thumbnails'
  when 'apse2'
    'https://dcttkvyqol.execute-api.ap-southeast-2.amazonaws.com/api/v1/thumbnails'
  when 'apne1'
    'https://og4nljwm74.execute-api.ap-northeast-1.amazonaws.com/api/v1/thumbnails'
  when 'apse1'
    'https://taecez5w5b.execute-api.ap-southeast-1.amazonaws.com/api/v1/thumbnails'
  when 'cace1'
    'https://ktol1xddkh.execute-api.ca-central-1.amazonaws.com/api/v1/thumbnails'
  else
    'https://55d2ydipn2.execute-api.us-west-2.amazonaws.com/api/v1/thumbnails'
  end

Log Evidence#

사용한 Datadog 쿼리:

text
service:cupixworks-worker "failed to generate thumbnail" status:error

대표 에러 로그 항목:

json
{
  "message": "failed to generate thumbnail - error: 503 Service Unavailable",
  "thumbnail_source": {
    "bucket": "cupixworks-source-b169da1a0187-mece1",
    "s3_bucket_region": "me-central-1",
    "key": "resources/3fgfeb/mece1/v1"
  },
  "thumbnail_target": {
    "bucket": "cupix-tesla-static",
    "s3_bucket_region": "us-west-2",
    "key": "c4c21b063c4736dd/thumbnail/panos/81738812"
  },
  "class": "Cupix::ThumbnailService",
  "function": "generate_thumbnail",
  "width": 300,
  "height": 200,
  "tenant": "cupix",
  "request_id": "2ad364f2781dd3aa12e24b41"
}

리전별 분포:

Region Host Error Count
eu-central-1 ip-10-1-80-125.eu-central-1.compute.internal 63
us-west-2 ip-10-1-18-233.us-west-2.compute.internal 6

시간대별 분포 (10분 단위):

Window (UTC) Count
09:00-09:10 4
09:10-09:20 2
09:20-10:00 0 (소강)
10:00-10:10 5
10:10-10:20 3
10:20-10:30 20
10:30-10:40 12
10:40-10:50 18
10:50-11:00 5

핵심 패턴:

  • 모든 69건의 에러가 me-central-1 S3 소스 버킷(cupixworks-source-*-mece1)에서 발생
  • 같은 시간대에 다른 리전(apse2) 소스의 thumbnail은 정상 생성 ("Thumbnail Generated" info 로그 확인)
  • 69개의 고유 pano ID — 재시도가 아닌 각각 별도 리소스의 실패
  • 하류 서비스(thumbnail Lambda)에서 503 에러 로그는 Datadog에서 발견되지 않음 — Lambda 자체 로그가 Datadog에 수집되지 않는 것으로 보임

정상 처리 흐름 (info 로그에서 확인):

text
service:cupixworks-worker (status:warn OR status:info) "thumbnail"

정상적인 thumbnail 생성은 4단계를 거친다:

  1. "Start Thumbnail Change" (Pano, change_thumbnail_job!)
  2. "Thumbnail Requested" (Cupix::ThumbnailService, generate_thumbnail!)
  3. "Thumbnail Generated" (Cupix::ThumbnailService, generate_thumbnail!)
  4. "Thumbnail Changed" (Pano, change_thumbnail_job!)

실패 건에서는 2단계("Thumbnail Requested") 이후 3단계("Thumbnail Generated")가 없고 바로 에러 로그가 기록된다.

Hypotheses Considered#

# Hypothesis Evidence for Evidence against Verdict
H1 Thumbnail Lambda의 일시적 503 + 재시도 로직 부재 에러 메시지가 정확히 "503 Service Unavailable", 같은 시간대 다른 리전 thumbnail은 성공, 간헐적 패턴, thumbnail_service.rb:56RestClient.post 직접 호출로 재시도 없음 Confirmed
H2 me-central-1 S3 접근 불가 (S3 장애) 모든 에러가 me-central-1 소스 503은 S3가 아닌 thumbnail Lambda API Gateway에서 반환, S3 접근은 Lambda 내부에서 수행되므로 S3 에러면 다른 메시지가 나와야 함. 또한 일부 me-central-1 소스 thumbnail은 같은 시간대에 성공 Rejected
H3 Thumbnail Lambda 배포 중 다운타임 503은 배포 중 발생 가능 에러가 2시간 이상 간헐적으로 지속 — 배포 다운타임은 보통 수 분. 또한 다른 리전 소스는 정상 Rejected
H4 API Gateway throttling (429가 503으로 변환) API Gateway는 높은 요청 시 503을 반환할 수 있음 에러 분포가 분당 ~2건으로 throttling을 유발할 정도로 높지 않음. 또한 429가 아닌 503이 반환됨 Rejected

Fix Recommendation#

즉시 조치 (Critical)#

  • app/services/cupix/thumbnail_service.rb:56: RestClient.post 직접 호출을 exponential backoff가 포함된 재시도 래퍼로 교체해야 한다.
  • 이미 수정됨: feature/TSLA-12414 브랜치에서 Cupix::HttpClient 모듈이 도입되었고 PR 86852로 develop에 머지됨. 503, 502, 504, 429에 대해 최대 3회 재시도 + exponential backoff를 적용한다.
  • Production 배포 필요: 현재 production 배포 버전(72acbce4)에는 이 수정이 포함되지 않음.

단기 개선 (1주 이내)#

  • app/models/concerns/thumbnailable.rb:104-110: change_thumbnail_job에서 rescue StandardError => false로 모든 에러를 삼키고 있어 Sidekiq 재시도가 무효화된다. thumbnail 생성 실패 시 최소한 로깅을 추가하거나, 재시도 가능한 에러는 다시 raise하는 로직을 고려해야 한다.
  • Thumbnail Lambda 호출 시 downstream service URL과 response body를 에러 로그에 포함하여 디버깅 가시성을 높인다.

장기 개선 (재발 방지)#

  • Cupix::HttpClient 모듈을 다른 외부 HTTP 호출에도 적용하여 일관된 재시도 정책을 갖추도록 한다.
  • Thumbnail Lambda의 503 발생 원인을 별도로 조사 — Lambda concurrency limit, cold start, 또는 me-central-1 cross-region S3 접근 지연이 원인일 수 있다.
  • me-central-1 전용 thumbnail Lambda endpoint 배포를 검토한다 — 현재 Cupix::Thumbnail.service_urlmece1 케이스가 없어 eu-central-1 endpoint로 라우팅되고 있다.

Monitoring#

  • Thumbnail 503 에러 빈도 모니터링:
text
service:cupixworks-worker "failed to generate thumbnail" status:error
  • Thumbnail Lambda 응답 시간 및 에러율 모니터링 (CloudWatch):
    • 5XXError metric for API Gateway xntqdgslbc (eu-central-1)
  • Production 배포 후 동일 에러 재발 여부 확인

Risk Assessment#

  • Risk level: medium
  • 예상 복잡도: trivial (수정 이미 완료, 배포만 필요)