ES /docs

ThumbnailService HTTP 500 — AWS API Gateway timeout

RCA: failed to generate thumbnail - error: 500 Internal Server Error

Error Log#

Datadog Logs

text
failed to generate thumbnail - error: 500 Internal Server Error

Impact#

  • Service: cupixworks-worker
  • 발생 횟수: 52
  • 최초 발생: 2026-04-07T00:44:19.527Z
  • 최근 발생: 2026-04-07T03:26:31.633Z

Root Cause Summary#

Cupix::ThumbnailService.generate_thumbnail!에서 AWS API Gateway 기반 외부 thumbnail 서비스(https://55d2ydipn2.execute-api.us-west-2.amazonaws.com/api/v1/thumbnails)에 HTTP POST 요청 시 500 응답을 수신하고 있습니다. 52건의 에러 모두 단일 호스트(ip-10-1-18-233.us-west-2.compute.internal, PID 957701)에서 발생했으며, 소스 이미지가 cross-region S3 버킷(us-west-1, us-east-1)에 위치합니다. 동일 시간대에 ap-southeast-2, ap-southeast-1 리전의 worker에서는 region-local 버킷을 사용하여 thumbnail 생성이 정상 동작하고 있어, us-west-2 리전의 thumbnail Lambda/API Gateway 서비스 자체의 일시적 장애 또는 cross-region S3 접근 시 발생하는 서비스 내부 오류가 원인으로 판단됩니다.

Technical Analysis#

Code Path#

  • Entry point: ThumbnailChangeWorker::Pano.perform (app/workers/concerns/thumbnail_change_worker/pano.rb:9)
  • Worker가 Pano.find_by_id로 엔티티를 찾고 change_thumbnail_job을 호출합니다.
ruby
# app/workers/concerns/thumbnail_change_worker/pano.rb:9-17
def perform(entity_id = nil)
  entity = ::Pano.find_by_id(entity_id)

  if entity.nil?
    Cupix::Logger.debug("Model not found: #{entity_id}")
  else
    entity.change_thumbnail_job
  end
end
  • change_thumbnail_jobchange_thumbnail_job!을 호출하고, 실패 시 StandardError를 삼켜서 false를 반환합니다 (app/models/concerns/thumbnailable.rb:104-110).
ruby
# app/models/concerns/thumbnailable.rb:104-110
def change_thumbnail_job
  change_thumbnail_job!
rescue StandardError
  false
else
  true
end
  • change_thumbnail_job!은 source/target S3 정보를 구성한 후 Cupix::ThumbnailService.generate_thumbnail을 호출합니다 (app/models/concerns/thumbnailable.rb:78-96).
ruby
# app/models/concerns/thumbnailable.rb:78-96
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
  )
  • generate_thumbnail (non-bang)은 generate_thumbnail!을 호출하고, Cupix::Errors::System을 catch하여 에러를 로깅합니다 (app/services/cupix/thumbnail_service.rb:16-24).
ruby
# app/services/cupix/thumbnail_service.rb:16-24
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
  • Failure point: generate_thumbnail!에서 RestClient.post가 500 응답을 받아 RestClient::Exception이 발생합니다 (app/services/cupix/thumbnail_service.rb:56-59).
ruby
# app/services/cupix/thumbnail_service.rb:55-60
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
  • 호출 대상 URL은 Cupix::Thumbnail.service_url로 결정됩니다. production 환경의 us-west-2 리전에서는 https://55d2ydipn2.execute-api.us-west-2.amazonaws.com/api/v1/thumbnails입니다 (lib/cupix/thumbnail.rb:44).
ruby
# lib/cupix/thumbnail.rb:32-45
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'
  # ... other regions ...
  else
    'https://55d2ydipn2.execute-api.us-west-2.amazonaws.com/api/v1/thumbnails'
  end
  • 기대 동작: RestClient.post가 200을 반환하고 thumbnail이 target S3에 저장됨
  • 실제 동작: 외부 thumbnail 서비스가 500을 반환하여 RestClient::Exception 발생

Log Evidence#

에러 로그 검색 쿼리:

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

52건 모두 동일한 패턴으로, 단일 호스트/PID에서 발생:

json
{
  "message": "failed to generate thumbnail - error: 500 Internal Server Error",
  "class": "Cupix::ThumbnailService",
  "function": "generate_thumbnail",
  "host": "ip-10-1-18-233.us-west-2.compute.internal",
  "pid": "957701",
  "thumbnail_source": {
    "bucket": "cupixworks-source-dc9dcff32488-usea1",
    "s3_bucket_region": "us-east-1"
  },
  "thumbnail_target": {
    "bucket": "cupix-tesla-static",
    "s3_bucket_region": "us-west-2"
  },
  "width": 300,
  "height": 200
}

