Blue-Green 배포 도입기

필자는 졸업 작품팀에서 백엔드 파트 및 인프라 구축을 담당하게 되었다. 서비스 출시 일정이 다가오면서, 어플리케이션의 안정성과 가용성에 대해 깊이 고민하게 되었다. 특히 배포 과정에서 발생할 수 있는 다운타임이 사용자 경험에 미칠 부정적인 영향을 우려하게 되었고, 이를 위해 Blue-Green 배포를 도입하기로 결정하였다.


무중단 배포란?

  무중단 배포(zero-downtime deployment)가 서비스의 가용성을 유지하면서 새로운 버전의 애플리케이션을 배포하는 기술이다. 이는 사용자 경험을 저해하지 않으면서 지속적인 서비스 개선과 유지보수를 가능하게 한다.

기존 배포 방식

gantt
    dateFormat  YYYY-MM-DD
    axisFormat %d

    section v1
    v1 운영중      :a1, 2023-01-01, 4d
    v1 종료        :a2, 2023-01-05, 1d

    section v2
    v2 개발완료    :b1, 2023-01-02, 2d
    v2 다운로드    :b2, 2023-01-04, 1d
    v2 실행        :b3, 2023-01-05, 1d
    v2 로드        :b4, 2023-01-06, 1d
    v2 운영중      :b5, 2023-01-07, 3d

    section 다운타임
    다운타임       :crit, 2023-01-05, 2d

  위 간트 차트를 통해 기존 배포 방식의 문제점을 살펴보자.

  1. 서비스 중단: v1에서 v2로 전환하는 과정에서 ‘2’라는 다운타임이 발생한다. 이는 사용자들이 서비스를 이용할 수 없는 기간을 의미한다.
  2. 긴 전환 시간: 새 버전(v2)의 실행부터 완전히 운영되기까지 3이라는 시간이 소요된다. 이는 배포 프로세스가 복잡하고 시간이 많이 걸림을 나타낸다.
  3. 리스크: 다운타임 동안 문제가 발생하면 서비스 재개가 더 지연될 수 있다. 이는 비즈니스에 심각한 영향을 미칠 수 있다.
  4. 유연성 부족: 이러한 방식으로는 빈번한 업데이트나 긴급 패치 적용이 어렵다.

무중단 배포의 주요 이점

  무중단 배포를 통해 얻을 수 있는 이점은 다음과 같다.

  • 서비스 연속성 보장: 업데이트 중에도 서비스 이용 가능
  • 리스크 감소: 문제 발생 시 신속한 롤백 가능
  • 사용자 경험 향상: 서비스 중단으로 인한 불편 최소화
  • 비즈니스 연속성 유지: 24/7 서비스 가용성으로 매출 손실 방지
  • 빈번한 업데이트 가능: 더 작은 단위의 변경사항을 자주 배포 가능
  • 신속한 버그 수정: 중요한 이슈 발생 시 빠른 대응 가능

  이러한 문제점들은 사용자 입장에서는 받아들이기 어려운 단점이다. 따라서 이를 해결하기 위해 무중단 배포를 통해 다운타임을 없애고, 배포 과정의 리스크를 줄이며, 더 빠르고 유연한 서비스 업데이트를 하고자 하였다.

무중단 배포 전략 종류

1. Blue-Green 배포

graph TD
    LB[Load Balancer]
    subgraph Blue
    B1[Server 1 V1]
    B2[Server 2 V1]
    end
    subgraph Green
    G1[Server 1 V2]
    G2[Server 2 V2]
    end
    LB -->|Current Traffic| Blue
    LB -.->|Future Traffic| Green

  Blue-Green 배포는 무중단 배포 전략의 한 종류로, 두 개의 동일한 프로덕션 환경을 운영하며 순간적으로 트래픽을 전환하는 방식이다. 이 두 환경은 관례적으로 ‘Blue’와 ‘Green’으로 불린다. 이 방법의 주요 특징은 다음과 같다.

  • 두 개의 동일한 프로덕션 환경을 준비한다 (Blue와 Green).
  • 한 환경은 현재 버전을 실행 중이고, 다른 환경은 대기 상태이다.
  • 새 버전을 배포할 때, 대기 환경에 새 버전을 설치하고 테스트한다.
  • 테스트가 완료되면, 트래픽을 새 환경으로 전환한다.
  • 이전 환경은 롤백을 위해 잠시 대기 상태로 유지된다

