Istio 카나리 배포 마스터하기: Jenkins, GitHub Actions 및 ArgoCD를 활용한 전체 CI/CD 파이프라인

발행: (2025년 12월 7일 오후 05:31 GMT+9)
7 min read
원문: Dev.to

Source: Dev.to

CI/CD에서 카나리 배포란?

카나리 배포는 새로운 버전을 천천히 릴리스하고, 소량의 사용자 트래픽으로 테스트한 뒤 트래픽을 단계적으로 늘려가는 방법을 의미합니다.
일반적인 진행 단계:

트래픽 분할버전
100 % → 0 %v1 → v2
90 % / 10 %v1 / v2
70 % / 30 %v1 / v2
50 % / 50 %v1 / v2
0 % / 100 %v1 / v2

문제가 감지되면 배포가 즉시 롤백됩니다. CI/CD와 Istio를 결합하면 이 과정을 완전 자동화할 수 있습니다.

Istio 카나리 파이프라인의 핵심 구성 요소

구성 요소역할
CI PipelineDocker 이미지 빌드, 테스트 실행, 아티팩트 푸시
CD PipelineKubernetes에 배포
Istio VirtualService버전 간 트래픽 분할
Istio DestinationRule서브셋(v1, v2) 정의
Monitoring (Prometheus)장애 감지
Rollback Logic필요 시 트래픽을 v1으로 전환

파이프라인은 Jenkins, GitHub Actions, ArgoCD 중 하나로 구현할 수 있으며, 모두 동일한 Istio YAML 파일을 사용합니다.

Git 저장소 폴더 구조

istio-microservices/
├── services/
│   └── orders-service/
│       ├── src/
│       └── Dockerfile
├── k8s/
│   └── orders/
│       ├── deployment.yaml
│       ├── service.yaml
│       ├── dest-rule.yaml
│       ├── vs-100-0.yaml
│       ├── vs-90-10.yaml
│       ├── vs-70-30.yaml
│       ├── vs-50-50.yaml
│       └── vs-0-100.yaml
└── ci/
    ├── Jenkinsfile
    └── github-actions.yaml

VirtualService 파일은 카나리 롤아웃의 한 단계를 나타냅니다.

Istio 카나리 매니페스트

DestinationRule (subsets)

apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: orders-service
  namespace: microservices
spec:
  host: orders-service
  subsets:
  - name: v1
    labels:
      version: v1
  - name: v2
    labels:
      version: v2

VirtualService 단계

단계 1 – 100 % v1 (시작점) – vs-100-0.yaml

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: orders-service
  namespace: microservices
spec:
  hosts:
  - orders-service
  http:
  - route:
    - destination:
        host: orders-service
        subset: v1
      weight: 100
    - destination:
        host: orders-service
        subset: v2
      weight: 0

단계 2 – 90 % v1 / 10 % v2 – vs-90-10.yaml

# similar structure, weights 90 and 10

vs-70-30.yaml, vs-50-50.yaml, vs-0-100.yaml 파일은 weight 값을 해당 비율에 맞게 조정하여 생성합니다.

자동 승격을 위한 헬스 메트릭

다음 트래픽 분할로 이동하기 전에 파이프라인은 아래 항목을 확인합니다 (Jenkinsfile 예시).

pipeline {
  agent any
  environment {
    NAMESPACE = 'microservices'
  }
  stages {
    stage('Deploy v2') {
      steps {
        sh "kubectl apply -f k8s/orders/deployment.yaml -n $NAMESPACE"
      }
    }

    stage('Canary 10%') {
      steps {
        sh "kubectl apply -f k8s/orders/vs-90-10.yaml -n $NAMESPACE"
      }
    }

    stage('Check Metrics') {
      steps {
        script {
          def errorRate = sh(script: "curl -s http://prometheus/api/v1/query?query=...", returnStdout: true).trim()
          if (errorRate.toDouble() > 0.01) {
            error "High error rate detected — Rolling back"
          }
        }
      }
    }

    stage('Shift to 50/50') {
      steps {
        sh "kubectl apply -f k8s/orders/vs-50-50.yaml -n $NAMESPACE"
      }
    }

    stage('Promote to 100% v2') {
      steps {
        sh "kubectl apply -f k8s/orders/vs-0-100.yaml -n $NAMESPACE"
      }
    }
  }

  post {
    failure {
      sh "kubectl apply -f k8s/orders/vs-100-0.yaml -n $NAMESPACE"
    }
  }
}

