API Bulkhead (선박 용어, 자원 분리, 실무 설계)

API Bulkhead 전략은 시스템을 여러 개의 독립된 영역으로 분리하여, 한 영역에서 발생한 장애가 다른 영역으로 확산되지 않도록 하는 설계 방식입니다. 특정 서비스 하나가 느려지기 시작했는데, 그게 전혀 관계없는 다른 기능까지 죽여버리는 상황을 겪어보셨습니까. 저는 그 경험을 하고 나서야 Bulkhead 전략을 진지하게 들여다보게 되었습니다. 장애 격리 설계는 "언젠가 필요하겠지"가 아니라, 한 번 터지고 나면 그때서야 절실해지는 것이더군요.

Bulkhead가 선박 용어?

Bulkhead는 원래 선박 용어입니다. 배 내부를 여러 칸막이로 나눠서, 한 구획에 물이 차도 다른 구획까지 침수되지 않게 막는 격벽 구조를 말합니다. 소프트웨어에서 이 개념을 그대로 가져온 건데, 사실 이름만큼 직관적인 비유도 드뭅니다.

시스템에 적용하면 이렇습니다. 여러 서비스가 스레드 풀(Thread Pool, 요청을 처리하는 작업자 집합)이나 커넥션 풀(Connection Pool, 데이터베이스나 외부 API와 연결을 미리 확보해두는 자원 집합)을 공유하지 않고, 각자 독립적으로 할당받아 사용합니다. A 서비스에서 트래픽이 폭발해도 B 서비스가 쓰는 자원에는 손도 못 대는 구조입니다.

일반적으로 이런 설계가 "무조건 안정적이다"라고 알려져 있지만, 제 경험상 이건 좀 다릅니다. 격벽 자체가 잘못 설계되면, 오히려 정상적인 상황에서도 자원이 부족한 구획이 생기기 시작합니다. 칸막이를 잘못 친 배는 오히려 불균형으로 더 불안정해질 수 있습니다.

자원 분리가 만드는 비효율, 실제로 얼마나 심각한가

Bulkhead의 가장 큰 단점은 자원 활용률 저하입니다. 자원을 미리 쪼개서 고정 할당하면, 한쪽이 한가할 때도 다른 쪽이 바쁘면 도와줄 수가 없습니다. 제가 직접 운영해봤는데, 결제 서비스 쪽에 스레드가 남아돌고 있는데 상품 조회 서비스는 큐가 쌓이는 상황이 벌어졌습니다. 자원은 충분한데 처리가 안 되는, 꽤나 답답한 순간이었습니다.

이 문제는 할당량을 어떻게 잡느냐에 따라 크게 달라집니다. 사용 패턴을 충분히 분석하지 않은 상태에서 직감으로 크기를 정해버리면, 나중에 반드시 어딘가에서 병목이 생깁니다. 더 골치 아픈 건, 그 병목이 자원 부족이 아니라 설계 탓이라는 사실을 파악하는 데 시간이 걸린다는 점입니다.

마이크로서비스 아키텍처(Microservice Architecture, 하나의 애플리케이션을 여러 개의 작고 독립적인 서비스로 나눈 구조)에서는 서비스 수가 많아질수록 이 문제가 더 두드러집니다. 격리 단위가 늘어날수록 설정 항목도 늘고, 운영자가 신경 써야 할 포인트도 비례해서 증가합니다. 출처: microservices.io에 따르면, Bulkhead 패턴은 서비스 간 장애 전파를 막는 데 유효하지만 자원 예약 방식에 따라 오버헤드가 발생할 수 있다고 명시되어 있습니다.

흔한 오해, Bulkhead만 있으면 장애를 막을 수 있다는 착각