2. Canary 배포

graph TD
    U[Users] --> LB[Load Balancer]
    LB --> |90%| S[Stable Version]
    LB --> |10%| C[Canary Version]

    subgraph Stable Environment
    S --> S1[Server 1 V1]
    S --> S2[Server 2 V1]
    S --> S3[Server 3 V1]
    end

    subgraph Canary Environment
    C --> C1[Server 1 V2]
    end

    style S fill:#blue,stroke:#333,stroke-width:2px
    style C fill:#yellow,stroke:#333,stroke-width:2px
    style LB fill:#lightgray,stroke:#333,stroke-width:2px
    style U fill:#lightgreen,stroke:#333,stroke-width:2px

  Canary 배포란 새 버전을 일부 사용자에게만 점진적으로 노출시키는 방식이다. 이 방법의 주요 특징은 다음과 같다.

  • 새 버전을 소수의 사용자 또는 서버에 먼저 배포
  • 문제 발생 시 영향을 받는 사용자 수가 적음
  • 실제 사용 환경에서 새 버전을 테스트할 수 있음
  • 점진적으로 새 버전의 트래픽 비율을 늘려감

3. Rolling 업데이트

graph LR
    LB[Load Balancer]
    subgraph Cluster
        S1[Server 1 V1]
        S2[Server 2 V1]
        S3[Server 3 V1]
    end
    LB --> S1
    LB --> S2
    LB --> S3
    S1 -.- S1V2[Server 1 V2]
    S2 -.- S2V2[Server 2 V2]
    S3 -.- S3V2[Server 3 V2]

  Rolling 업데이트를 서버 그룹을 순차적으로 업데이트하는 방식이다. 주요 특징은 다음과 같다.

  • 서버를 작은 배치로 나누어 순차적으로 업데이트
  • 전체 시스템의 가용성을 유지하면서 점진적으로 새 버전 배포
  • 리소스 사용이 효율적임
  • 롤백이 Blue-Green보다는 복잡할 수 있음

4. A/B 테스팅

graph TD
    U[Users] --> LB[Load Balancer]
    LB -->|50%| A[Version A]
    LB -->|50%| B[Version B]
    A --> M[Metrics]
    B --> M
    M --> AN[Analysis]

  A/B 테스팅은 두 가지 버전을 동시에 운영하며 성능을 비교하는 방식이다. 주요 특징은 다음과 같다.

  • 두 가지 버전을 동시에 실행하여 성능, 사용성 등을 비교
  • 사용자 피드백을 바탕으로 더 나은 버전 선택 가능
  • 새로운 기능의 효과를 실제 환경에서 측정 가능
  • 복잡한 설정과 모니터링이 필요할 수 있음

Blue-Green 배포 전략 도입