에러 발생 시간 분포 (4개 버스트):

  • 00:44:19Z — 4건 (source: us-east-1)
  • 01:18:51Z ~ 01:44:21Z — 19건 (source: us-east-1)
  • 03:23:57Z ~ 03:26:31Z — 29건 (source: us-east-1 19건, us-west-1 10건)

소스 버킷 분포:

  • cupixworks-source-dc9dcff32488-usea1 (us-east-1): 33건
  • cupixworks-source-888a512bf858-uswe1 (us-west-1): 19건

정상 동작 로그 검색 쿼리:

text
service:cupixworks-worker status:info "thumbnail"

동일 시간대에 ap-southeast-2와 ap-southeast-1 리전에서는 thumbnail 생성이 정상 완료됨:

json
{
  "message": "Thumbnail Generated",
  "class": "Cupix::ThumbnailService",
  "function": "generate_thumbnail!",
  "host": "ip-10-1-19-158.ap-southeast-2.compute.internal",
  "thumbnail_source": {
    "bucket": "cupixworks-source-a63f5f96341d-apse2",
    "s3_bucket_region": "ap-southeast-2"
  },
  "thumbnail_target": {
    "bucket": "cupix-tesla-au-static",
    "s3_bucket_region": "ap-southeast-2"
  }
}

주요 관찰: 성공 사례는 모두 region-local S3 버킷(source/target 동일 리전)을 사용하지만, 실패 사례는 모두 cross-region S3 소스(us-east-1, us-west-1 → us-west-2 thumbnail 서비스)를 사용합니다.

cupixworks-api에서 thumbnail 관련 에러 로그는 발견되지 않았습니다:

text
service:cupixworks-api "thumbnail" status:error

결과: 0건

Fix Recommendation#

즉시 조치 (Critical)#

  • us-west-2 리전의 thumbnail Lambda/API Gateway 서비스(55d2ydipn2.execute-api.us-west-2.amazonaws.com) 상태를 확인해야 합니다. CloudWatch 로그에서 해당 시간대의 500 에러 원인을 파악해야 합니다.
  • thumbnail 서비스가 cross-region S3 소스를 처리할 때 IAM 권한 또는 presigned URL 생성에 문제가 있는지 확인이 필요합니다.

단기 개선 (1주 이내)#

  • Cupix::ThumbnailService.generate_thumbnail! (app/services/cupix/thumbnail_service.rb:58-59)에서 RestClient::Exception을 catch할 때 response body도 함께 로깅하도록 개선해야 합니다. 현재는 e.message만 로깅하여 "500 Internal Server Error"라는 정보만 얻을 수 있어, 실제 실패 원인(Lambda timeout, S3 접근 실패, 메모리 초과 등)을 파악할 수 없습니다. e.response&.body를 추가 로깅하면 디버깅이 용이해집니다.
  • ThumbnailChangeWorker의 retry를 1에서 3으로 늘려 일시적 장애에 대한 복원력을 높이는 것을 고려해야 합니다.

장기 개선 (재발 방지)#

  • thumbnail 서비스가 cross-region S3 접근을 안정적으로 처리할 수 있도록 아키텍처를 검토해야 합니다. 현재 다른 리전(ap-southeast-2 등)에서는 region-local 버킷으로 정상 동작하므로, us-west-2 리전에서도 source가 다른 리전에 있을 때의 처리 로직을 확인해야 합니다.
  • thumbnail 서비스에 circuit breaker 패턴을 도입하여, 연속 실패 시 불필요한 재시도를 방지하고 빠르게 실패를 감지할 수 있도록 해야 합니다.

Monitoring#

  • thumbnail 서비스 500 에러 모니터링:
text
service:cupixworks-worker status:error "failed to generate thumbnail"
  • 리전별 thumbnail 생성 성공/실패 비교:
text
service:cupixworks-worker ("Thumbnail Generated" OR "failed to generate thumbnail") @host:*us-west-2*
  • thumbnail Lambda CloudWatch 에러 모니터링 추가 (AWS Console)

Risk Assessment#

  • Risk level: medium
  • 예상 복잡도: standard — thumbnail 서비스(Lambda/API Gateway) 측 로그 확인이 필요하며, worker 코드 자체의 수정은 에러 로깅 개선 수준으로 간단하나, 근본 원인이 외부 서비스에 있으므로 인프라 점검이 함께 필요합니다.