ArgoCD 의 Rollout을 가지고 실제 트래픽 기반으로 배포를 구성하는 방식에 대해서 진행해보겠습니다. 실제 Kubernetes Rolling Update는 단순한 업데이트 제어만 할 뿐 특정 조건에 따라 배포를 하지 않아 실제 서비스에서는 배포한 앱이 장애가 나면 수동으로 롤백해야되는 문제점이 있습니다.

ArgoCD는 이러한 상황에서 조건을 가지고 자동으로 배포가 이루어지고 문제 시 특정 메트릭 조건으로 배포를 제어할 수 있습니다.

가장 많이 사용되는 Canary, Blue/Green 배포에 대해서 알아보겠습니다.

항목 Kubernetes Rolling Update Argo Rollouts 방식

리소스 종류 Deployment 객체 (기본) Rollout CRD + (선택) AnalysisTemplate, AnalysisRun 등
주요 설정 필드 spec.strategy.type = RollingUpdate + rollingUpdate.maxSurge / maxUnavailable strategy.canary (또는 strategy.blueGreen) + steps (예: setWeight, pause) + trafficRouting + analysis 옵션
트래픽 제어 라우팅 제어 기본 제공 없음 — Pod 교체 순서만 제어 가능 트래픽 가중치 제어 가능 (예: “새 버전에 트래픽 10% 먼저”, 단계별 증가) + Service/Ingress/VirtualService 연동 가능
메트릭 기반 자동화 기본 제공 안함. 수동 롤백/승격 중심 메트릭 기반 자동 롤백/승격 가능 — 외부 메트릭 제공자 연동 가능
설정 복잡성 낮음 — 익숙한 Deployment 설정 높음 — CRD 추가, 트래픽 제어(메쉬/Ingress) 준비, 메트릭/분석 통합 필요
사용 적합성 표준 서비스, 충분한 안정성, 리스크 낮음 고가용성/대규모/리스크 민감 서비스, progressive delivery 필요 시
운영 부담 상대적으로 낮음 트래픽 제어, 분석 상태 모니터링, 단계별 승격 관리 등 부가 운영 요소 존재

Argo Rollout 설치하기

모든 코드는 https://github.com/hanship0530/Learning/tree/main/ci-cd-cookbook/5w/argo-rollout 에 있습니다.

https://hanship.tistory.com/3 앞에서 설명한 ArgoCD 설치하기 글을 참조합니다.

 

아래의 파일들을 작성하고 commit & push를 해줍니다.

  • apps/argo-rollouts/application.yaml 파일
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: argo-rollouts
  namespace: argocd
  finalizers:
    - resources-finalizer.argocd.argoproj.io
spec:
  project: dev
  source:
    repoURL: <https://github.com/><자신의 Github ID>/argo-rollout.git
    targetRevision: main
    path: bootstrap/argo-rollouts
  destination:
    server: <https://kubernetes.default.svc>
    namespace: argo-rollouts
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true
    retry:
      limit: 1
      backoff:
        duration: 5s
        factor: 2
        maxDuration: 3m
  • bootstrap/argo-rollouts/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

namespace: argo-rollouts

resources:
- <https://github.com/argoproj/argo-rollouts/releases/latest/download/install.yaml>
- namespace.yaml

labels:
- pairs:
    app.kubernetes.io/name: argo-rollouts
    app.kubernetes.io/part-of: argo-rollouts
  • bootstrap/argo-rollouts/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: argo-rollouts
  labels:
    app.kubernetes.io/name: argocd-rollouts
    app.kubernetes.io/part-of: argocd-rollouts
  • 위 사항을 작성하고 commit & push 합니다.
  • 확인
kubectl get all -n argo-rollouts
NAME                                 READY   STATUS    RESTARTS   AGE
pod/argo-rollouts-68bffbdf98-4fgtk   1/1     Running   0          23s

NAME                            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
service/argo-rollouts-metrics   ClusterIP   10.96.152.216   <none>        8090/TCP   23s

NAME                            READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/argo-rollouts   1/1     1            1           23s

NAME                                       DESIRED   CURRENT   READY   AGE
replicaset.apps/argo-rollouts-68bffbdf98   1         1         1       23s

kubectl get crd | grep rollouts
rollouts.argoproj.io                   2025-11-15T11:11:58Z

kubectl get application argo-rollouts -n argocd
NAME            SYNC STATUS   HEALTH STATUS
argo-rollouts   OutOfSync     Healthy

Rollout Extension 설치하기

https://github.com/argoproj-labs/rollout-extension#kustomize-patch 를 참조합니다.

bootstrap/argo-cd/kustomization.yaml 파일에 아래의 항목을 추가하여 commit & push를 해줍니다.

- target:
    kind: Deployment
    name: argocd-server
  patch: |-
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: argocd-server
    spec:
      template:
        spec:
          initContainers:
            - name: rollout-extension
              image: quay.io/argoprojlabs/argocd-extension-installer:v0.0.8
              env:
                - name: EXTENSION_URL
                  value: <https://github.com/argoproj-labs/rollout-extension/releases/download/v0.3.7/extension.tar>
              volumeMounts:
                - name: extensions
                  mountPath: /tmp/extensions/
              securityContext:
                runAsUser: 1000
                allowPrivilegeEscalation: false
          containers:
            - name: argocd-server
              volumeMounts:
                - name: extensions
                  mountPath: /tmp/extensions/
          volumes:
            - name: extensions
              emptyDir: {}

추가적으로 프로젝트를 생성해줍니다

  • apps/projects/dev.yaml
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: dev
  namespace: argocd
spec:
  description: Dev Env
  sourceRepos:
    - '*'
  destinations:
    - namespace: '*'
      server: '*'
  clusterResourceWhitelist:
    - group: '*'
      kind: '*'
  namespaceResourceWhitelist:
    - group: '*'
      kind: '*'

 

모든 내용을 commit & push 하고 반영해줍니다.

설치된것을 확인할 수 있습니다.

Rollout Test를 위한 Prometheus 설치하기

ArgoCD에서 Prometheus를 연동하여 트래픽의 http status 가 200 으로 일정시간동안 유지시 배포가 되도록 설정하기 위한 Prometheus를 설치해줍니다.

argocd 설치하기에서 생성한 github repository인 argo-rollout 레포에서 이어서 작업합니다.

prometheus 설치외에 기존에 배포한 ingress-nginx도 metric 설정을 해줍니다.

아래에 명시된 파일들을 생성하고 commit & push 를 해줍니다.

  • apps/prometheus/application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: prometheus
  namespace: argocd
  finalizers:
    - resources-finalizer.argocd.argoproj.io
spec:
  project: infra
  sources:
    # Helm chart 소스
    - repoURL: <https://prometheus-community.github.io/helm-charts>
      chart: kube-prometheus-stack
      targetRevision: "61.0.0"
      helm:
        releaseName: prometheus
        includeCRDs: true
        values: |
          # Prometheus 설정
          prometheus:
            prometheusSpec:
              retention: 30d
              storageSpec:
                volumeClaimTemplate:
                  spec:
                    accessModes: ["ReadWriteOnce"]
                    resources:
                      requests:
                        storage: 50Gi
              serviceMonitorSelectorNilUsesHelmValues: false
              podMonitorSelectorNilUsesHelmValues: false
              ruleSelectorNilUsesHelmValues: false
              # 모든 ServiceMonitor를 선택하도록 명시적으로 설정 (빈 selector = 모든 것 선택)
              serviceMonitorSelector: {}
              podMonitorSelector: {}
              # 모든 네임스페이스의 ServiceMonitor를 찾을 수 있도록 설정 (빈 selector = 모든 네임스페이스)
              serviceMonitorNamespaceSelector: {}
              podMonitorNamespaceSelector: {}
              # Service 생성 (Argo Rollouts에서 접근 가능하도록)
              service:
                type: ClusterIP
                port: 9090
              serviceAccount:
                create: true
              # 리소스 제한 (kind 환경에 맞게 조정)
              resources:
                requests:
                  memory: 512Mi
                  cpu: 200m
                limits:
                  memory: 1Gi
                  cpu: 500m
            # Ingress 설정 (별도로 관리하므로 비활성화)
            ingress:
              enabled: false
          
          # Grafana 설정 (선택사항)
          grafana:
            enabled: true
            adminPassword: admin  # 프로덕션에서는 Secret 사용 권장
            service:
              type: ClusterIP
              port: 80
            resources:
              requests:
                memory: 128Mi
                cpu: 100m
              limits:
                memory: 256Mi
                cpu: 200m
          
          # Alertmanager 설정
          alertmanager:
            enabled: true
            alertmanagerSpec:
              storage:
                volumeClaimTemplate:
                  spec:
                    accessModes: ["ReadWriteOnce"]
                    resources:
                      requests:
                        storage: 2Gi  # kind 환경에 맞게 조정
              resources:
                requests:
                  memory: 128Mi
                  cpu: 100m
                limits:
                  memory: 256Mi
                  cpu: 200m
          
          # Node Exporter
          nodeExporter:
            enabled: true
          
          # Kube State Metrics
          kubeStateMetrics:
            enabled: true
          
          # Prometheus Operator
          prometheusOperator:
            enabled: true
            resources:
              requests:
                memory: 128Mi
                cpu: 100m
              limits:
                memory: 256Mi
                cpu: 200m
          
          # 기본 ServiceMonitor 생성
          defaultRules:
            create: true
          
          # 네임스페이스 설정
          namespaceOverride: monitoring
    # Certificate 및 Ingress 소스 (kustomization)
    - repoURL: <https://github.com/><자신의 Github ID>/argo-rollout.git
      targetRevision: main
      path: bootstrap/prometheus
  destination:
    server: <https://kubernetes.default.svc>
    namespace: monitoring
  ignoreDifferences:
    # CRD annotation 크기 제한 문제로 인해 CRD를 ArgoCD 관리에서 완전히 제외
    # CRD는 Helm chart가 설치하되, ArgoCD는 동기화하지 않음
    - group: apiextensions.k8s.io
      kind: CustomResourceDefinition
      name: alertmanagers.monitoring.coreos.com
    - group: apiextensions.k8s.io
      kind: CustomResourceDefinition
      name: prometheuses.monitoring.coreos.com
    - group: apiextensions.k8s.io
      kind: CustomResourceDefinition
      name: prometheusagents.monitoring.coreos.com
    - group: apiextensions.k8s.io
      kind: CustomResourceDefinition
      name: thanosrulers.monitoring.coreos.com
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true
      - ServerSideApply=true  # CRD annotation 크기 제한 문제 해결
      - RespectIgnoreDifferences=true  # ignoreDifferences 설정 존중
    retry:
      limit: 1
      backoff:
        duration: 5s
        factor: 2
        maxDuration: 3m
  • apps/ingress-nginx/kustomization.yaml 를 아래와 같이 수정
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

