마이크로서비스 아키텍처를 위한 CI/CD

Azure

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

CI/CD란?

CI/CD에 대해 논의할 때 실제로는 연속 통합, 지속적인 업데이트 및 지속적인 배포와 같은 여러 관련 프로세스에 대해 논의합니다.

  • 연속 통합. 코드 변경 내용이 기본 분기에 자주 병합됩니다. 자동화된 빌드 및 테스트 프로세스는 기본 분기의 코드가 항상 프로덕션 품질이 되도록 보장합니다.

  • 지속적인 업데이트. CI 프로세스를 거친 코드 변경은 프로덕션과 유사한 환경에 자동으로 게시됩니다. 라이브 프로덕션 환경으로 배포에는 수동 승인이 필요할 수 있지만 그렇지 않은 경우 자동화됩니다. 목표는 코드가 항상 프로덕션 환경으로 배포될 준비가 되어야 하는 것입니다.

  • 연속 배포. 이전 두 단계를 통과하는 코드 변경 내용은 프로덕션에 자동으로 배포됩니다.

마이크로 서비스 아키텍처에 대한 강력한 CI/CD 프로세스의 목표는 다음과 같습니다.

  • 각 팀은 다른 팀에 영향을 주거나 방해하지 않고 독립적으로 소유한 서비스를 빌드하여 배포할 수 있습니다.

  • 새 버전의 서비스를 프로덕션 환경에 배포하기 전에 개발/테스트/QA 환경에 배포하여 유효성을 검사합니다. 각 단계에서 품질 게이트를 적용합니다.

  • 새 버전의 서비스를 이전 버전과 함께 배포할 수 있습니다.

  • 충분한 액세스 제어 정책을 적용합니다.

  • 컨테이너화된 워크로드의 경우 프로덕션 환경에 배포된 컨테이너 이미지를 신뢰할 수 있습니다.

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

기존의 모놀리식 애플리케이션에는 출력이 애플리케이션 실행 파일인 단일 빌드 파이프라인이 있습니다. 모든 개발 작업은 이 파이프라인으로 피드합니다. 우선 순위가 높은 버그가 발견되는 경우 수정은 통합, 테스트 및 게시되어야 합니다. 이는 새로운 기능의 릴리스를 지연시킬 수 있습니다. 잘 구성된 모듈을 갖고 코드 변경 내용의 영향을 최소화하도록 기능 분기를 사용하여 이러한 문제를 완화할 수 있습니다. 하지만 애플리케이션 증가가 더욱 복잡해지고 더 많은 기능이 추가될수록 모놀리식에 대한 릴리스 프로세스는 더욱 불안정해지고 손상될 가능성이 많아지는 경향이 있습니다.

마이크로 서비스 기본 원칙을 따르면 모든 팀이 줄을 서야 하는 긴 릴리스 기차가 없어야 합니다. 서비스 "A"를 빌드하는 팀은 병합, 테스트 및 배포될 서비스 "B"의 변경 내용을 기다릴 필요 없이 언제든지 업데이트를 릴리스할 수 있습니다.

CI/CD 모놀리식의 다이어그램

높은 릴리스 속도를 달성하려면 위험을 최소화하기 위해 릴리스 파이프라인을 자동화하고 매우 안정적이어야 합니다. 매일 하나 이상의 프로덕션으로 릴리스하는 경우 회귀 또는 서비스 중단은 거의 발생하지 않아야 합니다. 동시에 잘못된 업데이트가 배포되는 경우 이전 버전의 서비스로 롤백하거나 롤포워드하는 신뢰할 수 있는 방법이 있어야 합니다.

