CI/CD for microservices architectures

2025. 2. 26. 23:39Spring Microservice

더 빠른 릴리스 주기는 마이크로서비스 아키텍처의 주요 이점 중 하나입니다. 하지만 좋은 CI/CD 프로세스가 없으면 마이크로서비스가 약속하는 민첩성을 달성할 수 없습니다. 이 섹션에서는 문제점을 설명하고 문제에 대한 몇 가지 접근 방식을 권장합니다.

CI/CD란?

CI/CD는 지속적 통합(Continuous Integration), 지속적 전달(Continuous Delivery), 지속적 배포(Continuous Deployment) 를 포함하는 여러 관련 프로세스를 의미합니다.

📌 지속적 통합(Continuous Integration, CI)

  • 코드 변경 사항이 자주(main 브랜치에) 병합됨
  • 자동화된 빌드 및 테스트 프로세스를 통해 코드가 항상 프로덕션 품질을 유지

📌 지속적 전달(Continuous Delivery, CD)

  • CI를 통과한 코드 변경 사항이 자동으로 프로덕션과 유사한 환경에 배포됨
  • 실제 프로덕션 배포는 수동 승인(Manual Approval)을 요구할 수도 있음
  • 목표: 코드가 항상 프로덕션 배포 준비 상태를 유지하는 것

📌 지속적 배포(Continuous Deployment, CD)

  • 이전 두 단계를 통과한 코드 변경 사항이 자동으로 프로덕션 환경에 배포됨

📌 마이크로서비스 아키텍처에서 CI/CD의 목표

1️⃣ 각 팀이 독립적으로 서비스 빌드 및 배포 가능

  • 다른 팀에 영향을 주지 않고 자체적으로 서비스를 관리할 수 있어야 함.

2️⃣ 새로운 서비스 버전은 프로덕션 배포 전에 개발(dev), 테스트(test), QA 환경에서 검증됨

  • 각 단계에서 품질 게이트(Quality Gates)를 적용 하여 안정성을 확보.

3️⃣ 이전 버전과 새로운 버전이 동시에 배포될 수 있어야 함

  • 서비스 간의 하위 호환성을 유지할 수 있도록 고려해야 함.

4️⃣ 적절한 접근 제어 정책(Access Control Policy)이 마련되어 있어야 함

5️⃣ 컨테이너화된 워크로드(Containerized Workloads)의 경우, 프로덕션에 배포되는 컨테이너 이미지를 신뢰할 수 있어야 함

  • 보안 및 무결성을 유지하기 위해 서명된 이미지(Signed Images) 또는 신뢰할 수 있는 레지스트리(Trusted Registry)를 사용해야 함.

강력한 CI/CD 파이프라인이 중요한 이유

모놀리식 애플리케이션(Monolithic Application)에서의 문제점

  • 전통적인 모놀리식 애플리케이션 은 단일 빌드 파이프라인을 가집니다.
  • 모든 개발 작업이 이 하나의 빌드 파이프라인에 통합 됩니다.
  • 높은 우선순위의 버그가 발견되면,
    • 수정 사항을 통합하고, 테스트하고, 배포하는 과정에서 새로운 기능의 출시가 지연될 수 있습니다.
  • 이러한 문제를 완화하려면
    • 잘 분리된 모듈(Modularization)과 기능 브랜치(Feature Branches) 를 활용해야 합니다.
  • 하지만 애플리케이션이 커지고 기능이 많아질수록,
    • 모놀리식 아키텍처의 릴리스 프로세스는 점점 더 취약(Brittle)하고 깨지기 쉬운(Breaking) 상태가 됩니다.

마이크로서비스(Microservices)에서의 개선점

  • 마이크로서비스 철학에 따르면,
    • 하나의 긴 릴리스 과정(Release Train)에서 모든 팀이 줄을 서서 대기하는 상황이 없어야 합니다.
  • 예를 들어,
    • 서비스 "A"를 개발하는 팀은, 서비스 "B"의 변경 사항을 기다릴 필요 없이 독립적으로 업데이트를 배포할 수 있어야 합니다.