Bulkhead를 도입하고 나서 팀 내부에서 "이제 장애 걱정은 없다"는 분위기가 생겼는데, 얼마 지나지 않아 데이터베이스 연결이 한꺼번에 끊기면서 전 구역이 동시에 타격을 받는 상황이 발생했습니다. Bulkhead는 장애를 없애는 게 아니라 장애의 파급 범위를 제한하는 도구입니다. 이 차이를 제대로 인식하지 못하면, 준비가 됐다는 착각 속에서 더 큰 사고를 맞이하게 됩니다.

데이터베이스나 네트워크 인프라처럼 공유 자원은 Bulkhead로 완전히 격리하기 어렵습니다. 서비스별로 커넥션 풀을 나눠도, 결국 동일한 DB 서버에 붙는다면 그 서버가 다운됐을 때는 격벽이 아무 의미가 없습니다. Bulkhead는 단일 해결책이 아니라, 여러 안정성 전략의 한 축으로 이해해야 합니다.

과도한 분리도 문제입니다. 제 경험상 이건 "많을수록 좋다"는 논리가 통하지 않는 대표적인 케이스입니다. 지나치게 잘게 나눈 시스템은 설정 실수 하나가 예상치 못한 곳에서 영향을 미쳤고, 오히려 디버깅이 더 복잡해졌습니다. 격벽이 많다고 배가 더 안전한 게 아닌 것처럼, 분리 단위가 많다고 시스템이 더 안정적인 건 아닙니다.

실무에서 Bulkhead를 제대로 쓰는 기준

Bulkhead를 효과적으로 적용하려면 몇 가지 판단 기준이 필요합니다. 제가 반복해서 적용하면서 정리한 순서는 이렇습니다.

  1. 장애 발생 시 파급력이 큰 경계 지점을 먼저 식별합니다. 외부 API 호출이나 결제처럼 지연이 잦고 실패가 치명적인 영역이 우선 대상입니다.
  2. 각 영역의 실제 트래픽 패턴을 최소 2~4주 관찰한 뒤 자원 크기를 설정합니다. 감으로 잡은 수치는 반드시 나중에 발목을 잡습니다.
  3. Circuit Breaker(서킷 브레이커, 연속된 오류가 발생하면 자동으로 해당 경로를 차단하는 패턴)와 Timeout(타임아웃, 응답이 일정 시간 내에 오지 않으면 요청을 포기하는 설정)을 함께 구성합니다. Bulkhead 단독으로는 절반의 효과밖에 못 냅니다.
  4. 도입은 전체 시스템이 아니라 위험도 높은 영역 한두 곳부터 시작합니다. 한 번에 전면 적용했다가 설정 오류로 전체가 흔들리는 상황을 저도 한 번 겪었습니다.
  5. 자원 사용률, 큐 대기 시간, 타임아웃 발생 빈도를 모니터링 지표로 지속 추적합니다. 설정은 한 번에 끝나는 게 아니라 운영하면서 계속 다듬는 것입니다.

이 흐름대로 적용하면 적어도 "설계는 맞는데 왜 이상하게 동작하지"라는 상황은 상당 부분 줄일 수 있었습니다. Bulkhead는 도입 자체보다 운영 중에 어떻게 조정하느냐가 더 중요한 전략이라는 걸, 저는 꽤 비싼 경험으로 배웠습니다.

Bulkhead 전략은 분명히 쓸모 있습니다. 하지만 "적용했다"는 사실보다 "어디에, 어떻게 적용했는가"가 실제 결과를 가릅니다. 시스템 안정성을 고민하고 있다면, Bulkhead를 검토하면서 Circuit Breaker나 Failover 전략도 함께 살펴보시길 권합니다. 하나의 패턴이 모든 걸 해결해주는 일은, 현실에서는 없습니다.


관련 글

댓글

이 블로그의 인기 게시물

HTTP 메서드의 필요성 (GET과 POST, PUT과 DELETE, API 보안)

API 이해하기 (서비스 연결, 시스템 협력, 디지털 구조)

API 없는 세상의 불편함 (로그인 연동, 서비스 구조, 디지털 인프라)