마이크로 서비스 아키텍처를 위한 신뢰할 수 있는 CI/CD(연속 통합/지속적인 업데이트) 프로세스를 만드는 것은 어려울 수 있습니다. 개별 팀은 다른 팀을 방해하거나 애플리케이션 전체를 불안정하게 하지 않고 서비스를 빠르고 안정적으로 릴리스할 수 있어야 합니다.
이 아티클에서는 AKS(Azure Kubernetes Service)에 마이크로 서비스를 배포하기 위한 예제 CI/CD 파이프라인에 대해 설명합니다. 모든 팀과 프로젝트가 다르므로 이 아티클을 일련의 엄격한 규칙으로 간주하지 마세요. 대신 고유한 CI/CD 프로세스를 디자인하기 위한 시작점이 됩니다.
Kubernetes 호스팅 마이크로 서비스를 위한 CI/CD 파이프라인의 목표는 다음과 같이 요약할 수 있습니다.
- 팀은 서비스를 독립적으로 빌드하고 배포할 수 있습니다.
- CI 프로세스를 거친 코드 변경은 프로덕션과 유사한 환경에 자동으로 배포됩니다.
- 파이프라인의 각 단계에서 품질 게이트를 적용합니다.
- 새 버전의 서비스를 이전 버전과 함께 배포할 수 있습니다.
자세한 배경 정보는 마이크로 서비스 아키텍처를 위한 CI/CD를 참조하세요.
가정
이 예제의 목적을 위한 개발 팀과 코드 베이스에 관한 몇 가지 가정은 다음과 같습니다.
- 코드 리포지토리는 마이크로 서비스별로 정리된 폴더를 사용하는 단일 리포지토리입니다.
- 팀의 분기 전략이 트렁크 기반 개발을 기반으로 합니다.
- 팀은 릴리스 분기를 사용하여 릴리스를 관리합니다. 각 마이크로 서비스를 위한 별도의 릴리스가 만들어집니다.
- CI/CD 프로세스는 Azure Pipelines를 사용하여 마이크로 서비스를 빌드 및 테스트하고 AKS에 배포합니다.
- 각 마이크로 서비스를 위한 컨테이너 이미지는 Azure Container Registry에 저장됩니다.
- 팀은 Helm 차트를 사용하여 각 마이크로 서비스를 패키지합니다.
- 푸시 배포 모델이 사용됩니다. 여기서 Azure Pipelines 및 관련 에이전트는 AKS 클러스터에 직접 연결하여 배포를 수행합니다.
이러한 가정에는 CI/CD 파이프라인에 대한 많은 구체적 세부 정보가 관련될 수 있습니다. 그러나 여기에 설명된 기본 접근 방식은 Jenkins 또는 Docker Hub와 같은 다른 프로세스, 도구 및 서비스에 맞게 조정됩니다.
대안
다음은 고객이 Azure Kubernetes Service를 통해 CI/CD 전략을 선택할 때 사용할 수 있는 일반적인 대안입니다.
- Helm을 패키지 관리 및 배포 도구로 사용하는 대신 Kustomize는 애플리케이션 구성을 사용자 지정하고 매개 변수화하는 템플릿 없는 방법을 도입하는 Kubernetes 네이티브 구성 관리 도구입니다.
- Git 리포지토리 및 파이프라인에 Azure DevOps를 사용하는 대신 GitHub 리포지토리를 프라이빗 및 퍼블릭 Git 리포지토리에 사용할 수 있으며 CI/CD 파이프라인에 GitHub Actions를 사용할 수 있습니다.
- 푸시 배포 모델을 사용하는 대신, GitOps(풀 배포 모델)를 사용하여 Kubernetes 구성을 대규모로 관리할 수 있습니다. 여기서 클러스터 내 Kubernetes 연산자는 Git 리포지토리에 저장된 구성에 따라 클러스터 상태를 동기화합니다.
유효성 검사 빌드
개발자가 Delivery Service라는 마이크로 서비스를 작업 중이라고 가정하세요. 개발자는 새 기능을 개발할 때 기능 분기에 코드를 체크 인합니다. 관례상 기능 분기는 feature/*
로 명명합니다.
빌드 정의 파일에는 분기 이름과 원본 경로에 의해 필터링되는 트리거가 포함되어 있습니다.
trigger:
batch: true
branches:
include:
# for new release to production: release flow strategy
- release/delivery/v*
- refs/release/delivery/v*
- master
- feature/delivery/*
- topic/delivery/*
paths:
include:
- /src/shipping/delivery/
각 팀은 이러한 접근법을 사용하여 자체 빌드 파이프라인을 구축할 수 있습니다. /src/shipping/delivery
폴더에 체크 인된 코드만 Delivery Service의 빌드를 트리거합니다. 필터와 일치하는 분기에 커밋을 푸시하면 CI 빌드가 트리거됩니다. 워크플로의 이 시점에서 CI 빌드는 최소한의 코드 확인을 실행합니다.
- 코드를 빌드합니다.
- 단위 테스트를 실행합니다.
개발자가 신속한 피드백을 얻을 수 있도록 빌드 시간을 짧게 유지하자는 것이 목표입니다. 기능을 마스터에 병합할 준비가 되면 개발자는 PR을 엽니다. 이 작업이 일부 추가 검사를 수행하는 다른 CI 빌드를 트리거합니다.
- 코드를 빌드합니다.
- 단위 테스트를 실행합니다.
- 런타임 컨테이너 이미지를 빌드합니다.
- 이미지에 대한 취약성 검사를 실행합니다.
참고 항목
Azure DevOps Repos에서 분기를 보호하는 정책을 정의할 수 있습니다. 예를 들어 마스터에 병합하려면 정책에 승인자의 서명과 성공적인 CI 빌드가 필요할 수 있습니다.
전체 CI/CD 빌드
어느 시점이 되면 팀은 새 버전의 Delivery Service를 배포할 수 있습니다. 릴리스 관리자는 release/<microservice name>/<semver>
이름 지정 패턴을 사용하여 메인 분기에서 분기를 만듭니다. 예들 들어 release/delivery/v1.0.2
입니다.
이 분기를 만들면 이전 단계와 다음을 모두 실행하는 전체 CI 빌드가 트리거됩니다.
- 컨테이너 이미지를 Azure Container Registry에 푸시합니다. 분기 이름에서 가져온 버전 번호로 이미지에 태그가 지정됩니다.
helm package
를 실행하여 서비스를 위한 Helm 차트를 패키지합니다. 또한, 차트에는 버전 번호로 태그가 지정됩니다.- Container Registry에 Helm 패키지를 푸시합니다.
이 빌드가 성공할 경우 Azure Pipelines 릴리스 파이프라인을 사용하여 배포 (CD) 프로세스가 트리거됩니다. 이 파이프라인에는 다음 단계가 있습니다.
- QA 환경에 Helm 차트를 배포합니다.
- 승인자가 서명한 후 패키지가 프로덕션 단계로 이동합니다. 승인을 통해 릴리스 배포 제어를 참조하세요.
- Azure Container Registry에서 프로덕션 네임스페이스용 Docker 이미지에 다시 태그를 지정합니다. 예를 들어 현재 태그가
myrepo.azurecr.io/delivery:v1.0.2
라면 프로덕션 태그는myrepo.azurecr.io/prod/delivery:v1.0.2
입니다. - 프로덕션 환경에 Helm 차트를 배포합니다.
단일 리포지토리에서도 이러한 작업의 범위를 개별 마이크로 서비스로 지정해 팀이 빠른 개발속도로 배포하도록 할 수 있습니다. 프로세스에는 PR 승인, 릴리스 분기 만들기 및 프로덕션 클러스터에 대한 배포 승인과 같은 몇 가지 수동 단계가 있습니다. 이러한 단계는 수동으로 실행합니다. 조직에서 선호하는 경우 이러한 단계를 자동화할 수 있습니다.
환경 격리
개발, 스모크 테스트, 통합 테스트, 부하 테스트 및 최종적으로 프로덕션을 위한 환경을 포함한 여러 환경에 서비스를 배포하게 될 것입니다. 이러한 환경에는 일정 수준의 격리가 필요합니다. Kubernetes에서는 물리적 격리와 논리적 격리 중에서 선택할 수 있습니다. 물리적 격리는 별도의 클러스터에 배포하는 것을 의미합니다. 논리적 격리는 앞에서 설명한 것처럼 네임스페이스와 정책을 사용합니다.
개발/테스트 환경에 사용할 별도의 클러스터와 함께 전용 프로덕션 클러스터를 만드는 방법을 권장합니다. 논리적 격리를 사용하여 개발/테스트 클러스터 내에서 별도의 환경을 격리하세요. 개발/테스트 클러스터에 배포된 서비스는 비즈니스 데이터를 보관하는 데이터 저장소에 절대로 액세스하면 안 됩니다.
빌드 프로세스
가능하면 빌드 프로세스를 Docker 컨테이너에 패키지합니다. 이 구성을 사용하면 각 빌드 머신에 빌드 환경을 구성하지 않고도 Docker를 사용하여 코드 아티팩트를 빌드할 수 있습니다. 컨테이너화된 빌드 프로세스를 사용하면 새 빌드 에이전트를 추가하여 CI 파이프라인을 쉽게 스케일 아웃할 수 있습니다. 또한, 팀의 모든 개발자는 빌드 컨테이너를 실행하여 코드를 빌드할 수 있습니다.
Docker에서 다단계 빌드를 사용하면 단일 Dockerfile에서 빌드 환경 및 런타임 이미지를 정의할 수 있습니다. 예를 들어 다음은 .NET 애플리케이션을 빌드하는 Dockerfile입니다.
FROM mcr.microsoft.com/dotnet/core/runtime:3.1 AS base
WORKDIR /app
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
WORKDIR /src/Fabrikam.Workflow.Service
COPY Fabrikam.Workflow.Service/Fabrikam.Workflow.Service.csproj .
RUN dotnet restore Fabrikam.Workflow.Service.csproj
COPY Fabrikam.Workflow.Service/. .
RUN dotnet build Fabrikam.Workflow.Service.csproj -c release -o /app --no-restore
FROM build AS testrunner
WORKDIR /src/tests
COPY Fabrikam.Workflow.Service.Tests/*.csproj .
RUN dotnet restore Fabrikam.Workflow.Service.Tests.csproj
COPY Fabrikam.Workflow.Service.Tests/. .
ENTRYPOINT ["dotnet", "test", "--logger:trx"]
FROM build AS publish
RUN dotnet publish Fabrikam.Workflow.Service.csproj -c Release -o /app
FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "Fabrikam.Workflow.Service.dll"]
이 Dockerfile은 여러 빌드 단계를 정의합니다. base
로 불리는 스테이지는 .NET 런타임을 사용하고 build
라는 스테이지는 전체 .NET SDK를 사용합니다. build
스테이지는 .NET 프로젝트를 빌드하는 데 사용됩니다. 그러나 최종 런타임 컨테이너는 런타임만 포함하고 전체 SDK 이미지보다 훨씬 작은 base
에서 빌드됩니다.
테스트 실행기 빌드
또 다른 좋은 방법은 컨테이너에서 단위 테스트를 실행하는 것입니다. 예를 들어 테스트 실행기를 빌드하는 Docker 파일의 일부는 다음과 같습니다.
FROM build AS testrunner
WORKDIR /src/tests
COPY Fabrikam.Workflow.Service.Tests/*.csproj .
RUN dotnet restore Fabrikam.Workflow.Service.Tests.csproj
COPY Fabrikam.Workflow.Service.Tests/. .
ENTRYPOINT ["dotnet", "test", "--logger:trx"]
개발자는 이 Docker 파일을 사용하여 테스트를 로컬로 실행할 수 있습니다.
docker build . -t delivery-test:1 --target=testrunner
docker run delivery-test:1
또한, CI 파이프라인은 빌드 확인 단계의 일부로 테스트를 실행해야 합니다.
이 파일은 Docker RUN
명령이 아닌 Docker ENTRYPOINT
명령을 사용하여 테스트를 실행한다는 점에 유의하세요.
RUN
명령을 사용하는 경우 이미지를 빌드할 때마다 테스트가 실행됩니다.ENTRYPOINT
를 사용하면 테스트가 옵트인됩니다.testrunner
스테이지를 명시적으로 대상으로 지정하는 경우에만 실행됩니다.- 테스트에 실패해도 Docker
build
명령이 실패하지는 않습니다. 이 방법으로 컨테이너 빌드 실패와 테스트 실패를 구분할 수 있습니다. - 테스트 결과를 탑재된 볼륨에 저장할 수 있습니다.
컨테이너 모범 사례
다음은 컨테이너에 대해 고려해야 할 몇 가지 다른 모범 사례입니다.
컨테이너 태그, 버전 관리에 대한 조직 전반의 규칙 및 클러스터(포드, 서비스 등)에 배포되는 리소스에 대한 명명 규칙을 정의합니다. 배포 문제를 진단하기 쉽게 만들 수 있습니다.
개발 및 테스트 주기 동안 CI/CD 프로세스는 많은 컨테이너 이미지를 빌드합니다. 이 이미지들 중 일부만이 릴리스 후보이며 해당 릴리스 후보 중 일부만 프로덕션으로 푸시됩니다. 현재 프로덕션에 배포되는 이미지를 알고 필요한 경우 이전 버전으로 롤백할 수 있도록 명확한 버전 관리 전략이 있어야 합니다.
항상
latest
가 아닌 특정 컨테이너 버전 태그를 배포합니다.Azure Container Registry에서 네임스페이스를 사용하여 아직 테스트 중인 이미지에서 프로덕션용으로 승인된 이미지를 분리합니다. 프로덕션에 배포할 준비가 될 때까지 이미지를 프로덕션 레지스트리로 이동하지 마세요. 이 연습을 컨테이너 이미지의 의미 체계 버전 관리에 결합하면 릴리스에 대해 승인되지 않은 버전을 실수로 배포하는 가능성을 줄일 수 있습니다.
권한 없는 사용자로 컨테이너를 실행하여 최소 권한 원칙을 따릅니다. Kubernetes에서 컨테이너가 루트로 실행되지 않도록 하는 Pod 보안 정책을 만들 수 있습니다.
Helm 차트
Helm을 사용하여 서비스를 빌드하고 배포하는 방안을 고려해 보세요. 다음은 CI/CD에 도움이 되는 Helm의 몇 가지 기능입니다.
- 단일 마이크로 서비스는 종종 여러 Kubernetes 개체에 의해 정의됩니다. Helm을 사용하면 이러한 개체를 단일 Helm 차트로 패키지할 수 있습니다.
- 일련의 kubectl 명령이 아닌 단일 Helm 명령으로 차트를 배포할 수 있습니다.
- 차트의 버전이 명시적으로 지정됩니다. Helm을 사용하여 버전을 릴리스하고, 릴리스를 보고, 이전 버전으로 롤백합니다. 이전 버전으로 롤백하는 기능과 함께 의미 체계 버전 관리를 사용하여 업데이트 및 수정 버전 추적.
- Helm 차트는 템플릿을 사용하여 여러 파일에서 레이블 및 선택기 등의 정보가 중복되지 않도록 방지합니다.
- Helm은 차트 간의 종속성을 관리할 수 있습니다.
- 차트는 Azure Container Registry와 같은 Helm 리포지토리에 저장하고 빌드 파이프라인에 통합할 수 있습니다.
Container Registry를 Helm 리포지토리로 사용하는 방법에 대한 자세한 내용은 애플리케이션 차트용 Helm 리포지토리로 Azure Container Registry 사용을 참조하세요.
단일 마이크로 서비스는 여러 Kubernetes 구성 파일을 포함할 수 있습니다. 서비스 업데이트는 이러한 모든 파일을 터치하여 선택기, 레이블 및 이미지 태그를 업데이트하는 것을 의미할 수 있습니다. Helm은 이 항목들을 차트라는 단일 패키지로 처리하며 사용자가 변수를 사용하여 YAML 파일을 쉽게 업데이트할 수 있도록 해줍니다. Helm은 템플릿 언어(Go 템플릿 기반)를 사용하여 매개 변수가 있는 YAML 구성 파일을 작성하도록 해줍니다.
예를 들어 다음은 배포를 정의하는 YAML 파일의 일부입니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "package.fullname" . | replace "." "" }}
labels:
app.kubernetes.io/name: {{ include "package.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
annotations:
kubernetes.io/change-cause: {{ .Values.reason }}
...
spec:
containers:
- name: &package-container_name fabrikam-package
image: {{ .Values.dockerregistry }}/{{ .Values.image.repository }}:{{ .Values.image.tag }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
- name: LOG_LEVEL
value: {{ .Values.log.level }}
배포 이름, 레이블 및 컨테이너 사양은 모두 배포 시 제공되는 템플릿 매개 변수를 사용하는 것을 볼 수 있습니다. 예를 들어 명령줄에서 다음을 수행합니다.
helm install $HELM_CHARTS/package/ \
--set image.tag=0.1.0 \
--set image.repository=package \
--set dockerregistry=$ACR_SERVER \
--namespace backend \
--name package-v0.1.0
CI/CD 파이프라인이 Kubernetes에 직접 차트를 설치할 수 있지만, 차트 보관 파일(.tgz 파일)을 만들고 Azure Container Registry와 같은 Helm 리포지토리(예: )로 차트를 푸시하는 것이 좋습니다. 자세한 내용은 Azure Pipelines의 Helm 차트에서 Docker 기반 앱 패키지를 참조하세요.
수정 내용
Helm 차트에는 항상 의미 체계 버전 관리를 사용해야 하는 버전 번호가 있습니다. 차트에는 appVersion
도 있을 수 있습니다. 이 필드는 선택 사항이며 차트 버전과 관련될 필요가 없습니다. 일부 팀은 차트에 대한 업데이트와 별도의 애플리케이션 버전을 원할 수 있습니다. 그러나 더 간단한 접근 방식은 하나의 버전 번호를 사용하여 차트 버전과 애플리케이션 버전 간에 1:1 관계가 성립되도록 하는 것입니다. 이렇게 하면 릴리스당 하나의 차트를 저장하고 원하는 릴리스를 쉽게 배포할 수 있습니다.
helm install <package-chart-name> --version <desiredVersion>
또 다른 좋은 방법은 배포 템플릿에서 변경 원인 주석을 제공하는 것입니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "delivery.fullname" . | replace "." "" }}
labels:
...
annotations:
kubernetes.io/change-cause: {{ .Values.reason }}
이렇게 하면 kubectl rollout history
명령을 사용하여 각 수정 버전에 대한 변경 원인 필드를 볼 수 있습니다. 이전 예제에서 변경 원인은 Helm 차트 매개 변수로 제공됩니다.
kubectl rollout history deployments/delivery-v010 -n backend
deployment.extensions/delivery-v010
REVISION CHANGE-CAUSE
1 Initial deployment
helm list
명령을 사용하여 수정 버전 이력을 볼 수도 있습니다.
helm list
NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
delivery-v0.1.0 1 Sun Apr 7 00:25:30 2020 DEPLOYED delivery-v0.1.0 v0.1.0 backend
Azure DevOps 파이프라인
Azure Pipelines에서 파이프라인은 빌드 파이프라인과 릴리스 파이프라인으로 나뉩니다. 빌드 파이프라인은 CI 프로세스를 실행하고 빌드 아티팩트를 만듭니다. Kubernetes 상의 마이크로 서비스 아키텍처의 경우 이러한 아티팩트들은 각 마이크로 서비스를 정의하는 컨테이너 이미지 및 Helm 차트입니다. 릴리스 파이프라인은 마이크로 서비스를 클러스터에 배포하는 CD 프로세스를 실행합니다.
이 아티클의 앞부분에서 설명한 CI 흐름에 기반해 빌드 파이프라인은 다음 작업으로 구성될 수 있습니다.
테스트 실행기 컨테이너를 빌드합니다.
- task: Docker@1 inputs: azureSubscriptionEndpoint: $(AzureSubscription) azureContainerRegistry: $(AzureContainerRegistry) arguments: '--pull --target testrunner' dockerFile: $(System.DefaultWorkingDirectory)/$(dockerFileName) imageName: '$(imageName)-test'
테스트 실행기 컨테이너에 대해 docker run을 호출하여 테스트를 실행합니다.
- task: Docker@1 inputs: azureSubscriptionEndpoint: $(AzureSubscription) azureContainerRegistry: $(AzureContainerRegistry) command: 'run' containerName: testrunner volumes: '$(System.DefaultWorkingDirectory)/TestResults:/app/tests/TestResults' imageName: '$(imageName)-test' runInBackground: false
테스트 결과를 게시합니다. 이미지 빌드를 참조하세요.
- task: PublishTestResults@2 inputs: testResultsFormat: 'VSTest' testResultsFiles: 'TestResults/*.trx' searchFolder: '$(System.DefaultWorkingDirectory)' publishRunAttachments: true
런타임 컨테이너를 빌드합니다.
- task: Docker@1 inputs: azureSubscriptionEndpoint: $(AzureSubscription) azureContainerRegistry: $(AzureContainerRegistry) dockerFile: $(System.DefaultWorkingDirectory)/$(dockerFileName) includeLatestTag: false imageName: '$(imageName)'
컨테이너 이미지를 Azure Container Registry(또는 다른 컨테이너 레지스트리)에 푸시합니다.
- task: Docker@1 inputs: azureSubscriptionEndpoint: $(AzureSubscription) azureContainerRegistry: $(AzureContainerRegistry) command: 'Push an image' imageName: '$(imageName)' includeSourceTags: false
Helm 차트를 패키지합니다.
- task: HelmDeploy@0 inputs: command: package chartPath: $(chartPath) chartVersion: $(Build.SourceBranchName) arguments: '--app-version $(Build.SourceBranchName)'
Helm 패키지를 Azure Container Registry(또는 다른 Helm 리포지토리)에 푸시합니다.
task: AzureCLI@1 inputs: azureSubscription: $(AzureSubscription) scriptLocation: inlineScript inlineScript: | az acr helm push $(System.ArtifactsDirectory)/$(repositoryName)-$(Build.SourceBranchName).tgz --name $(AzureContainerRegistry);
CI 파이프라인의 출력은 프로덕션 준비 컨테이너 이미지이며 마이크로 서비스를 위한 업데이트된 Helm 차트입니다. 이 시점에서 릴리스 파이프라인으로 이어질 수 있습니다. 각 마이크로 서비스를 위한 고유한 릴리스 파이프라인이 있습니다. 릴리스 파이프라인은 아티팩트가 게시된 CI 파이프라인에 대해 트리거 원본을 설정하도록 구성됩니다. 이 파이프라인을 사용하면 각 마이크로 서비스를 독립적으로 배포할 수 있습니다. 릴리스 파이프라인은 다음 단계를 수행합니다.
- Helm 차트를 개발/QA/스테이징 환경에 배포합니다.
Helm upgrade
명령은--install
플래그와 함께 사용하여 첫 번째 설치 및 후속 업그레이드를 지원할 수 있습니다. - 승인자가 배포를 승인하거나 거부할 때까지 기다립니다.
- 릴리스를 위해 컨테이너 이미지에 다시 태그 지정
- 릴리스 태그를 컨테이너 레지스트리에 푸시합니다.
- 프로덕션 클러스터에 Helm 차트를 배포합니다.
릴리스 파이프라인을 만드는 방법에 대한 자세한 내용은 릴리스 파이프라인, 초안 릴리스 및 릴리스 옵션을 참조하세요.
다음 다이어그램은 이 아티클에 설명된 엔드투엔드 CI/CD 프로세스를 보여줍니다.
참가자
Microsoft에서 이 문서를 유지 관리합니다. 원래 다음 기여자가 작성했습니다.
보안 주체 작성자:
- 존 풀 | 선임 클라우드 솔루션 설계자
비공개 LinkedIn 프로필을 보려면 LinkedIn에 로그인하세요.
다음 단계
- Adopt a Git branching strategy(Git 분기 전략 채택)
- Azure Pipelines란 무엇인가요?
- 릴리스 파이프라인, 초안 릴리스 및 릴리스 옵션
- 승인을 통해 릴리스 배포 제어
- Azure의 컨테이너 레지스트리 소개