CI/CD 파이프라인 실무 사례 아카이브
CI/CD 파이프라인은 소프트웨어 딜리버리의 핵심 인프라이지만, 조직 규모가 커질수록 운영 복잡성, 보안 위협, 성능 병목 등 다양한 문제에 직면하게 된다. 이 글에서는 Spotify, GitHub, Datadog, Lyft, Coinbase 등 글로벌 기술 기업들이 실제 프로덕션 환경에서 겪은 CI/CD 파이프라인 운영 경험을 정리하였다. Jenkins 마이그레이션, Self-hosted Runner 대규모 운영, 소프트웨어 공급망 보안 강화, Canary 자동 배포, 모노레포 빌드 최적화까지 각 사례에서 직면한 문제와 해결 과정, 그리고 실무 교훈을 다룬다.
참고: 아래 사례들은 공개된 엔지니어링 블로그에서 수집한 내용이다. 원문 URL이 변경되었거나 접근이 불가할 수 있으므로, 제목으로 검색하여 원문을 확인하는 것을 권장한다. 저작권 보호를 위해 원문을 그대로 인용하지 않고, 주요 내용만 요약하여 정리하였다.
1. Spotify의 Jenkins에서 자체 CI/CD 플랫폼으로의 전환
- 출처: How We Improved Developer Productivity for Our DevOps Teams
- 저자/출처: Spotify Infrastructure Team
- 플랫폼: Spotify Engineering Blog
1.1 상황
Spotify는 2,000명 이상의 엔지니어가 근무하는 대규모 조직으로, 수백 개의 마이크로서비스를 운영하고 있었다. 초기에는 Jenkins를 중앙 CI/CD 시스템으로 사용하였으며, 각 팀이 독립적으로 Jenkins 파이프라인을 구성하고 관리하는 분산형 운영 모델을 채택하고 있었다. 수천 명의 엔지니어가 하루에도 수백 건의 빌드와 배포를 수행하였고, 서비스 수가 빠르게 증가하면서 Jenkins 인프라의 운영 부담이 기하급수적으로 커졌다.
Spotify의 엔지니어링 문화는 자율적인 Squad(분대) 모델에 기반하고 있었다. 각 Squad가 독립적으로 기술 스택을 선택하고 운영할 수 있는 구조였기 때문에, CI/CD 파이프라인 역시 팀마다 완전히 다른 방식으로 구축되어 있었다. 이로 인해 동일한 조직 내에서도 빌드/배포 프로세스의 일관성이 전혀 없는 상태였다.
1.2 문제
Jenkins 아키텍처의 근본적 한계:
- Jenkins 마스터 노드가 단일 장애 지점(SPOF)으로 동작하였다. 마스터 노드 장애 시 수백 개 팀의 빌드/배포가 동시에 중단되는 사태가 발생하였다.
- Jenkins의 상태 관리(stateful) 아키텍처 특성상, 마스터 노드에 Job 설정, 빌드 이력, 플러그인 상태 등이 저장되어 수평 확장이 구조적으로 불가능하였다.
- 마스터 노드의 JVM 힙 메모리 사용량이 지속적으로 증가하여, 주기적인 재시작이 필요하였다.
플러그인 의존성 문제:
- 수백 개의 플러그인이 설치되어 있었으며, 플러그인 간 버전 호환성 이슈가 빈번하게 발생하였다.
- 특정 플러그인의 보안 패치를 적용하면 다른 플러그인이 동작하지 않는 상황이 반복되어, Jenkins 코어 및 플러그인 업그레이드가 사실상 동결된 상태였다.
- 이로 인해 알려진 보안 취약점(CVE)이 패치되지 않은 채 방치되는 보안 위험이 존재하였다.
표준화 부재로 인한 운영 부담:
- Jenkinsfile의 Groovy 스크립트가 팀마다 제각각으로 작성되어 있었다. 어떤 팀은 Scripted Pipeline을, 어떤 팀은 Declarative Pipeline을 사용하였고, 공유 라이브러리의 활용 수준도 천차만별이었다.
- 새로운 서비스를 생성할 때마다 CI/CD 파이프라인을 처음부터 구축해야 하여, 서비스 온보딩에 수일이 소요되었다.
- Jenkins 인프라를 관리하는 전담 팀(Platform Team)의 인력이 절대적으로 부족하였으며, 대부분의 시간을 장애 대응과 플러그인 호환성 이슈 해결에 소비하였다.
성능 병목:
- 빌드 큐 대기 시간이 피크 시간대(오전 10시~12시)에 30분 이상으로 증가하였다.
- 빌드 에이전트의 리소스 활용률이 불균형하여, 일부 에이전트는 과부하 상태이고 다른 에이전트는 유휴 상태인 경우가 많았다.
- 대규모 모노레포가 아닌 멀티레포 환경이었음에도, 공유 라이브러리의 변경이 다운스트림 서비스의 빌드를 연쇄적으로 트리거하여 빌드 폭풍(build storm)이 발생하는 경우가 있었다.
1.3 해결
Backstage 내부 개발자 플랫폼 구축:
- Spotify는 자체 내부 개발자 플랫폼인 Backstage를 구축하였다. Backstage는 이후 CNCF에 기증되어 오픈소스 프로젝트로 성장하였다.
- Backstage의 핵심은 서비스 카탈로그(Service Catalog)로, 조직 내 모든 서비스의 메타데이터(소유 팀, 기술 스택, API 명세, CI/CD 상태 등)를 중앙에서 관리하는 것이다.
- CI/CD 파이프라인을 Backstage 플랫폼에 통합하여, 개발자가 단일 인터페이스에서 빌드 상태 확인, 배포 트리거, 로그 조회 등을 수행할 수 있도록 하였다.
Golden Path(권장 경로) 도입:
- “Golden Path”라는 개념을 도입하여, 각 프로그래밍 언어와 프레임워크별로 검증된 CI/CD 파이프라인 템플릿을 제공하였다.
- 예를 들어, Java Spring Boot 서비스를 생성하면 빌드, 테스트, 컨테이너 이미지 빌드, 배포까지의 전체 파이프라인이 자동으로 구성되었다.
- Golden Path는 강제가 아닌 권장 사항이었지만, 대부분의 팀이 이를 채택함으로써 자연스럽게 표준화가 이루어졌다.
- Golden Path를 벗어나는 팀에게는 자체 파이프라인 운영의 책임을 부여하되, 기본적인 가드레일(보안 스캔, 취약점 검사 등)은 필수로 적용되도록 하였다.
빌드 시스템의 아키텍처 전환:
- 컨테이너 기반의 분산 빌드 아키텍처로 재설계하여 수평 확장이 가능하도록 하였다.
- 빌드 실행 환경을 Stateless하게 설계하여, 빌드 노드의 추가/제거가 서비스 중단 없이 가능하도록 하였다.
- 빌드 메타데이터와 아티팩트를 외부 저장소(오브젝트 스토리지, 데이터베이스)에 저장하여, 빌드 실행 노드의 장애가 데이터 손실로 이어지지 않도록 하였다.
소프트웨어 템플릿과 셀프서비스:
- Backstage의 Software Template 기능을 통해, 개발자가 UI에서 몇 번의 클릭만으로 새로운 서비스를 생성하고, Git 레포지토리 생성, CI/CD 파이프라인 구성, 모니터링 대시보드 설정까지 자동으로 완료되도록 하였다.
- 이를 통해 새 서비스 온보딩 시간이 수일에서 수분으로 단축되었다.
1.4 주요 교훈
- Jenkins의 근본적 문제는 기술적 한계보다 운영 부담과 표준화 부재에 있다. Jenkins 자체는 유연한 도구이지만, 대규모 조직에서 그 유연성이 오히려 파편화와 운영 복잡성을 초래한다.
- 대규모 조직에서 CI/CD는 단순한 도구가 아닌 내부 플랫폼의 일부로 바라보아야 한다. Platform Engineering 관점에서 CI/CD를 설계하면, 개별 팀의 운영 부담을 줄이면서 전체 조직의 엔지니어링 효율성을 높일 수 있다.
- Golden Path 패턴은 강제적 표준화와 완전한 자율 사이의 균형점이다. 검증된 경로를 기본으로 제공하되 벗어날 자유를 허용하는 방식이 대규모 조직에서 효과적이다.
- 빌드 시스템은 반드시 Stateless하게 설계해야 한다. Stateful 아키텍처는 초기에는 간편하지만, 규모가 커지면 확장, 장애 복구, 업그레이드 모두에서 병목이 된다.
- 셀프서비스 방식의 개발자 경험(DevEx)이 CI/CD 플랫폼의 성공을 결정한다. 아무리 기술적으로 우수한 플랫폼이라도 개발자가 사용하기 어려우면 채택률이 낮아지고, 결국 섀도우 IT가 발생한다.
2. GitHub의 GitHub Actions 대규모 Self-hosted Runner 운영
- 출처: How GitHub Uses GitHub Actions and Actions Runner Controller
- 저자/출처: GitHub Engineering Team
- 플랫폼: GitHub Engineering Blog
2.1 상황
GitHub은 세계 최대의 코드 호스팅 플랫폼으로, 자사 서비스의 CI/CD를 GitHub Actions로 운영하고 있었다. GitHub은 “Dogfooding”(자사 제품을 자사가 직접 사용) 원칙에 따라 GitHub Actions를 가장 적극적으로 활용하는 사용자이기도 하였다.
GitHub의 내부 워크로드는 크게 세 가지로 분류되었다:
- 코드 빌드 및 테스트: GitHub.com 모노레포의 Ruby on Rails 애플리케이션 빌드, 수만 개의 테스트 실행
- 인프라 프로비저닝: Terraform, Ansible 등을 사용한 인프라 변경 작업
- 보안 민감 작업: 프로덕션 환경 배포, 시크릿 로테이션, 데이터베이스 마이그레이션 등 내부 네트워크 접근이 필수적인 작업
GitHub-hosted Runner(GitHub이 제공하는 관리형 VM)만으로는 이 세 가지 워크로드를 모두 처리할 수 없었다. 특히 내부 네트워크에 접근해야 하는 보안 민감 작업과, 대규모 빌드에 필요한 고사양 머신 요구사항을 충족하기 어려웠다. 이에 따라 Self-hosted Runner를 Kubernetes 클러스터에서 대규모로 운영하는 방안을 추진하였다.
2.2 문제
GitHub-hosted Runner의 한계:
- GitHub-hosted Runner는 공유 인프라에서 실행되므로, 내부 VPC나 프라이빗 네트워크에 접근할 수 없었다.
- 사전 설치된 도구와 런타임 환경이 고정되어 있어, 커스텀 도구나 특정 버전의 의존성이 필요한 빌드에 유연하게 대응할 수 없었다.
- 대규모 빌드에 필요한 고사양 머신(대용량 메모리, 고속 디스크)의 가용성이 제한적이었다.
VM 기반 Self-hosted Runner의 운영 문제:
- Self-hosted Runner를 VM으로 운영할 경우, VM의 프로비저닝과 시작에 수 분이 소요되어 빌드 대기 시간이 길어졌다.
- VM은 리소스의 정적 할당 방식이므로, 피크 타임에 맞춰 프로비저닝하면 평시에는 리소스 낭비가 발생하고, 평시에 맞추면 피크 타임에 빌드 큐가 적체되었다.
- VM의 라이프사이클 관리(패치, 업그레이드, 상태 모니터링)에 상당한 운영 인력이 소요되었다.
Runner 오염(Runner Contamination) 보안 문제:
- 동일한 Runner가 여러 워크플로우 Job을 순차적으로 실행할 때, 이전 Job의 아티팩트, 환경 변수, 캐시된 인증 토큰 등이 다음 Job의 실행 환경에 남아있는 문제가 발생하였다.
- 악의적인 PR을 통해 Runner에 악성 코드를 설치하면, 이후 동일 Runner에서 실행되는 모든 Job이 영향을 받을 수 있는 보안 위험이 있었다.
- 특히 오픈소스 프로젝트에서 외부 기여자의 PR에 대해 Self-hosted Runner를 사용하는 것은 심각한 보안 위협이었다.
대규모 Runner 관리의 복잡성:
- 수천 개의 Runner 인스턴스를 동시에 관리해야 하였으며, Runner의 등록/해제, 버전 업데이트, 상태 모니터링을 자동화하는 것이 기술적 과제였다.
- Runner의 라벨링과 Job 라우팅을 세밀하게 제어하여, 특정 워크로드가 적절한 사양의 Runner에서 실행되도록 보장해야 하였다.
2.3 해결
Actions Runner Controller(ARC) 도입:
- ARC는 Kubernetes Custom Resource Definition(CRD)으로 구현된 컨트롤러로, Runner를 Kubernetes Pod로 관리한다.
- Runner의 라이프사이클(생성, 실행, 삭제)을 Kubernetes 네이티브 방식으로 관리할 수 있어, Kubernetes의 성숙한 스케줄링과 리소스 관리 기능을 그대로 활용할 수 있었다.
- ARC는 GitHub API와 연동하여 대기 중인 워크플로우 Job을 감지하고, 필요한 Runner Pod를 자동으로 생성하는 Pull 기반 스케일링 방식을 사용하였다.
Ephemeral Runner를 통한 보안 격리:
- Ephemeral(일시적) Runner 모드를 활성화하여, 각 Job 실행이 완료되면 Runner Pod를 즉시 삭제하고 다음 Job에는 완전히 새로운 Pod를 생성하였다.
- 이를 통해 Runner 오염 문제를 근본적으로 해결하였다. 매 Job마다 깨끗한 실행 환경이 보장되므로, 이전 Job의 데이터가 다음 Job으로 유출될 가능성이 원천적으로 차단되었다.
- Ephemeral Runner는 Pod의 파일시스템, 네트워크 네임스페이스, 프로세스 네임스페이스가 완전히 격리되므로, VM 수준의 격리에 근접하는 보안성을 제공하였다.
자동 스케일링 구성:
- ARC의 RunnerSet 리소스를 통해 Runner의 최소/최대 수를 정의하고, Kubernetes HPA(Horizontal Pod Autoscaler)와 연동하여 대기 중인 워크플로우 수에 따라 Runner Pod를 자동으로 스케일링하였다.
- 스케일 업은 수 초 이내에 완료되어, VM 기반 대비 빌드 대기 시간이 크게 단축되었다.
- 스케일 다운 시에는 실행 중인 Job이 완료될 때까지 Graceful Shutdown을 보장하여, 빌드 중단으로 인한 데이터 손실을 방지하였다.
Runner 이미지 최적화:
- Runner 컨테이너 이미지를 용도별로 분리하여 최적화하였다. 범용 빌드 이미지, 테스트 전용 이미지, 배포 전용 이미지를 각각 관리하여 불필요한 도구 설치를 최소화하였다.
- 빈번하게 사용되는 도구와 의존성은 이미지에 사전 포함하여 Job 실행 시 설치 시간을 줄이되, 이미지 크기가 과도하게 커지지 않도록 레이어 캐싱 전략을 적용하였다.
- 이미지 빌드 파이프라인을 구축하여, 보안 패치가 발표되면 자동으로 Runner 이미지를 재빌드하고 배포하는 프로세스를 자동화하였다.
네트워크 및 시크릿 관리:
- Kubernetes NetworkPolicy를 사용하여 Runner Pod의 네트워크 접근 범위를 워크로드 유형에 따라 세밀하게 제어하였다.
- 빌드에 필요한 시크릿은 Kubernetes Secret 또는 외부 시크릿 관리 시스템과 연동하여 주입하되, Runner Pod의 라이프사이클과 함께 자동으로 생성/삭제되도록 구성하였다.
2.4 주요 교훈
- Self-hosted Runner 운영의 핵심은 보안 격리와 자동 스케일링이다. 이 두 가지를 동시에 달성하지 못하면, 보안을 위해 성능을 희생하거나 성능을 위해 보안을 타협하는 상황에 놓이게 된다.
- Ephemeral Runner는 Self-hosted Runner 보안의 기본 원칙이다. 장기 실행(persistent) Runner는 비용 효율적으로 보일 수 있으나, 보안 리스크가 크므로 프로덕션 환경에서는 반드시 Ephemeral 모드를 사용해야 한다.
- ARC를 사용하면 Kubernetes의 스케줄링, 리소스 관리, 네트워크 정책, 시크릿 관리 기능을 Runner 운영에 그대로 활용할 수 있다. Kubernetes를 이미 운영하고 있는 조직이라면 ARC 도입이 가장 효율적인 선택이다.
- Runner 이미지의 경량화와 캐시 전략이 빌드 성능에 직접적인 영향을 미친다. 이미지 Pull 시간이 빌드 대기 시간의 상당 부분을 차지할 수 있으므로, 이미지 크기 최적화와 로컬 캐시 활용이 중요하다.
- Runner의 라벨링과 Node Affinity를 활용한 세밀한 워크로드 라우팅이 리소스 효율성의 핵심이다. 모든 빌드를 동일한 사양의 Runner에서 실행하는 것은 비효율적이며, 워크로드 특성에 맞는 Runner Pool을 운영해야 한다.
3. Datadog의 CI/CD 파이프라인 보안 및 Supply Chain 강화
- 출처: How Datadog Secures Its Software Supply Chain
- 저자/출처: Datadog Security Engineering Team
- 플랫폼: Datadog Engineering Blog
3.1 상황
Datadog은 클라우드 기반 관측성(Observability) SaaS 플랫폼으로, 인프라 모니터링, APM(Application Performance Monitoring), 로그 관리, 보안 모니터링 등의 서비스를 제공하고 있다. 수백 개의 마이크로서비스로 구성된 대규모 분산 시스템을 운영하며, 하루에도 수십 회의 프로덕션 배포를 수행하고 있었다.
2020년 12월에 발생한 SolarWinds Orion 공급망 공격은 업계 전체에 큰 충격을 주었다. 이 사건에서 공격자는 SolarWinds의 빌드 시스템에 침투하여 정식 소프트웨어 업데이트에 백도어(SUNBURST)를 삽입하였고, 이를 통해 미국 정부 기관을 포함한 수만 개의 고객 조직이 피해를 입었다. 이후 Codecov 빌드 스크립트 변조(2021년), ua-parser-js 및 event-stream 등의 NPM 패키지 공급망 공격이 연이어 발생하면서, 소프트웨어 공급망 보안에 대한 경각심이 크게 높아졌다.
Datadog은 고객의 인프라 데이터를 처리하는 보안 민감 서비스를 운영하고 있었기 때문에, 자사 CI/CD 파이프라인의 보안을 체계적으로 강화할 필요성이 더욱 절실하였다. 만약 Datadog의 소프트웨어에 악성 코드가 삽입된다면, 수만 고객의 인프라에 직접적인 보안 위협이 될 수 있었다.
3.2 문제
CI/CD 파이프라인의 공격 표면:
- CI/CD 파이프라인은 소스 코드를 받아 실행 가능한 바이너리/이미지를 생성하고 프로덕션에 배포하는 전체 과정을 자동화한다. 이 과정의 어느 한 지점이라도 침해되면, 정식 배포 프로세스를 통해 악성 코드가 프로덕션에 도달할 수 있다.
- CI/CD 파이프라인은 소스 코드 저장소, 패키지 레지스트리, 컨테이너 이미지 레지스트리, 프로덕션 환경 등 주요 인프라에 대한 높은 권한을 보유하고 있어, 공격자에게 매력적인 타겟이었다.
서드파티 의존성 위험:
- 빌드 과정에서 사용하는 수많은 서드파티 의존성(오픈소스 라이브러리, 컨테이너 베이스 이미지, GitHub Actions, CI 플러그인 등)에 악성 코드가 삽입될 위험이 상존하였다.
- Dependency Confusion 공격(내부 패키지와 동일한 이름의 악성 패키지를 공개 레지스트리에 등록), Typosquatting(유사한 이름의 악성 패키지 등록), 유지보수 포기된 패키지의 계정 탈취 등 다양한 공격 벡터가 존재하였다.
- GitHub Actions의 서드파티 액션을 태그(v1, v2)로 참조할 경우, 액션 제작자가 태그를 악성 커밋으로 이동시키면 빌드 과정에서 임의의 코드가 실행될 수 있었다.
아티팩트 무결성 보장 부재:
- 빌드 과정에서 생성된 컨테이너 이미지가 레지스트리에 저장되고 프로덕션에 배포되기까지의 전 과정에서, 아티팩트가 변조되지 않았음을 검증하는 체계가 부재하였다.
- 컨테이너 이미지 레지스트리가 침해되거나, 이미지 Pull 과정에서 중간자 공격(MITM)이 발생할 경우, 변조된 이미지가 프로덕션에 배포될 수 있었다.
시크릿 유출 경로:
- CI 파이프라인에 주입된 시크릿(API 키, 데이터베이스 비밀번호, 인증 토큰 등)이 빌드 로그에 평문으로 출력되거나, 에러 메시지에 포함되어 노출될 수 있었다.
- 빌드 환경의 환경 변수로 시크릿을 주입하는 방식은, 빌드 과정에서 실행되는 모든 프로세스가 해당 시크릿에 접근할 수 있다는 문제가 있었다.
- 시크릿의 로테이션 주기가 길어, 유출 시 피해 범위가 넓었다.
3.3 해결
SLSA(Supply-chain Levels for Software Artifacts) 프레임워크 채택:
- Google이 주도하여 개발한 SLSA 프레임워크를 채택하여, 빌드 프로세스의 보안 수준을 단계적으로 강화하였다.
- SLSA는 Level 1부터 Level 4까지의 보안 성숙도 단계를 정의한다:
- Level 1: 빌드 프로세스의 문서화 (Provenance 생성)
- Level 2: 호스팅된 빌드 서비스 사용 (빌드 재현성)
- Level 3: 빌드 환경의 격리 및 변조 방지
- Level 4: 완전한 Hermetic 빌드 (외부 의존성 사전 선언, 빌드 중 네트워크 접근 차단)
- Datadog은 단계적으로 SLSA Level을 높여가며 빌드 프로세스의 보안을 강화하였다.
아티팩트 서명 및 검증 (Sigstore/Cosign):
- Sigstore 프로젝트의 Cosign 도구를 활용하여 빌드 아티팩트(컨테이너 이미지)에 대한 디지털 서명을 자동으로 수행하도록 CI 파이프라인에 통합하였다.
- 서명에 사용되는 키는 KMS(Key Management Service)에서 관리하여 키 유출 위험을 최소화하였다.
- 프로덕션 Kubernetes 클러스터에 Admission Controller(예: Kyverno 또는 OPA Gatekeeper)를 배포하여, 서명이 검증되지 않은 컨테이너 이미지의 배포를 자동으로 차단하였다.
- 이를 통해 빌드 파이프라인을 거치지 않은 이미지(수동 빌드, 변조된 이미지 등)가 프로덕션에 배포되는 것을 원천적으로 방지하였다.
SBOM(Software Bill of Materials) 자동 생성:
- 모든 빌드 아티팩트에 대해 SBOM을 자동으로 생성하여, 해당 아티팩트에 포함된 모든 의존성(라이브러리, 패키지, 베이스 이미지)의 목록과 버전을 기록하였다.
- SBOM은 SPDX 또는 CycloneDX 포맷으로 생성되어 업계 표준을 준수하였다.
- 새로운 CVE(공개 취약점)가 발표되면, SBOM을 기반으로 영향받는 서비스를 즉시 식별하고 패치 우선순위를 결정할 수 있었다. 이는 Log4Shell(CVE-2021-44228) 같은 광범위한 취약점 대응에서 특히 유용하였다.
의존성 보안 강화:
- 서드파티 의존성을 내부 미러링 레지스트리를 통해 관리하여, 외부 레지스트리의 가용성이나 보안 문제에 영향받지 않도록 하였다.
- GitHub Actions의 서드파티 액션을 태그 대신 특정 커밋 SHA로 핀닝(pinning)하여, 태그 변조를 통한 공격을 방지하였다.
- Dependabot 등의 자동화 도구를 활용하여 의존성의 취약점 스캔과 업데이트를 지속적으로 수행하였다.
시크릿 관리 중앙화:
- CI 파이프라인의 시크릿 관리를 HashiCorp Vault로 중앙화하였다.
- 시크릿의 TTL(Time-To-Live)을 짧게 설정(수 시간~수일)하여, 유출 시 자동으로 만료되도록 하였다.
- 동적 시크릿(Dynamic Secret) 기능을 활용하여, 빌드가 시작될 때 임시 자격 증명을 발급하고 빌드 완료 후 자동으로 폐기하는 방식을 도입하였다.
- 시크릿 접근 로그를 모니터링하여 비정상적인 접근 패턴(시간대 외 접근, 비정상적으로 높은 빈도, 미인가 IP에서의 접근 등)을 탐지하고 알림을 발생시켰다.
3.4 주요 교훈
- CI/CD 파이프라인은 소프트웨어 공급망의 핵심 구간이며, 보안이 취약하면 전체 시스템이 위험에 노출된다. SolarWinds 사례가 보여주듯, 빌드 시스템의 침해는 정식 배포 채널을 통해 악성 코드를 유포할 수 있어 탐지가 매우 어렵다.
- SLSA 프레임워크는 빌드 보안의 성숙도를 측정하고 개선하기 위한 체계적인 로드맵을 제공한다. 처음부터 Level 4를 목표로 하기보다, Level 1부터 단계적으로 구현하는 것이 현실적이다.
- 아티팩트 서명/검증과 SBOM은 현대 CI/CD 보안의 필수 요소이다. 서명이 검증되지 않은 이미지의 프로덕션 배포를 차단하는 것은 가장 효과적인 방어선 중 하나이다.
- 시크릿 관리는 환경변수 직접 주입 방식보다 Vault 같은 중앙 관리 시스템을 사용해야 한다. 최소 권한 원칙(Principle of Least Privilege), 짧은 TTL, 동적 시크릿을 조합하면 시크릿 유출의 영향 범위를 크게 줄일 수 있다.
- 서드파티 의존성은 CI/CD 보안의 가장 취약한 고리이다. 액션 SHA 핀닝, 내부 미러링, 자동 취약점 스캔을 통해 외부 의존성의 위험을 관리해야 한다.
4. Lyft의 Canary 배포와 자동 롤백 시스템 구축
- 출처: Confident Deployments with Automated Canary Analysis
- 저자/출처: Lyft Engineering Team
- 플랫폼: Lyft Engineering Blog (Medium)
4.1 상황
Lyft는 라이드셰어링 서비스로, 실시간 차량 매칭, 가격 산정, 결제 처리, 지도/경로 계산 등 수백 개의 마이크로서비스를 운영하고 있었다. 하루에도 수십 회의 프로덕션 배포가 수행되었으며, 서비스의 실시간성 특성상 배포 실패로 인한 장애가 사용자 경험과 매출에 즉각적인 영향을 미쳤다.
Lyft의 서비스 아키텍처는 Envoy Proxy를 중심으로 한 서비스 메시 구조였다(Envoy 프록시는 Lyft에서 개발하여 CNCF에 기증한 프로젝트이다). 마이크로서비스 간 통신은 Envoy를 통해 이루어졌으며, Envoy의 트래픽 관리 기능을 활용하여 세밀한 트래픽 라우팅이 가능한 환경이었다.
배포 빈도가 증가하면서, 모든 배포를 수동으로 모니터링하고 문제 발생 시 수동으로 롤백하는 기존 방식의 한계가 명확해졌다. 엔지니어들이 배포 후 대시보드를 지켜보며 이상 징후를 확인하는 데 상당한 시간을 소비하고 있었으며, 이는 개발 생산성의 저하로 이어졌다.
4.2 문제
수동 배포 모니터링의 비효율성:
- 기존 Rolling Update 방식에서는 새 버전의 Pod가 점진적으로 교체되지만, 교체 과정에서 문제가 발생해도 이를 자동으로 탐지하는 메커니즘이 없었다.
- 배포 후 엔지니어가 Grafana 대시보드에서 에러율, 레이턴시, 성공률 등의 메트릭을 수동으로 확인해야 하였다.
- 하루 수십 회의 배포가 이루어지는 환경에서, 모든 배포에 대해 엔지니어가 직접 모니터링하는 것은 현실적으로 불가능하였다.
문제 탐지의 지연:
- 배포 후 문제가 발생해도 메트릭에 반영되기까지 시간 지연(lag)이 있었으며, 미세한 성능 저하는 대시보드에서 육안으로 식별하기 어려웠다.
- 특히 P99 레이턴시의 미세한 증가나, 특정 API 엔드포인트에서만 발생하는 에러율 상승 같은 부분적인 문제는 전체 대시보드에서 쉽게 놓칠 수 있었다.
- 문제를 인지한 후에도 “이것이 배포로 인한 문제인가, 아니면 일시적인 변동인가?”를 판단하는 데 추가적인 시간이 소요되었다.
수동 롤백의 위험:
- 문제가 확인된 후 수동으로 롤백 명령을 실행하는 과정에서 추가적인 실수가 발생하는 경우가 있었다. 잘못된 버전으로 롤백하거나, 롤백 자체가 실패하는 상황이 있었다.
- 롤백을 실행할 수 있는 권한을 가진 엔지니어가 부재하거나(휴가, 퇴근 후), 의사 결정 과정에서 시간이 지체되는 경우가 있었다.
- MTTR(Mean Time To Recovery)이 길어지면서, 장애의 영향 범위가 확대되었다.
Canary 배포의 수동 운영 부담:
- Canary 배포를 수동으로 운영할 때, 엔지니어가 직접 Canary Pod를 배포하고, 트래픽 비율을 Envoy 설정을 통해 조절하고, 메트릭을 비교 분석하는 전 과정을 수행해야 하였다.
- 통계적으로 의미 있는 비교를 위해서는 충분한 양의 트래픽 데이터가 수집되어야 하며, 이 대기 시간 동안 엔지니어의 주의가 묶여 있었다.
- Canary 분석의 기준(어느 수준의 성능 저하를 허용할 것인가?)이 팀마다 다르게 적용되어 일관성이 없었다.
4.3 해결
자동화된 Canary 분석 시스템 구축:
- 전체 배포 프로세스를 자동화하는 Canary 분석 시스템을 구축하였다. 이 시스템은 Canary 배포, 메트릭 수집, 통계 분석, 자동 롤백/승격의 전 과정을 인간의 개입 없이 수행한다.
Baseline-Canary 비교 아키텍처:
- 단순히 “이전 버전 vs. 새 버전”을 비교하는 것이 아니라, 동일한 시점에 배포된 Baseline(현재 버전의 신규 인스턴스)과 Canary(새 버전)를 동시에 배포하여 비교하는 방식을 채택하였다.
- 이 접근 방식의 핵심은 시간에 따른 외부 변수(트래픽 패턴 변화, 외부 서비스 영향 등)를 통제하는 것이다. 동일 시점에 동일 조건에서 실행되는 두 버전을 비교하므로, 성능 차이가 코드 변경에 의한 것인지 외부 요인에 의한 것인지를 정확하게 판별할 수 있다.
- 전체 트래픽의 소량(5~10%)을 Canary와 Baseline에 균등하게 분배하였다. Envoy 프록시의 가중치 기반 라우팅 기능을 활용하여 트래픽 분배를 정밀하게 제어하였다.
주요 메트릭 수집 및 비교:
- 비교 대상 메트릭으로 에러율(5xx 응답 비율), P50/P90/P99 레이턴시, 요청 성공률, CPU/메모리 사용량 등을 설정하였다.
- 메트릭은 Prometheus에서 수집하고, 분석 시스템이 PromQL 쿼리를 통해 Baseline과 Canary의 메트릭을 동일한 시간 윈도우로 조회하였다.
- 각 메트릭에 대해 가중치를 설정하여, 주요 메트릭(에러율)의 이상이 부수적 메트릭(CPU 사용량)의 이상보다 높은 우선순위로 평가되도록 하였다.
통계적 분석 적용:
- Mann-Whitney U 검정(비모수적 통계 검정)을 사용하여, Canary의 성능이 Baseline 대비 통계적으로 유의미하게 나쁜지를 판별하였다.
- Mann-Whitney U 검정을 선택한 이유는 메트릭 분포가 정규 분포를 따르지 않는 경우(레이턴시 분포는 대부분 Long-tail 분포)에도 적용할 수 있기 때문이다.
- 유의 수준(significance level)과 최소 샘플 수를 설정하여, 통계적으로 신뢰할 수 있는 결론을 도출하도록 하였다. 불충분한 데이터로 잘못된 판단을 내리는 것을 방지하기 위해, 최소 분석 시간(예: 15분)을 설정하였다.
자동 롤백 및 Progressive Delivery:
- Canary 분석이 “실패”로 판정되면(통계적으로 유의미한 성능 저하가 감지되면), 즉시 Canary Pod를 삭제하고 트래픽을 기존 버전으로 복원하는 자동 롤백을 수행하였다.
- Canary 분석이 “성공”으로 판정되면, 트래픽 비율을 단계적으로 증가시키는 Progressive Delivery를 수행하였다. 예를 들어, 5% -> 20% -> 50% -> 100%의 단계로 트래픽을 확대하며, 각 단계마다 Canary 분석을 재수행하였다.
- 롤백 발생 시 Slack/PagerDuty를 통해 관련 팀에 자동으로 알림을 발송하여, 엔지니어가 원인을 분석할 수 있도록 하였다.
Canary 분석 설정의 표준화:
- 서비스 유형별로 Canary 분석 프로파일을 정의하여, 각 서비스가 적절한 메트릭과 임계치를 사용하도록 표준화하였다.
- 사용자 대면 API 서비스, 백그라운드 워커, 데이터 파이프라인 등 서비스 유형에 따라 중요한 메트릭과 허용 가능한 성능 편차가 다르기 때문에, 이를 프로파일로 관리하였다.
4.4 주요 교훈
- 프로덕션 배포의 안전성은 자동화된 Canary 분석과 자동 롤백에 의해 결정된다. 사람의 판단에 의존하는 수동 배포 모니터링은 배포 빈도가 높아지면 확장이 불가능하다.
- Baseline-Canary 동시 배포 방식이 단순한 “이전 vs. 신규” 비교보다 정확하다. 시간에 따른 외부 변수를 통제할 수 있으므로, 성능 차이의 원인을 정확하게 식별할 수 있다.
- 통계적 방법론(Mann-Whitney U 검정 등)을 적용한 자동 분석이 필수적이다. 단순 임계치 기반 비교(“에러율 > 5%이면 롤백”)는 트래픽 변동이나 일시적 이상치에 의한 오탐(False Positive)이 많다. 통계적 검정을 통해 실제로 의미 있는 성능 변화인지를 판별해야 한다.
- Progressive Delivery는 위험을 점진적으로 분산시킨다. 소량의 트래픽에서 시작하여 단계적으로 확대하면, 문제 발생 시 영향 범위를 최소화할 수 있다.
- Argo Rollouts이나 Flagger 같은 도구를 활용하면 Kubernetes 환경에서 Progressive Delivery를 선언적으로 구현할 수 있다. 이 도구들은 Canary, Blue-Green, A/B 테스팅 등 다양한 배포 전략을 CRD(Custom Resource Definition)로 정의하고 자동으로 실행한다.
- 메트릭 선정과 통계적 신뢰도 기준의 설정이 자동 Canary 분석 시스템의 성패를 결정한다. 잘못된 메트릭을 기준으로 분석하면 실제 문제를 놓치거나(False Negative), 정상 배포를 불필요하게 롤백(False Positive)하는 결과를 초래한다.
5. Coinbase의 CI/CD 파이프라인 최적화와 빌드 시간 단축
- 출처: How We Scaled CI/CD at Coinbase
- 저자/출처: Coinbase Infrastructure Team
- 플랫폼: Coinbase Engineering Blog
5.1 상황
Coinbase는 미국 최대의 암호화폐 거래소로, 보안이 극도로 중요한 금융 서비스의 CI/CD 파이프라인을 운영하고 있었다. 수십억 달러 규모의 디지털 자산을 관리하고 있었기 때문에, 코드 변경의 정확성과 안전성이 사업의 존속과 직결되는 환경이었다.
Coinbase는 모노레포(Monorepo) 구조를 채택하고 있었다. 모노레포는 모든 서비스와 라이브러리의 코드를 단일 Git 저장소에서 관리하는 방식으로, 코드 공유와 리팩토링이 용이하고, 의존성 버전을 일관되게 관리할 수 있다는 장점이 있다. Google, Meta, Microsoft 등 대규모 기술 기업들이 채택하는 방식이기도 하다.
그러나 코드베이스가 빠르게 성장하면서 CI 파이프라인의 빌드 시간과 리소스 소비가 급격히 증가하였다. 엔지니어 수가 늘고 서비스 수가 증가하면서, CI 파이프라인은 개발 생산성의 주요 병목으로 부상하였다.
5.2 문제
빌드 시간 폭증:
- 모노레포의 모든 Pull Request에 대해 전체 테스트 스위트를 실행하는 방식을 사용하고 있었다. 코드베이스가 성장하면서 테스트 수가 수만 개로 증가하였고, 전체 테스트 실행 시간이 40분 이상으로 늘어났다.
- 개발자가 PR을 제출한 후 CI 결과를 확인하기까지 40분 이상을 대기해야 하였으며, 한 번 실패하면 수정 후 다시 40분을 기다려야 하는 악순환이 반복되었다.
- 이로 인해 개발자들이 PR을 가능한 한 크게 만들어 CI 대기 횟수를 줄이려는 경향이 생겼고, 이는 코드 리뷰의 품질 저하와 통합 위험 증가로 이어졌다.
CI 리소스 과부하:
- CI 서버의 컴퓨팅 리소스(CPU, 메모리, 디스크 I/O) 사용량이 업무 시간대의 피크 시간에 최대치에 도달하여 빌드 큐가 적체되었다.
- 빌드 큐에 수십 개의 Job이 대기하면서, 실제 빌드 실행 시간보다 큐 대기 시간이 더 긴 경우가 빈번하였다.
- 피크 타임에 대응하기 위해 CI 리소스를 증설하면 비피크 시간대의 리소스 낭비가 심해지는 딜레마가 존재하였다.
Flaky Test 문제:
- 전체 테스트 중 일정 비율의 테스트가 간헐적으로 실패하는 Flaky Test였다. 이 테스트들은 코드 변경과 무관하게 비결정적(non-deterministic)으로 실패하여, CI 파이프라인의 신뢰도를 크게 떨어뜨렸다.
- 개발자들은 CI 실패가 자신의 코드 문제인지, Flaky Test에 의한 거짓 실패인지를 판단하기 위해 추가적인 시간을 소비해야 하였다.
- Flaky Test로 인한 거짓 실패가 반복되면서, “CI가 실패해도 머지해도 된다”는 잘못된 관행이 확산될 위험이 있었다.
컴퓨팅 비용 증가:
- 불필요한 테스트 실행(변경과 무관한 테스트)과 Flaky Test의 재실행으로 인한 컴퓨팅 비용이 월 수만 달러 규모로 증가하였다.
- 특히 금융 서비스 특성상 보안 스캔, 컴플라이언스 검증 등의 추가적인 CI 단계가 필요하여, 빌드 파이프라인의 총 비용이 일반 기술 기업 대비 높았다.
5.3 해결
변경 영향 분석(Change Impact Analysis) 시스템 구축:
- PR에서 변경된 파일과 해당 파일의 의존성 그래프(dependency graph)를 정적으로 분석하여, 변경에 의해 실제로 영향받는 서비스와 테스트만 식별하는 시스템을 구축하였다.
- 의존성 그래프는 소스 코드의 import/require 구문, 빌드 설정 파일(BUILD, Makefile 등), 서비스 간 API 의존성을 분석하여 자동으로 생성되었다.
- 예를 들어, 결제 서비스의 내부 로직만 변경한 PR에 대해서는 결제 서비스의 단위 테스트와 관련 통합 테스트만 실행하고, 무관한 사용자 인증 서비스나 시장 데이터 서비스의 테스트는 건너뛰도록 하였다.
- 이를 통해 평균 빌드 시간이 40분에서 약 10~15분으로 단축되었으며, 변경 범위가 작은 PR의 경우 수 분 이내에 CI가 완료되었다.
원격 빌드 캐시(Remote Build Cache) 도입:
- 빌드 아티팩트를 원격 캐시 서버에 저장하여, 동일한 입력(소스 코드, 의존성, 빌드 설정)에 대한 빌드를 반복하지 않고 캐시에서 재사용하도록 최적화하였다.
- 원격 캐시는 개별 개발자의 로컬 환경이 아닌 팀/조직 전체가 공유하는 방식이므로, 한 개발자가 빌드한 결과를 다른 개발자가 재사용할 수 있었다.
- 캐시 히트율을 모니터링하여 캐시의 효율성을 지속적으로 추적하고, 캐시 무효화(invalidation) 로직의 정확성을 검증하였다. 잘못된 캐시 히트는 오래된 아티팩트를 사용하게 되어 빌드 정합성 문제를 일으킬 수 있기 때문이다.
테스트 병렬 분산 실행:
- 대규모 테스트 스위트를 여러 CI 노드에 분산하여 병렬로 실행하는 시스템을 도입하였다.
- 테스트를 단순히 파일 수 기준으로 균등 분배하는 것이 아니라, 각 테스트의 과거 실행 시간 데이터를 기반으로 노드 간 실행 시간이 균등하도록 최적 분배(optimal partitioning)를 수행하였다.
- 이를 통해 병렬 실행의 효율성을 극대화하고, 가장 느린 노드에 의해 전체 실행 시간이 결정되는 “Straggler 문제”를 최소화하였다.
Flaky Test 자동 탐지 및 격리:
- 모든 테스트의 실행 이력을 데이터베이스에 기록하고, 통계적 분석을 통해 Flaky Test를 자동으로 식별하는 시스템을 구축하였다.
- Flaky Test로 판정된 테스트는 CI 파이프라인의 필수 통과 조건에서 제외(격리)하되, 별도의 Flaky Test 대시보드에서 관리하여 소유 팀에게 수정을 요청하였다.
- Flaky Test의 원인 분류(타이밍 이슈, 외부 서비스 의존성, 테스트 순서 의존성, 리소스 경합 등)를 자동으로 수행하여 수정 방향을 제시하였다.
- Flaky Test 비율을 엔지니어링 품질 지표로 관리하여, 비율이 일정 수준을 초과하면 팀에 알림을 발송하였다.
빌드 분석 대시보드 및 모니터링:
- CI 파이프라인의 주요 메트릭(빌드 시간, 성공률, 큐 대기 시간, 캐시 히트율, Flaky Test 비율 등)을 실시간으로 추적하는 대시보드를 구축하였다.
- 빌드 시간이 임계치를 초과하거나, 성공률이 급격히 하락하면 자동으로 알림을 발생시켜, CI 인프라 팀이 선제적으로 대응할 수 있도록 하였다.
5.4 주요 교훈
- 모노레포 환경에서 CI 파이프라인 최적화의 핵심은 변경 영향 분석을 통한 선택적 테스트 실행이다. 전체 테스트를 매번 실행하는 방식은 코드베이스 규모가 커지면 확장이 불가능하다. 의존성 그래프 기반의 영향 분석을 통해 변경과 관련된 테스트만 실행해야 한다.
- 원격 빌드 캐시는 빌드 시간 단축의 가장 효과적인 방법 중 하나이다. 동일한 입력에 대한 중복 빌드를 제거하여, 조직 전체의 컴퓨팅 리소스를 절약할 수 있다. 단, 캐시 무효화 로직의 정확성이 핵심이다.
- Flaky Test는 CI 파이프라인의 신뢰도를 가장 크게 훼손하는 요인이다. Flaky Test가 방치되면 “CI 실패를 무시해도 된다”는 문화가 형성되어, CI 파이프라인의 존재 의의가 사라진다. 자동 탐지와 격리 체계를 반드시 갖추어야 한다.
- 테스트 병렬화에서는 균등 분배보다 실행 시간 기반 최적 분배가 중요하다. 각 테스트의 실행 시간이 크게 다르기 때문에, 단순 균등 분배는 Straggler 문제를 초래한다.
- Bazel, Nx, Turborepo 같은 빌드 도구들은 의존성 그래프 분석, 영향 범위 계산, 원격 캐시 기능을 기본으로 제공한다. 모노레포를 운영하는 조직이라면 이러한 도구의 도입을 적극적으로 고려해야 한다.
- CI 파이프라인의 성능을 지속적으로 모니터링하고 최적화하는 것은 개발 생산성에 직접적인 영향을 미친다. CI 빌드 시간은 개발자 생산성의 선행 지표(Leading Indicator)이며, 빌드 시간이 길어지면 PR 크기 증가, 코드 리뷰 품질 저하, 통합 위험 증가의 악순환으로 이어진다.