ArgoCD Rollout Blue/Green, Canary

Blue/Green 및 Canary 배포 전략으로 무중단 배포, 롤백을 통해 서비스 안정성을 높일 수 있습니다.

ArgoCD Rollout Blue/Green, Canary
Photo by Fab Lentz / Unsplash

개요

Blue/Green 배포

Blue/Green 배포는 동일한 프로덕션 환경을 두 벌로 구성하여 무중단 배포를 실현하는 전략입니다. 한 환경(Blue)이 실제 운영 중일 때 다른 환경(Green)에 새 버전을 배포하고 테스트한 후, 트래픽을 전환하는 방식입니다.

주요 특징

  • 무중단 배포: 사용자 서비스 중단 없이 새 버전 배포 가능
  • 빠른 롤백: 문제 발생 시 즉시 이전 환경으로 전환 가능
  • 안전성: 새 버전을 실제 환경과 동일한 조건에서 완벽하게 테스트 가능

ArgoCD 구현 방식

  1. 두 개의 Deployment 관리
    • Blue 환경용 Deployment
    • Green 환경용 Deployment
  2. Service를 통한 트래픽 제어
    • selector를 사용하여 활성 환경 지정
    • 라벨링을 통한 트래픽 전환
  3. 배포 프로세스
    • 비활성 환경에 새 버전 배포
    • 새 버전 테스트 및 검증
    • Service selector 변경으로 트래픽 전환
    • 이전 환경 정리 또는 대기

Canary 배포

개요

Canary 배포는 새로운 버전을 점진적으로 도입하는 전략입니다. 소수의 사용자에게 새 버전을 먼저 제공하고, 모니터링을 통해 안정성을 확인한 후 점차 트래픽 비율을 늘려가는 방식입니다.

주요 특징

  • 위험 최소화: 문제 발생 시 영향받는 사용자 수 제한
  • 점진적 롤아웃: 트래픽을 단계적으로 전환
  • 실시간 모니터링: 새 버전의 성능과 안정성을 실제 사용자 환경에서 검증

ArgoCD 구현 방식

  1. Argo Rollouts 사용
    • Kubernetes native Rollout 리소스 활용
    • 트래픽 가중치 설정 가능
  2. 배포 설정
    • 초기 트래픽 비율 설정 (예: 10%)
    • 단계별 증가 비율 정의
    • 성공/실패 메트릭 설정
  3. 배포 프로세스
    • 새 버전 초기 배포
    • 메트릭 기반 모니터링
    • 점진적 트래픽 증가
    • 문제 발생 시 자동 롤백

전략 선택 기준

Blue/Green 배포 적합 상황

  • 즉각적인 전체 롤백이 필요한 경우
  • 새 버전 전체 테스트가 필요한 경우
  • 리소스 여유가 충분한 경우

Canary 배포 적합 상황

  • 점진적인 위험 관리가 필요한 경우
  • 실제 사용자 피드백이 중요한 경우
  • 리소스를 효율적으로 사용해야 하는 경우

구현 시 고려사항

  • 배포 상태 모니터링
  • 자동 롤백 조건 설정
  • 배포 승인 프로세스
  • 리소스 산정 및 비용 최적화

Preinstall

Package & Plugin Install

💡 Argo CD에 Argo Rollouts 라는 새로운 플러그인을 추가해야합니다.
  • Docs & Repo

bookmark

link_preview

  • kubectl 통합
brew install argoproj/tap/kubectl-argo-rollouts

# argo-rollout auto complete
sudo bash -c 'cat <<EOF >/usr/local/bin/kubectl_complete-argo-rollouts
#!/usr/bin/env sh
kubectl argo rollouts __complete "\$@"
EOF'

sudo chmod +x /usr/local/bin/kubectl_complete-argo-rollouts

echo "source <(kubectl-argo-rollouts completion zsh)" >> ~/.zshrc
source ~/.zshrc

테스트 코드

Deployment를 모두 Argo Rollout API(argoproj.io/v1alpha1)를 사용하여 전환하여야합니다.

Kustomize를 사용하고 있으며 각 배포별 strategy를 확인하면 됩니다. 예시는 다음과 같습니다.

