~/infra-dev.md
INFRA_DEV

멀티 리전 서버리스는 왜 “두 군데 배포”가 아니라 헬스 계약일까: Cloud Run service health로 보는 자동 failover

10분 읽기·2026.07.24·출처 5·00
오늘의 질문

리전을 두 개 켜두면 자동으로 고가용성이 되는 걸까, 아니면 어느 리전을 버릴지 판단하는 헬스 계약이 먼저일까?

infra-dev.md
Cloud Run service health

멀티 리전 서버리스는 왜 “두 군데 배포”가 아니라 헬스 계약일까: Cloud Run service health로 보는 자동 failover

1. 왜 지금 봐야 하나

2026년 7월 20일 Google Cloud는 Cloud Run 멀티 리전 서비스의 고가용성 기능을 소개했다. 핵심은 “Cloud Run을 여러 리전에 배포할 수 있다”가 아니다. 그 정도는 이미 여러 플랫폼에서 가능한 이야기다. 이번 변화에서 운영자가 봐야 할 부분은 readiness probe에서 나온 인스턴스 단위 신호를 서비스 단위 health로 집계하고, 이 health를 serverless NEG를 통해 load balancer가 사용할 수 있게 한다는 점이다.

작은 팀은 고가용성을 종종 이렇게 생각한다.

us-central1에도 띄우고 asia-northeast3에도 띄웠으니 리전 장애에는 안전하겠지.

하지만 실제 장애 대응에서 중요한 질문은 조금 다르다.

  • 어느 리전이 지금 “정상”인가?
  • 정상의 기준은 단순히 프로세스가 살아 있다는 뜻인가, 아니면 새 요청을 받아도 된다는 뜻인가?
  • 부분 장애가 났을 때 트래픽은 자동으로 빠지는가?
  • 데이터베이스나 큐가 단일 리전에 묶여 있으면 앱 리전만 늘린 것이 정말 HA인가?
  • failover 후 되돌릴 때 어떤 지표를 보고 판단할 것인가?

Cloud Run 블로그는 multi-region services를 global external Application Load Balancer와 연결하면 여러 리전에서 트래픽을 제공할 수 있고, 새 service health 기능으로 unhealthy region에서 traffic이 자동으로 빠질 수 있다고 설명한다. 또 public internet 앱은 global external Application Load Balancer, private network 앱은 cross-regional internal Application Load Balancer를 고려하라고 안내한다.

오늘의 핵심 문장은 이것이다.

멀티 리전 서버리스의 본질은 리전 수가 아니라, “이 리전은 지금 트래픽을 받아도 된다”를 판단하는 헬스 계약과 그 신호를 소비하는 라우팅 계층이다.

2. 핵심 개념

Cloud Run 멀티 리전 구성을 이해하려면 세 가지 층을 나눠야 한다.

첫째, 배포 층이다. Cloud Run 서비스는 기본적으로 개별 리전에 배포된다. 문서에서는 같은 서비스를 여러 리전에 배포하거나 gcloud run deploy SERVICE_NAME --image=IMAGE_URL --regions=REGIONS처럼 multi-region service로 배포할 수 있다고 설명한다. Terraform에서는 google_cloud_run_v2_servicelocation = "global"multi_region_settings로 여러 리전을 지정하는 방식이 안내된다.

여기서 중요한 운영 포인트가 있다. 문서는 scaling parameter가 여러 리전에 걸쳐 적용되며, 예를 들어 minimum instances를 설정하면 그 값이 각 배포 리전마다 적용된다고 설명한다. 즉 min instances = 2이고 리전이 3개라면 “전체 최소 2개”가 아니라 리전별 최소 인스턴스 비용을 생각해야 한다. 멀티 리전은 무료 마법이 아니라 비용 곱셈을 동반하는 가용성 선택이다.

둘째, 헬스 층이다. Cloud Run health check 문서는 startup, liveness, readiness probe를 구분한다.

  • startup probe는 컨테이너가 시작되어 요청을 받을 준비가 되었는지 확인한다.
  • liveness probe는 컨테이너를 재시작해야 할 정도로 회복 불가능한 상태인지 판단한다.
  • readiness probe는 해당 인스턴스가 새 트래픽을 받아도 되는지 판단한다.

특히 readiness probe가 실패 threshold를 넘으면 Cloud Run은 새 트래픽을 그 인스턴스에 보내지 않지만, 인스턴스를 종료하지는 않는다. 다시 성공 조건을 만족하면 트래픽을 받을 수 있다. 이것은 장애 대응에서 굉장히 중요한 차이다. “죽여서 재시작할 문제”와 “살아 있지만 트래픽을 잠시 빼야 할 문제”는 다르다.

셋째, 라우팅 층이다. serverless NEG는 load balancer backend가 IP/port endpoint가 아니라 Cloud Run 같은 serverless resource를 가리키도록 해주는 개념이다. global external Application Load Balancer와 연결하면 public endpoint, 고정 IP, 인증서, Cloud Armor, CDN 같은 기능과 함께 serverless backend로 요청을 라우팅할 수 있다. Cloud Run 블로그가 말하는 service health는 readiness probe에서 온 인스턴스 단위 상태를 리전별 서비스 상태로 집계해 이 라우팅 계층에 전달한다.

정리하면 구조는 이렇다.

zsh — 생존확인.sh
사용자 요청
  -> Global / Cross-region Application Load Balancer
  -> Serverless NEG의 리전별 backend health 판단
  -> healthy로 판단된 Cloud Run region
  -> readiness를 통과한 instance

3. 최신 이슈와 연결

이번 Cloud Run 업데이트가 의미 있는 이유는 serverless 운영의 추상화가 한 단계 더 올라갔기 때문이다.

예전에는 서버리스 고가용성을 말할 때 “인스턴스를 직접 관리하지 않는다”, “자동 확장된다”, “리전 하나에 배포가 쉽다”가 중심이었다. 하지만 제품이 커지면 장애 모드는 더 복잡해진다. 특정 리전의 네트워크 경로가 나빠질 수 있고, 의존 서비스의 partial outage가 날 수 있고, 새 revision이 한 리전에서만 잘못된 환경 변수나 secret 조합을 만날 수 있다. 이때 단순 liveness만으로는 부족하다.

예를 들어 프로세스는 살아 있고 /는 200을 반환하지만, 해당 리전에서만 데이터베이스 write latency가 5초를 넘는다고 해보자. liveness 기준으로는 “살아 있음”이다. 하지만 사용자 요청을 계속 보내면 timeout과 재시도가 쌓이고, 큐 backlog가 늘고, 다른 리전까지 영향을 받을 수 있다. 이 상황에서 readiness는 이렇게 설계되어야 한다.

이 인스턴스는 프로세스가 살아 있는가? 가 아니라, 지금 새 요청을 받으면 SLO 안에 처리할 가능성이 있는가?

Cloud Run 문서의 readiness 동작은 이 판단과 맞물린다. readiness가 실패하면 새 트래픽을 중단하지만 컨테이너를 죽이지 않는다. 즉 앱은 내부 복구, connection pool 재연결, cache warming, dependency 회복을 시도할 수 있다. 그리고 service health가 이 신호를 리전 단위로 집계하면 load balancer는 unhealthy region을 피할 수 있다.

다만 공식 블로그도 설계 고려사항을 분명히 말한다. active-active 구성이 잘 맞고, 단일 장애 지점을 없애야 하며, 데이터 복제와 RPO를 검토해야 한다. Cloud Run 서비스만 여러 리전에 두고 데이터베이스, 큐, object storage, secret, external API dependency가 한 리전에만 있으면 고가용성이 아니라 “프론트 문만 여러 개”인 구조가 된다.

4. 개발자 관점 해석

개발자에게 이 주제는 클라우드 네트워크 설정 암기가 아니다. 핵심은 health endpoint를 제품 계약으로 설계하는 능력이다.

나쁜 readiness endpoint는 보통 이렇게 생겼다.

zsh — 생존확인.sh
GET /ready -> process is running -> 200

이런 readiness는 배포 자동화에는 편할 수 있지만, 장애 격리에는 약하다. 프로세스가 떠 있기만 하면 계속 트래픽을 받기 때문이다.

좋은 readiness는 “이 인스턴스가 새 요청을 받아도 되는 조건”을 표현한다. 예를 들면 다음과 같은 조건을 생각할 수 있다.

  • 필수 환경 변수와 secret을 읽었다.
  • 서버가 warm-up을 끝냈다.
  • DB connection pool이 최소 연결을 확보했다.
  • migration mismatch나 schema compatibility 문제가 없다.
  • downstream API의 최근 실패율이 임계값을 넘지 않는다.
  • worker라면 queue backlog와 처리 지연이 임계값 안에 있다.
  • read-only degradation 모드라면 쓰기 endpoint는 readiness에서 제외하거나 별도 라우팅한다.

하지만 readiness에 너무 많은 것을 넣어도 위험하다. 외부 API 한 번 실패했다고 전체 리전이 내려가면 failover flap이 생길 수 있다. readiness는 “즉시 트래픽을 빼야 하는 조건”만 담아야 하고, 나머지는 metric과 alert로 분리해야 한다.

여기서 실무 판단 기준은 세 가지다.

첫째, liveness와 readiness를 섞지 않는다. liveness는 죽여서 새로 띄우는 것이 더 낫다는 판단이다. deadlock, event loop 정지, fatal internal state처럼 프로세스 재시작이 복구 수단일 때 쓰는 편이 안전하다. 반면 readiness는 잠시 트래픽을 빼는 판단이다. dependency 지연, warm-up 미완료, 임시 backpressure처럼 살아 있는 채 회복할 수 있는 조건에 맞다.

둘째, region health는 instance health의 합성이라는 점을 기억한다. 인스턴스 하나가 나쁜 것과 리전 전체가 나쁜 것은 다르다. 그래서 readiness endpoint는 인스턴스 로컬 상태와 리전 공통 의존성 상태를 구분해 기록해야 한다. 로그에는 readiness_failed_reason=database_latency처럼 원인을 남겨야 장애 리뷰에서 “왜 리전이 빠졌는지”를 재구성할 수 있다.

셋째, 데이터 계층의 RPO/RTO를 같이 설계한다. Cloud Run 블로그는 데이터 복제와 RPO를 검토하라고 말한다. 애플리케이션 트래픽은 몇 초 안에 다른 리전으로 빠져도, 데이터가 따라오지 않으면 사용자는 오래된 데이터를 보거나 쓰기 실패를 겪는다. 특히 Postgres 단일 primary 구조라면 앱 멀티 리전은 read path에는 도움이 되지만 write path에는 한계가 있다. 이 경우 “자동 failover”라는 말을 쓰기 전에 어떤 기능이 degraded mode로 남는지 정해야 한다.

5. 내 프로젝트에 적용할 체크포인트

작은 팀이 바로 점검할 수 있는 체크리스트는 다음과 같다.

A. 멀티 리전이 필요한 요청을 먼저 고른다

모든 API를 한 번에 멀티 리전화할 필요는 없다. 먼저 아래 질문에 답한다.

  • 사용자에게 가장 자주 보이는 read API는 무엇인가?
  • 장애 시에도 최소 기능으로 살아 있어야 하는 endpoint는 무엇인가?
  • write가 필요한 기능과 read-only로 degrade 가능한 기능은 무엇인가?
  • 정적 자산, 인증, 결제, 알림, DB 중 어느 계층이 단일 장애 지점인가?

B. readiness endpoint를 “트래픽 수락 조건”으로 다시 쓴다

/ready가 단순 200이면 다음 항목을 추가로 검토한다.

zsh — 생존확인.sh
필수 설정 로딩 완료 여부
DB pool 연결 가능 여부
최근 N초 내부 오류율
schema version compatibility
warm cache 또는 model loading 완료 여부
backpressure flag 여부

단, 모든 dependency를 매 probe마다 강하게 호출하면 probe 자체가 부하가 될 수 있다. 주기적으로 갱신한 내부 상태를 readiness가 읽는 방식이 더 안전한 경우가 많다.

C. load balancer와 serverless NEG의 책임을 이해한다

Cloud Run 문서는 Cloud Run 서비스를 여러 리전에 배포하고 global load balancing을 구성해야 한다고 설명한다. serverless NEG는 Cloud Run 같은 serverless resource를 load balancer backend로 연결하는 역할을 한다. 이 구성을 쓰면 Cloud Armor, CDN, 고정 IP, 인증서 같은 edge 기능도 함께 설계할 수 있다.

