파이프라인 난립

다섯 팀에게 CI/CD를 각자 구성하라고 하면 다섯 가지 완전히 다른 파이프라인이 만들어진다. 린팅 방식이 다르고, 테스트 전략이 다르고, 배포 절차가 다르다. 이것을 20개 팀으로 확장하면 아무도 전체를 파악하지 못하는 유지보수 지옥이 되는 것이다.

과연 자유가 최선인가? 그렇지 않다. A팀과 B팀이 서로 다른 방식으로 배포하다가 둘 다 프로덕션에서 장애를 일으키면, 원인을 파악하기 위해 팀마다 다른 구조를 먼저 이해해야 하는 상황이 발생한다. 자유에는 일관성이라는 대가가 따르는 셈이다.

Code vs Configuration

Pipeline as CodePipeline as Configuration
개념팀이 파이프라인 로직을 직접 작성공유 템플릿에 파라미터만 채움
유연성최대템플릿 옵션으로 제한
일관성낮음 -- 전부 제각각높음 -- 동일한 구조
유지보수각 팀이 개별 관리플랫폼 팀이 템플릿 관리
온보딩학습 곡선 높음빈칸만 채우면 됨

플랫폼 팀이 지향해야 하는 방향은 Configuration 쪽이다. 각 팀에게는 실제로 중요한 항목만 지정하게 하는 것이다. 사용하는 언어, 테스트 명령어, 배포 대상 정도가 그에 해당한다. 빌드 과정의 세부 사항이나 보안 스캔 같은 나머지 영역은 플랫폼이 일괄적으로 담당하여 일관성과 품질을 보장하는 것이다.

재사용 가능한 워크플로우

GitHub Actions의 재사용 워크플로우는 이러한 접근 방식의 구체적인 예시이다.

# .github/workflows/deploy.yml (각 팀 레포)
jobs:
  deploy:
    uses: org/platform-workflows/.github/workflows/standard-deploy.yml@v2
    with:
      service-name: my-api
      environment: production
    secrets: inherit

팀은 무엇을 배포할지만 지정하고, 빌드, 스캔, 테스트, 배포의 실제 실행은 플랫폼이 처리한다. 이 구조가 가지는 가장 큰 장점은 파이프라인을 한 번 개선하면 해당 템플릿을 사용하는 모든 팀이 자동으로 업데이트를 받는다는 점이다. 보안 취약점이 발견되어 스캔 단계를 추가해야 할 때에도, 중앙 템플릿 하나만 수정하면 조직 전체에 적용되는 것이다.

GitLab CI에서는 include와 CI/CD 컴포넌트를 통해 동일한 패턴을 구현할 수 있다. 도구는 다르지만 핵심 원칙은 같다. 로직은 중앙에서 관리하고, 설정만 각 팀에 분산하는 것이다.

환경 프로모션

커밋 --> 빌드 --> dev (자동) --> staging (자동) --> prod (수동 승인)
                                    |
                                    +-- 통합 테스트 실행

아티팩트는 한 번만 빌드하고, 동일한 이미지를 모든 환경에 배포하는 것이 원칙이다. 환경 간 차이는 환경 변수와 시크릿 같은 설정으로만 제한해야 한다. 환경에 따라 빌드 결과물이 달라지는 순간, "스테이징에서는 됐는데 프로덕션에서 안 된다"는 문제가 발생하기 때문이다. 이 원칙을 지키면 프로모션 과정에서의 신뢰도가 확보되어, 스테이징에서 통과한 아티팩트가 프로덕션에서도 동일하게 동작할 것이라는 확신을 가질 수 있게 되는 것이다.

배포 전략

배포 전략에는 대표적으로 세 가지가 있으며, 각각의 트레이드오프가 다르다.

롤링 업데이트는 인스턴스를 하나씩 순차적으로 교체하는 방식이다. 구현이 간단하지만 문제 발생 시 롤백이 느리다는 단점이 있다. 블루-그린은 동일한 환경 두 개를 유지하고 트래픽을 한 번에 전환하는 방식이다. 롤백이 빠르다는 장점이 있지만 동일한 인프라를 이중으로 유지해야 하므로 비용이 두 배로 든다. 카나리는 새 버전에 소량의 트래픽만 먼저 보내어 검증하는 방식이다. 가장 안전한 접근이지만, 소량의 트래픽에서 발생하는 문제를 감지할 수 있는 관측 가능성 인프라가 갖춰져 있어야 비로소 의미가 있는 것이다.

실제 운영에서는 롤링 업데이트로 시작하고, 관측 가능성이 충분히 갖춰진 이후에 카나리로 전환하는 것이 현실적인 경로이다.

파이프라인 속도

느린 파이프라인은 생산성을 직접적으로 떨어뜨린다. "어차피 CI가 잡아주겠지"라는 이유로 로컬 테스트를 건너뛰는 팀이 있었는데, 그 CI 파이프라인이 45분이나 걸렸다. 피드백까지 45분을 기다려야 하는 상황에서 개발자는 결국 파이프라인 결과를 무시하는 법을 배우게 되는 것이다. 빠른 피드백이 보장되지 않으면 CI/CD의 가치 자체가 훼손되는 셈이다.

파이프라인 속도를 확보하기 위해서는 의존성 캐싱, 테스트 병렬화, 변경된 부분만 빌드하는 전략이 필요하다. 린트와 유닛 테스트는 5분 이내에 완료되어야 하고, 시간이 오래 걸리는 통합 테스트는 머지 이후 단계로 분리하는 것이 바람직하다.

결국 최고의 파이프라인이란 개발자가 그 존재를 의식할 필요가 없는 파이프라인이다. 코드를 푸시하면 빌드와 테스트가 알아서 돌아가고, 결과가 빠르게 돌아오는 것이다. 파이프라인이 보이지 않을 때 비로소 제 역할을 하고 있는 셈이다.

다음 포스트에서는 관측 가능성을 다룬다.