Kargo + Argo Rollouts로 카나리 롤백 자동화하기: AnalysisTemplate과 Freight 전파 차단 실전
배포가 두렵던 적 있으신가요? 저는 있었습니다. 특히 "이번 배포 10% 트래픽만 먼저 보내볼게요"라고 말하고 나서, 슬랙 채널을 새로고침하며 에러율 대시보드를 뚫어지게 바라보던 그 순간이요. 수동으로 지표를 읽고, 판단하고, 롤백 여부를 결정하는 그 과정 자체가 병목이었습니다. 사람이 피곤하면 판단도 흐려지고, 새벽 배포라면 더욱 그렇습니다.
Kargo와 Argo Rollouts를 함께 쓰면 이 판단 자체를 코드로 옮길 수 있습니다. "에러율이 5% 넘으면 자동으로 막아라"를 YAML로 선언하면, 이후 파이프라인이 알아서 Prometheus를 읽고, 성공 기준을 평가하고, 실패 시 Freight 전파를 차단합니다. 실제로 이 구조로 배포 파이프라인을 바꾼 뒤 새벽에 울리는 알림이 눈에 띄게 줄었습니다. 이 글에서는 AnalysisTemplate 기반 성공 기준 설정부터 Kargo Stage 검증, 그리고 자동 롤백이 실제로 작동하는 두 가지 독립 레이어까지 구체적인 YAML과 함께 풀어봅니다.
이 글의 전제: Kubernetes를 운영 중이고 Argo CD로 GitOps 배포를 하고 있는 분들에게 특히 도움이 됩니다. Prometheus, PromQL, CRD 같은 용어가 사전 설명 없이 등장하니 참고해 주세요.
Argo Rollouts의 분석 엔진: AnalysisTemplate
Progressive Delivery란 카나리, 블루/그린처럼 트래픽을 점진적으로 전환하면서 안정성을 검증하는 배포 방식입니다. 전통적인 롤링 업데이트보다 위험 범위를 줄일 수 있습니다.
Argo Rollouts는 Kubernetes에서 카나리, 블루/그린 배포를 담당하는 컨트롤러입니다. 그런데 "카나리 트래픽 10% 보냈다"에서 끝나지 않고, 그 트래픽이 건강한지 자동으로 판단하는 기능이 있습니다. 바로 AnalysisTemplate입니다.
AnalysisTemplate이란 어떤 지표를 어떤 주기로 얼마나 수집해서 어떤 기준으로 성공/실패를 판정할지를 선언하는 Kubernetes CRD입니다.
AnalysisRun은 이 템플릿을 한 번 실행한 인스턴스예요.
AnalysisTemplate의 중요한 설계 포인트 하나: Rollout 오브젝트에 직접 묶여 있지 않아도 됩니다. 독립적으로 존재할 수 있고, 다른 도구—Kargo 포함—가 참조할 수 있습니다. 이 점이 Kargo와의 연동을 가능하게 하는 핵심입니다.
또 하나 짚어두고 싶은 것이 있습니다. successCondition과 failureCondition을 둘 다 정의하면 중간값이 생깁니다. 성공 기준이 95% 이상이고 실패 기준이 80% 미만이면, 80~95% 구간은 Inconclusive(유보) 상태가 됩니다. failureCondition만 지정하면 그 이하가 아닌 모든 경우는 자동으로 성공으로 처리되고요. 저도 처음엔 이 차이를 놓쳐서 "왜 분석이 항상 유보 상태지?"라고 한참 헤맨 적 있습니다.
Kargo가 프로모션을 오케스트레이션하는 방식
Kargo는 Akuity(Argo 프로젝트 창시팀)가 만든 GitOps 기반 프로모션 오케스트레이터입니다. 세 가지 오브젝트만 이해하면 전체 그림이 보입니다.
| 오브젝트 | 역할 |
|---|---|
| Warehouse | 컨테이너 이미지, Git 커밋, Helm 차트 변경을 감지하고 Freight를 생성 |
| Freight | 특정 버전 아티팩트 묶음의 불변 오브젝트. Stage 사이를 이동하며 프로모션 이력이 됨 |
| Stage | dev, staging, prod 같은 각 환경. Freight를 받아 검증하고 하류로 넘기는 역할 |
Freight를 "배포 티켓"으로 생각하면 이해가 쉽습니다. 이미지 버전
v1.2.3의 Freight가 dev Stage 검증을 통과하면Verified스탬프가 찍히고, staging Stage가 그 Freight를 받을 자격이 생깁니다. 검증에 실패하면 Freight는 그 단계에서 멈춥니다.
두 도구가 연결되는 지점 — 롤백이 일어나는 두 가지 레이어
여기서 가장 많이 헷갈리는 부분을 짚고 넘어갑니다. Kargo와 Argo Rollouts가 함께 동작할 때 "롤백"은 사실 두 개의 독립된 레이어에서 일어납니다. 이걸 하나인 것처럼 이해하면 실제 구현 시 반드시 막힙니다.
레이어 1 — Argo Rollouts 레이어: Rollout 오브젝트의 카나리 스텝에 analysis가 직접 정의되어 있는 경우입니다. Argo Rollouts가 자체적으로 AnalysisRun을 생성하고, 분석이 실패하면 Rollout 오브젝트를 Aborted 상태로 전환해 카나리 가중치를 0으로 되돌립니다. 이 동작은 Kargo와 무관하게 Argo Rollouts가 단독으로 처리합니다.
레이어 2 — Kargo 레이어: Kargo Stage의 spec.verification.analysisTemplates에서 AnalysisTemplate을 참조하는 경우입니다. 프로모션이 완료되면 Kargo가 AnalysisRun을 생성하고 결과를 평가합니다. 이 분석이 실패하면 Kargo는 Freight를 Verified 처리하지 않습니다. 즉, 그 Freight는 다음 Stage로 전파되지 않습니다. Argo Rollouts의 Rollout 오브젝트 상태를 직접 변경하는 것은 아닙니다.
정리하면 이렇습니다:
| 레이어 | 실패 시 동작 | 담당 컴포넌트 |
|---|---|---|
| Argo Rollouts 분석 | 현재 Rollout 중단 + 카나리 가중치 0 복귀 | Argo Rollouts |
| Kargo Stage 검증 | 해당 Freight의 하류 Stage 전파 차단 | Kargo |
두 레이어를 함께 쓰면 강력해집니다. Argo Rollouts가 현재 배포의 트래픽을 즉시 복원하는 동안, Kargo가 그 나쁜 Freight가 다음 환경으로 흘러가는 것을 막는 구조입니다.
전체 흐름을 정리하면 이렇습니다:
CI 빌드/푸시
↓
Kargo Warehouse가 변경 감지 → Freight 생성
↓
Stage가 Freight 수신, Git 매니페스트 업데이트
↓
Argo CD가 클러스터에 동기화 (카나리 트래픽 분기 시작)
↓
Argo Rollouts: Rollout 내 analysis 스텝 실행 (정의된 경우)
→ 실패 시: 카나리 가중치 0으로 즉시 복귀 (Argo Rollouts 단독 처리)
↓
Kargo: Stage 검증용 AnalysisRun 생성 → Prometheus 지표 평가
→ 성공: Freight에 Verified 마크 → 하류 Stage 이동 허용
→ 실패: 하류 Stage 전파 차단 (Rollout 상태는 직접 변경하지 않음)ClusterAnalysisTemplate도 잠깐 언급하겠습니다. AnalysisTemplate이 네임스페이스 범위라면, ClusterAnalysisTemplate은 클러스터 전체에서 참조 가능합니다. 조직 전체의 검증 기준을 한 곳에서 관리하고 싶을 때 유용하고, Kargo Stage의 spec.verification.analysisTemplates에서도 kind: ClusterAnalysisTemplate으로 지정해 참조할 수 있습니다.
실전 적용
예시 1: Prometheus 기반 AnalysisTemplate 작성
실무에서 가장 자주 쓰는 패턴은 성공률(success rate)과 에러율(error rate) 두 가지를 동시에 측정하는 것입니다. 하나만 보면 놓치는 케이스가 생기거든요. 처음엔 성공률만 봤다가 특정 엔드포인트의 에러가 희석되어 통과해버린 경험이 있어서, 이후론 항상 두 지표를 함께 정의하고 있습니다.
interval × count = 총 관찰 시간으로 계산됩니다. 아래 예시에서 interval: 5m에 count: 6이면 "5분마다 측정하되, 총 30분 동안 지켜보겠다"는 뜻입니다. 이 값을 환경별로 다르게 설정하는 것이 핵심인데, dev는 짧게(count: 2), prod는 길게(count: 12) 잡는 편입니다.
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
name: success-rate-check
spec:
args:
- name: service-name # Stage에서 동적으로 주입
metrics:
- name: success-rate
interval: 5m # 5분마다 측정
count: 6 # 총 6회 = 30분간 관찰
successCondition: result[0] >= 0.95 # 성공 기준: 95% 이상
failureCondition: result[0] < 0.80 # 실패 기준: 80% 미만 (80~95%는 Inconclusive)
failureLimit: 2 # 2회 연속 실패 시 즉시 Analysis 실패
provider:
prometheus:
address: http://prometheus.monitoring:9090
query: |
sum(rate(http_requests_total{
service="{{args.service-name}}",
status!~"5.."
}[5m])) /
sum(rate(http_requests_total{
service="{{args.service-name}}"
}[5m]))
- name: error-rate
interval: 5m
count: 6 # success-rate와 동일하게 30분간 관찰
successCondition: result[0] < 0.05 # 에러율 5% 미만
failureCondition: result[0] >= 0.05 # 에러율 5% 이상이면 즉시 실패
failureLimit: 3
provider:
prometheus:
address: http://prometheus.monitoring:9090
query: |
sum(rate(http_requests_total{
service="{{args.service-name}}",
status=~"5.."
}[5m])) /
sum(rate(http_requests_total{
service="{{args.service-name}}"
}[5m]))주요 필드별 역할을 정리하면 이렇습니다:
| 필드 | 값 | 의미 |
|---|---|---|
interval |
5m |
5분마다 PromQL 쿼리 실행 |
count |
6 |
총 30분간 관찰 후 최종 판정 |
successCondition |
result[0] >= 0.95 |
Go 표현식으로 평가 |
failureCondition |
result[0] < 0.80 |
즉시 실패 처리 조건 |
failureLimit |
2 |
누적 실패 허용 횟수 초과 시 중단 |
result[0]은 PromQL 쿼리가 반환한 시계열 벡터의 첫 번째 값입니다. PromQL은 기본적으로 벡터를 반환하기 때문에 인덱스([0])로 접근합니다.scalar()함수로 단일 숫자를 반환하는 쿼리라면result만 써도 됩니다.
예시 2: Kargo Stage 검증 설정
AnalysisTemplate을 만들었으면 Stage에서 참조시키는 것만 남았습니다.
apiVersion: kargo.akuity.io/v1alpha1
kind: Stage
metadata:
name: staging
namespace: my-project
spec:
requestedFreight:
- origin:
kind: Warehouse
name: my-warehouse
sources:
stages:
- dev # dev Stage에서 Verified된 Freight만 수신
verification:
analysisTemplates:
- name: success-rate-check # 같은 Namespace의 AnalysisTemplate 참조
args:
- name: service-name
value: my-serviceKargo v1.3부터는 args 값에 표현식을 쓸 수 있어서, 검증 중인 커밋 해시를 동적으로 넘기는 것도 가능합니다:
args:
- name: service-name
value: my-service
- name: commit
value: "${{ freight.git.commit }}" # 배포된 커밋 해시 동적 주입예시 3: 다단계 파이프라인 — 금융 서비스 배포 패턴
실무에서 실제로 참고할 수 있는 패턴 하나를 소개합니다. 핀테크 환경에서 결제 서비스를 배포할 때 자주 쓰는 구성입니다. 이 구성을 쓰기 전에는 prod 배포 때마다 누군가가 30분씩 대시보드를 바라보며 수동으로 판단해야 했습니다.
# dev Stage: 가벼운 HTTP 헬스체크
apiVersion: kargo.akuity.io/v1alpha1
kind: Stage
metadata:
name: dev
namespace: payments
spec:
requestedFreight:
- origin:
kind: Warehouse
name: payments-warehouse
verification:
analysisTemplates:
- name: http-healthcheck # Job 기반 간단 헬스체크 (별도 정의)
---
# staging Stage: Prometheus 기반 30분 분석
apiVersion: kargo.akuity.io/v1alpha1
kind: Stage
metadata:
name: staging
namespace: payments
spec:
requestedFreight:
- origin:
kind: Warehouse
name: payments-warehouse
sources:
stages:
- dev
verification:
analysisTemplates:
- name: success-rate-check
args:
- name: service-name
value: payments-service
---
# prod Stage: 수동 승인 게이트 + Prometheus 분석
apiVersion: kargo.akuity.io/v1alpha1
kind: Stage
metadata:
name: prod
namespace: payments
spec:
requestedFreight:
- origin:
kind: Warehouse
name: payments-warehouse
sources:
stages:
- staging
promotionTemplate:
spec:
steps:
- uses: wait-for-approval # 수동 승인 게이트
- uses: argocd-update
verification:
analysisTemplates:
- name: success-rate-check
args:
- name: service-name
value: payments-servicestaging에서 에러율이 5%를 넘으면 Freight가 Verified 처리되지 않아 prod로 아예 넘어가지 않습니다. Argo Rollouts가 Rollout 스텝 내 analysis를 함께 정의해뒀다면 카나리 가중치도 0으로 돌아옵니다. 사람이 새벽에 모니터링하지 않아도 되는 구조입니다. prod에서 관찰 시간을 더 길게 잡고 싶다면, count를 파라미터로 받는 별도의 AnalysisTemplate을 만들거나 prod 전용 템플릿을 분리하는 것도 흔한 패턴입니다.
실제로 써보니: 좋았던 것과 조심해야 할 것
좋았던 것
| 항목 | 내용 |
|---|---|
| GitOps 완전 통합 | 모든 프로모션 이력이 Git에 기록되어 롤백이 곧 git revert |
| 자동 품질 게이트 | 실패 Freight가 상류에서 하류로 전파되는 것을 자동 차단 |
| 재사용성 | ClusterAnalysisTemplate으로 조직 전체 검증 기준을 한 곳에서 관리 |
| 풍부한 메트릭 프로바이더 | Prometheus, Datadog, New Relic, CloudWatch, Kubernetes Job 등 지원 |
| 선언적 성공 기준 | Go 표현식 기반 조건으로 복잡한 비즈니스 규칙도 표현 가능 |
조심해야 할 것
| 항목 | 내용 | 대응 방안 |
|---|---|---|
| 메트릭 인프라 필수 | Prometheus 등 관측성 스택이 먼저 구축되어 있어야 함 | kube-prometheus-stack Helm 차트로 빠르게 구성 가능 |
| 초기 설정 복잡도 | Argo Rollouts + Kargo + Argo CD 세 컴포넌트 동시 운영 | 로컬 k3d 환경에서 먼저 연습 후 프로덕션 적용 권장 |
| 분석 대기 시간 | count × interval 동안 다음 Stage 프로모션이 블로킹됨 |
환경별 count를 다르게 설정 (dev는 짧게, prod는 길게) |
| False Negative 위험 | 지표 정의가 부정확하면 나쁜 배포가 통과하거나 좋은 배포가 차단될 수 있음 | 임계값을 점진적으로 조정하며 튜닝 |
| 샤딩 환경 주의 | 분산 클러스터 환경에서 AnalysisRun이 엉뚱한 샤드의 지표를 읽어 오탐이 발생할 수 있음 | Kargo 샤드 설정과 AnalysisRun의 타겟 클러스터를 일치시켜야 함 |
실무에서 가장 흔한 실수
-
failureCondition없이successCondition만 정의하는 경우 — 지표가 95% 아래로 떨어져도Inconclusive로만 처리되어 분석이 영원히 끝나지 않을 수 있습니다. 두 조건을 명확히 분리해서 정의하거나,failureCondition만 쓰는 심플한 방법도 유효합니다. -
분석 시작 타이밍을 고려하지 않는 경우 — 카나리 Pod가 Ready 상태가 되기 전에 AnalysisRun이 시작되면 초반 측정값이 왜곡될 수 있습니다.
initialDelay필드로 Pod 안정화 시간을 확보하는 것이 좋습니다. -
Kargo 검증과 Argo Rollouts 롤아웃 단계를 같은 레이어로 혼동하는 경우 — 앞서 설명했듯이 두 레이어는 독립적입니다. 카나리 트래픽이 실제로 흘러가는 시점에 Kargo의 AnalysisRun이 실행되도록 파이프라인 순서를 명확하게 설계해야 합니다.
마치며
이 글에서 다룬 것을 짧게 정리하면: AnalysisTemplate으로 성공 기준을 코드로 선언하고, Kargo Stage 검증으로 Freight 흐름을 제어하면 두 레이어가 각자의 역할로 배포 안정성을 지켜줍니다. Argo Rollouts가 현재 카나리를 즉시 되돌리는 동안, Kargo는 그 나쁜 버전이 상류 환경으로 번지는 것을 막습니다.
슬랙 채널을 새로고침하는 대신, 그 판단 로직을 YAML에 위임하고 다음 문제로 넘어갈 수 있습니다. "배포를 지켜보는 사람"이 아닌 "배포를 설계하는 사람"이 될 수 있습니다.
지금 바로 시작해볼 수 있는 3단계:
-
로컬 환경에 Argo Rollouts를 설치해
AnalysisTemplate단독으로 실험해볼 수 있습니다. 설치는 이 한 줄입니다:kubectl create namespace argo-rollouts && kubectl apply -n argo-rollouts -f https://github.com/argoproj/argo-rollouts/releases/latest/download/install.yaml— 이 글의success-rate-checkYAML을 복사해서 Prometheus 주소만 바꿔 적용해볼 수 있습니다. -
Kargo를 Argo CD가 있는 클러스터에 추가로 설치하고, dev → staging 두 Stage짜리 미니 파이프라인을 만들어볼 수 있습니다. 공식 QuickStart를 따라가면 30분 내에 Freight가 Stage 사이를 이동하는 것을 직접 확인할 수 있습니다.
-
staging Stage에 이 글의
verification블록을 붙이고, 의도적으로 에러를 발생시켜 자동 차단이 작동하는지 확인해볼 수 있습니다. 실패 시나리오를 직접 눈으로 보고 나면 이후 프로덕션 설계가 훨씬 구체적으로 그려집니다.
참고 자료
- Kargo: Verifying Freight in a Stage | kargo.io
- Kargo: Analysis Templates Reference | kargo.io
- Kargo Core Concepts | kargo.io
- Kargo v1.3 릴리스 노트 — 조건부 스텝 & 고급 검증 | akuity.io
- Kargo v1.10 릴리스 노트 | akuity.io
- What is Kargo? | akuity.io
- Argo Rollouts: Analysis & Progressive Delivery | argo-rollouts.readthedocs.io
- Argo Rollouts: Prometheus 분석 프로바이더 | argo-rollouts.readthedocs.io
- Progressive Delivery with Argo Rollouts: Canary with Analysis | infracloud.io
- Continuous Promotion on Kubernetes with GitOps | piotrminkowski.com
- Canary delivery with Argo Rollout and Amazon VPC Lattice for EKS | aws.amazon.com