namespace: ingress-nginx

resources:
- <https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml>
- servicemonitor.yaml

patches:
- target:
    kind: Deployment
    name: ingress-nginx-controller
  patch: |-
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: ingress-nginx-controller
      namespace: ingress-nginx
    spec:
      template:
        metadata:
          annotations:
            prometheus.io/scrape: "true"
            prometheus.io/port: "10254"
        spec:
          nodeSelector:
            kubernetes.io/os: linux
            ingress-ready: "true"
          containers:
          - name: controller
            ports:
            - containerPort: 10254
              name: metrics
              protocol: TCP
- target:
    kind: Service
    name: ingress-nginx-controller
  patch: |-
    apiVersion: v1
    kind: Service
    metadata:
      name: ingress-nginx-controller
      namespace: ingress-nginx
      labels:
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/component: controller
    spec:
      ports:
      - name: metrics
        port: 10254
        protocol: TCP
        targetPort: metrics
- target:
    kind: ConfigMap
    name: ingress-nginx-controller
  patch: |-
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: ingress-nginx-controller
      namespace: ingress-nginx
    data:
      enable-ssl-passthrough: "true"
      # kind 환경에서 localhost를 address로 설정
      publish-status-address: "localhost"
      # 메트릭 활성화 (기본값이지만 명시적으로 설정)
      enable-metrics: "true"
      # 호스트별 메트릭 수집 활성화 (host 레이블을 위해 필요)
      metrics-per-host: "true"

patchesJson6902:
- target:
    group: apps
    version: v1
    kind: Deployment
    name: ingress-nginx-controller
    namespace: ingress-nginx
  path: metrics-args-patch.json
  • apps/ingress-nginx/metrics-args-patch.json 생성
[
  {
    "op": "add",
    "path": "/spec/template/spec/containers/0/args/-",
    "value": "--enable-metrics=true"
  },
  {
    "op": "add",
    "path": "/spec/template/spec/containers/0/args/-",
    "value": "--metrics-per-host=true"
  }
]
  • apps/ingress-nginx/servicemonitor.yaml 생성
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: ingress-nginx-controller
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    release: prometheus
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: ingress-nginx
      app.kubernetes.io/component: controller
  endpoints:
  - port: metrics
    interval: 30s
    path: /metrics
    scheme: http
  namespaceSelector:
    matchNames:
    - ingress-nginx
  • host 추가
# host 추가
echo "127.0.0.1 prometheus.example.com" | sudo tee -a /etc/hosts

Canary Test

Canary 배포를 테스트해보겠습니다.

생성해야 할 파일들은 아래와 같습니다.

  • analysis-template.yaml : 배포 매트릭 체크를 위한 analysis template
  • cacary.yaml : rollout 파일
  • certificate.yaml : tls
  • ingress.yaml : ingress 생성을 위함
  • service.yaml : 서비스 파일

시나리오는 다음과 같습니다.

  • rollout-demo를 blue로 배포
  • rollout-demo를 yellow로 수정 후 3분 동안 200 status 가 80% 이상 유지되면 20%, 40%, 60%, 80%, 100% 배포를 한다
  • 2번 이상 실패 시 롤백한다.

이전에 만들어둔 argo-rollout repository에서 작업합니다. 아래의 파일들을 생성하고 commit & push 를 진행해줍니다.

  • rollouts/canary-test/analysis-template.yaml
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
  name: rollouts-demo-analysis
