상태 기반 오케스트레이션의 숨은 비용

SchemaBridge Team · 2026-02-02 · Distributed Systems, Immutable Infrastructure, Orchestration, Anti-Patterns, Reliability

'단순한' 내부 도구가 가장 큰 안정성 위험이 될 수 있는 이유, 그리고 불변 인프라 패턴을 애플리케이션 로직에 적용해 이를 막는 방법.

마이크로서비스의 세계에서 우리는 디커플링에 집착합니다. 큐를 쓰고, 이벤트 기반 아키텍처를 채택하고, 모놀리스를 쪼갭니다. 그런데도 오케스트레이션 엔진 내부에서는 분산 시스템의 대죄를 자주 저지릅니다. 상태를 가진 루프를 작성하는 것입니다.

SchemaBridge는 최근 핵심 시스템 워크플로, 즉 우리 자신의 점진적 롤아웃을 구동하는 워크플로를 전면 개편했습니다. 그 과정에서 많은 엔지니어링 조직을 괴롭히는 것과 같은 안티패턴과 마주하게 되었습니다. 이것은 단순히 릴리스 노트에 관한 이야기가 아니라, 불변 인프라 원칙을 애플리케이션 계층에 적용해 보는 연습입니다.

'루프' 안티패턴

고전적인 '폴러'를 생각해 봅시다:

while not deployment.is_ready():
    sleep(60)
    check_status()

이 코드는 안정적인 세계를 가정합니다. 폴링 프로세스가 영원히 살아 있을 것이라고 가정하지요. 실제로 이것은 분산 모놀리스의 산물입니다. 긴 동기적 대기 동안 상태를 메모리에 들고 있는 프로세스는 취약합니다. 호스트가 재시작되거나 호출이 멈추면 그 상태를 잃거나 아무도 지켜보지 않는 '좀비'로 남게 되고, 장애 모드는 예측할 수 없게 됩니다.

해결책: 재귀적 체이닝

우리는 내부 while 루프에서 함수형 프로그래밍의 연속 전달 스타일(Continuation Passing Style)과 유사한 재귀적 실행 모델로 전환했습니다.

불변 로직: 정규화 해싱을 통한 버전 관리

버전 관리는 어렵기로 유명합니다. 워크플로 정의를 업데이트하면 이미 실행 중인 실행들은 어떻게 될까요?

SchemaBridge의 워크플로 버전은 불변입니다. 배포는 새 버전을 만들고, 실행은 시작할 때의 버전에 묶입니다. 우리가 추가한 것은 정규화된 DSL 해싱입니다. 가져온 모든 워크플로 정의는 정규화된 JSON 형식으로 직렬화되어 해시되고, 그 해시는 워크플로와 함께 저장됩니다(dslHash).

이 해시는 예전에는 놀라울 만큼 답하기 어려웠던 질문, 로직이 실제로 바뀌었는가?에 답해 줍니다. 설정 가져오기나 우리 자체의 시스템 워크플로 부트스트랩이 이미 운영 중인 것과 해시가 같은 정의를 만나면, 재배포 대신 no-op을 계획합니다. 그리고 원본 정의에서 벗어난 운영 중인 워크플로는 해시를 비교해 감지할 수 있습니다.

'갓 모드' 봉쇄하기

마지막으로 내부 도구의 보안을 다뤘습니다. 내부 '정리' 스크립트에 root 권한을 주는 일은 흔하지만, 이는 최소 권한 원칙을 위반합니다.

우리 자체의 내부 'Gradual Rollout' 도구는 작업 대상 워크플로를 하드코딩된 논리 ID로 찾고 있었기 때문에, 엉뚱한 워크플로를 가리켜 덮어쓰기 쉬웠습니다. 우리는 이를 두 가지 방법으로 고쳤습니다:

결론

오케스트레이션이 sleep()에 의존한다면, 여러분은 클라우드와 싸우고 있는 것입니다.

재귀, 불변성, 최소 권한을 받아들임으로써, 우리는 '희망에 기댄' 오케스트레이션에서 상태를 들여다볼 수 있고 영향 범위를 제한할 수 있는 오케스트레이션으로 나아갑니다.

더 읽을거리:

살펴보기