무중단 배포 톺아보기

Jimin Kim·2026년 6월 14일

무중단 배포란?

서비스가 중단되지 않은 상태로 새로운 버전을 배포하는 것이다. 과거 서비스 업데이트마다 필수적으로 발생했던 점검 시간으로 인한 매출 손실, 고객 이탈, 사용자 경험 저하를 해결하기 위해 등장했다.

프로젝트 환경

1.blue-green

(1) 개념

블루그린 배포는 현재 운영 중인 환경과 새 버전 환경을 분리해두고 새 버전이 정상 동작하는 것을 확인한 뒤 트래픽을 한 번에 전환하는 방식이다.

현재 트래픽을 처리하는 환경을 Blue 새로 배포할 환경을 Green이라고 하면, Green을 먼저 실행하고 정상 동작을 확인한 뒤 트래픽을 한 번에 Green으로 전환한다.

Green 배포 기준으로 전환 과정은 다음과 같다.

Blue 운영 중
→ Green 새 버전 실행
→ Green 헬스체크
→ Blue에서 Green으로 트래픽 전환
→ Green 헬스체크
→ Blue 컨테이너 종료

이번 실습에서는 v1.0.0이 Blue에서 실행 중인 상태에서 v1.0.1을 Green에 배포하는 시나리오로 진행했다.

(2)실습

Blue와 Green은 각각 replica 3개로 구성했다.

또한 k6로 20rps요청을 두었다.

Traefik은 dynamic/bg-routers.yaml 파일을 기준으로 요청을 어느 환경으로 보낼지 결정한다.

1.배포 전(Blue 운영 중)

Blue 환경(v1.0.0)만 활성 상태이며 모든 트래픽이 Blue로 전달된다. Green은 실행 전이다.

2.Green실행(트래픽 전환 전)

Green 환경을 새 버전으로 실행하고 헬스체크를 진행한다.

3. Green 헬스체크 통과 후 라우터에 등록

Green 컨테이너 3개 모두 healthy하다면 라우터에 Green을 등록한다.

4.트래픽 전환(Green 100%)

트래픽을 Blue 100%에서 Green 100%로 전환한다.

5.기존 환경(Blue) 라우터에서 제거

Blue를 라우터에서 제거하고 Green만 라우팅 대상으로 남긴다.

6.Blue 종료(배포 완료)

Blue 컨테이너를 종료하고 Green만 운영 상태를 유지한다.

(3) 실행 결과

그래프의 Y축은 1분 단위로 집계된 버전별 응답 수를 의미한다.

이번 테스트에서는 k6가 20 RPS로 지속적으로 요청을 보냈다. 따라서 1분 기준으로 1200응답이 표시된다.

초기에는 Blue 환경이 모든 트래픽을 처리하므로 v1.0.0 응답만 발생한다. 이후 Green 환경을 실행하고 헬스체크를 진행하지만, 아직 라우팅은 Blue 100%이므로 v1.0.1 응답은 발생하지 않는다.

라우팅 전환 시점부터 v1.0.0 응답 수는 감소하고 v1.0.1 응답 수는 증가한다. 전환이 완료된 뒤에는 v1.0.1 응답만 유지된다.

즉, 이 결과를 통해 배포 중 요청이 끊기지 않았고, 트래픽이 Blue에서 Green으로 정상적으로 전환되었음을 확인할 수 있다.

컨테이너 상태

구분BlueGreen트래픽
배포 전v1.0.0 실행 중없음Blue
배포 중v1.0.0 실행 중v1.0.1 실행 중Blue
전환 후v1.0.0 실행 중v1.0.1 실행 중Green
배포 완료종료v1.0.1 실행 중Green

(4) 장단점

장점

  • 배포 전환 속도가 빠르다.

애플리케이션을 그 순간 새로 띄우는 것이 아니라 이미 준비된 환경으로 트래픽만 넘기기 때문에 전환 시간이 짧다.

  • 롤백이 빠르다.

새 버전에 문제가 생기면 이전 버전이 올라가 있는 blue 환경으로 트래픽을 다시 돌리면 된다. 그래서 코드 재배포 없이 이전 환경으로 복귀할 수 있다.

  • 배포 환경이 명확하게 분리된다.

blue와 green 환경이 분리되어 있기 때문에 현재 운영 중인 버전과 새로 배포할 버전의 경계가 명확하다. 운영자는 현재 어느 환경이 트래픽을 받고 있는지, 문제가 생겼을 때 어느 환경으로 되돌아가야 하는지 쉽게 파악할 수 있다.

단점

  • 인프라 비용이 크다.