spec:
  args:
    # Prometheus 주소를 환경에 맞게 수정하세요
    # 예: <http://prometheus-kube-prometheus-prometheus.monitoring.svc:9090>
    - name: prometheus-address
      value: <http://prometheus-kube-prometheus-prometheus.monitoring.svc:9090>
    # 앱 이름 (기본값: rollouts-demo)
    - name: app-name
      value: rollouts-demo
    # Ingress 호스트명 (환경에 맞게 수정)
    - name: ingress-host
      value: rollouts-demo.example.com
    # Ingress 서비스 이름
    - name: ingress-service
      value: rollouts-demo
  metrics:
    # Ingress HTTP 200 응답 지속성 확인 - 3분 동안 지속적으로 HTTP 200 응답이 있어야 함
    # 이 메트릭은 3분 동안 매 30초마다 확인하여 HTTP 200 응답 비율이 80% 이상인지 확인합니다
    - name: ingress-http200-duration
      interval: 30s
      count: 6  # 6번 확인 (30초 * 6 = 3분)
      successCondition: result[0] >= 0.80  # HTTP 200 응답 비율이 80% 이상
      failureLimit: 2  # 2번 실패하면 롤백
      provider:
        prometheus:
          address: "{{args.prometheus-address}}"
          query: |
            # Ingress를 통한 HTTP 200 응답 비율 (3분 윈도우)
            # rollouts-demo만 필터링 (host로 필터링)
            (
              sum(rate(nginx_ingress_controller_response_duration_seconds_count{
                host="{{args.ingress-host}}",
                status="200"
              }[3m]))
              /
              sum(rate(nginx_ingress_controller_response_duration_seconds_count{
                host="{{args.ingress-host}}"
              }[3m]))
            ) or vector(0)
    
    # Ingress HTTP 200 응답 수 확인 - 최소 요청 수 확인 (트래픽이 있는지 확인)
    - name: ingress-http200-count
      interval: 30s
      count: 6  # 3분 동안 확인
      successCondition: result[0] >= 0.1  # 초당 0.1 개 이상의 요청이 있어야 함
      failureLimit: 6  # 모든 체크에서 실패해야 롤백 (더 관대하게)
      provider:
        prometheus:
          address: "{{args.prometheus-address}}"
          query: |
            # 3분 동안의 HTTP 200 응답 수 (rollouts-demo만)
            sum(rate(nginx_ingress_controller_response_duration_seconds_count{
              host="{{args.ingress-host}}",
              status="200"
            }[3m])) * 180
    
    # Ingress 레이턴시 메트릭 (P95) - Ingress를 통한 응답 시간 모니터링
    - name: ingress-latency-p95
      interval: 30s
      count: 6  # 3분 동안 확인
      successCondition: result[0] <= 1000  # P95 레이턴시가 1000ms 이하
      failureLimit: 3
      provider:
        prometheus:
          address: "{{args.prometheus-address}}"
          query: |
            # Ingress를 통한 P95 레이턴시 (밀리초) - rollouts-demo만
            # 메트릭이 없을 경우 NaN 방지
            (
              histogram_quantile(0.95,
                sum(rate(nginx_ingress_controller_response_duration_seconds_bucket{
                  host="{{args.ingress-host}}"
                }[3m])) by (le)
              ) * 1000
            ) or vector(0)
  • rollouts/canary-test/canary.yaml
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: rollouts-demo
spec:
  replicas: 5
  strategy:
    canary:
      # Analysis를 사용하여 트래픽 기반 자동 배포
      # 각 단계에서 메트릭을 확인하고 조건을 만족하면 자동으로 다음 단계로 진행
      analysis:
        templates:
        - templateName: rollouts-demo-analysis
        args:
        - name: prometheus-address
          value: <http://prometheus-kube-prometheus-prometheus.monitoring.svc:9090>  # 환경에 맞게 수정
        - name: app-name
          value: rollouts-demo
        - name: ingress-host
          value: rollouts-demo.example.com  # Ingress 호스트명 수정
        - name: ingress-service
          value: rollouts-demo  # Ingress 서비스 이름
        startingStep: 2  # 2단계부터 analysis 시작
        successfulRunHistoryLimit: 3
        unsuccessfulRunHistoryLimit: 3
      steps:
      - setWeight: 20
      - pause:
          duration: 30s  # 30초 대기 후 자동 진행 (또는 analysis 완료 시 자동 진행)
      - setWeight: 40
      - pause:
          duration: 10s  # 최소 대기 시간
      - analysis:
          templates:
          - templateName: rollouts-demo-analysis
          args:
          - name: prometheus-address
            value: <http://prometheus-kube-prometheus-prometheus.monitoring.svc:9090>
          - name: app-name
            value: rollouts-demo
          - name: ingress-host
            value: rollouts-demo.example.com
          - name: ingress-service
            value: rollouts-demo
      - setWeight: 60
      - pause:
          duration: 10s
      - analysis:
          templates:
          - templateName: rollouts-demo-analysis
          args:
          - name: prometheus-address
            value: <http://prometheus-kube-prometheus-prometheus.monitoring.svc:9090>
          - name: app-name
            value: rollouts-demo
          - name: ingress-host
            value: rollouts-demo.example.com
          - name: ingress-service
            value: rollouts-demo
      - setWeight: 80
      - pause:
          duration: 10s
      - analysis:
          templates:
          - templateName: rollouts-demo-analysis
          args:
          - name: prometheus-address
            value: <http://prometheus-kube-prometheus-prometheus.monitoring.svc:9090>
          - name: app-name
            value: rollouts-demo
          - name: ingress-host
            value: rollouts-demo.example.com
          - name: ingress-service
            value: rollouts-demo
  revisionHistoryLimit: 2
  selector:
    matchLabels:
      app: rollouts-demo
  template:
    metadata:
      labels:
        app: rollouts-demo
    spec:
      containers:
      - name: rollouts-demo
        image: argoproj/rollouts-demo:blue
        ports:
        - name: http
          containerPort: 8080
          protocol: TCP
        resources:
          requests:
            memory: 32Mi
            cpu: 5m
  • apps/canary-test/certificate.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: rollouts-demo-tls
  namespace: default