고속 릴리스를 위한 자동화된 파이프라인

  • 높은 릴리스 속도(High Release Velocity)를 달성하려면,
    • 릴리스 파이프라인이 자동화되고, 신뢰성이 높아야 합니다.
  • 하루에도 여러 번 프로덕션에 배포 하려면
    • 서비스 장애(Service Disruptions)나 회귀 버그(Regressions)가 거의 발생하지 않아야 합니다.
  • 잘못된 업데이트가 배포되더라도,
    • 빠르게 롤백(Rollback)하거나, 롤포워드(Roll Forward) 할 수 있는 신뢰할 수 있는 방법이 필요합니다.

도전 과제(Challenges)

1️⃣ 많은 독립적인 코드베이스 (Many small independent code bases)

  • 각 팀은 자신의 서비스 개발 및 빌드 파이프라인을 독립적으로 관리 합니다.
  • 일부 조직에서는 각 팀이 개별적인 코드 저장소(Repository)를 사용할 수도 있습니다.
  • 각 팀만이 서비스 빌드 및 배포 방법을 알고 있다면, 조직 전체에서 애플리케이션을 배포하는 방법을 명확히 알기 어려울 수 있습니다.
  • 예를 들어, 재해 복구(Disaster Recovery) 시나리오에서
    • 빠르게 새로운 클러스터에 배포해야 할 경우,
    • 조직 내 누구도 전체 애플리케이션을 배포하는 방법을 모를 위험이 있습니다.

해결책 (Mitigation):

  • 서비스를 빌드하고 배포하는 단일화된 자동화된 파이프라인을 구축 합니다.
  • 이를 통해 각 팀이 가진 지식이 숨겨지지 않고 공유될 수 있도록 합니다.

2️⃣ 다양한 언어 및 프레임워크 사용 (Multiple languages and frameworks)

  • 각 팀이 자신만의 기술 스택(Language & Framework)을 사용할 경우,
    • 조직 전체에서 단일 빌드 프로세스를 구성하는 것이 어려울 수 있습니다.
  • 빌드 프로세스는 모든 팀이 자신이 선택한 언어와 프레임워크에 맞게 조정할 수 있도록 유연해야 합니다.

해결책 (Mitigation):

  • 각 서비스의 빌드 프로세스를 컨테이너화(Containerize)합니다.
  • 빌드 시스템은 컨테이너 실행만 지원하면 되므로, 서비스별 언어나 프레임워크에 대한 의존성이 줄어듭니다.

3️⃣ 통합 및 부하 테스트 (Integration and Load Testing)

  • 각 팀이 독립적으로 업데이트를 배포 하면,
    • 서비스 간의 종속성(Dependencies) 때문에 엔드 투 엔드(End-to-End) 테스트를 설계하는 것이 어려울 수 있습니다.
  • 실제 프로덕션 환경과 동일한 규모의 클러스터에서 테스트하는 것은 비용이 많이 듭니다.
    • 따라서 각 팀이 개별적으로 프로덕션과 같은 규모의 테스트 환경을 운영하는 것은 비현실적일 수 있습니다.

4️⃣ 릴리스 관리 (Release Management)

  • 각 팀은 독립적으로 프로덕션 배포를 수행할 수 있어야 합니다.
  • 하지만 모든 팀원이 프로덕션 배포 권한을 가져야 하는 것은 아닙니다.
  • 중앙화된 릴리스 매니저(Release Manager) 역할이 배포 속도를 저하시킬 가능성이 있음.

해결책 (Mitigation):

  • CI/CD 프로세스를 최대한 자동화하고 신뢰성을 높이면, 중앙 승인 없이도 배포가 가능해집니다.
  • 단, 대형 기능 업데이트(Major Feature Updates)와 사소한 버그 수정(Minor Bug Fixes)에 대한 릴리스 정책을 다르게 운영할 수 있습니다.
  • 탈중앙화(Decentralization)는 곧 "무질서(Zero Governance)"를 의미하는 것이 아닙니다.

5️⃣ 서비스 업데이트 (Service Updates)

  • 새로운 버전의 서비스가 배포될 때, 기존 서비스의 기능을 깨뜨리지 않아야 합니다.

