본문 바로가기
사이드 프로젝트

[CI/CD 프로젝트] ArgoRollout로 Blue/Green 배포하기

by 간장공장공차장 2024. 11. 20.
반응형

K8S RollingUpdate 한계점

  • k8s는 이미 rolling update를 지원하고 있지만, 배포 속도가 느리고 새버전으로의 트래픽 조절을 하지 못한다. 또한 상태확인이 Readiness Probe로만 이루어 지고 있어 운영환경에 적합하지 않는다. 자동 롤백 기능도 지원하지 않는다.

ArgoRollout

Argo Rollouts는 Kubernetes 애플리케이션의 배포를 점진적으로 관리하기 위한 오픈소스 툴이다.

Argo 프로젝트의 일부로, Canary 및 Blue-Green 방식과 같은 다양한 롤아웃 전략을 지원하며, 이를 통해 배포 과정에서 안정성을 높이고 리스크를 줄일 수 있습니다. 주로 CD(Continuous Delivery) 환경에서 Kubernetes 네이티브 방식으로 애플리케이션 배포를 세밀하게 제어할 수 있도록 설계되어있다.

Argo Rollouts의 주요 기능

  • Blue-Green 및 Canary 배포: 두 가지 배포 전략을 모두 지원한다.
  • 트래픽 세분화: 트래픽을 세밀하게 조절하여 새로운 버전으로 서서히 이동 가능하다.
  • 자동 롤백 및 프로모션: 실패 시 자동으로 롤백하고 안정성 확인 후 배포 확장이 가능하다.
  • 수동 판단: 배포 중 수동으로 배포를 멈추거나 진행 가능하다.
  • 메트릭 기반 검증: Prometheus, Wavefront 등과 통합하여 비즈니스 KPI 기반 검증 가능하다.
  • Ingress 및 Service Mesh 통합: NGINX, ALB, Istio, Linkerd, SMI와 같은 툴을 지원한다.

따라서 ArgoRollout을 사용하게 되면, 배포 리스크가 감소되고 다양한 배포전략을 선택하여 진행할 수 있게 된다.

Blue-Green 배포

그림 출처) https://docs.aws.amazon.com/

  1. Blue 환경 (구 버전): 현재 버전의 애플리케이션이 실행 중이며 모든 사용자 트래픽이 Blue 환경으로 향한다.
  2. Green 환경 (새 버전): 새로운 버전을 Green 환경에 배포합니다. 이 시점에서는 Green 환경에 배포된 애플리케이션이 트래픽을 받지 않고, Blue 환경에서만 트래픽을 처리합니다.
  3. 테스트 및 검증: Green 환경에 배포된 새로운 버전의 애플리케이션을 테스트하고, 기존 환경에 영향을 주지 않으면서 새 버전이 제대로 작동하는지 검증할 수 있다.
  4. 트래픽 전환: 새 버전의 안정성이 확인되면 모든 트래픽을 한번에 Blue 환경에서 Green 환경으로 전환합니다. 일반적으로 로드 밸런서 또는 트래픽 라우팅을 변경하여 한 번에 전환한다.
  5. 롤백 옵션: 만약 Green 환경에서 문제가 발생하면, Blue 환경으로 트래픽을 다시 전환하여 빠르게 롤백할 수 있습니다.

Blue-Green 배포는 트래픽을 빠르게 전환할 수 있고, 문제가 발생하면 바로 Blue로 트래픽을 돌려 롤백이 가능하기 때문에 안정성이 높다. 또한 충분한 검증을 통해서 Green 환경을 확인할 수 있기 때문에 안정적이다. 하지만, 두 환경을 모두 세팅하여 사용하기 때문에 비용적인 문제가 발생한다.

CRD(Custom Resource Definition)란?

Argo Rollouts CRD는 Argo Rollouts가 Kubernetes에 추가하는 사용자 정의 리소스 정의이다.

Kubernetes는 기본적으로 Pod, Service, Deployment 같은 리소스를 제공하지만, Argo Rollouts는 Canary, Blue-Green 배포와 같은 고급 배포 전략을 위해 Rollout이라는 커스텀 리소스를 추가한다.

