상태 기반 오케스트레이션의 숨은 비용
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)과 유사한 재귀적 실행 모델로 전환했습니다.
- 패턴: 각 라운드는
sleep()대신 영속적인DELAY단계로 기다리는 모니터 자식 워크플로를 생성하고, 그다음SPAWNER단계가 다음 라운드를 자식 워크플로로 시작합니다. 즉, 롤아웃 워크플로가 자기 자신의 또 다른 실행을 생성합니다. - 결과: 부모는 여전히 자식이 결과를 보고하기를 기다리지만, 영속적으로 기다립니다. 대기는 스레드나 인메모리 루프가 아니라 PostgreSQL의 한 행(자식의 콜백을 기다리는
WAIT_ALL머지)입니다. 롤아웃 도중 워커가 죽어도 아무것도 잃지 않으며, 데이터베이스 상태가 계속 진실의 원천으로 남습니다.
불변 로직: 정규화 해싱을 통한 버전 관리
버전 관리는 어렵기로 유명합니다. 워크플로 정의를 업데이트하면 이미 실행 중인 실행들은 어떻게 될까요?
- 안티패턴: '제자리 업데이트'. 새 코드를 배포하면 기존 실행이 갑자기 다르게 동작하기 시작합니다.
- 업계 표준: 불변 인프라. 서버에 SSH로 접속해 패치하지 않고 교체하듯이, 실행 중인 워크플로 정의도 패치해서는 안 됩니다.
SchemaBridge의 워크플로 버전은 불변입니다. 배포는 새 버전을 만들고, 실행은 시작할 때의 버전에 묶입니다. 우리가 추가한 것은 정규화된 DSL 해싱입니다. 가져온 모든 워크플로 정의는 정규화된 JSON 형식으로 직렬화되어 해시되고, 그 해시는 워크플로와 함께 저장됩니다(dslHash).
이 해시는 예전에는 놀라울 만큼 답하기 어려웠던 질문, 로직이 실제로 바뀌었는가?에 답해 줍니다. 설정 가져오기나 우리 자체의 시스템 워크플로 부트스트랩이 이미 운영 중인 것과 해시가 같은 정의를 만나면, 재배포 대신 no-op을 계획합니다. 그리고 원본 정의에서 벗어난 운영 중인 워크플로는 해시를 비교해 감지할 수 있습니다.
'갓 모드' 봉쇄하기
마지막으로 내부 도구의 보안을 다뤘습니다. 내부 '정리' 스크립트에 root 권한을 주는 일은 흔하지만, 이는 최소 권한 원칙을 위반합니다.
우리 자체의 내부 'Gradual Rollout' 도구는 작업 대상 워크플로를 하드코딩된 논리 ID로 찾고 있었기 때문에, 엉뚱한 워크플로를 가리켜 덮어쓰기 쉬웠습니다. 우리는 이를 두 가지 방법으로 고쳤습니다:
- 이름이 아닌 역할로 찾기: 이제 롤아웃은 하드코딩된 논리 ID 조회 대신, 고유성이 검증된 명시적 태그를 통해 대상 엔트리 포인트를 찾습니다.
- 최소 권한 자격 증명: 롤아웃 워크플로 안의 Gateway 단계는 자체 이름이 붙은 API 키로 인증하며, 이 키에는 시스템 테넌트의 설정을 다시 쓸 수 있는 권한인
config:manage가 의도적으로 빠져 있습니다. 가장 신뢰도가 낮은 곳에서 실행되는 자동화는 이제 자신이 따르는 규칙을 바꿀 수 없습니다.
결론
오케스트레이션이 sleep()에 의존한다면, 여러분은 클라우드와 싸우고 있는 것입니다.
재귀, 불변성, 최소 권한을 받아들임으로써, 우리는 '희망에 기댄' 오케스트레이션에서 상태를 들여다볼 수 있고 영향 범위를 제한할 수 있는 오케스트레이션으로 나아갑니다.
더 읽을거리: