502 게이트웨이를 없애보자. (이론편)

반응형

배포를 진행하게 되면 일정 시간 동안 딜레이가 발생합니다.

현재 화면은 배포 전 상태이며, localhost인 이유는 로컬 환경에서 배포를 진행했기 때문입니다.

문제는 배포가 완료되는 과정에서 잠시 동안 502 Bad Gateway 오류가 발생한다는 점입니다.
이는 프록시 서버가 백엔드 서버와 원활하게 통신하지 못하고 있음을 의미합니다.
일정 시간이 지나야 정상적으로 서비스가 동작하며, 새로고침을 해야 변경된 화면을 확인할 수 있습니다.

지금 보고 있는 화면은 Swagger이기 때문에 크게 문제가 없어 보일 수 있습니다.
하지만 이 상황을 실제 운영 중인 사용자 UI라고 가정해보겠습니다.

수백, 수천 명이 동시에 서비스를 이용하는 상황에서 배포가 이루어지고, 그 순간마다 502 오류가 발생한다면 어떻게 될까요?
상품을 구매하려던 사용자는 갑자기 튕기고, 웹을 탐색하던 사용자도 오류 화면을 마주하게 됩니다.
이러한 경험이 반복된다면 서비스에 대한 신뢰도는 크게 떨어질 수밖에 없습니다.

이처럼 배포 과정에서 발생하는 일시적인 중단은 사용자 경험을 해칠 수 있습니다.
이를 해결하기 위해 사용하는 방법이 바로 무중단 배포(Zero-Downtime Deployment)입니다.
무중단 배포란, 배포가 진행되는 동안에도 서비스가 끊기지 않도록 하여 사용자가 중단을 인지하지 못하게 만드는 방식입니다.
그렇다면, 어떻게 하면 배포 중에도 서비스가 끊기지 않도록 만들 수 있을까요?


무중단 배포를 하는 이유와 무중단 배포가 무엇인지는 알아봤으니
어떻게 하면 무중단 배포를 적용할 수 있는지 확인해보겠습니다.

무중단 배포는 어떻게 제공할 수 있을까?

무중단 배포의 핵심은 단순합니다.
배포 중에도 트래픽을 받을 수 있는 인스턴스를 항상 최소 1개 이상 유지하는 것입니다.
즉, 새 버전을 올리는 순간에 기존 서버가 내려가면서 생기는 "빈 구간"을 없애야 하고, 이를 위해 보통 서버를 2개 이상 운영하며 트래픽을 스위칭하는 방식을 사용합니다. 대표적으로 Rolling 배포, Blue-Green 배포, Canary 배포가 존재합니다.

Rolling 배포

인스턴스를 하나씩 교체하며 점진적으로 업데이트하는 방식
여기서 인스턴스를 하나씩 교체한다는 말은 다음과 같이 표현할 수 있습니다.

즉, 배포 과정 내내 항상 하나 이상의 인스턴스가 Up 상태를 유지하도록 만드는 것이 롤링 배포의 핵심입니다.
롤링 배포는 여기까지가 전부입니다. 그런데 여기서 두 가지 한계점이 존재합니다.

1. 단일 도메인 뒤에서 이루어지는 트래픽 분산
브라우저에서 URL을 입력하면 항상 같은 도메인으로 접속합니다.
서버 IP가 변경되더라도, 사용자가 접속하는 도메인 자체가 바뀌는 일은 없습니다.
하지만 실제로는 하나의 서버가 아니라, 로드밸런서(Nginx 등)를 통해 여러 인스턴스 중 하나로 요청이 전달됩니다.
즉, 사용자는 하나의 서버에 접속한다고 인식하지만, 실제로는 여러 인스턴스가 하나의 도메인 뒤에서 동작하고 있는 구조입니다.

2. 모든 서버(인스턴스)를 전부 띄워서 유지를 시켜야 할까?
이 질문에 대한 답은 뒤에서 이어서 다루겠습니다.
사실 이런 배포는 쿠버네티스를 활용하면 훨씬 편하게 진행할 수 있습니다.
kubectl rollout 같은 기능으로 배포 과정을 자동화할 수 있고, 쿠버네티스의 기본 배포 전략 자체가 Rolling Update이기 때문입니다.

하지만 현재 환경에서는 쿠버네티스를 사용하고 있지 않습니다. 그렇다면 방법이 없을까요?

결론부터 말하면, 방법은 있습니다.
바로 Nginx를 이용해 트래픽을 제어하고, 그 과정을 Jenkins 파이프라인으로 자동화하는 방식입니다.

즉, 쿠버네티스가 해주던 인스턴스 제외 → 교체 → 헬스체크 → 복귀 과정을
Jenkins + Nginx 스크립트(혹은 설정 전환)로 직접 구현하는 것입니다.

이 배포 방법에는 한 가지 문제가 존재합니다.
롤링 배포는 배포 과정에서 new 버전과 old 버전이 동시에 올라가 있는 구간이 발생할 수 있습니다.