해결책 (Mitigation):

  • Blue-Green 배포 또는 Canary 배포 같은 배포 기법을 활용하여,
    • 기존 서비스가 중단되지 않는 방식으로 업데이트를 진행합니다.
  • API의 호환성을 깨뜨리는 변경(Breaking API Changes) 가 필요한 경우:
    • 기존 API 버전과 새로운 API 버전을 나란히 배포(Side-by-Side Deployment)하여
    • 기존 API를 사용하는 서비스가 새로운 API로 안전하게 전환할 수 있도록 합니다.
  • 자세한 내용은 아래 "Updating Services" 섹션에서 다룹니다.

모노레포(Monorepo) vs. 멀티레포(Multi-repo)

CI/CD 워크플로우를 생성하기 전에, 코드베이스(Codebase)의 구조와 관리 방식 을 결정해야 합니다.

고려할 질문

  • 팀별로 각각의 코드 저장소(Repository)를 사용할 것인가, 아니면 단일 저장소(Monorepo)를 사용할 것인가?
  • 브랜치 전략(Branching Strategy)은 어떻게 할 것인가?
  • 누가 프로덕션에 코드를 푸시할 수 있는가? 릴리스 매니저(Release Manager) 역할이 필요한가?

최근 모노레포(Monorepo) 방식이 인기를 얻고 있지만, 두 방식 모두 장점과 단점이 존재 합니다.

🔹 모노레포(Monorepo) vs. 멀티레포(Multi-repo) 비교

  모노레포 (Monorepo) 멀티레포 (Multiple Repos)
✅ 장점(Advantages) 코드 공유(Code Sharing) 용이 팀별 코드 소유권 명확 (Clear ownership per team)
  코드 표준화(Standardization) 및 도구 통합 용이 병합 충돌(Merge Conflicts) 발생 가능성 감소
  리팩토링(Refactoring) 쉬움 마이크로서비스 간 결합도(Decoupling) 유지 용이
  코드 탐색성(Discoverability) 증가 - 코드 전체를 한눈에 볼 수 있음  
⚠️ 도전 과제(Challenges) 공유 코드 변경이 여러 마이크로서비스에 영향을 줄 수 있음 코드 공유(Dependency Management) 어려움
  병합 충돌 가능성 증가 코딩 표준(Coding Standards) 강제 적용 어려움
  대규모 코드베이스에서 도구(빌드/테스트) 확장 필요 분산된 코드베이스(Diffuse Code Base)로 인해 코드 탐색 어려움
  액세스 제어(Access Control) 복잡 공유 인프라(Shared Infrastructure) 부족
  배포 프로세스(Deployment Process) 더 복잡할 수 있음  

🔹 선택 기준

  • 서비스 간 코드 공유가 많다면?모노레포(Monorepo) 추천
  • 마이크로서비스 간 독립성이 중요하다면?멀티레포(Multi-repo) 추천
  • 조직 내 표준화 및 도구 일관성이 필요하다면?모노레포(Monorepo) 추천
  • 팀별 독립적인 배포를 원한다면?멀티레포(Multi-repo) 추천

서비스 업데이트(Updating Services)

운영 중인 서비스의 업데이트에는 여러 가지 전략이 있습니다.
여기서는 롤링 업데이트(Rolling Update), 블루-그린 배포(Blue-Green Deployment), 카나리 릴리스(Canary Release) 세 가지 일반적인 방법을 설명합니다.

1️⃣ 롤링 업데이트 (Rolling Updates)

  • 새로운 인스턴스를 배포하면 즉시 요청을 받기 시작 합니다.
  • 기존 인스턴스가 제거되면서 점진적으로 새 버전으로 교체됩니다.

✅ 예시

📌 Kubernetes

  • 기본적으로 롤링 업데이트 방식을 사용합니다.
  • 새로운 ReplicaSet 을 생성한 후,
    • 새로운 인스턴스를 점진적으로 추가하고, 기존 인스턴스를 점진적으로 제거 합니다.
  • 기존 Pod을 즉시 삭제하지 않고, 새 Pod이 준비될 때까지 유지 합니다.
  • 업데이트 히스토리를 저장 하므로, 필요하면 롤백할 수도 있습니다.

📌 Azure Service Fabric

  • 기본적으로 롤링 업데이트 전략을 사용합니다.
  • 업그레이드 도메인(Update Domain) 단위로 점진적으로 업데이트 진행
  • 만약 특정 업그레이드 도메인에서 실패하면,
    • 이전 버전으로 롤백됨
  • 애플리케이션 유형(Application Type) 내에서 여러 서비스가 동시에 업데이트될 경우,
    • 한 서비스라도 실패하면 전체 애플리케이션이 이전 버전으로 롤백됨