블루그린 배포는 blue와 green 두 환경을 동시에 유지해야 한다. 즉, 같은 애플리케이션 서버를 두 벌로 띄워야 하고, 경우에 따라 로드밸런서, 캐시, 모니터링, 네트워크 설정도 이중으로 관리해야 한다. 트래픽이 큰 서비스라면 green 환경도 실제 운영 트래픽을 감당할 수 있는 수준으로 준비해야 하므로 비용 부담이 커진다.

  • 트래픽 전환 순간의 영향 범위가 크다.

블루그린은 보통 트래픽을 한 번에 blue에서 green으로 넘긴다. 그래서 사전 테스트에서 발견하지 못한 문제가 green 환경에 있으면, 전환 직후 전체 사용자가 영향을 받을 수 있다.

  • 데이터베이스 변경에 취약하다.

블루그린에서 애플리케이션 서버는 두 환경으로 나눌 수 있지만, 데이터베이스는 하나를 공유하는 경우가 많다. 이때 새 버전에서 DB schema를 변경했는데 기존 버전과 호환되지 않으면 롤백이 어려워질 수 있다. 예를 들어 green 배포 후 DB 컬럼을 삭제하거나 이름을 바꿨다면, 다시 blue로 트래픽을 돌려도 blue 버전이 정상 동작하지 않을 수 있다. 그래서 블루그린 배포에서는 backward-compatible migration이 중요하다.

(5) 블루그린 배포를 안전하게 하는 법

1. Connection Draining 설정

기존 blue 환경으로 들어간 요청은 일정 시간 동안 마저 처리하게 하고, 새 요청만 green 환경으로 보내야 한다.

이를 통해 배포 전환 중 처리 중이던 요청이 갑자기 끊기는 문제를 줄일 수 있다.

2. Graceful Shutdown 적용

blue 서버를 바로 종료하지 않고, 현재 처리 중인 요청이 끝날 때까지 기다린 뒤 종료해야 한다.

서버가 즉시 종료되면 결제, 예약, 파일 업로드처럼 처리 시간이 긴 요청이 실패할 수 있다.

3. Health Check와 Smoke Test 후 전환

green 환경이 정상적으로 요청을 받을 수 있는지 확인한 뒤 트래픽을 전환해야 한다.

단순히 서버가 실행 중인지뿐만 아니라 DB 연결, Redis 연결, 주요 API 응답, 로그인 같은 핵심 기능을 함께 확인하는 것이 좋다.

블루그린 배포를 안전하게 하려면 단순히 bluegreen 환경을 나누는 것만으로는 부족하다.

트래픽 전환 시 기존 요청을 안전하게 마무리하고, 세션과 DB 호환성을 유지하며, 긴 작업과 외부 연동이 중단되지 않도록 설계해야 한다.


2. Canary

(1) 개념

카나리 배포는 새 버전을 전체 사용자에게 한 번에 노출하지 않고, 일부 트래픽만 먼저 보내 검증한 뒤 점진적으로 전환하는 방식이다.

기존 트래픽을 처리하는 안정 환경을 Stable, 새로 배포할 환경을 Canary라고 하면, Canary를 먼저 실행하고 정상 동작을 확인한 뒤 트래픽 비율을 조금씩 늘려간다.

트래픽을 늘리는 비율과 단계는 서비스 상황에 따라 다르게 정할 수 있다. 이번 실습에서는 카나리 트래픽을 5%, 20%, 50%, 100% 순서로 늘리며 배포를 진행했다.

Canary 배포 기준으로 전환 과정은 다음과 같다.

Stable 100% 운영 중
→ Canary 새 버전 실행
→ Canary 헬스체크
→ Canary 5% 트래픽 전환
→ Canary 헬스체크
→ Canary 20% 트래픽 전환
→ Canary 헬스체크
→ Canary 50% 트래픽 전환
→ Canary 헬스체크
→ Canary 100% 트래픽 전환
→ 기존 Stable 컨테이너 종료

이번 실습에서는 v1.0.0이 안정 환경에서 실행 중인 상태에서 v1.0.1을 카나리 환경에 배포하는 시나리오로 진행했다.

새 버전을 바로 전체 트래픽에 적용하지 않고, 작은 비율부터 점진적으로 늘리면서 배포 안정성을 확인했다.

(2) 실습

카나리 배포에서는 안정 환경과 카나리 환경을 각각 app-a, app-b로 구성했다.

또한 k6로 20rps 요청을 계속 보내면서, 배포 중 트래픽이 단계적으로 전환되는지 확인했다.

Traefik은 dynamic/canary-routers.yaml 설정을 기준으로 안정 환경과 카나리 환경에 트래픽을 분산한다.

1.배포 전 Stable 100%

기존 안정 환경인 app-a만 실행 중이며, 모든 트래픽이 app-a로 전달된다.

카나리 환경인 app-b는 아직 실행 전이다.

2. Canary 실행