spec:
  secretName: rollouts-demo-tls
  issuerRef:
    name: selfsigned-issuer
    kind: ClusterIssuer
  dnsNames:
  - rollouts-demo.example.com
  duration: 8760h # 1 year
  renewBefore: 720h # 30 days
  • apps/canary-test/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: rollouts-demo-ingress
  namespace: default
  annotations:
    # SSL 리다이렉트 (HTTP도 허용하도록 false로 설정, 또는 HTTPS만 사용하려면 true)
    nginx.ingress.kubernetes.io/ssl-redirect: "false"
    # 메트릭 수집을 위한 annotation (Prometheus가 메트릭을 수집할 수 있도록)
    prometheus.io/scrape: "true"
    prometheus.io/port: "10254"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - rollouts-demo.example.com
    secretName: rollouts-demo-tls
  rules:
  - host: rollouts-demo.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: rollouts-demo
            port:
              number: 80
  • apps/canary-test/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: rollouts-demo
spec:
  ports:
  - port: 80
    targetPort: 8080  # Rollout 컨테이너의 실제 포트
    protocol: TCP
    name: http
  selector:
    app: rollouts-demo

그리고 나서 application을 생성해줍니다. 테스트를 위해 image 를 argocd에서 sync하는 옵션은 끄기로 한다.

  • apps/canary-test/application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: canary-test
  namespace: argocd
  finalizers:
    - resources-finalizer.argocd.argoproj.io
spec:
  project: dev
  source:
    repoURL: <https://github.com/><자신의 Github ID>/argo-rollout.git
    targetRevision: main
    path: rollouts/canary-test
  destination:
    server: <https://kubernetes.default.svc>
    namespace: default
  ignoreDifferences:
    # kubectl patch로 변경한 image를 ArgoCD가 되돌리지 않도록 설정
    - group: argoproj.io
      kind: Rollout
      name: rollouts-demo
      namespace: default
      jsonPointers:
        - /spec/template/spec/containers/0/image
  syncPolicy:
    automated:
      prune: false
      selfHeal: false
    syncOptions:
      - RespectIgnoreDifferences=true
  • 호스트 추가
echo "127.0.0.1 rollouts-demo.example.com" | sudo tee -a /etc/hosts

앱이 배포 된 이후 prometheus에서 아래의 메트릭이 조회되는지 확인을 하고 canary 배포를 진행한다.

rollouts-demo.example.com 창을 열어두고 http://prometheus.example.com/ 에서 메트릭 조회를 해본다.

sum(rate(nginx_ingress_controller_response_duration_seconds_count{host="rollouts-demo.example.com"}[3m]))

 

메트릭이 조회되는 것을 확인 할 수 있다.

 

배포를 시작해본다. 현재 상태는 blue 이며 화면에서 처럼 Error 율을 조정할 수 있다.

3분 동안 성공 비율이 80% 이상이면 배포가 진행된다. 에러 비율을 30%로 조정해본다.

 

yellow로 배포해본다.

kubectl patch rollout rollouts-demo -n default --type='json' -p='[
  {
    "op": "replace",
    "path": "/spec/template/spec/containers/0/image",
    "value": "argoproj/rollouts-demo:yellow"
  }
]'

revision이 2로 되었으며 실제 20%가 배포되었다.

 

현재 설정은 아래와 같다. 30s 동안 대기 후 자동으로 처리되며 pause.duration: {}는 수동으로 한다. failureLimit 2 는 2번 정도 실패하면 롤백을 진행한다.

  • failureLimit: 2
  • pause.duration: 30s 로

