- Published on
GCP Cloud Run 503·콜드스타트 줄이는 설정 7가지
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
Cloud Run을 운영하다 보면 특정 시간대에만 503이 튀거나, 트래픽이 다시 들어오는 순간 첫 요청 지연이 크게 늘어나는 문제가 반복됩니다. 흔히 “서버리스라 알아서 확장되겠지”라고 생각하지만, Cloud Run의 확장·축소는 컨테이너 인스턴스가 준비되는 시간(콜드스타트) 과 요청을 처리할 수 있는 동시 처리 용량(동시성·인스턴스 수) 에 의해 결정됩니다.
이 글은 Cloud Run에서 503과 콜드스타트를 줄이기 위해 운영에서 자주 쓰는 설정 7가지를 정리합니다. 각 항목은 “왜 효과가 있는지”와 “어떻게 적용하는지”를 함께 다룹니다.
참고로
503은 애플리케이션이500을 낸 것이 아니라, Cloud Run/프록시 계층에서 요청을 처리할 준비가 안 된 상태(인스턴스 부족, 시작 지연, 타임아웃, 과부하 등)로 반환되는 경우가 많습니다.
1) 최소 인스턴스(min-instances)로 콜드스타트 자체를 없애기
가장 확실한 방법은 항상 떠 있는 인스턴스를 유지하는 것입니다. Cloud Run은 유휴 시 0으로 스케일다운할 수 있는데, 이때 다음 요청은 새 인스턴스를 띄우며 콜드스타트가 발생합니다.
- 콜드스타트가 치명적인 API(로그인, 결제, 웹훅 수신 등)는
min-instances를 1 이상으로 두는 것이 효과적입니다. - 비용은 증가하지만, “첫 요청만 느린” 문제를 구조적으로 제거합니다.
적용 예시(gcloud)
gcloud run services update SERVICE_NAME \
--region=asia-northeast3 \
--min-instances=1
실전 팁
- 트래픽 패턴이 뚜렷하면
min-instances를 시간대별로 바꾸는 방식(스케줄러로 업데이트)도 가능합니다. min-instances를 늘렸는데도 느리다면, 콜드스타트가 아니라 앱 초기화(DB 연결, 마이그레이션, 모델 로딩 등) 가 병목일 가능성이 큽니다.
2) 최대 인스턴스(max-instances)로 폭주 시 503를 “제어 가능한 실패”로 만들기
max-instances는 “무한 확장”을 막는 안전장치입니다. 잘못 설정하면 트래픽이 늘 때 인스턴스가 충분히 늘지 못해 503이 증가할 수 있습니다.
- 너무 낮으면: 확장 상한에 걸려
503이 급증 - 너무 높으면: 백엔드(DB, 외부 API)까지 동시 부하가 전달되어 더 큰 장애로 번질 수 있음
적용 예시
gcloud run services update SERVICE_NAME \
--region=asia-northeast3 \
--max-instances=50
실전 팁
max-instances는 Cloud Run만의 문제가 아니라 DB 커넥션, 외부 API 레이트리밋, 캐시 용량과 함께 정해야 합니다.- DB가 병목이면, 무작정
max-instances를 올리는 것이 오히려 장애를 키울 수 있습니다. (이 경우는 커넥션 풀/프록시/쿼리 튜닝이 우선입니다. 데드락 분석은 MySQL InnoDB 데드락 로그로 원인 쿼리 찾기 도 함께 참고하세요.)
3) 동시성(concurrency)을 서비스 특성에 맞게 조정하기
Cloud Run의 핵심 파라미터 중 하나가 인스턴스당 동시 처리 요청 수인 concurrency입니다.
concurrency가 높을수록: 인스턴스 수는 덜 늘어나지만, 한 인스턴스가 더 많은 요청을 동시에 처리해야 함concurrency가 낮을수록: 인스턴스가 더 자주 늘어나며, 확장 시 콜드스타트가 더 자주 나타날 수 있음
어떤 값이 좋은가?
- CPU 바운드(이미지 처리, 암호화, 무거운 JSON 변환):
concurrency를 낮게(예: 1~10) - I/O 바운드(외부 API 호출, DB 대기, 네트워크 대기):
concurrency를 높게(예: 40~200)
적용 예시
gcloud run services update SERVICE_NAME \
--region=asia-northeast3 \
--concurrency=40
실전 팁
503이 늘어나는 순간의 지표를 보면 힌트가 나옵니다. CPU가 100%에 붙어 있으면concurrency과다, 반대로 CPU는 낮은데 지연이 크면 외부 I/O 병목일 가능성이 큽니다.
4) CPU 할당 방식: “유휴에도 CPU 유지”로 워밍업/캐시를 살리기
Cloud Run은 기본적으로 요청이 없으면 CPU가 제한될 수 있습니다(설정에 따라 다름). 이 경우 다음 현상이 발생합니다.
- 백그라운드 워밍업(캐시 프리로드, 커넥션 유지, 주기 작업)이 중단되거나 느려짐
- 첫 요청에서 초기화 작업이 몰리며 콜드스타트 체감이 커짐
해결책은 유휴 상태에서도 CPU를 계속 할당하는 옵션을 사용하는 것입니다.
적용 예시(개념)
Cloud Run 콘솔에서 CPU 관련 옵션을 “항상 할당”으로 바꾸거나, 배포 시 해당 옵션을 지정합니다.
# 예시는 개념 표현입니다. 실제 플래그는 gcloud 버전/제품 변경에 따라 달라질 수 있습니다.
# 콘솔에서 "CPU is always allocated" 옵션을 확인하세요.
gcloud run services update SERVICE_NAME \
--region=asia-northeast3 \
--cpu=1
언제 쓰면 좋은가?
- 인메모리 캐시를 적극 활용하는 API
- 앱 부팅 시 초기화가 무거운 서비스
- 일정 주기로 토큰 갱신/프리패치가 필요한 서비스
주의
- 비용이 증가합니다.
min-instances와 함께 쓰면 “항상 켜진 서버”에 가까워집니다.
5) 메모리/CPU 리소스 상향: “시작 속도”와 “GC/스왑”을 동시에 개선
콜드스타트는 단지 컨테이너 생성만이 아니라, 애플리케이션 런타임 초기화가 대부분을 차지합니다.
- JVM/Node/Python 모두 메모리가 부족하면 GC가 잦아지고, 초기 로딩이 느려집니다.
- CPU가 부족하면 부팅 시 컴파일/로딩/암호화 같은 작업이 늘어져 타임아웃에 걸릴 수 있습니다.
적용 예시
gcloud run services update SERVICE_NAME \
--region=asia-northeast3 \
--memory=1Gi \
--cpu=2
실전 팁
- 메모리를 올렸더니
503이 줄었다면, 실제 원인은 “확장 실패”가 아니라 프로세스가 느리게 준비되는 문제였을 가능성이 큽니다. - 컨테이너가 OOM으로 죽으면 트래픽이 몰릴 때 인스턴스가 계속 재시작하며
503이 연쇄적으로 발생합니다. 리눅스 관점의 메모리 문제 분석은 리눅스 OOM Killer로 프로세스 죽음 진단·방지도 함께 보면 도움이 됩니다.
6) Startup Probe / Readiness 설계: “준비되기 전 트래픽”을 차단하기
Cloud Run은 인스턴스가 뜨자마자 요청을 받기 시작할 수 있습니다. 하지만 앱이 내부적으로 준비되지 않았다면(예: DB 연결, 마이그레이션, 모델 로딩) 첫 요청들이 실패하거나 매우 느려집니다.
핵심은 두 가지입니다.
- 프로세스가 뜬 것과 “서비스 준비 완료”는 다르다
- 준비되기 전에는 트래픽을 받지 않도록 해야
503과 초기 오류가 줄어든다
앱 레벨 헬스 엔드포인트 예시(Node.js)
import express from "express";
const app = express();
let ready = false;
async function init() {
// 예: DB 연결, 캐시 프리로드 등
await new Promise((r) => setTimeout(r, 1500));
ready = true;
}
app.get("/healthz", (req, res) => {
if (!ready) return res.status(503).send("not ready");
return res.status(200).send("ok");
});
app.get("/", (req, res) => res.send("hello"));
const port = process.env.PORT || 8080;
app.listen(port, () => {
init().catch((e) => {
console.error(e);
process.exit(1);
});
});
실전 팁
- “처음 요청이 느리다”를 사용자에게 떠넘기지 말고, 준비가 끝난 뒤에만 트래픽을 받게 만들면 체감 품질이 크게 좋아집니다.
- 외부 의존성(DB, 타사 API)이 준비되지 않았을 때는 빠르게 실패시키고 재시도 정책을 분리하는 것이 낫습니다.
7) 요청 타임아웃과 클라이언트 재시도 정책을 함께 조정하기
Cloud Run의 요청 타임아웃이 너무 짧으면, 콜드스타트나 순간 부하에서 정상 요청도 타임아웃으로 끊기며 503 또는 게이트웨이 계층 오류로 보일 수 있습니다. 반대로 너무 길면, 느린 요청이 오래 붙잡고 있어 동시성이 고갈되어 또 503이 늘 수 있습니다.
적용 예시
gcloud run services update SERVICE_NAME \
--region=asia-northeast3 \
--timeout=30s
함께 봐야 하는 것
- 클라이언트(또는 API Gateway/로드밸런서)의 타임아웃
- 재시도(backoff) 정책
- 멱등성(idempotency) 보장 여부
재시도가 잘못 설계되면, 일시적 지연이 있을 때 요청이 폭증해 503을 더 악화시킵니다. 특히 외부 API 호출이 많은 서비스는 “타임아웃 + 재시도”가 2차 폭주를 만들기 쉬우니, 게이트웨이 오류 관점의 트러블슈팅 습관도 중요합니다. 비슷한 유형의 장애 분석 방식은 OpenAI Responses API 502 Bad Gateway 원인과 해결 글의 접근법도 참고할 만합니다.
운영 체크리스트: 503과 콜드스타트를 구분하는 관찰 포인트
설정을 바꾸기 전에, 아래를 먼저 확인하면 “원인에 맞는 설정”을 고르기 쉬워집니다.
- 첫 요청만 느린가, 모든 요청이 느린가
- 첫 요청만 느리면
min-instances, CPU 유휴 할당, 초기화 최적화가 우선
- 첫 요청만 느리면
- CPU가 포화되는가
- 포화면
concurrency하향 또는 CPU 상향
- 포화면
- 메모리 사용량이 스파이크 치는가
- 스파이크면 메모리 상향, 초기 로딩/캐시 전략 점검
- 외부 의존성(특히 DB)에서 지연이 발생하는가
- Cloud Run만 건드려서는 해결이 안 되고, 커넥션 풀/쿼리/레이트리밋을 함께 봐야 함
마무리: “확장”보다 “준비”가 먼저다
Cloud Run의 503과 콜드스타트는 단순히 인스턴스를 더 늘리면 해결되는 문제가 아닙니다. 인스턴스가 늘어나는 순간에 앱이 얼마나 빨리 준비 상태가 되는지, 그리고 동시 요청을 어떤 밀도로 처리할지를 맞추는 것이 핵심입니다.
정리하면, 효과가 큰 순서로는 보통 다음 흐름을 추천합니다.
min-instances로 콜드스타트 제거concurrency와 CPU/메모리로 인스턴스당 처리량 안정화max-instances로 백엔드 보호- readiness/헬스 설계로 “준비 전 트래픽” 차단
- 타임아웃과 재시도 정책으로 폭주 방지
위 7가지를 한 번에 다 바꾸기보다, 1~2개씩 바꾸고 지표(지연, 오류율, 인스턴스 수, CPU/메모리)를 비교하면서 최적점을 찾는 것이 가장 빠릅니다.