이 Rollout CRD는 Argo Rollouts가 제공하는 주요 리소스로, Rollout이라는 커스텀 리소스를 생성하고 관리할 수 있게 해줍니다.

아래와 같이 crd리스트를 확인할 수 있다.

kubectl get crds | grep rollouts.argoproj.io

여기서, kubernetes의 확장성을 볼 수 있다. 다양한 CNCF의 프로젝트를 통해서 쿠버네티스는 제공하는 리소스보다 더 다양한 기능을 제공해준다.

실습

ArgoRollout 설치

  • ArgoRollout 다운로드
kubectl create namespace argo-rollouts
kubectl apply -n argo-rollouts -f https://github.com/argoproj/argo-rollouts/releases/latest/download/install.yaml
  • cli 설치
curl -LO https://github.com/argoproj/argo-rollouts/releases/latest/download/kubectl-argo-rollouts-linux-amd64
chmod +x ./kubectl-argo-rollouts-linux-amd64
sudo mv ./kubectl-argo-rollouts-linux-amd64 /usr/local/bin/kubectl-argo-rollouts
kubectl argo rollouts version

정상적으로 설치되었다.

  • cli 자동완성 설치
cat <<EOF >kubectl_complete-argo-rollouts
#!/usr/bin/env sh

# Call the __complete command passing it all arguments
kubectl argo rollouts __complete "\$@"
EOF

chmod +x kubectl_complete-argo-rollouts
sudo mv ./kubectl_complete-argo-rollouts /usr/local/bin/
  • version 1.0 배포

정적 페이지에 v1.0을 추가 후 git tag와 함께 push한다.

git tag -a v1.0 -m "Release version 1.0"
git push origin v1.0
git add .
git push hellospring main

이렇게 진행하면 이전에 설정하였던 git action을 통해 v1.0에 대한 tag를 가진 docker image가 dockerhub로 push된다.

  • Dockerhub

정상적으로 생성되었다.

  • Helm 구성 수정

Argo Rollouts의 Blue-Green 배포 전략을 지원하도록 Deployment 대신 Rollout 리소스를 사용해야 한다. Helm 차트에 필요한 Rollout 리소스 정의와 Service리소스 구성이 필요하며, 서비스는 각 배포 버전 간 트래픽을 전환할 수 있도록 구성한다.

이전 문서에서 작성하였던 Helm Chart에 대한 내용을 수정하도록 한다.

  • Chart.yaml
apiVersion: v2
name: hellospring
description: A Helm chart for deploying a Spring Boot application with argorollout
type: application
version: 0.1.0
appVersion: "1.0"
keywords:
  - spring
  - boot
  - java
sources:
  - https://github.com/suhyeonXYZ/hellospring
  • values.yaml
replicaCount: 1

image:
  repository: suhyeonsong/hellospring
  pullPolicy: Always
  tag: v1.0

service:
  type: ClusterIP
  port: 3000

labels:
  app: bluegreen

