使用 Azure DevOps 和 Helm 在 Kubernetes 上建置微服務的 CI/CD 管線

Azure Kubernetes Service (AKS)
Azure Container Registry
Azure DevOps

為微服務架構建立可靠的持續整合/持續傳遞(CI/CD)程式可能會很困難。 個別小組必須能夠快速且可靠地發行服務,而不會中斷其他小組或破壞整個應用程式的穩定。

本文說明將微服務部署至 Azure Kubernetes Service (AKS) 的範例 CI/CD 管線。 每個小組和專案都不同,因此請勿將本文視為一組硬式且快速的規則。 相反地,它是設計您自己的 CI/CD 程式的起點。

Kubernetes 託管微服務的 CI/CD 管線目標摘要如下:

  • Teams 可以獨立建置和部署其服務。
  • 傳遞 CI 程式的程式代碼變更會自動部署到類似生產環境的環境。
  • 質量閘道會在管線的每個階段強制執行。
  • 新版本的服務可以與舊版並存部署。

如需更多背景,請參閱 微服務架構的 CI/CD。

假設

針對此範例的目的,以下是開發小組和程式代碼基底的一些假設:

  • 程序代碼存放庫是monorepo,由微服務組織的資料夾。
  • 小組的分支策略是以主幹為基礎的開發為基礎
  • 小組會使用 發行分支 來管理發行。 系統會為每個微服務建立個別版本。
  • CI/CD 程式會使用 Azure Pipelines 來建置、測試和部署微服務至 AKS。
  • 每個微服務的容器映像都會儲存在 Azure Container Registry 中。
  • 小組會使用 Helm 圖表來封裝每個微服務。
  • 使用推送部署模型,其中 Azure Pipelines 和相關聯的代理程式會藉由直接連線到 AKS 叢集來執行部署。

這些假設會驅動 CI/CD 管線的許多特定詳細數據。 不過,此處所述的基本方法會針對其他進程、工具和服務進行調整,例如 Jenkins 或 Docker Hub。

替代項目

以下是使用 Azure Kubernetes Service 選擇 CI/CD 策略時,客戶可能會使用的常見替代方案:

驗證組建