Error rate를 6% 로 조정하니 배포가 순차적으로 진행된다.

 

 

 

이후 다시 Error 비율을 28%로 늘리니 Blue로 완전히 롤백되었다.

 

 

Revision 이 다시 자동으로 1로 돌아갔다.

다시 배포를 하기 위해 이번에는 Error Rate 를 5% 로 하고 배포를 진행해본다.

kubectl patch rollout rollouts-demo -n default --type='json' -p='[
  {
    "op": "replace",
    "path": "/spec/template/spec/containers/0/image",
    "value": "argoproj/rollouts-demo:yellow"
  }
]'
rollout.argoproj.io/rollouts-demo patched (no change)

진행 시 successCondition: result[0] 초당 1개의 요청은 있어야 하기에 창을 항상 켜놓는다.

정상적으로 배포가 되었다.
모두 yellow로 변경되었다.

Blue/Green 배포

Blue, Green 두 개를 배포하고 200 응답을 받은 비율이 80% 이상일 경우 Green으로 온전히 배포하는 테스트를 진행해보겠습니다.

동일하게 현재 Repository에서 아래의 파일들을 생성하고 commit & push 를 진행해줍니다.

  • rollouts/blue-green-test/analysis-template.yaml
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
  name: rollouts-demo-bg-analysis
spec:
  args:
    # Prometheus 주소를 환경에 맞게 수정하세요
    - name: prometheus-address
      value: <http://prometheus-kube-prometheus-prometheus.monitoring.svc:9090>
    # 앱 이름
    - name: app-name
      value: rollouts-demo-bg
    # Active Ingress 호스트명
    - name: ingress-host-active
      value: rollouts-demo-bg-active.example.com
    # Preview Ingress 호스트명
    - name: ingress-host-preview
      value: rollouts-demo-bg-preview.example.com
  metrics:
    # Ingress HTTP 200 응답 비율 확인 - 80% 이상이어야 함
    # 20% 단계 분석용 메트릭 (Preview 서비스 분석)
    - name: ingress-http200-ratio-20
      interval: 30s
      count: 6  # 6번 확인 (30초 * 6 = 3분)
      successCondition: result[0] >= 0.80  # HTTP 200 응답 비율이 80% 이상
      failureLimit: 2  # 2번 실패하면 롤백
      provider:
        prometheus:
          address: "{{args.prometheus-address}}"
          query: |
            # Preview Ingress를 통한 HTTP 200 응답 비율 (3분 윈도우)
            (
              sum(rate(nginx_ingress_controller_response_duration_seconds_count{
                host="{{args.ingress-host-preview}}",
                status="200"
              }[3m]))
              /
              sum(rate(nginx_ingress_controller_response_duration_seconds_count{
                host="{{args.ingress-host-preview}}"
              }[3m]))
            ) or vector(0)
    
    # 60% 단계 분석용 메트릭 (Preview 서비스 분석)
    - name: ingress-http200-ratio-60
      interval: 30s
      count: 6  # 6번 확인 (30초 * 6 = 3분)
      successCondition: result[0] >= 0.80  # HTTP 200 응답 비율이 80% 이상
      failureLimit: 2  # 2번 실패하면 롤백
      provider:
        prometheus:
          address: "{{args.prometheus-address}}"
          query: |
            # Preview Ingress를 통한 HTTP 200 응답 비율 (3분 윈도우)
            (
              sum(rate(nginx_ingress_controller_response_duration_seconds_count{
                host="{{args.ingress-host-preview}}",
                status="200"
              }[3m]))
              /
              sum(rate(nginx_ingress_controller_response_duration_seconds_count{
                host="{{args.ingress-host-preview}}"
              }[3m]))
            ) or vector(0)
    
    # 100% 단계 분석용 메트릭 (Preview 서비스 분석)
    - name: ingress-http200-ratio-100
      interval: 30s
      count: 6  # 6번 확인 (30초 * 6 = 3분)
      successCondition: result[0] >= 0.80  # HTTP 200 응답 비율이 80% 이상
      failureLimit: 2  # 2번 실패하면 롤백
      provider:
        prometheus:
          address: "{{args.prometheus-address}}"
          query: |
            # Preview Ingress를 통한 HTTP 200 응답 비율 (3분 윈도우)
            (
              sum(rate(nginx_ingress_controller_response_duration_seconds_count{
                host="{{args.ingress-host-preview}}",
                status="200"
              }[3m]))
              /
              sum(rate(nginx_ingress_controller_response_duration_seconds_count{
                host="{{args.ingress-host-preview}}"
              }[3m]))
            ) or vector(0)
  • rollouts/blue-green-test/blue-green.yaml
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: rollouts-demo-bg
spec:
  replicas: 3
  strategy:
    blueGreen:
      # Active 서비스는 현재 프로덕션 트래픽을 받는 서비스
      activeService: rollouts-demo-bg-active
      # Preview 서비스는 새 버전(green)의 트래픽을 받는 서비스
      previewService: rollouts-demo-bg-preview
      # Auto Promotion: 분석이 성공하면 자동으로 green을 active로 전환
      autoPromotionEnabled: true
      # Promotion Policy: 분석이 성공하면 자동으로 전환
      scaleDownDelaySeconds: 30  # 전환 후 30초 후에 이전 버전(blue) 제거
      # Analysis를 사용하여 트래픽 기반 자동 배포
      # HTTP 200 응답이 80% 이상일 때 자동으로 green을 active로 전환
      # 20%, 60%, 100% 세 단계로 분석 수행
      prePromotionAnalysis:
        templates:
        - templateName: rollouts-demo-bg-analysis
        args:
        - name: prometheus-address
          value: <http://prometheus-kube-prometheus-prometheus.monitoring.svc:9090>
        - name: app-name
          value: rollouts-demo-bg
        - name: ingress-host-active
          value: rollouts-demo-bg-active.example.com
        - name: ingress-host-preview
          value: rollouts-demo-bg-preview.example.com
        successfulRunHistoryLimit: 3
        unsuccessfulRunHistoryLimit: 3
      # Post Promotion Analysis: 전환 후에도 모니터링 (Active 서비스 분석)
      postPromotionAnalysis:
        templates:
        - templateName: rollouts-demo-bg-analysis
        args:
        - name: prometheus-address
          value: <http://prometheus-kube-prometheus-prometheus.monitoring.svc:9090>
        - name: app-name
          value: rollouts-demo-bg
        - name: ingress-host-active
          value: rollouts-demo-bg-active.example.com
        - name: ingress-host-preview
          value: rollouts-demo-bg-preview.example.com
        successfulRunHistoryLimit: 3
        unsuccessfulRunHistoryLimit: 3
  revisionHistoryLimit: 2
  selector:
    matchLabels:
      app: rollouts-demo-bg
  template:
    metadata:
      labels:
        app: rollouts-demo-bg
    spec:
      containers:
      - name: rollouts-demo-bg
        image: argoproj/rollouts-demo:blue
        ports:
        - name: http
          containerPort: 8080
          protocol: TCP
        resources:
          requests:
            memory: 32Mi
            cpu: 5m
  • rollouts/blue-green-test/certificate.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: rollouts-demo-bg-active-tls
  namespace: default
spec:
  secretName: rollouts-demo-bg-active-tls
  issuerRef:
    name: selfsigned-issuer
    kind: ClusterIssuer
  dnsNames:
  - rollouts-demo-bg-active.example.com
  duration: 8760h # 1 year
  renewBefore: 720h # 30 days
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: rollouts-demo-bg-preview-tls
  namespace: default
spec:
  secretName: rollouts-demo-bg-preview-tls
  issuerRef:
    name: selfsigned-issuer
    kind: ClusterIssuer
  dnsNames:
  - rollouts-demo-bg-preview.example.com
  duration: 8760h # 1 year
  renewBefore: 720h # 30 days

  • rollouts/blue-green-test/ingress.yaml
# Active 서비스용 Ingress (프로덕션 트래픽)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: rollouts-demo-bg-active-ingress
  namespace: default
  annotations:
    # SSL 리다이렉트 (HTTP도 허용하도록 false로 설정, 또는 HTTPS만 사용하려면 true)
    nginx.ingress.kubernetes.io/ssl-redirect: "false"
    # 메트릭 수집을 위한 annotation (Prometheus가 메트릭을 수집할 수 있도록)
    prometheus.io/scrape: "true"
    prometheus.io/port: "10254"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - rollouts-demo-bg-active.example.com
    secretName: rollouts-demo-bg-active-tls
  rules:
  - host: rollouts-demo-bg-active.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: rollouts-demo-bg-active  # Active 서비스로 트래픽 라우팅
            port:
              number: 80
---
# Preview 서비스용 Ingress (테스트 트래픽)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: rollouts-demo-bg-preview-ingress
  namespace: default
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "false"
    # 메트릭 수집을 위한 annotation (Prometheus가 메트릭을 수집할 수 있도록)
    prometheus.io/scrape: "true"
    prometheus.io/port: "10254"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - rollouts-demo-bg-preview.example.com
    secretName: rollouts-demo-bg-preview-tls
  rules:
  - host: rollouts-demo-bg-preview.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: rollouts-demo-bg-preview  # Preview 서비스로 트래픽 라우팅
            port:
              number: 80
  • rollouts/blue-green-test/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: rollouts-demo-bg-active
spec:
  ports:
  - port: 80
    targetPort: 8080
    protocol: TCP
    name: http
  selector:
    app: rollouts-demo-bg