ingress:
  path: /hello
  • rollout.yaml
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: {{ include "hellospring.name" . }}-rollout
  labels:
    app: {{ .Values.labels.app }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      app: {{ .Values.labels.app }}
  template:
    metadata:
      labels:
        app: {{ .Values.labels.app }}
    spec:
      containers:
        - name: {{ include "hellospring.name" . }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          ports:
            - name: http
              containerPort: 3000
          resources:
            {}
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          ports:
            - name: http
              containerPort: 3000
              protocol: TCP
  strategy:
    blueGreen:
      activeService: {{ include "hellospring.name" . }}-blue
      previewService: {{ include "hellospring.name" . }}-green
      autoPromotionEnabled: false
      scaleDownDelaySeconds: 30
  • activeService와 previewService는 말그대로, blue/green 서비스를 지정하는 부분이다. 각각을 인식하여 argocd는 신/구 버전에 대한 서비스 트래픽을 조절한다.
  • autoPromotionEnabled를 설정하여 수동 프로모션을 허용합니다. 자동으로 프로모션을 원하면 ture로 설정하면 된다.
  • Service.yaml
apiVersion: v1
kind: Service
metadata:
  name: {{ include "hellospring.name" . }}-green
  labels:
    app: {{ .Values.labels.app }}
spec:
  type: {{ .Values.service.type }}
  ports:
    - port: 3000
      targetPort: 3000
  selector:
    app: {{ .Values.labels.app }}

---

# preview Service
apiVersion: v1
kind: Service
metadata:
  name: {{ include "hellospring.name" . }}-blue
  labels:
    app: {{ .Values.labels.app }}
spec:
  type: {{ .Values.service.type }}
  ports:
    - port: 3000
      targetPort: 3000
  selector:
    app: {{ .Values.labels.app }}
  • Ingress.yaml

여기서 ingress는 active

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: {{ include "hellospring.name" . }}
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
    - http:
        paths:
          - path: {{ .Values.ingress.path }} 
            pathType: Prefix
            backend:
              service:
                name: {{ include "hellospring.name" . }}-green
                port:
                  number: {{ .Values.service.port }}

모두 수정을 하였으면 helm을 수정하여 패키징한다.

helm lint .
helm package hellospring/
helm repo index . --url https://suhyeonxyz.github.io/hellospring-helm/

패키징이 완료되었으면, git으로 push한다.

push가 완료되면, hellospring application이 sync를 시작한다.

Sync 완료

  • 이때 hellospring의 blue, green 서비스를 살펴본다. 모두 같은 endpoint를 가리키고 있는 것을 볼 수 있다.

  • 버전 확인

  • 이제 버전을 수정 후, helm 을 수정하여 배포해본다.

아래를 보면 수동으로 배포를 진행하고 있기 때문에, 수정된 버전에 대한 pod가 만들어져서 대기하고 있다. service들의 endpoint들이 달라진 것을 볼 수 있다. 기존 버전은 active상태로 서비스하고 있는 상태임을 보여준다.

kubectl argo rollouts get rollout hellospring-rollout -n backend -w

이제 배포를 수행해보겠다.

kubectl argo rollouts promote hellospring-rollout -n backend

아래 처럼, blue/green으로 배포되는 것 중 최신상태의 service가 active로 전환되는 것을 볼 수 있다.

ingress경로인 /hello로 접속해보면, 새로운 버전의 서비스가 제공되는 것을 볼 수 있다.

  • 실습 Blue-Green Deployment 동작 과정 정리
  1. helm update를 하면, argorollout이 update된 내용에 대한 pod를 생성한 후 대기한다.
  2. 현재 green, blue service 모두 기존 blue pod를 endpoint로 지정하고 있다.
  3. promote가 수행되면, green서비스는 기존에 만들어 놓은 v2.0버전의 green pod로 endpoint를 변경하게 된다.

트래픽은 hellospring-green으로 들어가게 되고, 고객은 v2.0 버전을 보게 된다.

이후 blue pod는 삭제되고, 다시 blue, greeen service는 모두 같은 endpoint를 가리키게 된다.

TroubleShooting

  • Degarded문제

argorollout 배포 이후 out of sync가 되었고,

Unable to load data: error getting cached app managed resources: cache: key is missing 메세지와 함께 연동불가 상태 지속, 검색하면 재시작하면 해결된다고 하였으나 재시작하여도 문제 지속

application 로그 확인

error: no kind "Rollout" is registered for version "argoproj.io/v1alpha1" in scheme "pkg/runtime/scheme.go:100"

k log rollout/hellospring-rollout -n backend
kubectl rollout restart statefulset argocd-application-controller -n argocd

—> 결국 원인은 selecter가 안맞아서 발생한 문제..

—> Probe check 취소 (내 app으로는 probe 체크할 수 없음)

  • 수동 배포 멈춤
kubectl patch rollouts hellospring-rollout -n backend -p '{"spec":{"paused":false}}' --type=merge
반응형