새 버전인 app-b를 카나리 환경으로 실행한다.

이 단계에서는 app-b 컨테이너를 1개만 먼저 실행하고 헬스체크를 진행한다.

아직 트래픽은 기존 안정 환경인 app-a가 100% 처리한다.

3.Canary 5% 전환

카나리 환경이 healthy 상태가 되면 전체 트래픽 중 5%만 app-b로 전달한다.

이 단계에서는 대부분의 요청은 여전히 안정 환경인 app-a가 처리하고, 일부 요청만 새 버전인 app-b로 전달된다.

4. Canary 20% 전환

5% 단계에서 문제가 없다면 카나리 트래픽을 20%로 늘린다.

이때 카나리 환경의 replica도 2개로 늘려 더 많은 요청을 처리할 수 있도록 한다.

트래픽은 app-a 80%, app-b 20% 비율로 분산된다.

5. Canary 50% 전환

20% 단계에서도 문제가 없다면 카나리 트래픽을 50%까지 늘린다.

이 단계에서는 안정 환경과 카나리 환경이 각각 절반씩 트래픽을 처리한다.

카나리 환경의 replica는 3개까지 증가시켜 안정 환경과 동일한 규모로 맞춘다.

6. Canary 100% 전환

50% 단계에서 문제가 없으면 모든 트래픽을 카나리 환경으로 전환한다.

이 시점부터 app-b가 전체 요청을 처리하고, 기존 안정 환경인 app-a는 더 이상 트래픽을 받지 않는다.

7. 기존 환경 종료

카나리 환경이 100% 트래픽을 정상적으로 처리하면 기존 환경을 라우터에서 제거하고 컨테이너를 종료한다.

배포 완료 후에는 새 버전인 app-b만 운영 상태로 남는다.

(3) 결과

초기에는 기존 안정 환경인 v1.0.0 응답만 발생한다. 이후 카나리 환경인 v1.0.1로 트래픽을 5%, 20%, 50%, 100% 순서로 늘리면서 v1.0.1 응답 수가 점진적으로 증가한다. 반대로 v1.0.0 응답 수는 점점 감소한다.

이번 스크립트에서는 각 카나리 단계마다 다음 과정을 수행한다.

트래픽 비율 변경
→ bake 대기
→ 헬스체크
→ 다음 단계로 이동

그래서 블루그린 배포처럼 한 번에 확 바뀌는 그래프가 아니라, 두 버전의 응답 수가 천천히 교차하면서 길게 이어지는 형태가 나온다.

따라서 그래프가 한 번에 전환되지 않고 완만하게 교차하는 것은 카나리 배포가 의도한 동작이다. 이를 통해 배포 중 요청이 끊기지 않았고, 트래픽이 기존 버전에서 새 버전으로 점진적으로 전환되었음을 확인할 수 있다.

단계별 전환 표

단계안정 환경카나리 환경카나리 replica설명
초기 상태100%0%0기존 버전만 운영
카나리 기동100%0%1새 버전 컨테이너 1개 실행 후 헬스체크
5% 전환95%5%1소량 트래픽으로 새 버전 검증
20% 전환80%20%2카나리 replica 증설 후 트래픽 확대
50% 전환50%50%3절반 트래픽으로 검증
100% 전환0%100%3새 버전으로 전체 전환
정리종료100%3기존 환경 제거

(4) 장단점

장점

  • 장애 영향 범위를 줄일 수 있다

새 버전을 처음부터 전체 사용자에게 배포하지 않고 일부 트래픽에만 노출하기 때문에, 문제가 발생하더라도 영향 범위를 제한할 수 있다.

  • 특정 사용자 그룹에만 먼저 노출할 수 있다

단순히 트래픽 비율만 조절하는 것이 아니라, 특정 사용자 그룹을 기준으로 새 버전을 먼저 노출할 수도 있다. 따라서 위험도가 낮거나 피드백을 받기 쉬운 사용자 집단을 대상으로 새 버전을 검증할 수 있다.

  • 롤백 부담이 상대적으로 작다

문제가 발생했을 때 새 버전으로 가는 트래픽만 차단하면 된다. 아직 대부분의 사용자는 기존 버전을 사용하고 있으므로 전체 롤백보다 부담이 작다.

단점

  • 운영과 설정이 복잡하다

카나리 배포는 기존 버전과 새 버전을 동시에 운영해야 한다. 따라서 단순히 새 버전을 배포하는 것보다 관리해야 할 요소가 많다.

  • 두 버전 간 호환성을 유지해야 한다

카나리 배포 중에는 기존 버전과 새 버전이 동시에 운영된다. 따라서 두 버전이 같은 DB, 캐시, 메시지 큐, 외부 API를 사용해도 문제가 없어야 한다.

  • 사용자 경험이 일관되지 않을 수 있다