과제

  • 다수의 작은 독립적인 코드 베이스 각 팀은 자체 빌드 파이프라인으로 자체 서비스 구축을 담당합니다. 일부 조직에서 팀은 별도 코드 리포지토리를 사용할 수 있습니다. 리포지토리가 별개로 존재하면 시스템을 구축하는 방법에 대한 정보가 팀 간에 분산되고 전체 애플리케이션을 배포하는 방법을 알고 있는 사람이 조직에서 아무도 없는 상황이 발생할 수 있습니다. 예를 들어 새 클러스터에 빠르게 배포해야 하는 경우 재해 복구 시나리오에서 무슨 일이 발생하나요?

    완화: 이 지식이 각 팀 내에서 "숨겨져" 있지 않도록 서비스를 빌드하고 배포하기 위한 통합되고 자동화된 파이프라인을 마련합니다.

  • 여러 언어 및 프레임워크 자체 기술 조합을 사용하는 각 팀과 함께 조직 전체에서 작동하는 단일 빌드 프로세스를 만드는 것은 어려울 수 있습니다. 빌드 프로세스는 모든 팀이 언어 또는 프레임워크의 선택으로 채택할 만큼 충분히 유연해야 합니다.

    완화: 각 서비스에 대한 빌드 프로세스를 컨테이너화합니다. 이렇게 하면 빌드 시스템에서 컨테이너를 실행할 수 있어야 합니다.

  • 통합 및 부하 테스트 팀의 페이스로 업데이트를 릴리스하는 팀과 함께 특히 서비스에 다른 서비스에 대한 종속성이 있는 경우 강력한 엔드투엔드 테스트를 디자인하는 것은 어려울 수 있습니다. 또한 전체 프로덕션 클러스터를 실행하는 것은 비용이 많이 들 수 있으므로 모든 팀이 단지 테스트를 위해 자체의 전체 클러스터를 프로덕션 규모로 실행할 가능성은 낮습니다.

  • 릴리스 관리 모든 팀은 프로덕션 환경에 업데이트를 배포할 수 있어야 합니다. 모든 팀 멤버가 그렇게 할 권한이 있음을 의미하는 것은 아닙니다. 하지만 중앙 집중식 릴리스 관리자 역할을 가지면 배포의 개발 속도를 줄일 수 있습니다.

    완화: CI/CD 프로세스가 더욱 자동화되고 안정적이 될수록 중앙 기관이 덜 필요합니다. 즉, 주요 기능 업데이트와 사소한 버그 수정 릴리스에 대해 다른 정책을 가질 수 있습니다. 탈중앙화가 된다는 것은 거버넌스가 없다는 것이 아닙니다.

  • 서비스 업데이트 서비스를 새 버전으로 업데이트하는 경우 종속된 다른 서비스를 중단하지 않아야 합니다.

    완화: 작업을 중단하지 않는 변경에 파란색-녹색 또는 카나리아 릴리스와 같은 배포 기술을 사용합니다. 작업을 중단하는 API 변경의 경우 새 버전을 이전 버전과 나란히 배포합니다. 이렇게 하면 이전 API를 사용하는 서비스를 업데이트하고 새 API에 대해 테스트할 수 있습니다. 아래 서비스 업데이트를 참조하세요.

단일 및 다중 리포지토리

CI/CD 워크플로를 만들기 전에 코드 베이스가 구조화되고 관리되는 방법을 알아야 합니다.

  • 팀이 별도의 리포지토리 또는 monorepo(단일 리포지토리)에서 작업합니까?
  • 분기 전략은 무엇입니까?
  • 프로덕션 환경에 코드를 푸시할 수 있는 사람은 누구입니까? 릴리스 관리자 역할이 있습니까?

단일 리포지토리 접근법이 지지를 받고 있지만 두 방식에는 모두 장단점이 있습니다.

  단일 리포지토리 다중 리포지토리
장점 코드 공유
코드 및 도구 표준화 용이
코드 리팩터링 용이
검색 기능 - 코드의 단일 보기
팀별로 명확한 소유권
잠재적으로 더 적은 병합 충돌
마이크로 서비스를 강제로 분리하는 데 유용
당면 과제 공유 코드에 대한 변경 사항이 여러 마이크로 서비스에 영향을 줄 수 있음
병합 충돌 가능성 증가
대규모 코드 베이스에 맞게 도구를 확장해야 함
Access Control
더 복잡한 배포 프로세스
코드를 공유하기가 더 어려움
코딩 표준을 적용하기가 더 어려움
종속성 관리
분산된 코드 베이스, 검색 기능 저하
공유 인프라 결여

서비스 업데이트

이미 프로덕션 환경에 있는 서비스를 업데이트하기 위한 다양한 전략이 있습니다. 여기에서는 세 가지 일반 옵션: 롤링 업데이트, 청록색 배포 및 카나리아 릴리스를 다룹니다.

롤링 업데이트

롤링 업데이트에서 서비스의 새 인스턴스를 배포하고 새 인스턴스는 즉시 요청 수신을 시작합니다. 새 인스턴스가 나타나면 이전 인스턴스가 제거됩니다.

예: Kubernetes에서 롤링 업데이트는 배포에 대해 Pod 사양을 업데이트할 때 기본 동작입니다. 배포 컨트롤러는 업데이트된 Pod에 대한 새 ReplicaSet를 만듭니다. 그런 다음 원하는 복제본 수를 유지하도록 기존 것을 축소하는 동안 새 ReplicaSet를 확장합니다. 새 것이 준비될 때까지 이전 Pod를 삭제하지 않습니다. Kubernetes는 업데이트의 기록을 유지하므로 필요한 경우 업데이트를 롤백할 수 있습니다.