도입 배경

  Blue-Green 배포 전략을 선택하는 주요 이유는 무중단 배포와 롤백의 용이성, 그리고 안정적인 테스트 환경 제공이다. 새로운 버전(Green)을 기존 버전(Blue)과 완전히 별도로 구축하여 즉시 전환할 수 있게 함으로써, 사용자 경험에 영향을 주지 않고 신속하게 업데이트할 수 있다. 만약 새 버전에서 문제가 발생하더라도 트래픽을 즉시 이전 버전으로 되돌릴 수 있어, 복잡한 롤백 절차 없이 빠르게 안정 상태로 복귀할 수 있다. 또한, 단순한 전환 메커니즘을 통해 배포 과정을 간소화하고 인적 오류의 가능성을 낮춤으로써 배포 프로세스의 복잡성을 크게 줄여준다. 이는 배포 과정의 안정성을 높이고 운영 팀의 스트레스를 줄이는 데 도움이 된다. 이러한 특징들로 인해 무중단 배포를 처음 도입하는 필자에게 있어서는 Blue-Green 배포가 가장 이해하기 쉽고 적용하기 용이한 방법이라고 생각되었다.

  또한, 필요 시 실제 운영 환경과 동일한 Green 환경에서 철저한 테스트를 수행할 수 있게 할 수 있다. 이를 통해 프로덕션 환경에서 발생할 수 있는 문제들을 사전에 발견하고 해결할 수 있다. Blue와 Green 환경은 동일하기 때문에 환경 차이로 인한 예기치 못한 오류 발생 가능성을 줄이고자 하였다.

  신규 기능 도입 측면에서도 Blue-Green 배포는 큰 장점을 제공한다. 새로운 Green 환경에 최신 업데이트를 적용한 후 전환함으로써, 이전 버전에 노출되는 시간을 최소화할 수 있다. 이는 기능 도입 뿐만 아니라, 지속적으로 변화하는 보안 위협에 대응하는 데에도 매우 효과적인 방법이다.

  이러한 이유들로 인해 Blue-Green 배포 방식은 특히 고가용성과 안정성이 중요한 시스템에서 널리 채택되고 있다. 다만, 이 방식을 적용하기 위해서는 추가적인 리소스가 필요하고 초기 설정에 더 많은 작업이 요구될 수 있다는 점을 고려해야 한다. 그럼에도 불구하고, 제공되는 이점들이 이러한 초기 투자를 충분히 상쇄할 수 있기 때문에 많은 조직에서 Blue-Green 배포를 선호하고 있다.

작업 사항

1. docker-compose.yml 수정 (was)

  필자는 Blue-Green 배포를 지원하기 위해 docker-compose.yml 파일을 수정했다. was_blue와 was_green 서비스를 추가하여 두 버전의 애플리케이션을 동시에 운영할 수 있도록 구성했다.

was_blue:
  container_name: was_blue
  image: pennyway/pennyway-was:${WAS_VERSION}
  ports:
    - "${WAS_BLUE_PORT:-8080}:8080"
  environment:
    - TZ=Asia/Seoul
  env_file:
    - .env
  depends_on:
    - rdb
    - cache

was_green:
  container_name: was_green
  image: pennyway/pennyway-was:${WAS_VERSION}
  ports:
    - "${WAS_GREEN_PORT:-8081}:8080"
  environment:
    - TZ=Asia/Seoul
  env_file:
    - .env
  depends_on:
    - rdb
    - cache

2. nginx.conf 수정 (bastion)

  필자는 nginx.conf 파일의 업스트림 서버 설정을 변경하여 was_blue와 was_green 서비스로 트래픽을 분산할 수 있게 수정했다.

upstream backend {
  server xxx.xxx.xxx.xxx:8080;
  server xxx.xxx.xxx.xxx:8081;
}

3. deploy.yml 수정

deploy.yml 파일을 수정하여 새로 작성한 deploy.sh 스크립트를 실행하도록 변경했습니다.

- name: Deploy to EC2
  run: |
    chmod +x deploy.sh
    bash -x ./deploy.sh

4. deploy.sh 작성

  필자는 Blue-Green 배포의 핵심 로직을 구현한 deploy.sh 스크립트를 새로 작성했다. 주요 기능은 다음과 같다.

#!/bin/bash

# 현재 실행 중인 서비스 확인
CURRENT_SERVICE=$(docker-compose ps --services --filter "status=running" | grep "was_")

# 새 서비스 결정
if [ "$CURRENT_SERVICE" = "was_blue" ]; then
  NEW_SERVICE="was_green"
  NEW_PORT="8081"
else
  NEW_SERVICE="was_blue"
  NEW_PORT="8080"
