IT/CI CD

Flagger vs Argo Rollouts, 1년 굴려보고 내린 결론

gfrog 2026. 6. 16. 21:13
SMALL

작년 이맘때쯤 progressive delivery를 본격적으로 도입하기로 결정했다. 그 때만 해도 우리 팀은 "둘 다 비슷한 거 아니야?"라는 분위기였고, 사실 데모만 보면 정말 비슷해 보였다. 캐너리, 블루그린, 메트릭 기반 자동 promotion. 똑같은 단어들이 양쪽 문서에 다 나온다.

그래서 우리는 좀 무책임한 결정을 했다. 둘 다 써보기로 했다. 일부 서비스는 Flagger, 일부는 Argo Rollouts. 1년이 지난 지금, 한쪽으로 통일하기로 했고, 그 과정에서 알게 된 차이들을 정리해 본다.

시작 환경

우리 환경을 먼저 짧게 적는다. 비교는 결국 환경 의존적이라서.

  • EKS 1.32 클러스터 두 대 (스테이지/프로덕션)
  • 서비스 메시는 Istio (ambient 아니고 sidecar)
  • GitOps는 ArgoCD
  • 메트릭은 Prometheus + VictoriaMetrics (장기 저장)
  • 트래픽 패턴: B2C 서비스가 다수, 일부 내부 API

이 조합은 사실 Argo Rollouts에 좀 유리한 출발점이다. ArgoCD 쓰는 팀이 Argo Rollouts 안 쓰는 건 부자연스럽긴 하니까. 그래도 Flagger 진영에서 "우리는 더 자동화돼 있어"라고 주장하길래, 그 부분이 실제로 운영에 어떻게 작용하는지 보고 싶었다.

마이그레이션 비용부터 다르더라

처음 부딪힌 차이는 마이그레이션 비용이었다.

Argo Rollouts는 Deployment 리소스를 Rollout CRD로 갈아끼워야 한다. Helm chart 쓰는 서비스라면 chart를 통째로 손봐야 한다. 우리는 Helm chart가 50개 정도 있는데, kind: Deploymentkind: Rollout으로 바꾸고 strategy spec을 추가하는 작업이 생각보다 지겹다. argo-rollouts 공식에 kubectl-argo-rollouts 플러그인이 있긴 한데, 일괄 변환 도구는 아니라서 결국 손으로 chart 템플릿을 수정했다.

Flagger는 정반대다. 기존 Deployment를 그대로 두고, 위에 Canary CRD를 하나 더 얹는다. 처음 도입할 때 "기존 리소스를 안 건드린다"는 게 정말 매력적이었다. 신규 서비스부터 적용할 때 진입 장벽이 낮다.

근데 6개월 뒤 보니까 이게 양날의 검이었다. Flagger는 Canary CRD를 만들면 Flagger 자체가 별도의 primary Deployment(my-app-primary)를 만들어 관리한다. 즉, 우리가 정의한 Deployment 위에서 Flagger가 자기 마음대로 또 다른 Deployment를 생성해 굴린다는 뜻이다. 디버깅할 때 헷갈린다. kubectl get deploy만 보고 있다가 "어 왜 primary가 두 개야?" 하면서 한참을 들여다본 적이 있다.

트래픽 분기 메커니즘

Istio 환경에서 두 도구가 트래픽을 어떻게 분기하는지가 또 다르다.

Argo Rollouts는 VirtualService를 직접 수정한다. 우리가 미리 만들어 둔 VirtualService에 weight를 넣어주는 식이다. 명시적이고, 우리 GitOps repo에 정의된 VirtualService가 항상 진실이다.

Flagger는 본인이 VirtualService를 통째로 관리한다. 우리가 Canary 정의에 트래픽 정책을 적으면, Flagger가 VirtualService를 만들어 굴린다. 이게 처음엔 편한데, 좀 복잡한 라우팅 규칙(예: 특정 헤더는 항상 primary로) 같은 걸 추가하려면 Flagger가 지원하는 필드 안에서만 해야 한다. Flagger가 추상화한 만큼, 우리가 직접 손댈 수 있는 영역이 좁아진다.

결정적이었던 사건이 하나 있다. 작년 11월에 트래픽 미러링이 필요한 케이스가 생겼는데(레거시 → 새 API로 옮기는 작업), Argo Rollouts 쪽 서비스는 VirtualService에 mirror 블록을 추가하는 걸로 끝났고, Flagger 쪽 서비스는 Canary CRD가 지원하지 않는 필드라 일단 Flagger를 우회해서 직접 VirtualService를 쓸 수밖에 없었다.

자동화 수준

Flagger가 자랑하는 게 "zero-touch automation"이다. Canary CRD에 한 번 설정해 두면 그 다음부터는 손 안 대도 자동으로 굴러간다는 거다. 메트릭 기반 promotion, rollback, 알림까지 다 알아서 한다.