카나리 배포에서는 일부 사용자는 기존 버전을 사용하고, 일부 사용자는 새 버전을 사용한다. 이때 두 버전의 응답 형식, UI, 기능 동작이 다르면 사용자 경험이 달라질 수 있다.

  • 낮은 트래픽에서는 판단이 어렵다

카나리 배포는 일부 트래픽을 새 버전에 보내고, 그 결과를 보고 안정성을 판단한다. 그런데 서비스 트래픽이 적으면 새 버전으로 충분한 요청이 들어오지 않는다.

(5) 카나리 배포를 안전하게 하는 방법

1. 작은 비율로 시작하기

새 버전에 문제가 있더라도 영향 범위를 일부 사용자로 제한할 수 있고, 문제가 없을 때만 점진적으로 트래픽을 늘릴 수 있다.

2. 모니터링 지표를 기준으로 진행하기

단순히 시간이 지났다고 다음 단계로 넘어가는 것이 아니라, 에러율, 응답 시간, CPU, 메모리 사용량, 주요 API 성공률 같은 지표를 확인하면서 진행해야 한다.

새 버전에 10% 트래픽을 보냈을 때 에러율이 증가하거나 응답 시간이 길어진다면, 30%나 50%로 확대하기 전에 배포를 중단해야 한다.

3. 롤백 기준을 미리 정해두기

카나리 배포 중 문제가 발생했을 때 빠르게 판단할 수 있도록 롤백 기준을 미리 정해두어야 한다.

예를 들어 5xx 에러율이 일정 기준 이상 증가하거나, 응답 시간이 기존 버전보다 크게 증가하거나, 핵심 기능 실패율이 증가하면 즉시 기존 버전으로 트래픽을 되돌리도록 기준을 세울 수 있다.

4. 같은 사용자는 같은 버전을 사용하게 하기

카나리 배포 중에는 기존 버전과 새 버전이 동시에 운영된다. 이때 트래픽을 단순히 비율로만 나누면 같은 사용자라도 어떤 요청은 기존 버전으로 가고, 어떤 요청은 새 버전으로 갈 수 있다.

만약 두 버전의 화면, API 응답 형식, 저장 방식이 조금이라도 다르면 사용자는 요청마다 다른 결과를 보게 될 수 있다. 예를 들어 게시글 작성은 새 버전에서 처리되고, 게시글 조회는 기존 버전에서 처리되면 데이터가 다르게 보이거나 오류가 발생할 수 있다.

따라서 user-based routing, sticky session, feature flag 등을 사용해 같은 사용자는 가능하면 같은 버전을 사용하도록 구성하는 것이 좋다.

5. DB 변경은 하위 호환되게 설계하기

기존 버전과 새 버전이 동시에 같은 DB를 사용할 수 있다. 따라서 새 버전에서만 동작하는 방식으로 DB schema를 변경하면 기존 버전이 오류를 낼 수 있다.

컬럼 삭제나 이름 변경처럼 위험한 변경은 한 번에 하지 않고, 새 컬럼 추가, 기존 컬럼과 병행 사용, 데이터 백필, 기존 버전 제거 후 컬럼 삭제처럼 단계적으로 진행해야 한다.

6. 외부 API와 Side Effect를 조심하기

결제, 주문, 예약, 문자 발송, 이메일 발송, 푸시 알림처럼 외부에 실제 영향을 주는 기능은 카나리 배포 중 특히 주의해야 한다.

새 버전에 버그가 있으면 중복 결제, 중복 예약, 중복 알림 같은 문제가 발생할 수 있다.

따라서 중요한 요청에는 idempotency key를 적용해 같은 요청이 여러 번 들어와도 한 번만 처리되도록 하고, 외부 API 호출은 sandbox 환경이나 테스트 계정을 활용해 검증하는 것이 좋다.


3.Rolling

(1)개념

롤링 배포는 실행 중인 구버전 컨테이너를 한 번에 모두 교체하지 않고, 하나씩 순차적으로 신버전 컨테이너로 교체하는 방식이다.

블루그린 배포처럼 별도의 전체 환경을 하나 더 띄우는 것이 아니라, 기존 인스턴스 집합 안에서 일부 컨테이너를 신버전으로 바꿔가며 배포한다.

이번 실습에서는 app-rolling 컨테이너 3개가 v1.0.0으로 실행 중인 상태에서, 이를 하나씩 v1.0.1로 교체하는 시나리오로 진행했다.

롤링 배포는 교체 순서에 따라 두 가지 방식으로 나눌 수 있다.

stop-first
구버전 제거 → 신버전 추가 → 헬스체크

start-first
신버전 추가 → 헬스체크 → 구버전 제거

이번 실습에서는 두 방식을 모두 구현했다.