#kustomize/base/rollout.yaml
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: test-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: test-app
  template:
    metadata:
      labels:
        app: test-app
    spec:
      containers:
      - name: test-app
        image: test-ecr
        ports:
        - containerPort: 3000
        resources:
          requests:
            cpu: "100m"
            memory: "512Mi"
          limits:
            cpu: "1000m"
            memory: "1Gi"

Blue/Green 배포 전환

Blue/Green 작업을 진행하기 위해선 UI 작업, CLI 작업 등 다양한 방법이 가능합니다.

Blue/Green 작업 strategy는 다음과 같습니다.

# overlays/dev/strategy-patch.yaml
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: test-app
spec:
  strategy:
    blueGreen:
      activeService: dev-test-app-active
      previewService: dev-test-app-preview
      scaleDownDelaySeconds: 300
      autoPromotionEnabled: false      # 수동 승인 필요

Argo Dashboard

kubectl 에서 argo rollouts 커맨드를 통합하였다면 아래 스크립트를 실행하게 된다면 localhost:3100 포트로 동작하게 됩니다.

$ k argo rollouts dashboard -n dev

INFO[0000] Argo Rollouts Dashboard is now available at http://localhost:3100/rollouts

ArgoCD rollout 대시보드 상에서 배포 전략과 Revision 정보들을 확인해볼 수 있습니다.

그냥 ArgoCD Dashboard에서도 마찬가지로 확인할 수 있습니다.

Notion Image

ArgoCD Dashboard를 확인해봤을 때 총 12개의 Replicaset을 확인할 수 있습니다.

Notion Image

Dashboard

Notion Image

대시보드상에서는 배포하려는 rollout을 Promote하면 됩니다.

Notion Image

Promote 하게 된다면 해당 작업은 scaleDownDelaySeconds에 따라 시간이 지난 후 종료되며 옵션은 다음과 같습니다.

strategy:
    blueGreen:
      activeService: dev-test-app-active
      previewService: dev-test-app-preview
      scaleDownDelaySeconds: 300 # 스메일 다운 지연시간
      autoPromotionEnabled: false # Auto Promotion Off

배포가 완료되면 정상적으로 리소스가 전환 후 삭제되는 것을 확인해볼 수 있습니다.

Notion Image

CLI

명령어는 아래와 같이 실행되며 Status 에 집중해야합니다.

Status가 "Paused" 상태인 것은 새로운 버전(Green)이 배포되어 준비는 되었지만, 실제 트래픽 전환을 위해 사용자의 승인(promote)을 기다리고 있다는 의미입니다.

$ kubectl argo rollouts get rollout <ROLLOUT_NAME> -n <NAMESPACE>

# 예시와 같이 출력됩니다.
Name:            dev-test-app
Namespace:       dev
Status:          ॥ Paused
Message:         BlueGreenPause
Strategy:        BlueGreen
Images:          759320821027.dkr.ecr.ap-northeast-2.amazonaws.com/bjchoi/test-ecr:20241205.0323.dcb1ce3 (stable, active)
                 759320821027.dkr.ecr.ap-northeast-2.amazonaws.com/bjchoi/test-ecr:20241205.0421.c1dd96e (preview)
Replicas:
  Desired:       6
  Current:       12
  Updated:       6
  Ready:         6
  Available:     6

NAME                                      KIND        STATUS        AGE   INFO
⟳ dev-test-app                            Rollout     ॥ Paused      170m
├──# revision:5
│  └──⧉ 
dev-test-app-69bd9b9df7
           ReplicaSet  ✔ Healthy     58s   preview
│     ├──□ dev-test-app-69bd9b9df7-2wxlm  Pod         ✔ Running     58s   ready:1/1
│     ├──□ dev-test-app-69bd9b9df7-mcmqg  Pod         ✔ Running     58s   ready:1/1
│     ├──□ dev-test-app-69bd9b9df7-skhls  Pod         ✔ Running     58s   ready:1/1
│     ├──□ dev-test-app-69bd9b9df7-th2cj  Pod         ✔ Running     58s   ready:1/1
│     ├──□ dev-test-app-69bd9b9df7-vcxzp  Pod         ✔ Running     58s   ready:1/1
│     └──□ dev-test-app-69bd9b9df7-xd9j2  Pod         ✔ Running     58s   ready:1/1
├──# revision:4
│  └──⧉ 
dev-test-app-696b6f9c6f
           ReplicaSet  ✔ Healthy     57m   stable,active