---
apiVersion: v1
kind: Service
metadata:
  name: rollouts-demo-bg-preview
spec:
  ports:
  - port: 80
    targetPort: 8080
    protocol: TCP
    name: http
  selector:
    app: rollouts-demo-bg
  • apps/blue-green-test/application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: blue-green-test
  namespace: argocd
  finalizers:
    - resources-finalizer.argocd.argoproj.io
spec:
  project: dev
  source:
    repoURL: <https://github.com/hanship0530/argo-rollout.git>
    targetRevision: main
    path: rollouts/blue-green-test
  destination:
    server: <https://kubernetes.default.svc>
    namespace: default
  ignoreDifferences:
    # kubectl patch로 변경한 image를 ArgoCD가 되돌리지 않도록 설정
    - group: argoproj.io
      kind: Rollout
      name: rollouts-demo-bg
      namespace: default
      jsonPointers:
        - /spec/template/spec/containers/0/image
  syncPolicy:
    automated:
      prune: false
      selfHeal: false
    syncOptions:
      - RespectIgnoreDifferences=true
  • host 추가
echo "127.0.0.1 rollouts-demo-bg-active.example.com" | sudo tee -a /etc/hosts
echo "127.0.0.1 rollouts-demo-bg-preview.example.com" | sudo tee -a /etc/hosts

위와 같이 설정을 하고 commit & push를 합니다. 그러면 argocd에서 배포된 앱을 확인 할 수 있습니다.

argocd에 등록된 화면

green로 배포를 진행해봅니다.

kubectl patch rollout rollouts-demo-bg -n default --type='json' -p='[
  {
    "op": "replace",
    "path": "/spec/template/spec/containers/0/image",
    "value": "argoproj/rollouts-demo:green"
  }
]'
rollout.argoproj.io/rollouts-demo-bg patched

 


active



preview

 

preview와 active가 blue, green 으로 배포된 것을 볼 수 있습니다.

active의 error rate를 26% 로 수정합니다.

revision 2 가 배포됨을 볼 수 있고 아래의 pod 조회를 통해서 rollouts-demo-bg-59f7f685b5(blue), rollouts-demo-bg-84c464fb4(green) 각 3개 씩 배포된 것을 확인 할 수 있습니다.

Analysis 2-pre에서는 Revision 2에 대한 분석을 각 단계별로 진행합니다.

pre는 대부분의 request가 성공인 반면, post는 모두 실패합니다.

Revision이 2로 변경됨

kubectl get pods -l app=rollouts-demo-bg
NAME                                READY   STATUS    RESTARTS   AGE
rollouts-demo-bg-59f7f685b5-tx4vj   1/1     Running   0          3m51s
rollouts-demo-bg-59f7f685b5-vtbjv   1/1     Running   0          3m51s
rollouts-demo-bg-59f7f685b5-w7hxt   1/1     Running   0          3m51s
rollouts-demo-bg-84c464fb4-bnkgj    1/1     Running   0          2m44s
rollouts-demo-bg-84c464fb4-gcvd9    1/1     Running   0          2m44s
rollouts-demo-bg-84c464fb4-qtvkb    1/1     Running   0          2m44s

요건을 충족하지 못해 Rollback 됩니다.

Revision 1로 돌아감



 active



preview

activate는 그대로 blue 이며, preview 503 에러가 발생합니다.

다시 blue로 전환 후 green으로 재배포 하며 이번에는 Error Rate를 5%로 조정해보겠습니다.

kubectl patch rollout rollouts-demo-bg -n default --type='json' -p='[
  {
    "op": "replace",
    "path": "/spec/template/spec/containers/0/image",
    "value": "argoproj/rollouts-demo:blue"
  }
]'

# 적용 후
kubectl patch rollout rollouts-demo-bg -n default --type='json' -p='[
  {
    "op": "replace",
    "path": "/spec/template/spec/containers/0/image",
    "value": "argoproj/rollouts-demo:green"
  }
]'

 

 

4번의 배포로 인해 희망하는 상태는 Revision4이며 Revision 분석결과를 지켜봅니다.

 

 

Analysis 4-pre 가 정상적으로 끝난 후 post가 실행됩니다.

이번에는 정상적으로 배포되었습니다. 성공조건과 트래픽 조건을 통해 배포 제어를 진행할 수 있습니다.

active가 green으로 변경됨

 

'스터디 > [gasida] ci-cd 스터디 1기' 카테고리의 다른 글

OpenLDAP + KeyCloak + Argo CD + Jenkins  (0) 2025.11.23
ArgoCD ApplicationSet  (0) 2025.11.23
ArgoCD + Ingress + Self Managed  (0) 2025.11.16
4주차: Argo  (0) 2025.11.09
3주차: Jenkins + ArgoCD  (0) 2025.11.01

+ Recent posts