예: Azure Service Fabric은 기본적으로 롤링 업데이트 전략을 사용합니다. 이 전략은 기존 API를 변경하지 않고 새 기능을 사용하여 서비스 버전을 배포하는 데 가장 적합합니다. Service Fabric은 애플리케이션 유형을 노드 또는 업데이트 도메인의 하위 집합으로 업데이트하여 업그레이드 배포를 시작합니다. 그런 다음, 모든 도메인이 업그레이드될 때까지 다음 업데이트 도메인으로 롤포워드합니다. 업그레이드 도메인을 업데이트하지 못하면 애플리케이션 유형이 모든 도메인에서 이전 버전으로 롤백됩니다. 여러 서비스가 있는 애플리케이션 유형(그리고 모든 서비스가 하나의 업그레이드 배포의 일부로 업데이트되는 경우)은 오류가 발생하기 쉽습니다. 한 서비스를 업데이트하지 못하면 전체 애플리케이션이 이전 버전으로 롤백되고 다른 서비스는 업데이트되지 않습니다.

롤링 업데이트의 한 가지 문제는 업데이트 프로세스 중 이전 버전과 새 버전의 혼합이 실행되고 트래픽을 수신한다는 것입니다. 이 기간 동안 모든 요청은 두 버전 중 하나로 라우팅될 수 있습니다.

작업을 중단하는 API 변경의 경우 이전 버전의 모든 클라이언트가 업데이트될 때까지 두 버전을 함께 지원하는 것이 좋습니다. API 버전 관리를 참조하세요.

청록색 배포

청록색 배포에서 이전 버전과 함께 새 버전을 배포합니다. 새 버전의 유효성을 검사한 후 이전 버전에서 새 버전으로 모든 트래픽을 한 번에 전환합니다. 전환 후 모든 문제에 대해 애플리케이션을 모니터링합니다. 문제가 발생하는 경우 이전 버전으로 바꿀 수 있습니다. 문제가 없는 경우 이전 버전을 삭제할 수 있습니다.

더 일반적인 모놀리식 또는 N 계층 애플리케이션으로 청록색 배포는 일반적으로 두 개의 동일한 환경 프로비전을 의미했습니다. 스테이징 환경에 새 버전을 배포한 다음, 스테이징 환경에 클라이언트 트래픽을 리디렉션합니다. 예를 들어 VIP 주소를 교환하여 리디렉션합니다. 마이크로 서비스 아키텍처에서 업데이트는 마이크로 서비스 수준에서 수행되므로 일반적으로 업데이트를 동일한 환경에 배포하고 서비스 검색 메커니즘을 사용하여 교환합니다.

예제. Kubernetes에서는 청록색 배포를 수행하기 위해 별도 클러스터를 프로비전할 필요가 없습니다. 대신 선택기를 활용할 수 있습니다. 새 Pod 사양 및 다른 집합의 레이블을 사용하여 새 배포 리소스를 만듭니다. 이전 배포를 삭제하거나 이를 가리키는 서비스를 수정하지 않고 이 배포를 만듭니다. 새 Pod가 실행되면 새 배포에 맞게 해당 서비스의 선택기를 업데이트할 수 있습니다.

파란색-녹색 배포한 가지 단점은 업데이트 중 서비스(현재 및 다음)에 대해 두 배 더 많은 Pod를 실행한다는 것입니다. Pod에 CPU 또는 메모리 리소스가 많이 필요한 경우 리소스 사용을 처리하기 위해 클러스터를 일시적으로 확장해야 합니다.

카나리아 릴리스

카나리아 릴리스에서는 업데이트된 버전을 적은 수의 클라이언트로 롤아웃합니다. 그런 다음 모든 클라이언트에 롤아웃하기 전에 새로운 서비스의 동작을 모니터링합니다. 이를 통해 제어된 방식으로 느린 출시를 수행하고, 모든 고객이 영향을 받기 전에 실제 데이터와 스폿 문제를 확인할 수 있습니다.

카나리아 릴리스는 요청을 서로 다른 버전의 서비스로 동적으로 라우팅해야 하기 때문에 청록색 또는 롤링 업데이트보다 관리하기가 복잡합니다.

예제. Kubernetes에서는 두 개의 복제본 세트(각 버전에 대해 하나씩)를 확장하고 복제본 수를 수동으로 조정하도록 서비스를 구성할 수 있습니다. 그러나 이 방법은 Kubernetes가 Pod에 대해 부하를 분산하는 방식으로 인해 정교하지 않습니다. 예를 들어 총 10개의 복제본이 있는 경우 10% 증분 단위로만 트래픽을 이동할 수 있습니다. 서비스 메시를 사용하는 경우 서비스 메시 라우팅 규칙을 사용하여 보다 정교한 카나리아 릴리스 전략을 구현할 수 있습니다.

다음 단계