│     ├──□ dev-test-app-696b6f9c6f-2gg9z  Pod         ✔ Running     57m   ready:1/1
│     ├──□ dev-test-app-696b6f9c6f-4sln2  Pod         ✔ Running     57m   ready:1/1
│     ├──□ dev-test-app-696b6f9c6f-7q4pf  Pod         ✔ Running     57m   ready:1/1
│     ├──□ dev-test-app-696b6f9c6f-8cqdx  Pod         ✔ Running     57m   ready:1/1
│     ├──□ dev-test-app-696b6f9c6f-n2mw6  Pod         ✔ Running     57m   ready:1/1
│     └──□ dev-test-app-696b6f9c6f-tnkr5  Pod         ✔ Running     57m   ready:1/1
├──# revision:3
│  └──⧉ dev-test-app-5b494976c4           ReplicaSet  • ScaledDown  132m
├──# revision:2
│  └──⧉ dev-test-app-6954c64b9c           ReplicaSet  • ScaledDown  137m
└──# revision:1
   └──⧉ dev-test-app-7bdb9dc4fd           ReplicaSet  • ScaledDown  143m

정상적으로 올라온게 확인되었다면 Promote 합니다

argo rollouts promote -n dev dev-test-app
rollout 'dev-test-app' promoted

Promote가 실행되었다면 새 버전(revision:5)이 현재 stable과 active 상태로 변경되었다는 것을 확인할 수 있고 전체적인 Rollout 상태가 "✔ Healthy"로 표시되어 있습니다.

이전 버전(revision:4)은 아직 실행 중(Delay)이지만 delay:4m4s로 표시되어 있는 것을 보아 곧 Scale Down될 예정입니다

Name:            dev-test-app
Namespace:       dev
Status:          ✔ Healthy
Strategy:        BlueGreen
Images:          759320821027.dkr.ecr.ap-northeast-2.amazonaws.com/bjchoi/test-ecr:20241205.0323.dcb1ce3
                 759320821027.dkr.ecr.ap-northeast-2.amazonaws.com/bjchoi/test-ecr:20241205.0421.c1dd96e (stable, active)
Replicas:
  Desired:       6
  Current:       12
  Updated:       6
  Ready:         6
  Available:     6

NAME                                      KIND        STATUS        AGE    INFO
⟳ dev-test-app                            Rollout     ✔ Healthy     173m
├──# revision:5
│  └──⧉ dev-test-app-69bd9b9df7           ReplicaSet  ✔ Healthy     3m36s  stable,active
│     ├──□ dev-test-app-69bd9b9df7-2wxlm  Pod         ✔ Running     3m36s  ready:1/1
│     ├──□ dev-test-app-69bd9b9df7-mcmqg  Pod         ✔ Running     3m36s  ready:1/1
│     ├──□ dev-test-app-69bd9b9df7-skhls  Pod         ✔ Running     3m36s  ready:1/1
│     ├──□ dev-test-app-69bd9b9df7-th2cj  Pod         ✔ Running     3m36s  ready:1/1
│     ├──□ dev-test-app-69bd9b9df7-vcxzp  Pod         ✔ Running     3m36s  ready:1/1
│     └──□ dev-test-app-69bd9b9df7-xd9j2  Pod         ✔ Running     3m36s  ready:1/1
├──# revision:4
│  └──⧉ dev-test-app-696b6f9c6f           ReplicaSet  ✔ Healthy     59m    delay:4m4s
│     ├──□ dev-test-app-696b6f9c6f-2gg9z  Pod         ✔ Running     59m    ready:1/1
│     ├──□ dev-test-app-696b6f9c6f-4sln2  Pod         ✔ Running     59m    ready:1/1
│     ├──□ dev-test-app-696b6f9c6f-7q4pf  Pod         ✔ Running     59m    ready:1/1
│     ├──□ dev-test-app-696b6f9c6f-8cqdx  Pod         ✔ Running     59m    ready:1/1
│     ├──□ dev-test-app-696b6f9c6f-n2mw6  Pod         ✔ Running     59m    ready:1/1
│     └──□ dev-test-app-696b6f9c6f-tnkr5  Pod         ✔ Running     59m    ready:1/1
├──# revision:3
│  └──⧉ dev-test-app-5b494976c4           ReplicaSet  • ScaledDown  134m
├──# revision:2
│  └──⧉ dev-test-app-6954c64b9c           ReplicaSet  • ScaledDown  140m
└──# revision:1
   └──⧉ dev-test-app-7bdb9dc4fd           ReplicaSet  • ScaledDown  146m

