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-worker의 Cupix::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#
- 07:53 UTC — me-central-1 소스 thumbnail 503 에러 최초 발생 (확장 시간대 분석)
- 09:08 UTC — 클러스터 감지 시간대 내 최초 에러
- 09:12~10:05 UTC — 약 50분간 에러 소강
- 10:20~10:40 UTC — 에러 최대 집중 구간 (분당 ~2건)
- 10:53 UTC — 클러스터 감지 시간대 내 마지막 에러
- ~11:13 UTC — 확장 시간대에서 에러 종료 (총 ~105건)
Error Log#
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을 호출한다.
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_job이 change_thumbnail_job!을 호출하되, 모든 StandardError를 잡아서 false를 반환한다. 이로 인해 Sidekiq retry가 트리거되지 않는다.
def change_thumbnail_job
change_thumbnail_job!
rescue StandardError
false
else
true
end
3. change_thumbnail_job!: S3 소스/타겟 정보를 구성하여 Cupix::ThumbnailService.generate_thumbnail을 호출한다.
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_thumbnail이 generate_thumbnail!을 호출하고, 에러 발생 시 로깅 후 false를 반환한다.
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 직접 호출 (재시도 없음):
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로 라우팅된다.
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 쿼리:
service:cupixworks-worker "failed to generate thumbnail" status:error
대표 에러 로그 항목:
{
"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 로그에서 확인):
service:cupixworks-worker (status:warn OR status:info) "thumbnail"
정상적인 thumbnail 생성은 4단계를 거친다:
"Start Thumbnail Change"(Pano, change_thumbnail_job!)"Thumbnail Requested"(Cupix::ThumbnailService, generate_thumbnail!)"Thumbnail Generated"(Cupix::ThumbnailService, generate_thumbnail!)"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:56에 RestClient.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_url에mece1케이스가 없어 eu-central-1 endpoint로 라우팅되고 있다.
Monitoring#
- Thumbnail 503 에러 빈도 모니터링:
service:cupixworks-worker "failed to generate thumbnail" status:error
- Thumbnail Lambda 응답 시간 및 에러율 모니터링 (CloudWatch):
5XXErrormetric for API Gatewayxntqdgslbc(eu-central-1)
- Production 배포 후 동일 에러 재발 여부 확인
Risk Assessment#
- Risk level: medium
- 예상 복잡도: trivial (수정 이미 완료, 배포만 필요)