fi

echo "Current service: $CURRENT_SERVICE"
echo "New service: $NEW_SERVICE"
echo "New port: $NEW_PORT"

# 버전 설정
if [ -z "$VERSION" ]; then
  VERSION="latest"
fi
echo "Version: $VERSION"

# .env 파일 업데이트
sed -i "s/WAS_VERSION=.*/WAS_VERSION=$VERSION/" .env

# 새 서비스 시작
docker-compose up -d $NEW_SERVICE

echo "Waiting for service to start..."
sleep 30

# 헬스 체크
for i in {1..30}; do
  echo "Attempting health check ($i/30)..."
  echo "Checking port: $NEW_PORT"

  if nc -z localhost $NEW_PORT; then
    echo "Port $NEW_PORT is open. Proceeding with HTTP check."

    CURL_OUTPUT=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:$NEW_PORT/v3/api-docs)

    if [ "$CURL_OUTPUT" -eq 200 ]; then
      echo "Health check passed. New version is healthy."

      # 이전 서비스 중지 및 제거
      if [ -n "$CURRENT_SERVICE" ] && [ "$CURRENT_SERVICE" != "$NEW_SERVICE" ]; then
        echo "Stopping and removing old service: $CURRENT_SERVICE"
        docker-compose stop $CURRENT_SERVICE
        docker-compose rm -f $CURRENT_SERVICE
      else
        echo "No old service to remove or new service is the same as current service."
      fi

      # 새 서비스가 실행 중인지 확인
      echo "Verifying new service is running..."
      RUNNING_SERVICE=$(docker-compose ps --services --filter "status=running" | grep "was_")
      if [ "$RUNNING_SERVICE" = "$NEW_SERVICE" ]; then
        echo "Deployment successful. New service $NEW_SERVICE is running."
        exit 0
      else
        echo "Error: New service $NEW_SERVICE is not running after deployment."
        exit 1
      fi
    else
      echo "Health check failed. HTTP status: $CURL_OUTPUT"
    fi
  else
    echo "Port $NEW_PORT is not open yet."
  fi

  echo "Checking container status and logs:"
  docker-compose ps $NEW_SERVICE
  docker-compose logs --tail=50 $NEW_SERVICE

  echo "Waiting 20 seconds before next attempt..."
  sleep 20
done

echo "Health check failed after 30 attempts. Rolling back..."
docker-compose stop $NEW_SERVICE
docker-compose rm -f $NEW_SERVICE
if [ -n "$CURRENT_SERVICE" ]; then
  echo "Ensuring old service is running: $CURRENT_SERVICE"
  docker-compose up -d $CURRENT_SERVICE
fi
echo "Rollback complete. Deployment failed."
exit 1

5. 동작 확인

  • Blue 배포

    alt text

  • Green 배포

    alt text

도입 결과 및 기대 효과

alt text

  현재 프로젝트의 인프라 아키텍처 및 배포 환경에서 Blue-Green 배포 전략을 도입한 후 이미지이다. 위 그림과 같이 Blue-Green 배포 전략을 도입하여 기대하고 있는 효과는 다음과 같다.

  1. 서비스 연속성을 보장한다. Bastion Host와 WAS Host 구조를 통해 Blue Instance를 유지하면서 새 버전을 배포할 수 있어, 사용자 경험 중단 없이 업데이트가 가능하다.
  2. 리스크를 효과적으로 관리한다. Nginx를 통한 트래픽 제어로 새 버전으로 점진적 전환이 가능하며, 문제 발생 시 빠른 롤백을 수행할 수 있다.
  3. 실제 환경에서 성능을 검증한다. 실제 운영 환경에서 새 버전을 테스트할 수 있어 배포 전 성능과 안정성을 확인할 수 있다.
  4. 탄력적인 운영이 가능하다. Docker와 docker-compose를 활용한 구조로 환경 확장과 관리가 용이하다.

관련 Pull-Request


References