이건 사실 맞는 말이다. 우리가 Flagger를 쓴 내부 API들은 정말 무탈하게 돌아갔다. 신경 안 써도 알아서 canary 띄우고 메트릭 확인하고 promote 했다.

Argo Rollouts는 좀 다르다. Rollout spec에 step을 명시적으로 적어야 한다.

strategy:
  canary:
    steps:
      - setWeight: 10
      - pause: { duration: 5m }
      - setWeight: 30
      - pause: { duration: 10m }
      - analysis:
          templates:
            - templateName: success-rate
      - setWeight: 60
      - pause: { duration: 5m }
      - setWeight: 100

처음엔 "이거 너무 verbose한 거 아니야?" 싶었는데, 운영하다 보니 이게 명확해서 좋다. 어떤 서비스가 어떤 progressive 정책으로 굴러가는지 spec만 봐도 알 수 있다. Flagger는 이게 default값에 의존하는 부분이 많아서, 실제 동작을 알려면 kubectl describe canary를 봐야 한다.

또 Argo Rollouts는 dashboard UI가 꽤 쓸 만하다. ArgoCD UI에 rollout 플러그인이 통합돼 있어서, 개발자가 직접 와서 "지금 30%까지 갔구나, 메트릭 어떤지 한번 보자" 하고 확인한다. Flagger는 별도 UI가 없어서(Grafana 대시보드는 있다) 개발자가 상태를 보려면 kubectl을 써야 한다. 이게 운영자 입장에선 별 차이 없지만, 개발팀과 소통할 때 진입 장벽이 다르다.

메트릭 분석 — 의외로 비등

AnalysisTemplate(Argo Rollouts)과 MetricTemplate(Flagger) 둘 다 Prometheus 쿼리를 작성해서 KPI 기반 promotion/rollback을 한다. 이 부분은 솔직히 표현력이나 정확도에서 큰 차이를 못 느꼈다.

다만 Argo Rollouts 쪽이 좀 더 다양한 provider를 지원한다. Datadog, NewRelic, Wavefront, CloudWatch까지. Flagger는 Prometheus/Datadog/CloudWatch/InfluxDB 정도다. 우리는 결국 Prometheus만 썼기 때문에 이 차이는 별 의미가 없었다.

1년 후 결정

결론부터 말하면 Argo Rollouts로 통일하기로 했다. 이유는 세 가지다.

첫째, 우리 GitOps 스택이 ArgoCD라서 동일 ecosystem 안에서 도구 통합이 자연스럽다. ArgoCD UI에서 rollout 상태가 그대로 보이는 건 운영팀과 개발팀 둘 다에게 메리트다.

둘째, 명시성. 처음엔 verbose하다고 느꼈던 spec이, 시간이 지날수록 "이 서비스는 어떤 식으로 배포되는지 한눈에 보인다"는 장점으로 작용했다. 새로 합류한 멤버에게 설명할 때도 spec 하나만 보여주면 끝이다.

셋째, 트래픽 라우팅 자유도. Istio VirtualService를 우리가 직접 관리할 수 있다는 게, 복잡한 라우팅 케이스에서 결정적이었다.

그렇다고 Flagger가 나쁜 도구라는 건 절대 아니다. Flux를 쓰는 팀이라면 거의 자동으로 Flagger가 답이 될 거고, 자동화 우선 철학을 좋아한다면 Flagger의 zero-touch 접근이 훨씬 잘 맞을 거다. 실제로 내부 API 서비스들에서는 Flagger가 정말 손 안 가게 잘 굴러갔다.

참고로 Argo Rollouts는 최근에 v1.9가 나왔는데, BlueGreen analysis 관련 버그 fix와 함께 Gateway API 통합이 더 안정화됐다. Gateway API로 트래픽 라우팅이 통일된다면 다음에 또 다른 비교가 가능해질 수도 있겠다 싶다.

사이드 노트 — 도구를 두 개 굴린 이유

이 글의 진짜 교훈은 마지막에 하나 있다. 둘 다 써본 1년이 헛수고는 아니었다. 문서만 봐서는 어떤 도구가 우리 환경에 맞는지 알 수 없다. 데모는 다 비슷하게 보이고, 벤치마크는 우리 워크로드와 다르다. 직접 굴려보고서야 "이건 우리 ecosystem이랑 안 맞네", "이건 우리 라우팅 패턴과 충돌하네" 같은 게 드러난다.

물론 production에서 두 도구를 동시에 굴리는 건 운영 부담이 두 배다. 만약 다시 시작한다면 신규 서비스 두세 개를 골라서 6개월간 양쪽을 비교하는 식으로 했을 거다. 그 정도면 충분했을 것 같다.

혹시 비슷한 결정을 앞두고 있는 분들 있으면, 어떤 결정을 내렸는지 댓글 남겨주세요. 우리도 6개월 뒤에 v1.9로 올리면서 또 새로운 이슈 만날 것 같아서, 비교 사례가 더 있으면 도움 될 것 같다.

BIG