(2)실습

이번 실습에서는 v1.0.0 인스턴스 3개가 운영 중인 상태에서 시작해, 이를 하나씩 v1.0.1 인스턴스로 교체하는 방식으로 롤링 배포를 구현했다.

또한 k6로 20rps 요청을 계속 보내면서, 인스턴스가 순차적으로 교체되는 동안 요청이 끊기지 않는지 확인했다.

stop-first 방식

1. 배포 전

v1.0.0 컨테이너 3개가 실행 중이다. 모든 요청은 기존 버전 컨테이너들이 처리한다.

2.stop-first 1회차:구버전 컨테이너 1개 제거

먼저 구버전 컨테이너 1개를 제거한다. 이 순간 실행 중인 컨테이너 수는 3개에서 2개로 줄어든다.

3. stop-first 1회차: 신버전 컨테이너 1개 추가

신버전 컨테이너 1개를 추가해 다시 replica 3개 상태로 복구한다.

추가된 신버전 컨테이너의 헬스체크를 진행한다.

4. stop-first 2회차: 구버전 인스턴스 1개 제거

다시 구버전 인스턴스 1개를 제거한다. 이 단계에서도 일시적으로 실행 중인 인스턴스 수가 2개로 줄어든다.

5.stop-first 2회차: 신버전 인스턴스 1개 추가

신버전 인스턴스 1개를 추가해 다시 3개 상태로 만든다. 추가된 신버전 컨테이너의 헬스체크를 진행한다.

6. stop-first 3회차: 마지막 구버전 인스턴스 제거

마지막 구버전 인스턴스를 제거한다.

7. stop-first 3회차: 신버전 인스턴스 추가 후 배포 완료

마지막 신버전 인스턴스를 추가하면 모든 인스턴스가 v1.0.1로 교체된다.

이로써 stop-first 방식의 롤링 배포가 완료된다.

start-first 방식

1. 배포 전

v1.0.0 컨테이너 3개가 실행 중이다.

모든 요청은 기존 버전 컨테이너들이 처리한다.

2. start-first 1회차: 신버전 컨테이너 추가

start-first 방식에서는 구버전을 먼저 제거하지 않고, 신버전 컨테이너를 1개 먼저 추가한다.

이 시점에는 일시적으로 컨테이너가 4개가 된다.

추가된 신버전 컨테이너가 healthy 상태인지 확인한다.

3. start-first 1회차: 구버전 컨테이너 제거

신버전 컨테이너가 헬스체크를 통과하면 구버전 컨테이너 1개를 제거한다.

이 과정을 통해 전체 컨테이너 수는 다시 3개가 된다.

4. start-first 2회차 반복

다시 신버전 컨테이너를 1개 추가하고, 헬스체크를 통과하면 구버전 컨테이너를 1개 제거한다.

5. start-first 3회차 : 신버전 컨테이너 추가

다시 신버전 컨테이너를 1개 추가하고 헬스체크를 한다.

6. start-first 3회차 : 구버전 컨테이너 제거 후 배포 완료

신버전 컨테이너가 헬스체크를 통과하면 구버전 컨테이너 1개를 제거한다.

모든 컨테이너가 신버전으로 교체되고 롤링 배포가 완료된다.

(3)결과

start-first

stop-first

start-first, stop-first 차이점

start-first와 stop-first 그래프에서 1번 구간과 2번 구간의 길이가 다르게 보이는 이유는 각 방식의 컨테이너 교체 순서가 다르기 때문이다.

start-first 방식은 신버전 컨테이너를 먼저 추가한 뒤 헬스체크를 진행하고, 일정 시간 동안 베이크 타임을 가진 후 구버전 컨테이너를 제거한다. 따라서 신버전 응답이 먼저 나타나고, 이후 구버전 응답이 줄어드는 흐름이 나타난다. 따라서 start-first 부분의 2번구간이 stop-first보다 더 완만하다.

반면 stop-first 방식은 구버전 컨테이너를 먼저 제거한 뒤 신버전 컨테이너를 추가하고 헬스체크를 진행한다. 따라서 1번구간이 start-first의 1번 구간보다 완만하고 길다.

(4) 장단점

장점

  • 추가 인프라 부담이 작다

롤링 배포는 새 컨테이너를 하나씩 띄우고 기존 컨테이너를 하나씩 내리는 방식이기 때문에, 리소스 증가 폭을 비교적 작게 가져갈 수 있다. 즉, 서버 자원이 넉넉하지 않은 환경에서도 적용하기 쉽다.

  • 기존 운영 구조를 크게 바꾸지 않아도 된다

