為微服務架構建立可靠的持續整合/持續傳遞(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 策略時,客戶可能會使用的常見替代方案:
- Kustomize 是使用 Helm 做為套件管理和部署工具的替代方案,是 Kubernetes 原生組態管理工具,引進了自定義和參數化應用程式組態的無範本方式。
- 除了針對 Git 存放庫和管線 使用 Azure DevOps,GitHub 存放庫 也可用於私人和公用 Git 存放庫,而 GitHub Actions 則可用於 CI/CD 管線。
- 除了使用推送部署模型,您也可以使用 GitOps(提取部署模型)大規模管理 Kubernetes 設定,其中叢集中 Kubernetes 操作員會根據儲存在 Git 存放庫中的設定來同步處理叢集狀態。
驗證組建
假設開發人員正在處理稱為「傳遞服務」的微服務。 開發新功能時,開發人員會將程式代碼簽入功能分支。 依照慣例,功能分支會命名為 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
代碼會觸發傳遞服務的組建。 將認可推送至符合篩選條件的分支會觸發 CI 組建。 此時在工作流程中,CI 組建會執行一些最少的程式代碼驗證:
- 建置程序代碼。
- 執行單元測試。
目標是縮短建置時間,讓開發人員可以快速取得意見反應。 一旦功能準備好合併到主圖形,開發人員就會開啟PR。 此作業會觸發另一個 CI 組建,以執行一些額外的檢查:
- 建置程序代碼。
- 執行單元測試。
- 建置運行時間容器映像。
- 在映像上執行弱點掃描。
完整 CI/CD 組建
在某些時候,小組已準備好部署新版本的傳遞服務。 發行管理員會使用此命名模式,從主要分支建立分支: release/<microservice name>/<semver>
。 例如: release/delivery/v1.0.2
。
此分支的建立會觸發執行上述所有步驟的完整 CI 組建,再加上:
- 將容器映像推送至 Azure Container Registry。 映射會以從分支名稱擷取的版本號碼標記。
- 執行
helm package
以封裝服務的 Helm 圖表。 圖表也會以版本號碼標記。 - 將 Helm 套件推送至 Container Registry。
假設此組建成功,它會使用 Azure Pipelines 發行管線來觸發部署 (CD) 程式。 此管線具有下列步驟:
- 將 Helm 圖表部署到 QA 環境。
- 核准者會在套件移至生產環境之前註銷。 請參閱 使用核准發行部署控制件。
- 在 Azure Container Registry 中重新標記生產命名空間的 Docker 映射。 例如,如果目前的標籤是
myrepo.azurecr.io/delivery:v1.0.2
,則生產標籤為myrepo.azurecr.io/prod/delivery:v1.0.2
。 - 將 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 流程,組建管線可能包含下列工作:
使用
Docker
工作建置測試執行器容器。藉由叫用 Docker 對測試執行器容器執行,以執行測試。 這會使用工作
Docker
。使用
PublishTestResults
工作發佈測試結果。 請參閱 建置映像。使用本機 Docker 組建和工作或使用
Docker
Azure Container Registry 建置和工作AzureCLI
來建置運行時間容器。使用
Docker
或AzureCLI
工作,將容器映射推送至 Azure Container Registry(或其他容器登錄)。使用工作
HelmDeploy
封裝 Helm 圖表。使用
HelmDeploy
工作將 Helm 套件推送至 Azure Container Registry(或其他 Helm 存放庫)。
CI 管線的輸出是生產就緒的容器映像,以及微服務的更新 Helm 圖表。 此時,發行管線可以接管。 每個微服務都有唯一的發行管線。 發行管線會設定為將觸發程式來源設定為發行成品的 CI 管線。 此管線可讓您擁有每個微服務的獨立部署。 發行管線會執行下列步驟:
- 將 Helm 圖表部署至開發/QA/預備環境。
helm upgrade
命令可與旗標搭配--install
使用,以支援第一次安裝和後續升級。 - 等候核准者核准或拒絕部署。
- 重新標記發行的容器映像
- 將發行標記推送至容器登錄。
- 在生產叢集中部署 Helm 圖表。
如需建立發行管線的詳細資訊,請參閱 發行管線、草稿版本和發行選項。
下圖顯示本文所述的端對端 CI/CD 程式: