대규모 분산 시스템에서 '일시적 실패(Transient Failure)'는 피할 수 없는 현실입니다. Netflix는 수천 개의 마이크로서비스를 전 세계에 배포하며, Spinnaker라는 오픈소스 지속적 전달(CD) 플랫폼을 핵심 엔진으로 사용해 왔습니다. 하지만 Spinnaker의 Clouddriver 서비스 내 복잡한 오케스트레이션 로직과 상태 관리의 한계로 인해, 클라우드 운영(Cloud Operation) 실패로 이어지는 배포가 약 4%에 달하는 문제에 직면했습니다. 이 글에서는 Netflix가 어떻게 Temporal이라는 Durable Execution 플랫폼을 도입하여 이 문제를 근본적으로 해결하고 배포 실패율을 4%에서 0.0001%로 극적으로 낮췄는지 그 여정을 살펴봅니다. 자세한 내용은 Netflix Tech Blog의 원문을 참고하세요.

Cloud computing and server infrastructure IT Technology Image

Spinnaker Clouddriver의 아킬레스건

Spinnaker 내에서 Clouddriver는 클라우드 인프라 변경(예: 서버 그룹 생성/삭제)을 실행하는 역할을 담당했습니다. Orca(오케스트레이션 엔진)가 Clouddriver에 작업을 요청하면, Clouddriver는 이를 하위 작업(AtomicOperation)으로 분해하고 실행하는 복잡한 내부 프로세스를 가졌습니다. 이 구조에는 몇 가지 근본적인 문제점이 있었습니다.

  1. 복잡한 내부 오케스트레이션: Clouddriver는 작업 상태 추적, 재시도, 롤백(Saga 패턴)을 위한 자체적인 논리를 구현해야 했습니다. 이는 본질적인 목표(인프라 변경 실행)와는 무관한 '미분화된 부담(Undifferentiated Lifting)'이었습니다.
  2. 인스턴스-로컬 상태: 작업 실행 상태가 특정 Clouddriver 인스턴스의 메모리에 저장되어, 해당 인스턴스가 다운되면 진행 중인 작업 상태가 완전히 소실되었습니다.
  3. 긴밀한 결합(Tight Coupling): Orca는 Clouddriver의 특정 상태 폴링 API와 에러 처리 방식을 정확히 알고 있어야 했습니다.

이러한 복잡성 속에서도 네트워크 불안정, 클라우드 공급자 장애 등 '일시적 실패'에 대한 견고한 처리는 어려웠고, 결국 4%의 배포 실패율이라는 숫자로 나타났습니다.

Data center server rack Developer Related Image

Temporal의 패러다임 전환: '실패가 없는 것처럼' 코딩하기

Temporal은 Durable Execution을 제공하는 플랫폼입니다. 개발자는 비즈니스 로직을 Workflow(결정론적인 단계들의 집합)와 Activity(비결정론적 외부 작업, 예: API 호출)로 구성하기만 하면, Temporal 서버가 실행 상태를 지속적으로 저장하고 관리합니다. 워커(Worker) 프로세스가 죽어도, 30일을 sleep 중이어도, Temporal은 상태를 보존하고 다른 워커에서 작업을 이어갈 수 있습니다.

Temporal 마이그레이션 후 아키텍처:

  1. Orca는 Clouddriver에 직접 요청하는 대신, Temporal 클라이언트를 사용해 UntypedCloudOperationRunner Workflow를 실행 요청합니다.
  2. Clouddriver 워커가 해당 Workflow 작업을 받아, 페이로드를 해석하고 적절한 CloudOperation Child Workflow를 실행합니다.
  3. Child Workflow는 실제 클라우드 공급자 API를 호출하는 Activity들을 실행합니다.
  4. Orca는 Temporal 클라이언트를 통해 Workflow 완료를 비동기적으로 기다립니다.

이 변화로 인해 Clouddriver는 더 이상 복잡한 오케스트레이션, 상태 관리, 재시도 로직을 직접 구현할 필요가 없어졌습니다. 모든 것은 Temporal 플랫폼이 책임지게 되었죠.

Network and system architecture diagram Coding Session Visual

얻은 성과와 교훈

주요 성과:

  • 배포 실패율: 4% → 0.0001% (약 40,000배 개선).
  • 결합도 감소: Orca와 Clouddriver는 Temporal을 매개로 느슨하게 연결되었습니다.
  • 상태 비저장성(Stateless): Clouddriver 인스턴스는 상태를 갖지 않게 되어 카우처럼 취급 가능해졌고, Chaos Monkey 적용도 가능해졌습니다.
  • 가시성 향상: Temporal UI를 통해 프로덕션에서 실행 중인 Workflow를 실시간으로 디버깅하고 모니터링하기 쉬워졌습니다.

실전에서 얻은 교훈:

  1. 불필요한 Child Workflow는 피하라: 코드 조직화를 위해 Child Workflow를 사용하면 디버깅이 복잡해질 수 있습니다. 클래스 컴포지션을 고려하세요.
  2. 단일 인자 객체를 사용하라: Workflow/Activity 메서드 시그니처를 변경하면 장기 실행 Workflow를 깨뜨릴 수 있습니다. 모든 인자를 담은 하나의 직렬화 가능한 클래스를 사용하는 것이 안전합니다.
  3. 비즈니스 실패와 워크플로우 실패를 분리하라: WorkflowResult와 같은 래퍼 타입을 사용해 비즈니스 로직의 실패를 워크플로우 실행 실패와 구분하면 에러 처리가 더 세밀해집니다.

Netflix는 이 성공적인 마이그레이션을 계기로 Temporal 사용이 급증했으며, 현재는 수백 가지의 다양한 유스 케이스에서 Temporal Cloud(SaaS)를 활용하고 있습니다. 복잡성 속에서 신뢰성을 확보해야 하는 모든 분산 시스템 설계자에게 귀중한 인사이트를 제공하는 사례입니다.