롤링 배포는 별도의 복잡한 배포 구조를 새로 만들기보다, 기존 컨테이너 운영 방식 위에 순차 교체 로직을 추가하는 방식에 가깝다. 블루그린처럼 blue와 green 환경을 명확히 나누거나, 카나리처럼 정교한 트래픽 비율 제어를 구성하지 않아도 된다.

  • 점진적으로 교체되기 때문에 급격한 전환 부담이 적다

단점

  • 기존 버전과 새 버전이 동시에 운영된다

두 버전의 API 응답 형식, DB 사용 방식, 캐시 구조, 메시지 처리 방식이 다르면 문제가 생길 수 있다.

  • DB 변경에 취약하다

롤링 배포 중에는 v1과 v2가 동시에 같은 DB를 사용할 수 있다.

따라서 새 버전 기준으로 DB schema를 한 번에 변경하면 아직 남아 있는 기존 버전이 오류를 낼 수 있다.

  • 사용자 경험이 일관되지 않을 수 있다

배포 중에는 같은 사용자의 요청이 어떤 때는 기존 버전으로, 어떤 때는 새 버전으로 갈 수 있다.

  • 배포 속도가 느릴 수 있다

컨테이너를 하나씩 교체하는 방식이기 때문에 전체 배포 완료까지 시간이 걸릴 수 있다.

(5) 롤링 배포를 안전하게 하는 방법

1. 한 번에 교체할 컨테이너 수를 작게 유지하기

한 번에 너무 많은 컨테이너를 새 버전으로 바꾸면 롤링 배포의 장점이 줄어든다. 문제가 생겼을 때 이미 많은 요청이 새 버전으로 들어가고 있을 수 있기 때문이다.

2.처리 중인 요청이 끝난 뒤 컨테이너 종료하기

롤링 배포에서는 기존 컨테이너를 새 버전 컨테이너로 순차적으로 교체한다. 이때 사용자의 요청을 처리 중인 컨테이너가 바로 종료되면 요청이 중간에 끊길 수 있다.

3.DB 변경은 하위 호환되게 설계하기

기존 버전과 새 버전이 동시에 같은 DB를 사용할 수 있다.

따라서 새 버전에서만 동작하는 방식으로 DB schema를 바꾸면 아직 남아 있는 기존 버전이 오류를 낼 수 있다.

4.배포 순서를 스크립트로 명확하게 관리하기

순서가 잘못되면 준비되지 않은 컨테이너에 요청이 들어가거나 기존 컨테이너가 너무 빨리 종료되어 요청이 실패할 수 있다.


4.Shadow

(1)개념

섀도우 배포는 실제 사용자 요청을 기존 버전이 처리하면서, 동일한 요청의 복사본을 신버전에도 함께 보내 검증하는 방식이다. 신버전의 응답은 사용자에게 전달되지 않고 무시된다.

섀도우 배포 기준으로 흐름은 다음과 같다.

사용자 요청
→ Traefik
→ 기존 버전이 요청 처리
→ 기존 버전 응답을 사용자에게 반환

동시에
→ 동일한 요청을 신버전으로 미러링
→ 신버전 응답은 무시

이번 실습에서는 v1.0.0 이 기존 버전으로 실행 중인 상태에서, v1.0.1 을 섀도우 환경으로 실행했다.

이후 Traefik의 미러링 기능을 사용해 실제 요청을 신버전에도 복사해서 전달했다.

(2)실습

이번 실습은 기존 버전 v1.0.0이 운영 중인 상태에서 시작했다. 이후 새 버전 v1.0.1을 섀도우 서버로 띄우고, 실제 요청을 복사해서 전달하는 방식으로 검증했다.

또한 k6로 20rps 요청을 계속 보내면서, 기존 버전과 섀도우 버전이 요청을 어떻게 처리하는지 확인했다.

이번 실습에서는 기존 버전은 3개, 섀도우 버전은 1개로 구성했다.

섀도우 서버는 사용자에게 직접 응답하는 서버가 아니라 검증용 서버이기 때문에, 반드시 기존 버전과 같은 개수로 띄울 필요는 없다. 다만 미러링 비율을 100%로 설정하면 모든 요청이 섀도우 서버에도 복사되므로, 실제 운영 환경에서는 낮은 비율부터 시작하는 것이 안전하다.

1. 배포 전

v1.0.0 기존 버전만 실행 중이다. 모든 요청은 기존 버전이 처리하고, 사용자에게 응답을 반환한다. 섀도우 버전은 아직 실행되지 않은 상태다.

2. 섀도우 컨테이너 실행

새 버전인 v1.0.1을 섀도우 환경으로 실행한다.

이 단계에서는 아직 미러링 라우팅을 적용하지 않았기 때문에, 실제 요청은 기존 버전만 처리한다.

섀도우 컨테이너는 먼저 실행된 뒤 헬스체크를 진행한다.

3. 미러링 라우팅 적용

