배포가 두렵지 않은 팀 만들기: Argo Rollouts로 카나리 배포 자동화하기
99.9% 가용성을 지키는 안전한 배포 파이프라인 구축기
안녕하세요, 딜라이트룸의 SRE Dan(민선홍)입니다.
저는 2025년 8월, 딜라이트룸에 SRE(Site Reliability Engineer)로 합류하게 되었습니다. 딜라이트룸은 매일 전 세계 350만 명이 사용하는 알람 앱 알라미(Alarmy)와, 합산 MAU 4,000만 명을 넘어서는 B2B 광고 수익화 솔루션 다로(DARO)를 운영하는 스타트업입니다. 이처럼 수많은 유저와 트래픽 환경에서 서비스 안정성을 책임지며 성장할 수 있겠다는 기대감에 합류를 결정했습니다.
SRE는 대규모 서비스의 안정성과 가용성을 소프트웨어 엔지니어링 방식으로 관리하는 역할입니다. 단순히 장애에 대응하는 것을 넘어 운영 자동화, 관찰 가능성(Observability, 시스템의 내부 상태를 외부 출력을 통해 파악하고 이해할 수 있는 능력) 강화, 인시던트(Incident, 서비스 중단이나 품질 저하를 유발하는 예상치 못한 이벤트) 대응 프로토콜 정립, 성능 개선까지 폭넓은 책임을 지게 됩니다.
오늘은 제가 입사 후 첫 번째로 맡은 업무, ‘Argo Rollouts를 통한 카나리 배포 자동화’를 주제로 글을 작성하고자 합니다. 기존 배포 방식에서 어떤 한계를 느꼈고, 왜 카나리 배포와 Argo Rollouts를 선택했는지, 그리고 실제로 어떻게 구현하고 적용했는지까지 솔직하게 공유하겠습니다. 비슷한 고민을 하고 있는 팀들에게 도움이 되길 바랍니다.
왜 배포 파이프라인을 개선해야 했나요?
SRE의 모든 업무는 SLO(Service Level Objective)라는 목표 아래 계획되고 실행됩니다. SLO는 서비스가 일정 기간 동안 달성해야 하는 성능, 가용성 등의 정량적인 목표이며, 에러 버짓(Error Budget)은 이 SLO 하에서 허용 가능한 실패의 총량을 의미합니다.
딜라이트룸은 ‘지난 1개월간 서비스 가용성(Availability, 시스템이 정상적으로 운영되어 사용 가능한 시간의 비율) 99.9% 이상’을 SLO로 설정하고 있습니다. 99.9%라는 수치가 갖는 의미가 무엇일까요? 다음과 같이 수식을 통해 에러 버짓을 계산해보겠습니다.
<수식 1>을 통해 구해 보면 한 달 기준 0.1%의 허용 실패율은 약 43.2분에 불과합니다. 이 43.2분이 저희 팀의 한 달치 에러 버짓입니다. 다시 말해, 단 5분의 장애가 약 4일치 에러 버짓을 소진시킵니다. 전 세계 수많은 유저들이 사용하는 서비스에서 이 숫자가 갖는 무게는 상당합니다. 몇 번의 배포 실수가 한 달 운영 안정성 목표 전체를 위협할 수 있기 때문입니다.
제가 입사했을 당시 딜라이트룸의 서버 배포는 쿠버네티스(Kubernetes, 컨테이너화된 애플리케이션의 배포, 관리, 확장을 자동화하는 오픈 소스 플랫폼)의 기본 Deployment(쿠버네티스에서 애플리케이션의 선언적 배포와 업데이트를 관리하는 리소스)를 사용한 롤링 업데이트(Rolling Update, 서비스 중단 없이 기존 버전을 점진적으로 새 버전으로 교체하는 배포 방식) 방식이었습니다.
이 방식에는 몇 가지 구조적인 한계가 있었습니다. 새 버전이 1~2분 내에 전체 트래픽을 받게 되는 구조였기 때문에 문제가 있는 코드가 배포되면 순식간에 모든 사용자에게 영향이 퍼졌습니다. 그리고 모니터링 대시보드에서 에러율 급증을 확인하고, 원인을 파악하고, 롤백을 결정하고, 실제로 롤백을 실행하기까지 짧게는 수 분, 길게는 10분 이상이 소요되었습니다. 게다가 이 모든 판단과 실행이 사람에게 의존하는 구조였기 때문에 담당자가 자리를 비운 시간에 문제가 발생하면 대응이 늦어질 수밖에 없었습니다.
이러한 환경에서 배포는 늘 긴장을 수반하는 작업이었습니다. 특히 많은 변경사항이 담긴 배포 직후에는 모니터링 대시보드에서 에러율과 레이턴시(Latency, 요청을 보낸 후 응답을 받기까지 걸리는 지연 시간) 지표가 안정적으로 유지되는지 몇 분간 지켜봐야 했고, 예상치 못한 케이스나 특정 API의 성능 저하가 발생하지 않을지 확인이 끝나야 비로소 다음 업무로 넘어갈 수 있었습니다. 이런 긴장감이 매 주요 배포마다 반복되었고, 배포를 진행한 엔지니어의 피로도가 높아질 수밖에 없는 구조였습니다.
이 문제를 해결하기 위해 저는 입사 후 첫 번째 과제로 배포 파이프라인 개선 프로젝트를 시작했습니다. 목표는 다음과 같이 정의했습니다.
서버 배포가 점진적으로 안전하게 이루어지고, 배포 중 이상 징후 발생 시 사람의 개입 없이도 자동으로 롤백되는 배포 파이프라인을 구축한다.
구체적으로는 본 프로젝트를 통해 다음 세 가지를 달성하고자 했습니다.
(1) 점진적 트래픽 전환: 새 버전에 처음부터 100% 트래픽을 보내지 않고, 5% → 20% → 50% → 100%와 같이 단계적으로 전환한다.
(2) 자동화된 이상 탐지: 에러율, 레이턴시 등 핵심 지표를 실시간으로 모니터링하고, 임계값 초과 시 자동으로 감지한다.
(3) 무인 롤백: 이상이 감지되면 사람의 개입 없이 즉시 이전 버전으로 롤백한다.
이 목표를 달성하기 위해 선택한 솔루션이 바로 Argo Rollouts와 데이터독(Datadog, 클라우드 기반의 인프라 모니터링 및 분석 플랫폼) 연동을 통한 카나리 배포 자동화였습니다.
카나리 배포란?
딜라이트룸은 모든 서버를 쿠버네티스 환경에서 관리하고 있습니다. 쿠버네티스 환경에서 애플리케이션의 새 버전을 배포하는 방식에는 여러 가지가 있으며, 대표적으로 롤링 업데이트(Rolling Update), 블루/그린(Blue/Green), 카나리(Canary) 배포가 있습니다.
롤링 업데이트는 쿠버네티스의 기본 배포 방식으로 별도의 설정 없이 사용할 수 있다는 장점이 있습니다. 그러나 앞서 설명했듯이 배포 속도가 빠르기 때문에 문제가 있는 버전이 빠르게 확산될 수 있고, 롤백 시에도 Pod(쿠버네티스에서 가장 작은 배포 단위로 하나 이상의 컨테이너를 포함한 실행 환경)를 다시 생성해야 하므로 복구 시간이 길어집니다.
블루/그린 배포는 새 버전(Green) 환경을 완전히 구성한 뒤 트래픽을 한 번에 전환하는 방식입니다. 롤백이 빠르다는 장점이 있지만, 배포를 완전히 마칠 때까지 두 벌의 인프라(Infrastructure, 시스템이나 애플리케이션을 운영하는 데 필요한 하드웨어, 소프트웨어, 네트워크 등의 기본 구조)를 유지해야 하므로 리소스 비용이 높습니다. 또한, 트래픽 전환이 0% 또는 100%로만 이루어지기 때문에 새 버전에 문제가 있을 경우 전환 즉시 전체 사용자가 영향을 받게 됩니다.
카나리 배포는 새 버전에 전체 트래픽의 일부(예: 10%)만 먼저 전환하고, 문제가 없음을 확인한 후 점진적으로 트래픽 비율을 높여가는 방식입니다. ‘카나리’라는 이름은 과거 광부들이 갱도에 카나리아 새를 데리고 들어가 유독 가스를 조기에 감지했던 것에서 유래했습니다. 이와 마찬가지로 카나리 배포는 소량의 트래픽을 새 버전에 먼저 보내 문제를 조기에 발견하는 역할을 합니다.
이 방식의 핵심적인 장점은 위험 노출 범위를 최소화할 수 있다는 점입니다. 새 버전에 문제가 있더라도 초기 단계에서는 소수의 사용자만 영향을 받으며, 이상 징후가 감지되면 트래픽을 즉시 기존 버전으로 되돌릴 수 있습니다.
딜라이트룸이 카나리 배포를 선택한 이유는 명확했습니다. 앞서 정의한 프로젝트 목표, 즉 점진적 트래픽 전환, 자동화된 이상 탐지, 무인 롤백을 구현하기에 카나리 배포가 가장 적합한 전략이었기 때문입니다. 5%의 트래픽으로 새 버전을 검증하는 동안 에러율이나 레이턴시에 이상이 감지되면 자동으로 롤백하고, 문제가 없으면 다음 단계로 트래픽을 확대하는 구조는 저희가 원하는 배포가 두렵지 않은 환경을 만들기 위한 핵심 요소였습니다.
Argo Rollouts란?
Argo Rollouts는 쿠버네티스 환경에서 점진적 배포 전략을 구현할 수 있게 해주는 오픈소스(Open Source, 소스 코드가 공개되어 누구나 자유롭게 사용, 수정, 배포할 수 있는 소프트웨어) 도구입니다. 쿠버네티스의 기본 Deployment 리소스를 대체하는 Rollout이라는 커스텀 리소스(Custom Resource, 쿠버네티스 API를 확장하여 사용자가 직접 정의한 리소스 유형)를 제공하며, 이를 통해 카나리 배포와 블루/그린 배포를 선언적으로 정의하고 실행할 수 있습니다.
Rollout 리소스는 기존 Deployment를 대체할 수 있도록 설계된 워크로드 리소스입니다. 기존 Deployment의 모든 기능을 그대로 지원하면서, Deployment만으로는 구현하기 어려운 고급 배포 기능들을 추가로 제공합니다.
대표적으로 블루/그린 및 카나리 배포 전략 지원, Ingress(클러스터 외부에서 내부 서비스로의 HTTP/HTTPS 트래픽 라우팅을 관리하는 리소스) 컨트롤러나 서비스 메시(Service Mesh, 마이크로서비스 간 통신을 관리하고 제어하는 인프라 계층)와의 연동을 통한 세밀한 트래픽 라우팅(Traffic Routing, 네트워크 트래픽을 특정 경로나 대상 서버로 전달하는 것), 데이터독, 프로메테우스(Prometheus, 시계열 메트릭 데이터를 수집하고 저장하는 오픈소스 모니터링 시스템) 등 메트릭 제공자(Metric Provider, 배포 분석에 필요한 메트릭 데이터를 제공하는 외부 시스템)와의 통합을 통한 배포 분석, 그리고 분석 결과에 따른 자동 승격(Promotion, 카나리 버전을 검증 후 안정 버전으로 전환하는 과정) 또는 롤백 기능이 있습니다.
또한, 기존에 딜라이트룸에서 사용하던 롤링 업데이트 방식도 Rollout 리소스에서 strategy 옵션을 통해 동일하게 사용할 수 있기 때문에 기존 배포 방식과의 호환성을 유지하면서 점진적으로 카나리 배포를 도입할 수 있다는 점도 큰 장점이었습니다.
카나리 배포를 구현할 수 있는 도구는 Argo Rollouts 외에도 Flagger, Spinnaker 등 여러 가지가 있습니다. 다만 다른 도구들을 검토했을 때 Flagger는 초기 설정이 간편하고 마이그레이션이 수월하다는 장점이 있지만, UI나 대시보드가 없어 배포 상태를 시각적으로 확인하기 어렵다는 점이 아쉬웠습니다. Spinnaker는 멀티 클라우드 환경에서 강력한 기능을 제공하지만, 자체적으로 무거운 인프라를 필요로 하며 초기 구축과 운영에 상당한 리소스가 요구됩니다.
딜라이트룸 환경에서 Argo Rollouts가 최적의 선택지였던 이유는 다음과 같습니다.
(1) 딜라이트룸은 서비스 메시를 사용하지 않고 Nginx Ingress 컨트롤러(Nginx 기반으로 구현된 쿠버네티스 Ingress 컨트롤러)를 사용하고 있는데, Argo Rollouts는 서비스 메시 없이도 Nginx Ingress 컨트롤러만으로 백분율 단위의 세밀한 트래픽 분할이 가능했습니다.
(2) 딜라이트룸은 모니터링 도구로 데이터독을 사용하고 있는데, Argo Rollouts는 AnalysisTemplate이라는 리소스를 통해 데이터독 메트릭(Metric, 시스템의 상태나 성능을 나타내는 측정 가능한 수치 데이터)을 기반으로 한 자동 롤백 로직을 유연하게 구성할 수 있었습니다.
(3) 설치와 운영이 비교적 단순하고, 전용 대시보드 UI를 제공하여 배포 상태를 시각적으로 모니터링할 수 있다는 점도 매력적이었습니다.
이러한 이유로 저희는 카나리 배포를 구현하기 위한 도구로 Argo Rollouts를 선택했습니다.
전체 아키텍처 살펴보기
<사진 4>를 통해 딜라이트룸의 Argo Rollouts를 활용한 카나리 배포 아키텍처의 전체 구조와 주요 구성 요소를 살펴보겠습니다.
(1) Argo Rollouts 컨트롤러: 클러스터 내의 Rollout 리소스 변경을 감지하고, 정의된 배포 전략에 따라 클러스터 상태를 자동으로 조정하는 역할을 합니다. 딜라이트룸은 여러 개의 EKS 클러스터를 운영하고 있는데, Argo Rollouts 컨트롤러는 멀티 클러스터를 지원하지 않기 때문에 각 클러스터마다 독립적으로 컨트롤러를 설치하여 운영하고 있습니다.
(2) 스테이블/카나리 ReplicaSet: Rollout 리소스가 생성되면 컨트롤러는 두 개의 ReplicaSet(지정된 수의 파드 복제본이 항상 실행되도록 보장하는 쿠버네티스 리소스)을 관리합니다. 기존 버전을 담당하는 스테이블 ReplicaSet(Stable ReplicaSet, 현재 안정적으로 운영 중인 버전의 파드 집합)과 새 버전을 담당하는 카나리 ReplicaSet(Canary ReplicaSet, 새 버전을 테스트하기 위해 소량 배포된 파드 집합)입니다. 카나리 배포가 진행되는 동안 두 ReplicaSet이 동시에 존재하며, 트래픽 비율에 따라 각각의 Pod으로 요청이 분산됩니다.
(3) Nginx Ingress 컨트롤러: 트래픽 라우팅을 담당합니다. Nginx Ingress 컨트롤러는 Argo Rollouts의 필수 구성 요소는 아니지만, 백분율 단위로 트래픽을 분할하기 위해서는 Ingress 컨트롤러나 서비스 메시와의 연동이 필요합니다. 딜라이트룸은 Nginx Ingress 컨트롤러를 사용하고 있어 이를 활용했습니다.
Ingress로 들어온 외부 트래픽은 Service(쿠버네티스에서 파드 집합에 대한 안정적인 네트워크 접근을 제공하는 추상화 계층)를 거쳐 스테이블 ReplicaSet과 카나리 ReplicaSet으로 분배됩니다. Argo Rollouts는 Nginx Ingress의 카나리 어노테이션을 활용하여 백분율 단위로 트래픽을 분할합니다. 예를 들어, 배포 초기에는 전체 트래픽의 5%만 카나리 ReplicaSet으로 보내고, 나머지 95%는 스테이블 ReplicaSet으로 유지하는 방식입니다.
(4) AnalysisTemplate과 AnalysisRun: 메트릭 기반 자동 분석 및 롤백을 담당하는 구성 요소입니다. AnalysisTemplate에는 어떤 메트릭을 조회하고, 어떤 조건에서 성공 또는 실패로 판단할지를 정의합니다. 배포가 시작되면 이 템플릿을 기반으로 AnalysisRun이 생성되어 실제 메트릭 분석을 수행합니다. 딜라이트룸은 데이터독을 메트릭 제공자로 연동하여 에러율, 레이턴시 등의 지표를 실시간으로 분석합니다. 분석 결과가 정상이면 다음 단계로 자동 승격되고, 임계값을 초과하면 즉시 롤백이 실행됩니다.
Argo Rollouts 구축 과정
앞서 설명했듯이 딜라이트룸은 기존에 쿠버네티스 Deployment를 이용해 애플리케이션을 배포하고 관리하고 있었습니다. 이러한 환경에서 Argo Rollouts를 가장 안전하게 도입하는 방법은 먼저 Argo Rollouts 컨트롤러를 포함한 모든 구성 요소를 갖춘 뒤, 기존 Deployment로 향하던 트래픽을 Rollout으로 안전하게 전환하는 것입니다.
딜라이트룸은 Argo CD를 이용한 깃옵스 전략으로 애플리케이션들을 관리하고 있으며, Argo Rollouts 구축 과정에서도 이 전략을 따랐습니다. 깃옵스(GitOps)는 깃 저장소(Git Repository, 프로젝트의 소스 코드와 변경 이력을 저장하고 관리하는 저장 공간)를 단일 진실 공급원(Single Source of Truth, 모든 데이터나 설정 정보의 신뢰할 수 있는 유일한 출처)으로 삼아 저장소에 선언된 상태와 실제 클러스터 상태를 자동으로 동기화하는 운영 방식입니다.
쉽게 말해, 배포하고 싶은 리소스의 YAML 파일을 깃 저장소에 커밋(Commit, 코드 변경 사항을 버전 관리 시스템에 기록하고 저장하는 행위)하면 Argo CD가 이를 감지하여 클러스터에 자동으로 반영해주는 방식입니다.
Argo Rollouts 컨트롤러와 대시보드는 공식 헬름 차트(Helm Chart, 쿠버네티스 애플리케이션을 패키징하고 배포하기 위한 템플릿 모음)를 이용해 배포했습니다. 공식 헬름 차트가 잘 구성되어 있어 대부분의 설정을 그대로 사용할 수 있었고, 저희 환경에 맞게 일부만 커스터마이징했습니다. 고가용성 확보를 위한 PDB(PodDisruptionBudget) 설정, 대시보드 접근을 위한 Ingress 설정, 그리고 슬랙 알림 설정 정도였습니다. 슬랙 알림은 공식 문서를 참고하여 Notification Templates를 구성했습니다. 모든 상태 변화에 대해 알림을 보내면 노이즈가 될 수 있으므로, analysis-run-error, analysis-run-failed, rollout-aborted, rollout-completed 등 중요한 이벤트에 대해서만 알림을 받도록 설정했습니다.
컨트롤러 설치를 마친 후에는 실제 애플리케이션에 적용할 Rollout과 AnalysisTemplate을 작성했습니다. 저희가 관리하는 여러 애플리케이션에 Argo Rollouts를 적용하기 위해 이 리소스들을 템플릿화했습니다. 이를 통해 values.yaml의 값만 입력하면 적은 리소스로 여러 애플리케이션에 동일한 구조를 적용할 수 있습니다. 실제 저희 B2B 제품인 광고수익화 솔루션 ‘다로(DARO)’에 적용된 Rollout과 AnalysisTemplate의 핵심 부분만 발췌해서 살펴보겠습니다.
먼저 Rollout 리소스입니다.
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: daro-api
namespace: daro
spec:
strategy:
canary:
canaryService: daro-api-canary-svc
stableService: daro-api-stable-svc
steps:
- setWeight: 1
- analysis:
templates:
- templateName: daro-api-step1-analysistemplate
- setWeight: 5
- analysis:
templates:
- templateName: daro-api-step2-analysistemplate
- setWeight: 15
- analysis:
templates:
- templateName: daro-api-step3-analysistemplate
- setWeight: 85
- pause:
duration: 1m
trafficRouting:
nginx:
stableIngress: daro-api-ingress-nginx
template:
# 일반적인 Deployment의 내용과 같음
Rollout 리소스의 핵심은 strategy.canary 부분입니다. 여기서 카나리 배포의 모든 동작 방식을 정의합니다.
canaryService와stableService는 각각 새 버전(카나리)과 기존 버전(안정)의 Pod에 트래픽을 전달할 Service를 지정합니다. 카나리 배포가 진행되는 동안에는 새 버전과 기존 버전이 동시에 존재하게 되는데, 이 두 Service를 통해 Argo Rollouts가 각 버전으로 트래픽을 적절히 분배할 수 있게 됩니다.steps는 카나리 배포가 어떤 단계를 거쳐 진행될지를 순서대로 정의합니다.setWeight는 새 버전에 전달할 트래픽의 비율(%)을 설정합니다. 예를 들어setWeight: 1은 전체 트래픽의 1%만 새 버전으로 보내고, 나머지 99%는 기존 버전이 처리한다는 의미입니다.analysis는 해당 단계에서 실행할 AnalysisTemplate을 지정합니다. 이 분석이 성공해야만 다음 단계로 넘어갈 수 있습니다. 위 설정에 따르면 배포는 1% → 5% → 15% → 85% → 100% 순으로 트래픽을 전환하며, 각 단계에서 AnalysisTemplate을 통해 새 버전의 상태를 검증합니다. 마지막에pause.duration: 1m은 최종 승격 전 1분간 대기하는 설정으로, 모든 검증이 끝난 후에도 잠시 여유를 두고 상태를 확인할 수 있게 합니다.trafficRouting.nginx.stableIngress는 트래픽 분할에 사용할 Nginx Ingress 리소스를 지정합니다. 위 코드 스니펫에는 마이그레이션이 완료된 최종 상태의 Ingress(daro-api-ingress-nginx)가 적혀 있지만, 실제 구축 단계에서는 이미 Deployment가 실제 트래픽을 받고 있는 상황이었기 때문에 Rollout 환경을 먼저 검증하는 것이 중요했습니다. 따라서 처음에는 실제 트래픽을 받는 Ingress가 아닌, 테스트용 임시 Ingress를 생성하여 연결해두었습니다.
template 부분은 기존 Deployment의 Pod 템플릿과 동일한 구조입니다. 컨테이너 이미지, 환경 변수, 리소스 제한 등 Pod의 스펙을 정의하는 부분으로, 기존 Deployment에서 사용하던 내용을 그대로 가져올 수 있습니다.
다음으로 AnalysisTemplate 리소스입니다. Rollout의 각 단계에서 실행되는 분석 로직을 정의합니다. 위 Rollout 설정에서 볼 수 있듯이 저희는 daro-api-step1-analysistemplate부터 daro-api-step3-analysistemplate까지 총 3개의 AnalysisTemplate을 사용하고 있는데, 구조가 유사하므로 여기서는 첫 번째 단계의 템플릿만 대표로 살펴보겠습니다.
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
name: daro-api-step1-analysistemplate
namespace: daro
spec:
metrics:
- initialDelay: 3m
count: 2
interval: 3m
failureLimit: 0
name: datadog-error-rate-metric
provider:
datadog:
apiVersion: v2
formula: a / max(b, 1)
interval: 3m
queries:
a: sum:trace.http.request.errors.by_http_status{service:daro-api, env:prod, http.status_class:5xx}.as_count().rollup(sum, 60).fill(zero)
b: sum:trace.http.request.hits.by_http_status{service:daro-api, env:prod}.as_count().rollup(sum, 60).fill(zero)
successCondition: default(result, 0) <= 0.001
AnalysisTemplate은 ‘어떤 메트릭을 조회하고, 어떤 조건을 만족해야 성공으로 판단할 것인가’를 정의합니다.
initialDelay는 분석을 시작하기 전 대기 시간입니다. 새 버전의 Pod이 막 생성된 직후에는 초기화 작업 등으로 분석이 원활하지 않을 수 있습니다. 3분의 대기 시간을 두어 Pod이 안정화된 후에 분석을 시작하도록 했습니다.count와interval은 메트릭을 몇 번, 얼마 간격으로 측정할지를 정의합니다. 위 설정에서는 2회 측정(count: 2)하며, 3분 간격(interval: 3m)으로 측정합니다.failureLimit은 허용되는 실패 횟수입니다. 0으로 설정하면 단 한 번의 실패로도 즉시 롤백이 실행됩니다. 저희는 장애 확산을 최소화하기 위해 엄격하게 0으로 설정했습니다.provider.datadog부분은 데이터독에서 메트릭을 조회하는 방법을 정의합니다. queries에서 두 개의 메트릭을 조회합니다. 쿼리a는 5xx 에러 응답 수를, 쿼리b는 전체 HTTP 요청 수를 가져옵니다.formula에서a / max(b, 1)로 에러율을 계산합니다.max(b, 1)을 사용한 이유는 요청이 없는 경우(b = 0) 0으로 나누는 오류를 방지하기 위함입니다.successCondition은 분석의 성공 조건입니다.default(result, 0) ≤ 0.001은 에러율이 0.1% 이하일 때 성공으로 판단한다는 의미입니다.default(result, 0)은 결과가 없는 경우(요청이 없는 경우) 0으로 처리하여 성공으로 간주합니다. 이 조건을 만족하지 못하면failureLimit에 따라 롤백이 실행됩니다.
여기까지 구축을 완료하면, 기존 Deployment로 배포된 Pod들과 새로 생성한 Rollout으로 배포된 Pod들이 동시에 존재하게 됩니다. 그러나 현재 실제 트래픽을 받는 Ingress의 백엔드 서비스는 Deployment의 Pod들을 바라보고 있기 때문에 Rollout으로 배포된 Pod들에게는 아직 실제 사용자 트래픽이 전달되지 않는 상태입니다.
기존 Deployment에서 Rollout으로 무중단 마이그레이션하기
Rollout 환경 구축을 마쳤으니, 이제 기존 Deployment로 향하는 트래픽을 Rollout으로 전환해야 합니다. 이 과정에서 가장 중요한 것은 무중단 마이그레이션입니다. 운영 환경에서 수많은 사용자의 트래픽이 오가는 만큼, 잠시의 다운타임도 허용할 수 없었습니다.
Argo Rollouts 공식 문서에서는 마이그레이션을 위한 여러 방법을 안내하고 있습니다. 그 중 “실제 운영 트래픽을 처리하고 있는 Deployment를 마이그레이션할 때는 Deployment를 삭제하거나 스케일 다운하기 전에 Rollout을 Deployment와 함께 병렬로 운영해야 한다”는 권장 사항에 따라, 저희는 기존 Deployment를 그대로 유지한 채 동일한 스펙의 Rollout을 별도로 생성하는 방식을 선택했습니다. 그리고 Ingress 단에서 트래픽을 점진적으로 전환하여 마이그레이션을 완료했습니다.
전체 마이그레이션 과정은 다음과 같습니다.
0단계: 초기 상태
마이그레이션을 시작하기 전 초기 상태입니다. 기존 Deployment와 이에 연결된 Ingress(이하 ‘기존 Ingress’), Service, Pod만 존재하며, 모든 트래픽은 이 경로를 통해 전달되고 있습니다.
1단계: Rollout 및 카나리 Ingress 생성
기존 Deployment와 동일한 스펙의 Rollout을 생성합니다. 이때 중요한 점은 Rollout의 spec.strategy.canary 하위 필드들(canaryService, stableService, steps, trafficRouting)을 아직 활성화하지 않는다는 것입니다. 현재 집중해야 할 것은 Deployment에서 Rollout으로 트래픽을 옮기는 것이지, Rollout의 카나리 배포 기능을 사용하는 것이 아닙니다. 사안의 복잡성을 낮추기 위해 한 번에 하나의 문제만 해결하는 접근을 택했습니다.
동시에 Rollout용 Ingress(이하 ‘카나리 Ingress’)를 생성합니다. 이 Ingress는 기존 Ingress와 동일한 호스트 주소(Host Address, 네트워크에서 특정 서버나 서비스에 접근하기 위한 도메인 이름 또는 IP 주소)를 사용하되, Nginx Ingress 컨트롤러의 카나리 Annotation(쿠버네티스 리소스에 메타데이터를 첨부하기 위한 키-값 쌍으로, 주로 외부 도구나 라이브러리에서 활용) 기능을 활용합니다. nginx.ingress.kubernetes.io/canary: “true”와 nginx.ingress.kubernetes.io/canary-weight: “0”을 설정하여, 해당 호스트 주소로 들어오는 트래픽 중 0%만 카나리 Ingress로 전달되도록 합니다. 이 상태에서는 모든 트래픽이 여전히 기존 Ingress를 통해 Deployment로 전달됩니다.
2단계: 카나리 Ingress로 트래픽 점진적 전환
카나리 Ingress의 canary-weight 값을 점진적으로 올립니다. 0% → 5% → 15% → … → 100%로 단계적으로 변경하면서 각 단계에서 에러율, 레이턴시 등 주요 지표를 모니터링합니다. 이상이 없으면 다음 단계로 진행하고, 문제가 발생하면 즉시 가중치를 0%로 되돌립니다. 최종적으로 가중치가 100%가 되면, 해당 호스트 주소의 모든 트래픽이 기존 Ingress가 아닌 카나리 Ingress를 통해 Rollout의 Pod으로 전달됩니다.
3단계: 기존 Ingress의 백엔드 서비스 변경
모든 트래픽이 카나리 Ingress로 전달되는 상태에서 기존 Ingress의 백엔드 서비스(Backend Service, 클라이언트 요청을 처리하고 비즈니스 로직을 수행하는 서버 측 애플리케이션)를 Rollout용 Service로 변경합니다. 이 시점에서 기존 Ingress는 트래픽을 받지 않고 있으므로, 이 변경은 사용자에게 영향을 주지 않습니다.
4단계: 기존 Ingress로 트래픽 복귀
카나리 Ingress의 canary-weight 값을 다시 점진적으로 낮춥니다. 100% → … → 15% → 5% → 0%로 단계적으로 변경합니다. 트래픽이 다시 기존 Ingress를 통해 전달되기 시작하는데, 3단계에서 백엔드 서비스를 변경해두었으므로 이제 기존 Ingress를 통한 트래픽도 Rollout의 Pod으로 전달됩니다.
5단계: 카나리 Ingress 삭제 및 마이그레이션 완료
카나리 Ingress의 가중치가 0%가 되면 카나리 Ingress를 삭제합니다. 이제 기존 Ingress → Rollout용 Service → Rollout Pod으로 이어지는 트래픽 경로가 완성됩니다. 기존 Deployment와 관련 리소스(Deployment용 Service, ReplicaSet 등)도 정리하면 Deployment에서 Rollout으로의 마이그레이션이 완료됩니다.
6단계: 카나리 배포 기능 활성화
마이그레이션은 완료되었지만, 아직 Rollout의 핵심 기능인 카나리 배포를 사용할 수 있는 상태는 아닙니다. 1단계에서 비활성화해두었던 spec.strategy.canary의 하위 필드들(canaryService, stableService, stpes, trafficRouting)을 활성화합니다. 이전 단락의 Rollout 코드 스니펫을 참고하면 됩니다.
이 설정이 적용되면 기존 Service와 Ingress는 스테이블 버전용으로 인식되고, 카나리 버전용 Service와 Ingress가 자동으로 생성됩니다. 이제부터 이미지 업데이트 등으로 배포가 트리거되면, stpes에 정의된 단계에 따라 트래픽이 점진적으로 카나리 버전으로 전환됩니다. 각 단계에서 AnalysisTemplate을 통한 검증이 수행되고, 모든 검증을 통과하여 트래픽이 100% 카나리 버전으로 전환되면, 승격이 이루어집니다. 승격이란 카나리 버전이 새로운 스테이블 버전이 되는 것을 의미합니다.
즉, 방금까지 새 버전이었던 카나리 ReplicaSet이 이제는 안정 버전을 담당하는 스테이블 ReplicaSet이 되고, 기존의 스테이블 ReplicaSet은 스케일 다운되어 정리됩니다. 이로써 하나의 배포 사이클이 완료됩니다.
이처럼 안전한 무중단 마이그레이션을 위해서는 섬세하고 단계적인 작업이 필요합니다. 저희는 개발 환경에서 지속적으로 트래픽을 흘려보내며 각 단계를 여러 차례 검증한 후, 운영 환경에 적용했습니다.
Argo Rollouts 적용 후 어떻게 되었나요?
저희는 지금까지 정리한 과정을 거쳐 2025년 9월 초부터 모든 운영 환경에 Argo Rollouts를 적용하기 시작했습니다. 적용 후 가장 많이 들었던 피드백은 배포에 대한 심리적 부담이 줄어들었다는 것이었습니다. 평소 배포를 자주 진행하는 엔지니어들이 직접 저에게 이런 변화를 전해주었을 때 이 프로젝트를 진행하길 잘했다는 보람을 느꼈습니다. 물론 개발 환경에서의 테스트는 여전히 철저히 진행하지만, 이전처럼 배포 버튼을 누른 뒤 모니터링 대시보드를 긴장하며 지켜볼 필요가 없어졌습니다.
실제로 자동 롤백이 동작한 사례도 있었습니다. 개발 환경에서는 재현되지 않았던 에러가 운영 환경에서 발생한 경우였는데, 카나리 배포 초기 단계에서 소량의 트래픽만 새 버전으로 전환했을 때 에러율 이상이 즉시 감지되었습니다. 에러율이 임계값을 초과하자 AnalysisTemplate에 정의된 대로 자동 롤백이 수행되었고, 사람의 개입 없이도 문제가 해결되었습니다. 만약 기존 방식대로 일괄 배포를 진행했다면 전체 사용자에게 영향을 미치는 대규모 장애로 이어졌을 수도 있는 상황이었습니다.
도입 이후에도 팀원들의 피드백을 반영하여 지속적으로 개선해나갔습니다.
첫 번째로, 배포 전략 선택 옵션을 추가했습니다. 처음에는 모든 배포를 카나리 배포로 진행하도록 설정했는데, 매우 간단한 수정의 경우에도 모든 검증 단계를 거쳐야 해서 배포 시간이 오래 걸린다는 피드백이 있었습니다. 물론 카나리 배포 중에 대시보드나 CLI(Command Line Interface, 텍스트 명령어를 입력하여 프로그램이나 시스템을 조작하는 인터페이스)를 통해 kubectl argo rollouts promote --full 명령으로 즉시 승격할 수도 있지만, 처음부터 롤링 업데이트로 배포하는 옵션도 필요하다고 판단했습니다.
기존에는 CI/CD 파이프라인(Continuous Integration/Continuous Deployment Pipeline, 코드 변경부터 테스트, 빌드, 배포까지의 과정을 자동화한 워크플로우)을 깃허브 액션(GitHub Actions, 깃허브에서 제공하는 CI/CD 및 워크플로우 자동화 플랫폼)으로 구성하고 있는데, 아래 <사진 6>과 같이 워크플로우(Workflow, 특정 목표를 달성하기 위해 정의된 일련의 자동화된 작업 흐름) 실행 시 배포 전략을 롤링 업데이트와 카나리 중에서 선택할 수 있도록 개선했습니다. Rollout 템플릿도 선택한 옵션에 따라 다르게 렌더링되도록 구성했습니다.
두 번째로, 슬랙 알림을 개선했습니다. 초기에는 단순히 주요 이벤트만 알림으로 보냈는데, 팀원들의 피드백을 반영하여 더 유용한 형태로 다듬었습니다. 아래 <사진 7>과 같이 알림 메시지에 대시보드로 바로 이동할 수 있는 링크를 추가하고, 각 배포 단계의 진행 상황은 스레드에 정리하여 알림 피로도를 줄였습니다. 배포 완료나 롤백 발생 같은 주요 변화만 채널에 포스트로 노출되도록 구성했습니다.
이러한 개선 과정을 거쳐 현재 엔지니어들은 새로운 배포 파이프라인에 빠르게 적응하여 자신감 있게 배포를 진행하고 있습니다. Rollout 리소스가 기존 Deployment와 완전히 호환되기 때문에, 배포하는 개발자 입장에서는 기존과 크게 달라지는 점이 없다는 것도 빠른 도입의 원동력이 되었습니다.
Argo Rollouts 도입으로 얻은 교훈과 향후 도전 과제
입사한 지 얼마 되지 않아 팀의 배포 방식을 변경하는 큰 프로젝트를 맡게 되었습니다. 배포 프로세스는 팀 전체가 사용하는 것이기 때문에, 그만큼 책임감을 가지고 임했습니다.
이 프로젝트를 진행하면서 가장 중요하게 생각한 것은 두 가지였습니다. 첫째는 수많은 트래픽을 안정적으로 다루는 것, 둘째는 실제 배포 업무를 진행하는 엔지니어들의 피로도를 줄이고 편의성을 높이는 것이었습니다. 기술적으로 아무리 훌륭한 시스템을 구축하더라도, 그것을 사용하는 사람들이 불편함을 느낀다면 좋은 도입이라고 할 수 없기 때문입니다.
이를 위해 엔지니어들을 위한 문서화에도 많은 공을 들였습니다. Deployment에서 Argo Rollouts로 전환하게 된 배경, 배포 프로세스가 어떻게 변화하는지, 배포 모니터링과 제어 방법은 어떻게 달라지는지, 그리고 트러블슈팅 가이드와 FAQ를 정리한 ‘Argo Rollouts 활용법 및 주의사항 안내’ 문서를 아래 <사진 8>과 같이 노션에 작성하여 공유했습니다.
딜라이트룸에서는 구성원 간의 커뮤니케이션을 매우 중요하게 여기고 있습니다. 그래서인지 문서화와 서로간의 정렬(Alignment)에 많은 신경을 쓰는 문화가 있다는 인상을 받았고, 저 역시 이에 맞춰 문서를 철저히 준비하고자 했습니다.
또한, 앞선 단락에서 언급했듯이 운영 환경 적용 이후에도 엔지니어들의 피드백을 받아 지속적으로 개선해나갔습니다. 운영 환경 적용은 9월 초에 마무리되었지만, 각종 개선 작업은 이후 몇 주간 더 이어졌습니다. 그리고 앞으로도 개선할 부분은 계속 나올 것이라고 생각합니다.
아쉬웠던 점도 있습니다. 개발 환경에서는 트래픽이 적어 AnalysisTemplate의 임계값이 실제로 잘 동작하는지 충분히 검증하기 어려웠습니다. 개발 환경에서 운영 환경과 유사한 트래픽을 시뮬레이션할 수 있는 방법을 미리 마련했다면 더 좋았을 것입니다.
그리고 처음부터 롤링 업데이트 옵션을 함께 제공했다면 엔지니어들의 초기 적응이 더 수월했을 것이라는 생각도 들었습니다. 모든 배포에 카나리 전략을 적용하는 것이 이상적이라고 생각했지만, 실제 운영 환경에서는 간단한 수정에도 긴 배포 시간이 소요되어 불편함을 초래했습니다. 사용자의 실제 워크플로우를 더 면밀히 파악한 뒤 설계했어야 했다는 교훈을 얻었습니다.
향후 개선 계획으로는 다음과 같은 것들을 고려하고 있습니다. 먼저 앞서 언급한 아쉬움을 해소하기 위해 개발 환경에서 운영 환경과 유사한 트래픽을 시뮬레이션할 수 있는 방법을 마련하고자 합니다. 이를 통해 AnalysisTemplate의 임계값을 운영 환경 적용 전에 충분히 검증할 수 있을 것입니다. 현재 운영 중인 임계값도 실제 운영 데이터를 기반으로 더욱 최적화할 예정입니다.
그리고 현재는 에러율을 주요 지표로 사용하고 있는데, 레이턴시 등 다른 효과적인 지표를 추가하여 검증의 정확도를 높이는 것도 검토하고 있습니다. 마지막으로 배포 파이프라인 내 더 많은 변수들을 템플릿화하여 배포를 진행하는 엔지니어들이 트래픽 전환율 등을 직접 조절할 수 있도록 개선할 계획입니다. 현재는 배포 전략(카나리/롤링 업데이트)만 선택할 수 있는데, 더 세밀한 제어가 가능하도록 확장해나갈 예정입니다.
이번 프로젝트를 통해 ‘배포가 두렵지 않은 팀’에 한 발 더 가까워졌다고 생각합니다. 물론 아직 개선해야 할 부분이 남아 있지만, 이제는 배포 버튼을 누른 뒤 긴장하며 대시보드를 지켜보는 대신, 시스템이 스스로 문제를 감지하고 대응할 것이라는 신뢰를 가질 수 있게 되었습니다. 이 글이 저희와 비슷한 고민을 하고 있는 팀들에게 조금이나마 도움이 되었으면 합니다. 긴 글 읽어주셔서 감사합니다.
⏰ 딜라이트룸에서 알라미와 함께 아침을 바꿀 분들을 모십니다 🙌