⚠️ 롤링 업데이트의 단점

  • 업데이트 중에 구버전과 신버전이 혼합되어 실행 됩니다.
  • 이로 인해 같은 요청이 구버전과 신버전 중 어디로 갈지 예측하기 어려움
  • API가 변경(Breaking API Change)되는 경우,
    • 기존 API와 새로운 API를 동시에 지원해야 함
    • → 자세한 내용은 API 버전 관리(API Versioning) 참고

2️⃣ 블루-그린 배포 (Blue-Green Deployment)

  • 신규 버전을 기존 버전과 나란히 배포
  • 신규 버전이 정상적으로 실행되는지 검증 후, 트래픽을 한 번에 전환(Switch)
  • 문제가 발생하면 즉시 기존 버전으로 롤백 가능

✅ 예시

📌 전통적인 모놀리식 또는 N-Tier 애플리케이션에서의 블루-그린 배포

  • 동일한 환경을 두 개(BLUE, GREEN) 운영
  • 새로운 버전을 스테이징 환경에 배포한 후, VIP 주소를 스와핑하여 트래픽을 전환

📌 Kubernetes에서의 블루-그린 배포

  • 별도의 클러스터를 생성하지 않고, 라벨(Labels)과 셀렉터(Selectors)를 활용
  • 새로운 Deployment를 생성하고, 기존 Deployment는 유지
  • 새 버전의 Pod이 준비되면, 서비스의 셀렉터를 새 Deployment로 업데이트하여 트래픽을 전환

⚠️ 블루-그린 배포의 단점

  • 배포 중에 두 배의 리소스를 사용
  • CPU 및 메모리 사용량이 높은 애플리케이션의 경우,
    • 일시적으로 클러스터를 확장(Scale Out)해야 할 수도 있음

3️⃣ 카나리 릴리스 (Canary Release)

  • 소수의 사용자(일부 트래픽)에게 먼저 새로운 버전을 제공
  • 신규 버전의 동작을 관찰한 후, 이상이 없으면 전체 트래픽으로 확대
  • 실제 트래픽 데이터를 기반으로 업데이트 안정성을 검증 가능

✅ 예시

📌 Kubernetes에서 카나리 릴리스 적용 방법

  • 두 개의 ReplicaSet을 생성 (각각 구버전, 신버전용)
  • Replica 개수를 조정하여 트래픽을 점진적으로 신버전으로 이동
  • Kubernetes의 기본 로드 밸런싱 방식으로 인해,
    • 트래픽 이동 비율을 10% 단위로 조정 가능

📌 서비스 메시(Service Mesh) 기반 카나리 릴리스

  • Istio 같은 서비스 메시를 활용하면 더 정교한 트래픽 제어 가능
  • 특정 사용자 그룹(예: 내부 사용자)에게만 신버전을 먼저 배포하는 방식 도 가능

⚠️ 카나리 릴리스의 단점

  • 트래픽을 동적으로 신구 버전으로 라우팅해야 하므로 관리가 복잡
  • 로드 밸런서 설정 및 서비스 메시 라우팅 규칙이 필요

📌 각 업데이트 방식 비교

배포 방식 트래픽 전환 방식 롤백 가능 여부 리소스 사용량 주요 단점
롤링 업데이트 점진적으로 트래픽 이동 가능 낮음 업데이트 중 구버전과 신버전이 혼합됨
블루-그린 배포 트래픽을 한 번에 전환 가능 높음 (일시적으로 2배) 추가 리소스 필요
카나리 릴리스 점진적으로 특정 사용자 그룹에 배포 가능 중간 라우팅 관리 복잡

롤링 업데이트API 변경이 없는 일반적인 업데이트에 적합
블루-그린 배포안정성이 중요한 경우, 즉시 롤백이 필요할 때 적합
카나리 릴리스대규모 트래픽을 대상으로 안정성을 점검해야 할 때 적합

 

출처 : https://learn.microsoft.com/en-us/azure/architecture/microservices/ci-cd