이때 old 버전에서는 문제가 없던 기능이 new 버전에서 버그가 발생하면, 사용자는 서비스를 정상적으로 사용하다가도 갑자기 특정 기능이 막히는 경험을 하게 됩니다.
즉, 사용자 경험을 개선하기 위해 도입한 무중단 배포가 역설적으로 일관성 측면에서 사용자 경험을 떨어뜨릴 수 있는 가능성도 존재합니다.

이제 두 번째 한계점에 대해 말씀드리겠습니다.
무중단 배포를 위해서 반드시 두 개의 인스턴스가 항상 동시에 떠 있어야 하는 것은 아닙니다.
다만, "항상 하나 이상이 살아있게 유지한다"는 특성은 롤링 배포의 구현 방식에서 비롯된 특징으로 이해하시면 좋습니다.

이러한 한계를 보완하기 위해 사용할 수 있는 방식이 바로 Blue-Green 배포입니다.

Blue-Green 배포

Blue-Green 배포는 기존 환경(Blue)과 신규 환경(Green)을 동시에 준비한 뒤,
검증이 완료되면 트래픽을 한 번에 전환하는 방식입니다.

Rolling 배포는 가용성을 유지한다는 점에서 훌륭한 전략입니다.
하지만 배포 과정에서 old 버전과 new 버전이 동시에 존재하는 구간이 발생할 수 있다는 한계가 있습니다.

이러한 한계를 보완하기 위한 방식이 바로 Blue-Green 배포입니다.

이것 또한 간단한 그림으로 표현할 수 있습니다.

여기서 중요한 점은, 버전이 섞이지 않는다는 것입니다. 전환 전에는 Blue만, 전환 후에는 Green만 사용자에게 노출됩니다.

다만 이 방식에도 단점은 존재합니다.
트래픽을 다른 환경으로 전환하는 순간, 요청이 새로운 서버로 연결되는 과정에서 아주 짧은 딜레이가 발생할 수 있습니다.

하지만 전환 시점을 헬스체크와 워밍업을 통해 충분히 검증하고, 트래픽 전환을 안전하게 설계해두면 이러한 지연은 대부분 사용자가 체감하지 못하는 수준으로 충분히 커버할 수 있습니다.

블루 그린 방식은 버전이 섞이지 않는다는 장점이 있습니다.
하지만 전환 순간, 전체 트래픽이 한 번에 새로운 버전(Green)으로 이동하게 됩니다.

만약 그린 환경에 치명적인 버그가 존재한다면, 그 영향은 일부 사용자가 아니라 전체 사용자에게 동시에 전달될 수 있습니다.
즉, 성공하면 매우 안정적이고 깔끔한 배포 전략이지만, 실패할 경우에는 한 번에 큰 장애로 이어질 수 있는 구조입니다.

Canary 배포

일부 트래픽만 신규 버전에 먼저 흘려보내며 문제 여부를 확인한 뒤 점진적으로 확대하는 방식

Rolling 배포는 배포 과정에서 버전 불일치가 발생할 수 있다는 한계가 있고,
Blue-Green 배포는 전환 순간에 문제가 발생할 경우 전체 사용자에게 영향을 줄 수 있다는 위험성을 가지고 있습니다.

이러한 단점을 보완하기 위해 등장한 전략이 바로 Canary 배포입니다.

Canary라는 이름은 과거 광부들이 유독가스를 감지하기 위해 사용했던 작은 노란 새, 카나리에서 유래했습니다.
카나리는 유독가스에 민감하게 반응하기 때문에, 위험을 조기에 감지하는 역할을 했습니다.

배포 전략에서도 이 개념을 동일하게 적용합니다.

새로운 버전을 전체 사용자에게 한 번에 공개하는 것이 아니라,
일부 사용자(예: 5% 또는 특정 그룹)에게 먼저 배포하여 안정성을 검증합니다.

이후 모니터링과 피드백을 통해 문제가 없다고 판단되면, 점진적으로 배포 범위를 확대하여 최종적으로 전체 사용자에게 적용합니다.

마무리

각 배포 전략은 서로 배타적인 선택지라기보다는, 상황에 따라 조합하여 사용할 수 있는 전략의 집합에 가깝다고 생각합니다.
하나의 방법을 선택했다고 해서, 다른 방법을 전혀 사용하지 않는 것은 아닙니다.

예를 들어,

  • Rolling + Blue-Green을 함께 사용할 수도 있고,
  • Rolling + Canary 전략을 적용할 수도 있으며,
  • 더 나아가 Rolling + Blue-Green + Canary를 조합하는 것도 가능합니다.

결국 배포 전략은 정답이 있는 문제가 아니라, 서비스의 규모, 리스크 허용 범위, 운영 환경에 따라
적절히 설계해야 하는 아키텍처 선택의 문제라고 생각합니다.

반응형

댓글

Designed by JB FACTORY