정상적으로 원복된 것을 확인할 수 있습니다.

Name:            dev-test-app
Namespace:       dev
Status:          ✔ Healthy
Strategy:        BlueGreen
Images:          759320821027.dkr.ecr.ap-northeast-2.amazonaws.com/bjchoi/test-ecr:20241205.0421.c1dd96e (stable, active)
Replicas:
  Desired:       6
  Current:       6
  Updated:       6
  Ready:         6
  Available:     6

NAME                                      KIND        STATUS        AGE    INFO
⟳ dev-test-app                            Rollout     ✔ Healthy     177m
├──# revision:5
│  └──⧉ dev-test-app-69bd9b9df7           ReplicaSet  ✔ Healthy     8m26s  stable,active
│     ├──□ dev-test-app-69bd9b9df7-2wxlm  Pod         ✔ Running     8m26s  ready:1/1
│     ├──□ dev-test-app-69bd9b9df7-mcmqg  Pod         ✔ Running     8m26s  ready:1/1
│     ├──□ dev-test-app-69bd9b9df7-skhls  Pod         ✔ Running     8m26s  ready:1/1
│     ├──□ dev-test-app-69bd9b9df7-th2cj  Pod         ✔ Running     8m26s  ready:1/1
│     ├──□ dev-test-app-69bd9b9df7-vcxzp  Pod         ✔ Running     8m26s  ready:1/1
│     └──□ dev-test-app-69bd9b9df7-xd9j2  Pod         ✔ Running     8m26s  ready:1/1
├──# revision:4
│  └──⧉ dev-test-app-696b6f9c6f           ReplicaSet  • ScaledDown  64m
├──# revision:3
│  └──⧉ dev-test-app-5b494976c4           ReplicaSet  • ScaledDown  139m
├──# revision:2
│  └──⧉ dev-test-app-6954c64b9c           ReplicaSet  • ScaledDown  144m
└──# revision:1
   └──⧉ dev-test-app-7bdb9dc4fd           ReplicaSet  • ScaledDown  150m

Blue/Green Fallback

Fallback의 경우도 동일하게 동작합니다.

단 그린 인스턴스가 모두 Health가 되었을 때 동작합니다. 하지만 주의할 점은 직접적으로 자동 Rollback을 제어하는 설정은 현재 Argo Rollouts 설정에 없으므로 만약 Rollback 해야하는 경우가 있다면 관리자가 판단 후 진행해야하며 이를 위해 scaleDownDelaySeconds을 설정하여 작업 중 자유로운 작업 전환이 가능해집니다.

Notion Image

Canary

Canary 작업 strategy는 다음과 같습니다.

Argo Rollouts의 Canary 배포에서는 단계별로 새로운 버전의 파드를 생성하고 이전 버전의 파드를 축소합니다. 이를 확인해보기 위해 파드를 10개로 조절하고 2개씩 조절되는지 확인해보도록 하겠습니다.

  • 20% → 2분 대기
  • 40% → 2분 대기
  • 60% → 2분 대기
  • 80% → 2분 대기
  • pause: {} → 100% 전환을 위해 수동 승인이 필요