섀도우 컨테이너가 healthy 상태가 되면 미러링 라우팅을 적용할 준비가 완료된다.

Traefik의 미러링 기능을 적용한다.

이후 사용자 요청은 기존 버전으로 전달되어 실제 응답을 반환하고, 동시에 동일한 요청의 복사본이 섀도우 버전으로 전달된다.

4. 섀도우 검증

섀도우 버전은 실제 사용자 요청과 동일한 요청을 처리한다.

하지만 섀도우 버전의 응답은 사용자에게 전달되지 않기 때문에, 신버전에 문제가 있더라도 사용자 경험에는 영향을 주지 않는다.

이 단계에서는 로그, 메트릭, 응답 시간, 에러 여부 등을 확인하면서 신버전이 실제 트래픽에서 정상 동작하는지 검증한다.

5.검증 완료 후 — 미러링 해제 또는 완전 전환

검증이 끝나면 두 가지 선택지가 있다.

문제가 없는 경우 — 블루그린이나 카나리 같은 방식으로 신버전으로 완전히 전환

문제가 발견된 경우 — 미러링을 즉시 중단한다. 가존 버전 전용 라우팅으로 복구한다.

(3)결과

k6가 실제로 받은 응답 그래프

사용자에게 실제 응답을 반환한 기존 버전 v1.0.0만 표시된다. 섀도우 서버인 v1.0.1은 동일한 요청의 복사본을 받아 처리하더라도, 그 응답을 k6에게 돌려주지 않기 때문에 이 그래프에는 나타나지 않는다.

로그 그래프

미러링된 요청은 신버전 애플리케이션까지 전달되기 때문에, 신버전이 사용자에게 응답을 반환하지 않더라도 요청 처리 로그는 남는다. 따라서 로그 그래프에서는 기존 버전 v1.0.0뿐만 아니라 섀도우 버전 v1.0.1도 함께 확인할 수 있다.

(4) 장단점

장점

  • 실제 운영 트래픽으로 새 버전을 검증할 수 있다.

로드 테스트나 유닛 테스트만으로는 발견하기 어려운 실제 요청 패턴, 예외 케이스, 데이터 형태, 성능 문제를 확인할 수 있다.

  • 사용자에게 직접적인 영향을 주지 않는다.

새로운 버전이 잘못된 응답을 반환해도 실제 사용자에게 영향을 미치지 않다.

  • 기존 버전과 새 버전의 결과를 비교할 수 있다.

같은 요청을 기존 버전과 새 버전이 함께 처리하므로, 두 버전의 응답 결과를 비교할 수 있다. 예를 들어 검색 결과, 추천 결과, 가격 계산, 점수 계산처럼 결과의 정확성이 중요한 기능에서 새 버전의 동작을 검증하기 좋다.

단점

  • 리소스 비용이 증가한다.

하나의 요청을 두 버전이 함께 처리하기 때문에 서버 자원, 네트워크, 로그 저장 공간, 모니터링 비용이 추가로 발생할 수 있다.

  • 쓰기 요청 처리에 주의해야 한다.

섀도우 배포는 읽기 요청 검증에는 유용하지만, 쓰기 요청을 그대로 복제하면 중복 생성이나 중복 결제 같은 부작용이 생길 수 있으므로 write 요청은 격리된 DB, mock 처리, side effect 차단, idempotency 설계가 필요하다.

  • 운영 구성이 복잡하다.

섀도우 배포는 단순히 새 버전을 띄우는 것만으로 끝나지 않는다. 실제 요청을 복제해서 보내는 구조, 새 버전의 응답을 사용자에게 반환하지 않는 구조, 응답 비교 로직, 로그 수집, 모니터링, Side Effect 차단 설정이 필요하다.

(5) 섀도우 배포를 안전하게 하는 방법

1.읽기 요청 중심으로 검증하고, 쓰기 요청은 격리하기

새버전에서 주문, 결제, 예약, 알림 발송, 이메일 발송처럼 외부에 영향을 주는 요청이 그대로 실행되면 중복 처리 문제가 발생할 수 있다. 따라서 격리된 DB를 사용하거나, 외부 API 호출을 mock 처리하는 방식으로 운영 데이터에 영향을 주지 않도록 구성해야 한다.

2. 모니터링 지표 분리하기

새 버전의 응답은 사용자에게 반환되지 않기 때문에 문제를 직접 발견하기 어렵다. 따라서 에러율, 응답 시간, CPU, 메모리 사용량을 기존 버전과 분리해서 모니터링해야 한다.


+쿠버네티스로 한다면?

(1)쿠버네티스에서 카나리 배포는 어떻게 설정 할수있을까?

쿠버네티스에서 카나리 배포는 다양한 방법이 있다.

1. Deployment 리소스를 사용한 점진적 배포