假設開發人員正在處理稱為「傳遞服務」的微服務。 開發新功能時,開發人員會將程式代碼簽入功能分支。 依照慣例,功能分支會命名為 feature/*

CI/CD workflow

組建定義檔包含依分支名稱和來源路徑篩選的觸發程式:

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 代碼會觸發傳遞服務的組建。 將認可推送至符合篩選條件的分支會觸發 CI 組建。 此時在工作流程中,CI 組建會執行一些最少的程式代碼驗證:

  1. 建置程序代碼。
  2. 執行單元測試。

目標是縮短建置時間,讓開發人員可以快速取得意見反應。 一旦功能準備好合併到主圖形,開發人員就會開啟PR。 此作業會觸發另一個 CI 組建,以執行一些額外的檢查:

  1. 建置程序代碼。
  2. 執行單元測試。
  3. 建置運行時間容器映像。
  4. 在映像上執行弱點掃描。

Diagram showing ci-delivery-full in the Build pipeline.

注意

在 Azure DevOps Repos 中,您可以定義原則來保護分支。 例如,原則可能需要成功的 CI 組建加上核准者的註銷,才能合併到 master。

完整 CI/CD 組建

在某些時候,小組已準備好部署新版本的傳遞服務。 發行管理員會使用此命名模式,從主要分支建立分支: release/<microservice name>/<semver>。 例如: release/delivery/v1.0.2

Diagram showing ci-delivery-full in the Build pipeline and cd-delivery in the Release pipeline.

此分支的建立會觸發執行上述所有步驟的完整 CI 組建,再加上:

  1. 將容器映像推送至 Azure Container Registry。 映射會以從分支名稱擷取的版本號碼標記。
  2. 執行 helm package 以封裝服務的 Helm 圖表。 圖表也會以版本號碼標記。
  3. 將 Helm 套件推送至 Container Registry。

假設此組建成功,它會使用 Azure Pipelines 發行管線來觸發部署 (CD) 程式。 此管線具有下列步驟:

  1. 將 Helm 圖表部署到 QA 環境。
  2. 核准者會在套件移至生產環境之前註銷。 請參閱 使用核准發行部署控制件。
  3. 在 Azure Container Registry 中重新標記生產命名空間的 Docker 映射。 例如,如果目前的標籤是 myrepo.azurecr.io/delivery:v1.0.2,則生產標籤為 myrepo.azurecr.io/prod/delivery:v1.0.2
  4. 將 Helm 圖表部署到生產環境。

即使在monorepo中,這些工作也可以限定為個別微服務,讓小組可以高速部署。 此程式有一些手動步驟:核准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 專案。 但最後一個運行時間容器是從 建置 base的,它只包含運行時間,而且明顯小於完整的SDK映射。

建置測試執行器

另一個最佳做法是在容器中執行單元測試。 例如,以下是建置測試執行器之 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 ENTRYPOINT 命令來執行測試,而不是 Docker RUN 命令。

  • 如果您使用 RUN 命令,則每次建置映射時都會執行測試。 藉由使用 ENTRYPOINT,測試會選擇加入。 它們只會在您明確將階段設為目標 testrunner 時執行。
  • 失敗的測試不會造成 Docker build 命令失敗。 如此一來,您就可以區分容器建置失敗與測試失敗。
  • 測試結果可以儲存到掛接的磁碟區。

容器最佳做法

以下是容器需要考慮的一些其他最佳做法:

  • 針對部署至叢集的資源定義整個組織的容器標籤、版本設定和命名慣例(Pod、服務等等)。 這可讓您更輕鬆地診斷部署問題。

  • 在開發和測試周期期間,CI/CD 程式會建置許多容器映射。 只有其中一些映像是發行的候選專案,然後只有其中一些發行候選專案會升階到生產環境。 有明確的版本控制策略,以便您知道哪些映射目前已部署到生產環境,並在必要時協助回復至舊版。

  • 一律部署特定的容器版本標籤,而不是 latest

  • 使用 Azure Container Registry 中的命名空間 ,將核准用於生產環境的映像與仍在測試的映像隔離。 在您準備好將映像部署到生產環境之前,請勿將映像移至生產命名空間。 如果您將這種做法與容器映像的語意化版本設定結合,可能會降低意外部署未核准發行的版本的機會。

  • 以非特殊許可權的使用者身分執行容器,以遵循最低許可權原則。 在 Kubernetes 中,您可以建立 Pod 安全策略,以防止容器以根目錄身分執行。

Helm 圖表

請考慮使用 Helm 來管理建置和部署服務。 以下是 Helm 的一些功能,可協助 CI/CD:

  • 單一微服務通常會由多個 Kubernetes 物件定義。 Helm 可讓這些物件封裝成單一 Helm 圖表。
  • 您可以使用單一 Helm 命令來部署圖表,而不是一系列 kubectl 命令。
  • 圖表已明確設定版本。 使用 Helm 來發行版本、檢視版本,以及復原至舊版。 使用語意版本控制追蹤更新和修訂,以及復原至舊版的能力。
  • Helm 圖表使用範本來避免在許多檔案之間複製資訊,例如標籤和選取器。
  • Helm 可以管理圖表之間的相依性。
  • 圖表可以儲存在 Helm 存放庫中,例如 Azure Container Registry,並整合到組建管線中。

如需使用 Container Registry 作為 Helm 存放庫的詳細資訊,請參閱 使用 Azure Container Registry 作為應用程式圖表的 Helm 存放庫。

單一微服務可能包含多個 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 檔案),並將圖表推送至 Helm 存放庫,例如 Azure Container Registry。 如需詳細資訊,請參閱 在 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 Pipeline

在 Azure Pipelines 中,管線分成 組建管線發行管線。 建置管線會執行 CI 程式,並建立組建成品。 對於 Kubernetes 上的微服務架構,這些成品是定義每個微服務的容器映像和 Helm 圖表。 發行管線會執行將微服務部署至叢集的CD程式。

根據本文稍早所述的 CI 流程,組建管線可能包含下列工作:

  1. 建置測試執行器容器。

    - task: Docker@1
      inputs:
        azureSubscriptionEndpoint: $(AzureSubscription)
        azureContainerRegistry: $(AzureContainerRegistry)
        arguments: '--pull --target testrunner'
        dockerFile: $(System.DefaultWorkingDirectory)/$(dockerFileName)
        imageName: '$(imageName)-test'
    
  2. 藉由叫用 Docker 對測試執行器容器執行,以執行測試。

    - task: Docker@1
      inputs:
        azureSubscriptionEndpoint: $(AzureSubscription)
        azureContainerRegistry: $(AzureContainerRegistry)
        command: 'run'
        containerName: testrunner
        volumes: '$(System.DefaultWorkingDirectory)/TestResults:/app/tests/TestResults'
        imageName: '$(imageName)-test'
        runInBackground: false
    
  3. 發佈測試結果。 請參閱 建置映像

    - task: PublishTestResults@2
      inputs:
        testResultsFormat: 'VSTest'
        testResultsFiles: 'TestResults/*.trx'
        searchFolder: '$(System.DefaultWorkingDirectory)'
        publishRunAttachments: true
    
  4. 建置運行時間容器。

    - task: Docker@1
      inputs:
        azureSubscriptionEndpoint: $(AzureSubscription)
        azureContainerRegistry: $(AzureContainerRegistry)
        dockerFile: $(System.DefaultWorkingDirectory)/$(dockerFileName)
        includeLatestTag: false
        imageName: '$(imageName)'
    
  5. 將容器映像推送至 Azure Container Registry(或其他容器登錄)。

    - task: Docker@1
      inputs:
        azureSubscriptionEndpoint: $(AzureSubscription)
        azureContainerRegistry: $(AzureContainerRegistry)
        command: 'Push an image'
        imageName: '$(imageName)'
        includeSourceTags: false
    
  6. 封裝 Helm 圖表。

    - task: HelmDeploy@0
      inputs:
        command: package
        chartPath: $(chartPath)
        chartVersion: $(Build.SourceBranchName)
        arguments: '--app-version $(Build.SourceBranchName)'
    
  7. 將 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 程式:

CD/CD pipeline

參與者

本文由 Microsoft 維護。 原始投稿人如下。

主體作者:

若要查看非公用LinkedIn配置檔,請登入LinkedIn。

下一步