운영 문서에는 최소한 아래가 있어야 한다.

  • public traffic: global external Application Load Balancer 사용 여부
  • private traffic: cross-regional internal Application Load Balancer 사용 여부
  • 각 리전 serverless NEG와 Cloud Run service 매핑
  • readiness 실패 시 routing이 어떻게 바뀌는지
  • failback은 자동으로 둘지, 수동 확인 후 할지

D. 비용을 리전 단위로 계산한다

멀티 리전 서비스에서 minimum instances 같은 scaling parameter가 각 리전에 적용될 수 있다는 점을 놓치면 비용 예측이 틀어진다.

예를 들어 다음 표를 만든다.

항목단일 리전2 리전3 리전
min instances246
readiness probe CPU/메모리 비용1배2배3배
로그/메트릭 cardinalityregion label 1개2개3개
DB/캐시 복제 비용없음추가더 큼

Cloud Run 블로그는 enhanced multi-region HA 기능 자체는 추가 비용 없이 제공되며 readiness probe에 필요한 표준 CPU와 메모리 비용만 지불한다고 설명한다. 하지만 전체 아키텍처 관점에서는 load balancer, 로그, 데이터 복제, min instances 비용을 같이 봐야 한다.

E. 장애 리뷰 질문을 미리 정한다

장애가 난 뒤에는 “왜 자동 failover가 안 됐지?”라고 묻기 쉽다. 사전에 질문을 정해두면 설계가 달라진다.

  • readiness는 실제 사용자 장애보다 먼저 실패했는가, 늦게 실패했는가?
  • 한 인스턴스 장애와 리전 장애를 구분했는가?
  • load balancer가 unhealthy region에서 트래픽을 빼는 데 걸린 시간은 얼마였는가?
  • failover 후 다른 리전의 cold start, min instances, quota는 충분했는가?
  • 데이터 RPO 때문에 사용자가 잃은 작업이 있는가?
  • failback은 자동이었는가, 수동 승인 후였는가?

6. 오늘 10분 액션

오늘은 클라우드 콘솔을 열지 않아도 된다. 대신 지금 운영 중인 서비스 하나를 골라 다음 표를 채워보자.

zsh — 생존확인.sh
서비스명:
현재 리전:
후보 보조 리전:
public/private 트래픽:
필수 dependency:
readiness가 확인해야 할 최소 조건 3개:
liveness로 재시작해야 할 조건 2개:
단일 장애 지점:
허용 가능한 RPO/RTO:
멀티 리전 시 늘어나는 최소 비용 항목:

그 다음 /ready endpoint의 현재 구현을 확인한다. 단순 200이면 이번 주 액션은 하나다.

readiness를 “프로세스 생존 확인”에서 “새 트래픽 수락 가능성 확인”으로 바꾸는 작은 PR을 만든다.

처음부터 완벽한 멀티 리전 HA를 만들 필요는 없다. 하지만 readiness의 의미를 바꾸는 순간, 배포·오토스케일링·장애 대응·비용 계산이 같은 언어로 묶이기 시작한다.

7. 더 볼 자료

중복 회피 메모

로컬 content/generated와 Supabase 최근 infra_dev 글을 확인했다. 최근 글은 GKE custom rollout sequencing으로 보는 클러스터 업그레이드 canary/soak, GitHub Linked Artifacts 기반 code-to-cloud traceability, Kubernetes In-place Pod Resize, Terraform state secret, GitHub Actions step-level parallelism, OpenTelemetry semantic convention 카디널리티, RDS delayed replica, GitHub secret scanning triage를 다뤘다. 이번 글은 Google Cloud와 Cloud Run을 다루지만 클러스터 업그레이드나 supply-chain 보안이 아니라, Cloud Run multi-region service health와 readiness probe, serverless NEG, Application Load Balancer를 연결해 “리전을 늘리는 것”보다 “트래픽을 받을 수 있는 리전을 판별하는 health 계약”에 집중해 중복을 피했다.

오늘 10분 액션+5%

댓글 0

최신순 ▾
한 줄 남기기

혹시 이 글을 읽는 동료 개발자가 있다면, GitHub으로 로그인하고 한 줄 흔적을 남겨줘요. (스팸 방지용 로그인이에요)