기존 버전과 새 버전의 Deployment를 나누고 각 Pod 수를 조절하여 새 버전에 전달되는 트래픽 비율을 점진적으로 늘리는 방식이다. 예를 들어 기존 버전 Pod 9개, 새 버전 Pod 1개로 시작하면 대략 10%의 트래픽이 새 버전으로 전달된다.

2. Istio를 이용한 트래픽 라우팅

Istio의 VirtualService를 사용하면 Pod 개수와 관계없이 트래픽 라우팅 규칙으로 비율을 조절할 수 있다. 예를 들어 기존 버전에 90%, 새 버전에 10%의 트래픽을 보내도록 설정할 수 있어 더 정교한 카나리 배포가 가능하다.

3. Argo Rollouts를 이용한 배포 자동화

Argo Rollouts는 Kubernetes의 기본 Deployment보다 더 세밀한 배포 전략을 제공하는 도구이다. 카나리 배포에서 새 버전에 전달할 트래픽 비율과 단계별 진행 순서를 정의할 수 있으며, 특정 단계에서 일시정지하거나 운영자의 승인을 받은 뒤 다음 단계로 진행하도록 설정할 수 있다. 또한 분석 결과에 따라 배포를 중단하거나 롤백할 수 있어, 배포 흐름을 선언적으로 관리할 수 있다.

4. Flagger

Flagger는 카나리 배포를 metric 기반으로 자동화하는 도구이다. Prometheus와 같은 모니터링 시스템에서 수집한 요청 성공률, 에러율, 응답 시간 등의 지표를 기준으로 새 버전의 안정성을 판단한다. 조건을 만족하면 새 버전의 트래픽 비율을 점진적으로 증가시키고, 성능 저하나 오류가 감지되면 자동으로 롤백한다.

(2)쿠버네티스는 기본적으로 롤링인가?

.spec.strategy는 기존 Pod를 새로운 Pod로 교체할 때 사용할 전략을 지정한다. .spec.strategy.type은 “Recreate” 또는 “RollingUpdate”가 될 수 있다. 기본값은 “RollingUpdate”이다.
따라서 별도로 .spec.strategy.type을 지정하지 않으면 기존 Pod를 한 번에 모두 종료하지 않고, 새 Pod를 점진적으로 생성하면서 기존 Pod를 점진적으로 줄이는 방식으로 업데이트가 진행된다.
대부분의 경우 Deployment에서는 RollingUpdate가 더 선호되는 update strategy이다. 반면 Recreate는 Pod를 singleton으로 실행하고 있으며, 단 몇 초라도 중복 Pod가 존재하면 안 되는 경우에 유용할 수 있다.

(3)stop-first start-first를 쿠버네티스에서는 어떻게 구현이될까?

Kubernetes에는 Docker Swarm이나 Docker Compose에서 사용하는 stop-first, start-first라는 옵션명이 그대로 존재하지 않는다. 대신 Deployment의 RollingUpdate 전략에서 maxUnavailablemaxSurge 값을 조정하여 유사한 동작을 구현할 수 있다.

start-first

start-first에 가까운 방식은 maxSurge를 1 이상으로 두고 maxUnavailable을 0으로 설정하는 것이다. 이렇게 하면 원하는 Pod 수보다 새 Pod를 먼저 추가로 생성할 수 있고, 새 Pod가 준비된 뒤 기존 Pod를 줄일 수 있다.

stop-first

stop-first에 가까운 방식은 maxUnavailable을 1 이상으로 두고 maxSurge를 0으로 설정하는 것이다. 이렇게 하면 원하는 Pod 수를 초과해서 새 Pod를 먼저 만들 수 없기 때문에 기존 Pod를 먼저 줄인 뒤 새 Pod를 생성하는 방식으로 동작한다.

최종 정리

지금까지 여러가지 방식의 무중단 배포 방식에 대해서 알아보았다.

서비스의 안정성을 생각한다면 일부 사용자에게만 적용해 먼저 검증할 수 있기 때문에 카나리 배포가 제일 좋은 것 같다. 하지만 카나리는 리소스가 많이든다.

따라서 실무에서는 리소스 문제 때문에 카나리+롤링 방식을 섞어쓰는 경우가 많다고한다.

다음번에는 이를 직접 구현해보겠다.

출처

https://www.cncf.co.kr/blog/kubernetes-canary-deploy/

https://octopus.com/devops/kubernetes-deployments/canary-deployments/

https://octopus.com/devops/kubernetes-deployments/kubernetes-deployment-strategies/

https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy

https://www.bluematador.com/blog/kubernetes-deployments-rolling-update-configuration?utm_source=chatgpt.com

깃허브

https://github.com/techeer-sv/zero-downtime-deployment

0개의 댓글