# overlays/dev/strategy-patch.yaml
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: test-app
spec:
  strategy:
    canary:
      stableService: dev-test-app-active  # 기존 activeService가 stableService로 변경
      canaryService: dev-test-app-preview # 기존 previewService가 canaryService로 변경
      steps:
      - setWeight: 20                     # 첫 단계: 트래픽 20% 전환
      - pause: {duration: 2m}            # 10분 대기
      - setWeight: 40                     # 트래픽 40%로 증가
      - pause: {duration: 2m}
      - setWeight: 60                     # 트래픽 60%로 증가
      - pause: {duration: 2m}
      - setWeight: 80                     # 트래픽 80%로 증가
      - pause: {duration: 2m}
      # 수동 승인이 필요한 최종 단계
      - pause: {}                         # 100% 전환 (수동 승인 필요)

Canary Dashboard

카나리 배포가 시작되면 다음과 같이 Strategy와 Weight을 확인할 수 있습니다.

20% 씩 트래픽이 전환되며 자세한 내용은 상세 페이지에서 확인할 수 있습니다.

Notion Image

현재 어느 단계를 수행하고 있는지 확인할 수 있습니다.

Notion Image
Notion Image

최종 단계는 Pause되어있기 때문에 앞선 Blue/Green 작업처럼 Promote해야 배포될 수 있습니다.

Notion Image

하지만 실제 테스트해보면 트래픽은 새로운 카나리 배포로 라우팅되지 않습니다.

왜냐하면 Load Balancer에서는 새로운 파드를 새로운 리소스로 인식하기 때문입니다. 이럴 때는 Canary 배포임에도 불구하고 Blue/Green처럼 동작하며 실제 리소스는 축소되기 때문에 trafficRouting 을 추가하여 기존의 ingress 설정에 리소스를 추가해야한다고 명시하여야 합니다.

canary:
      maxSurge: "20%"        # 최대 초과 생성 허용 Pod 수 (백분율 또는 정수)
      maxUnavailable: 0      # 동시에 사용 불가능한 최대 Pod 수
      stableService: dev-test-app-active
      canaryService: dev-test-app-preview
      trafficRouting:
        alb:
          ingress: dev-test-app
          servicePort: 80
          rootService: dev-test-app-active
      steps:
      - setWeight: 20                     # 트래픽 20% 전환
      - pause: {duration: 1m}            
      - setWeight: 40                     # 트래픽 40%로 증가
      - pause: {duration: 1m}
      - setWeight: 60                     # 트래픽 60%로 증가
      - pause: {duration: 30s}
      - setWeight: 80                     # 트래픽 80%로 증가
      - pause: {duration: 30s}
      # 수동 승인이 필요한 최종 단계
      - pause: {}                         # 무기한 일시 중지 (수동 승인 필요)
  • Ingress 수정
    • port로 use-annotation 을 추가합니다.
      • 해당 port는 Rollout yaml에서 servicePort로 사용하게 됩니다.
    • 카나리 배포가 시작됩니다.

rootService 지정 및 annotation으로 설정

# overlay/dev/ingress-patch.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: test-app
spec:
  rules:
    - host: bjchoi.saops.smileshark.help
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:            name: dev-test-app-active # rootService 지정이 필요합니다        port:
          name: use-annotation

자세한 내용은 아래 링크를 통해 확인할 수 있으며 Application Load Balancer의 기본 기능들도 활용 가능합니다.

최종 트래픽이 전환되는 과정입니다.

이때 각 파드는 모두 축소되지 않고 동작하며 단순 트래픽만 가중치에 따라 전달됩니다. Promote시 최종적으로 모든 트래픽이 전환되며 종료됩니다.

Notion Image
Notion Image
Notion Image
Notion Image

또한 최종적으로 대상그룹은 삭제되지 않습니다. 이후 새로운 파드가 추가되면 기존 대상 그룹에 리소스가 추가되고 가중치가 조절되는 방식으로 진행되며, 이후 진행 방식은 동일합니다.

  • 트래픽 분배는 ALB의 action annotation을 통해 이루어지게 됩니다.

결론

이러한 배포 방식 외에도 Argo Rollouts의 abort 명령어 등을 통해 진행 중인 Rollout을 중단하고 이전 stable 버전으로 돌아가거나 배포 과정 중 에러 메시지 확인을 통해 수월하게 CI/CD 작업을 진행할 수 있습니다.