GitHub Actions 워크플로 (간단, 클라우드‑네이티브)

name: Istio Canary Deployment
on:
  push:
    branches: [ main ]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Build Image
        run: |
          docker build -t myrepo/orders-service:${{ github.run_number }} services/orders-service
          docker push myrepo/orders-service:${{ github.run_number }}

      - name: Deploy v2
        run: |
          sed -i "s|image:.*|image: myrepo/orders-service:${{ github.run_number }}|g" k8s/orders/deployment.yaml
          kubectl apply -f k8s/orders/deployment.yaml -n microservices

      - name: Canary 10%
        run: kubectl apply -f k8s/orders/vs-90-10.yaml -n microservices

      - name: Wait & Check Prometheus
        run: |
          sleep 60
          ERR=$(curl -s "http://prometheus/api/v1/query?query=sum(rate(istio_requests_total{response_code=~\"5.*\"}[1m]))/sum(rate(istio_requests_total[1m]))")
          ERR_VAL=$(echo $ERR | jq '.data.result[0].value[1]' | tr -d '"')
          if (( $(echo "$ERR_VAL > 0.01" | bc -l) )); then exit 1; fi

      - name: Shift to 50/50
        run: kubectl apply -f k8s/orders/vs-50-50.yaml -n microservices

      - name: Promote 100% v2
        run: kubectl apply -f k8s/orders/vs-0-100.yaml -n microservices

ArgoCD를 활용한 GitOps 버전

ArgoCD가 저장소를 지속적으로 감시하고 Istio 매니페스트를 적용하여 완전 자동화된 GitOps 워크플로를 구현합니다.

ArgoCD Application 정의 (argo-app.yaml)

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: istio-canary-app
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/myrepo/istio-microservices
    path: k8s/orders
  destination:
    server: https://kubernetes.default.svc/
    namespace: microservices
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

애플리케이션 배포:

kubectl apply -f argo-app.yaml

트래픽을 전환하려면 원하는 VirtualService 파일(예: vs-90-10.yaml)을 커밋하고 푸시하면 됩니다. ArgoCD가 변화를 감지하고 즉시 적용합니다.

자동 롤백 전략

다음 조건 중 하나라도 만족하면 롤백이 즉시 트리거됩니다:

  • 오류율 > 1 %
  • p95 레이턴시가 정의된 임계값 초과
  • Pod 재시작 횟수 급증
  • Prometheus가 감지한 트래픽 이상 징후

실패 시 파이프라인에서 사용하는 롤백 명령:

kubectl apply -f k8s/orders/vs-100-0.yaml -n microservices

CI/CD에서의 가시성

  • Grafana – 메트릭 대시보드
  • Jaeger – 버전별 분산 트레이싱
  • Kiali – 트래픽 그래프 (색상으로 v1 vs v2 구분)
  • Prometheus – 원시 메트릭 및 알림

카나리 롤아웃 중 Kiali는 트래픽 분할을 실시간으로 표시해 운영자가 새로운 버전의 영향을 즉시 파악할 수 있게 합니다.

최종 요약

이 가이드에서는 다음을 포함하는 완전한 Istio 카나리 배포 파이프라인을 구축했습니다:

  • 자동 Docker 이미지 빌드
  • 새로운 마이크로서비스 버전(v2) 배포
  • Istio VirtualServiceDestinationRule을 이용한 트래픽 전환
  • 승격 전 Prometheus 기반 헬스 체크
  • 실패 시 자동 롤백
  • ArgoCD를 활용한 GitOps 구현
  • Jenkins와 GitHub Actions 예시를 통한 CI/CD 구현
  • 관측 스택(Grafana, Jaeger, Kiali, Prometheus)

이러한 패턴은 Netflix, IBM, Red Hat, Airbnb 등 기업의 프로덕션 환경에서도 사용됩니다.

Back to Blog

관련 글

더 보기 »