Blue/Green Deployment vs Canary Deployment

1,429 단어·3 분·원문(.md)

블루 그린 배포 #

블루/그린은 매우 직관적이다 현재 서비스중인 환경 BLUE와 완벽하게 동일한 인프라 환경 GREEN을 하나 더 구축해놓고 배포 시점에 로드밸런서의 스위치를 딸깍하고 100퍼센트 전환하는 방식이다

Zero-Downtime의 정석이라고 볼 수 있고 스위칭 순간에 세션이 끊기지 않도록 하는 Connection Draining (기존 요청은 끝까지 처리하고 새 요청만 green으로 전환) 설정만 잘 되어있다면 완벽한 무중단 배포가 가능하다.

Warm-up도 가능한데, 실사용자 트래픽을 넣기 전에, Green 환경에서 사내 테스트 트래픽을 흘려보내서 내부 도메인 사용이나 캐시를 미리 채워두거나 jvm jitcompiler 최적화등 최종 qa용으로도 사용할 수 있다.

단점은 비용인데 평소 100대의 서버로 운영한다면 배포를 위해 100대의 서버를 추가로 띄워야해서 클라우드 환경에서는 배포직후 blue를 바로 죽이면 비용을 아낄수있으나 on-promise나 그런환경에서는 하드웨어 비용이 2배로 든다.

카나리 배포 #

간 보는 방법으로 이해하면편한데, 과거 광부들이 유독가스를 탐지하기 위해 탄광에서 카나리아 새를 먼저 들여보냈던 것에서 유래했다.

신버전 서버 1대 또는 5%수준만 먼저 배포를 투입하고 일부 유저의 트래픽만 라우팅하여 에러율이나 지연시간이 튀는지 모니터링 한뒤 점진적으로 100%까지 늘린다.

Blast Radius(폭발 반경) 최소화가 가능한데 코드에 치명적인 버그가 있어도 전체유저 대상이아닌 5% 유저만 에러를 겪기 때문에 비즈니스 리스크를 극도로 줄이거나 에러버짓을 아낄 수 있다.

버전 혼재의 위험성이 있는데 현재 시스템에 구버전 신버전이 동시에 돌아간다. 만약 db스키마 변경이 되었거나 api 응답 스펙이 달라졌다면 클라이언트에서 v1 v2 모두 예외없이 처리할 수 있게 구현해놓고 100퍼센트 배포가 완료되면 구버전을 삭제치는 방식으로 운영하자.

세션 유지 필수다 유저 a가 새로고침할때마다 한번은 구버전 한번은 신버전으로 가면 데이터 정합성 혹은 ux에 큰 혼란이 올 수 있기에 한 번 카나리로 배정된 유저는 계속 신버전으로만 가도록 쿠키나 헤더기반의 라우팅 룰이 필요하다.

Example #

블루/그린 롤백 k8s service selector를 보겠다.

k8s 환경에서는 ingress나 로드밸런서를 건드릴 필요없이 트래픽을 연결하는 Service의 라벨타겟만 변경하여 트래픽을 100% 넘긴다.

# 1. 현재 구버전(Blue)을 바라보고 있는 Service 설정
apiVersion: v1
kind: Service
metadata:
  name: my-backend-service
spec:
  selector:
    app: my-backend
    version: v1.0 # <--- 현재는 Blue(v1.0) 파드들로 트래픽이 감
  ports:
    - port: 80
      targetPort: 8080

kubectl patch service my-backend-service -p '{"spec":{"selector":{"version":"v2.0"}}}' 다른 방법들도 있게지만 직관적인 예시로 이런식으로 버전을 변경해줘 트래픽을 green v2로 향하게 해둘수있다.

카나리 배포도 nginx split_clients를 활용해서 트래픽 분산해볼건데

Nginx 자체 기능인 split_clients를 사용하여 유저의 ip 또는 세션 쿠키를 해싱해 5% 트래픽만 카나리 서버로 내보내는 구성이다.

http {
    # 사용자 IP($remote_addr)를 기준으로 트래픽을 쪼갬
    split_clients "${remote_addr}" $upstream_variant {
        5%      backend_canary; # 5%의 유저는 신버전으로
        * backend_main;   # 나머지 95%는 구버전으로
    }

    # 구버전 (Main) 서버 그룹
    upstream backend_main {
        server 10.0.1.10:8080;
        server 10.0.1.11:8080;
    }

    # 신버전 (Canary) 서버 그룹
    upstream backend_canary {
        server 10.0.1.99:8080; # 새로 배포된 서버 1대
    }

    server {
        listen 80;
        location / {
            # 쪼개진 결과($upstream_variant)에 따라 트래픽 라우팅
            proxy_pass http://$upstream_variant;
        }
    }
}

이 설정의 핵심은 remote_addr(사용자 ip)를 해싱한다는 것이다. 따라서 특정 유저 A는 언제 접속해도 항상 카나리로 가거나 항상 메인으로 가게 되어 세션 유지 문제가 어느정도 해결된다.

실무에서는 돈이 많거나, 서버가 그렇게 많지 않다면 블루/그린을 선호하고 최근에는 마이크로서비스와 k8s 환경에서 리소스를 낭비하지 않기위해서 istio, argocd같은 도구들로 자동화된 카나리 배포가 사실상 자주 쓰이는듯하다.

SRE/question/q_44.md