使用 Cloud Build,以 GitOps 模式持續推送軟體更新


本頁說明如何僅使用託管產品和熱門的 GitOps 方法,在 Google Cloud 上建立持續整合和持續推送軟體更新 (CI/CD) 管道。

Google 工程師長期以來一直在我們的主要原始碼存放區中儲存設定檔案和部署檔案。此方法在網站穩定性工程,第 8 章 (Beyer 等人,2016 年) 一書中有所描述,並由 Kelsey Hightower 在 2017 年 Google Cloud Next 大會的主題演講中進行了展示。

「環境式程式碼」概念是 GitOps 的關鍵要素,亦即使用儲存在 Git 存放區中的檔案 (例如 Kubernetes 資訊清單),以宣告的方式說明您的部署。

在本教學課程中,您將建立一個 CI/CD 管道,該管道會從已提交的程式碼自動建構容器映像檔,將該映像檔儲存在 Artifact Registry 中,接著更新 Git 存放區中的 Kubernetes 資訊清單,並使用該資訊清單將應用程式部署到 Google Kubernetes Engine (GKE)。

CI/CD 管道的架構

本教學課程使用兩個 Git 存放區:

  • app 存放區:包含應用程式本身的原始碼
  • env 存放區:包含 Kubernetes Deployment 的資訊清單

當您將變更推送到「app」存放區時,Cloud Build 管道會執行測試、建構容器映像檔,並將其推送到 Artifact Registry。在推送映像檔後,Cloud Build 會更新部署資訊清單,並將其推送到「env」存放區。這會觸發另一個 Cloud Build 管道,該管道將資訊清單套用於 GKE 叢集;如果成功套用,則會將資訊清單儲存在「env」存放區的另一個分支中。

我們之所以將「app」和「env」存放區兩者區隔開來,是因為它們具有不同的生命週期和用途。「app」存放區的主要使用者是人,且此存放區是專用於特定應用程式。「env」存放區的主要使用者是自動化系統 (例如 Cloud Build),且可能會有多個應用程式共用此存放區。「env」存放區可以有多個分支版本,每個分支版本都會對應到特定環境 (在本教學課程中您只會使用生產環境) 並參考特定的容器映像檔,而「app」存放區則不會。

完成本教學課程後,您將擁有一個可以輕鬆實現以下目的的系統:

  • 透過檢視 Cloud Build 歷史記錄來區分失敗和成功的部署;
  • 透過檢視「env」存放區的「production」分支來存取目前使用的資訊清單;
  • 透過重新執行對應的 Cloud Build 建構作業,復原到任何之前的版本。

CI/CD 管道的流程

關於本教學課程

本教學課程使用 Cloud Source Repositories 來託管 Git 存放區,但您可以使用其他第三方產品 (如 GitHub、Bitbucket 或 GitLab) 來實現相同的結果。

此管道並未在部署作業之前導入驗證機制。 如果您使用的是 GitHub、Bitbucket 或 GitLab,可以修改此管道,使其使用提取要求來實現此目的。

雖然我們建議希望實施進階部署模式 (藍/綠、初期測試分析、多雲端等) 的團隊使用 Spinnaker,但對於較小的機構和專案而言,即使未使用其中的功能集,也可能打造成功的 CI/CD 策略。在本教學課程中,您將瞭解如何使用工具,針對由 GKE 託管的應用程式建立適合的 CI/CD 管道。

為簡單起見,本教學課程在「env」存放區中使用單一生產環境,但您也可以視需要將其部署到多個環境。

目標

  • 在 Cloud Source Repositories 中建立 Git 存放區。
  • 使用 Cloud Build 建立容器映像檔,並儲存在 Artifact Registry 中。
  • 建立 CI 管道。
  • 建立 CD 管道。
  • 測試 CI/CD 管道。

費用

在本文件中,您會使用 Google Cloud的下列計費元件:

如要根據預測用量估算費用,請使用 Pricing Calculator

初次使用 Google Cloud 的使用者可能符合免費試用資格。

完成本文所述工作後,您可以刪除已建立的資源,避免繼續計費。詳情請參閱清除所用資源一節。

事前準備

  1. 選取或建立 Google Cloud 專案。

    前往「Manage Resources」(管理資源)

  2. 啟用專案的計費功能。

    啟用計費功能

  3. 開啟 Cloud Shell,執行本教學課程中列出的指令。Cloud Shell 是一個互動式殼層環境,可讓您透過網路瀏覽器管理專案和資源。 Google Cloud

    前往 Cloud Shell

  4. 如果 gcloud config get-value project 指令未傳回您選取的專案 ID,請將 Cloud Shell 設定為使用您的專案,

    gcloud config set project [PROJECT_ID]
    
  5. 在 Cloud Shell 中,啟用所需的 API。

    gcloud services enable container.googleapis.com \
        cloudbuild.googleapis.com \
        sourcerepo.googleapis.com \
        artifactregistry.googleapis.com
    
  6. us-central1 區域建立名為 my-repository 的 Artifact Registry Docker 存放區,以便儲存容器映像檔。

    gcloud artifacts repositories create my-repository \
      --repository-format=docker \
      --location=us-central1
    
  7. 在 Cloud Shell 中建立一個 GKE 叢集,您將用此叢集來部署本教學課程的範例應用程式。

    Autopilot

    建立名為 hello-cloudbuild 的 Autopilot 叢集:

    gcloud container clusters create-auto hello-cloudbuild \
        --region us-central1
    

    標準

    建立名為 hello-cloudbuild 的單一節點 Standard 叢集:

    gcloud container clusters create hello-cloudbuild \
        --num-nodes 1 --region us-central1
    
  8. 如果您從未在 Cloud Shell 中使用 Git,請使用您的姓名和電子郵件地址對其進行設定。未來您在 Cloud Shell 中建立修訂版本時,Git 將使用這些資訊識別您的作者身分。

    git config --global user.email "YOUR_EMAIL_ADDRESS"
    git config --global user.name "YOUR_NAME"
    

完成本教學課程後,您可以刪除建立的資源以避免繼續計費。詳情請參閱清除所用資源一節。

在 Cloud Source Repositories 中建立 Git 存放區

在本節中,您將建立本教學課程中使用的兩個 Git 存放區 (「app」和「env」),並使用一些範例程式碼來初始化「app」存放區

  1. 在 Cloud Shell 中,建立兩個 Git 存放區。

    gcloud source repos create hello-cloudbuild-app
    gcloud source repos create hello-cloudbuild-env
    
  2. 從 GitHub 複製範例程式碼。

    cd ~
    git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
    cd ~/kubernetes-engine-samples/management/gitops-style-delivery/
    
  3. 將 Cloud Source Repositories 設定為遠端。

    PROJECT_ID=$(gcloud config get-value project)
    git remote add google \
        "https://source.developers.google.com/p/${PROJECT_ID}/r/hello-cloudbuild-app"
    

您複製的程式碼包含「Hello World」應用程式。

from flask import Flask
app = Flask('hello-cloudbuild')

@app.route('/')
def hello():
  return "Hello World!\n"

if __name__ == '__main__':
  app.run(host = '0.0.0.0', port = 8080)

使用 Cloud Build 建立容器映像檔

複製的程式碼包含下列 Dockerfile。

FROM python:3.13-slim
RUN pip install flask
WORKDIR /app
COPY app.py /app/app.py
ENTRYPOINT ["python"]
CMD ["/app/app.py"]

使用此 Dockerfile,您可以使用 Cloud Build 建立容器映像檔,並將其儲存在 Artifact Registry 中。

  1. 在 Cloud Shell 中,使用以下指令建立以最新修訂版本為基礎的 Cloud Build 建構作業。

    cd ~/kubernetes-engine-samples/management/gitops-style-delivery/
    COMMIT_ID="$(git rev-parse --short=7 HEAD)"
    gcloud builds submit --tag="us-central1-docker.pkg.dev/${PROJECT_ID}/my-repository/hello-cloudbuild:${COMMIT_ID}" .
    

    執行此指令時,Cloud Build 會將建立容器映像檔產生的記錄串流到您的終端機。

  2. 建構完成後,請確認您的新容器映像檔確實存在於 Artifact Registry 中。

    前往 Artifact Registry

    Artifact Registry 中的 hello-cloudbuild 映像檔

建立持續整合管道

在本節中,您會將 Cloud Build 設定為自動執行小型單元測試、建構容器映像檔,然後將其推送到 Artifact Registry。將新修訂版本推送到 Cloud Source Repositories 會自動觸發此管道。程式碼中的 cloudbuild.yaml 檔案是管道的設定。

steps:
# This step runs the unit tests on the app
- name: 'python:3.13-slim'
  id: Test
  entrypoint: /bin/sh
  args:
  - -c
  - 'pip install flask && python test_app.py -v'

# This step builds the container image.
- name: 'gcr.io/cloud-builders/docker'
  id: Build
  args:
  - 'build'
  - '-t'
  - 'us-central1-docker.pkg.dev/$PROJECT_ID/my-repository/hello-cloudbuild:$SHORT_SHA'
  - '.'

# This step pushes the image to Artifact Registry
# The PROJECT_ID and SHORT_SHA variables are automatically
# replaced by Cloud Build.
- name: 'gcr.io/cloud-builders/docker'
  id: Push
  args:
  - 'push'
  - 'us-central1-docker.pkg.dev/$PROJECT_ID/my-repository/hello-cloudbuild:$SHORT_SHA'
  1. 開啟 Cloud Build 的「Triggers」(觸發條件) 頁面。

    前往「Triggers」(觸發條件)

  2. 按一下「建立觸發條件」

  3. 填寫下列選項:

    • 在「Name」(名稱) 欄位中輸入 hello-cloudbuild
    • 在「事件」下方,選取「推送至分支版本」
    • 在「來源」下方,將「存放區」設為 hello-cloudbuild-app,並將「分支版本」設為 ^master$
    • 在「建構設定」下方,選取「Cloud Build 設定檔」
    • 在「Cloud Build 設定檔位置」欄位中,在 / 後面輸入 cloudbuild.yaml
  4. 按一下「建立」即可儲存建構觸發條件。

    提示:如果您需要為多個專案建立自動建構觸發條件,可以使用 Build Triggers API

  5. 在 Cloud Shell 中,將應用程式程式碼推送到 Cloud Source Repositories,以在 Cloud Build 中觸發 CI 管道。

    cd ~/kubernetes-engine-samples/management/gitops-style-delivery/
    git push google master
    
  6. 開啟 Cloud Build 主控台。

    前往 Cloud Build

    畫面上會顯示您最近執行及完成的建構作業。您可以點選建構作業,追蹤執行情況並查看記錄檔。

建立持續推送軟體更新管道

Cloud Build 也會用於持續推送軟體更新管道。每次將修訂版本推送到「hello-cloudbuild-env」存放區的「candidate」分支版本時,管道都會執行。管道會將新版本的資訊清單套用於 Kubernetes 叢集,如果套用成功,則會將資訊清單複製到「production」分支。此過程具有以下屬性:

  • 「candidate」分支版本是部署作業的嘗試記錄。
  • 「production」分支版本是成功部署的歷史記錄。
  • 您可以查看 Cloud Build 中成功和失敗的部署。
  • 您可以重新執行對應的 Cloud Build 建構作業,復原到任何之前的部署。復原作業還會更新「production」分支版本,以真實地反映部署的歷史記錄。

您將會修改持續整合管道以更新「hello-cloudbuild-env」存放區的「candidate」分支版本,從而觸發持續推送軟體更新管道。

將 GKE 存取權授予 Cloud Build

如要在 Kubernetes 叢集部署應用程式,Cloud Build 需要 Kubernetes Engine 開發人員 Identity and Access Management 角色

Shell

在 Cloud Shell 執行下列指令:

PROJECT_NUMBER="$(gcloud projects describe ${PROJECT_ID} --format='get(projectNumber)')"
gcloud projects add-iam-policy-binding ${PROJECT_NUMBER} \
    --member=serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com \
    --role=roles/container.developer

主控台

  1. 在 Google Cloud 控制台中,開啟「Cloud Build Settings」(Cloud Build 設定) 頁面:

    開啟 Cloud Build 設定

    畫面上會顯示「Service account permissions」(服務帳戶權限) 頁面:

    服務帳戶權限頁面的螢幕截圖

  2. Kubernetes Engine 開發人員角色的狀態設為「Enable」(啟用)

初始化「hello-cloudbuild-env」存放區

您需要使用兩個分支版本 (「production」和「candidate」) 以及描述部署過程的 Cloud Build 設定檔來初始化「hello-cloudbuild-env」存放區。

  1. 在 Cloud Shell 中,複製「hello-cloudbuild-env」存放區並建立「production」分支版本。

    cd ~
    gcloud source repos clone hello-cloudbuild-env
    cd ~/kubernetes-engine-samples/management/gitops-style-delivery/
    git checkout -b production
    
  2. 複製「hello-cloudbuild-app」存放區中提供的 cloudbuild-delivery.yaml 檔案,並確認變更。

    cd ~/kubernetes-engine-samples/management/gitops-style-delivery/
    cp ~/hello-cloudbuild-app/cloudbuild-delivery.yaml ~/kubernetes-engine-samples/management/gitops-style-delivery/cloudbuild.yaml
    git add .
    git commit -m "Create cloudbuild.yaml for deployment"
    

    cloudbuild-delivery.yaml 檔案描述了要在 Cloud Build 中執行的部署過程。其中有兩個步驟:

    1. Cloud Build 在 GKE 叢集上套用資訊清單。

    2. 如果成功,Cloud Build 將在「production」分支版本上複製資訊清單。

    steps:
    # This step deploys the new version of our container image
    # in the hello-cloudbuild Kubernetes Engine cluster.
    - name: 'gcr.io/cloud-builders/kubectl'
      id: Deploy
      args:
      - 'apply'
      - '-f'
      - 'kubernetes.yaml'
      env:
      - 'CLOUDSDK_COMPUTE_REGION=us-central1'
      - 'CLOUDSDK_CONTAINER_CLUSTER=hello-cloudbuild'
    
    # This step copies the applied manifest to the production branch
    # The COMMIT_SHA variable is automatically
    # replaced by Cloud Build.
    - name: 'gcr.io/cloud-builders/git'
      id: Copy to production branch
      entrypoint: /bin/sh
      args:
      - '-c'
      - |
        set -x && \
        # Configure Git to create commits with Cloud Build's service account
        git config user.email $(gcloud auth list --filter=status:ACTIVE --format='value(account)') && \
        # Switch to the production branch and copy the kubernetes.yaml file from the candidate branch
        git fetch origin production && git checkout production && \
        git checkout $COMMIT_SHA kubernetes.yaml && \
        # Commit the kubernetes.yaml file with a descriptive commit message
        git commit -m "Manifest from commit $COMMIT_SHA
        $(git log --format=%B -n 1 $COMMIT_SHA)" && \
        # Push the changes back to Cloud Source Repository
        git push origin production
  3. 建立一個「candidate」分支版本,並將兩個分支版本推送到 Cloud Source Repositories。

    git checkout -b candidate
    git push origin production
    git push origin candidate
    
  4. 將原始碼存放區寫入者身分與存取權管理角色授予「hello-cloudbuild-env」存放區的 Cloud Build 服務帳戶。

    PROJECT_NUMBER="$(gcloud projects describe ${PROJECT_ID} \
        --format='get(projectNumber)')"
    cat >/tmp/hello-cloudbuild-env-policy.yaml <<EOF
    bindings:
    - members:
      - serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com
      role: roles/source.writer
    EOF
    gcloud source repos set-iam-policy \
        hello-cloudbuild-env /tmp/hello-cloudbuild-env-policy.yaml
    

為持續推送軟體更新管道建立觸發條件

在本節中,您會為 Cloud Build 設定觸發條件:只要對「hello-cloudbuild-env」存放區的「candidate」分支版本進行推送,即會觸發 Cloud Build。

  1. 開啟 Cloud Build 的「Triggers」(觸發條件) 頁面。

    前往「Triggers」(觸發條件)

  2. 按一下「建立觸發條件」

  3. 填寫下列選項:

    • 在「Name」(名稱) 欄位中輸入 hello-cloudbuild-deploy
    • 在「事件」下方,選取「推送至分支版本」
    • 在「來源」下方,將「存放區」設為 hello-cloudbuild-env,並將「分支版本」設為 ^candidate$
    • 在「Configuration」(設定) 下方,選取「Cloud Build configuration file (yaml or json)」(Cloud Build 設定檔 (yaml 或 json))
    • 在「Cloud Build 設定檔位置」欄位中,在 / 後面輸入 cloudbuild.yaml
  4. 點選「建立」

修改持續整合管道,觸發持續推送軟體更新管道

在本節中,您將向持續整合管道新增一些步驟,以便產生新版本的 Kubernetes 資訊清單,並將其推送到「hello-cloudbuild-env」存放區,藉此觸發持續推送軟體更新管道。

  1. cloudbuild.yaml 檔案替換為 cloudbuild-trigger-cd.yaml 檔案中的擴充範例。

    cd ~/kubernetes-engine-samples/management/gitops-style-delivery/
    cp cloudbuild-trigger-cd.yaml cloudbuild.yaml
    

    cloudbuild-trigger-cd.yamlcloudbuild.yaml 檔案的擴充版本。該版本新增的步驟如下:產生新的 Kubernetes 資訊清單,並觸發持續推送軟體更新管道。

    # This step clones the hello-cloudbuild-env repository
    - name: 'gcr.io/cloud-builders/gcloud'
      id: Clone env repository
      entrypoint: /bin/sh
      args:
      - '-c'
      - |
        gcloud source repos clone hello-cloudbuild-env && \
        cd hello-cloudbuild-env && \
        git checkout candidate && \
        git config user.email $(gcloud auth list --filter=status:ACTIVE --format='value(account)')
    
    # This step generates the new manifest
    - name: 'gcr.io/cloud-builders/gcloud'
      id: Generate manifest
      entrypoint: /bin/sh
      args:
      - '-c'
      - |
         sed "s/GOOGLE_CLOUD_PROJECT/${PROJECT_ID}/g" kubernetes.yaml.tpl | \
         sed "s/COMMIT_SHA/${SHORT_SHA}/g" > hello-cloudbuild-env/kubernetes.yaml
    
    # This step pushes the manifest back to hello-cloudbuild-env
    - name: 'gcr.io/cloud-builders/gcloud'
      id: Push manifest
      entrypoint: /bin/sh
      args:
      - '-c'
      - |
        set -x && \
        cd hello-cloudbuild-env && \
        git add kubernetes.yaml && \
        git commit -m "Deploying image us-central1-docker.pkg.dev/$PROJECT_ID/my-repository/hello-cloudbuild:${SHORT_SHA}
        Built from commit ${COMMIT_SHA} of repository hello-cloudbuild-app
        Author: $(git log --format='%an <%ae>' -n 1 HEAD)" && \
        git push origin candidate
    
  2. 確認修改並將其推送到 Cloud Source Repositories。

    cd ~/kubernetes-engine-samples/management/gitops-style-delivery/
    git add cloudbuild.yaml
    git commit -m "Trigger CD pipeline"
    git push google master
    

    這會觸發 Cloud Build 中的持續整合管道。

  3. 檢查持續整合建構作業。

    前往 Cloud Build

    您最近為「hello-cloudbuild-app」存放區執行及完成的建構作業會顯示在畫面上。您可以按一下建構作業,追蹤執行情況並檢查記錄。此管道的最後一個步驟會將新的資訊清單推送到「hello-cloudbuild-env」存放區,並觸發持續推送軟體更新管道。

  4. 檢查持續推送軟體更新建構作業。

    前往 Cloud Build

    您最近為 hello-cloudbuild-env 存放區執行及完成的建構作業會顯示在畫面上。您可以點選該建構作業,追蹤執行情況並查看記錄檔。

測試完整管道

完整的 CI/CD 管道現已設定完畢,在本節中,您將進行端到端的測試。

  1. 前往「GKE Services」(GKE 服務) 頁面。

    前往 Google Kubernetes Engine 服務

    清單中應該有一個名為 hello-cloudbuild 的服務,這是由最近完成的持續推送軟體更新建構作業所建立。

  2. 按一下「hello-cloudbuild」服務的端點。 畫面上會顯示「Hello World!」。如果沒有端點,或者若您看到負載平衡器錯誤,則可能需要等待幾分鐘才能完全初始化負載平衡器。 視需要點選「重新整理」來更新頁面。

  3. 在 Cloud Shell 中,在應用程式和單元測試中將「Hello World」替換為「Hello Cloud Build」。

    cd ~/kubernetes-engine-samples/management/gitops-style-delivery/
    sed -i 's/Hello World/Hello Cloud Build/g' app.py
    sed -i 's/Hello World/Hello Cloud Build/g' test_app.py
    
  4. 確認並將變更推送到 Cloud Source Repositories。

    git add app.py test_app.py
    git commit -m "Hello Cloud Build"
    git push google master
    

    這會觸發完整的 CI/CD 管道。

  5. 幾分鐘後,在瀏覽器中重新載入應用程式。 畫面會顯示「Hello Cloud Build!」。

測試復原作業

在本節中,您將復原到顯示「Hello World!」的應用程式版本。

  1. 開啟 Cloud Build 主控台以存取「hello-cloudbuild-env」存放區。

    前往 Cloud Build

  2. 點選第二新的可用版本。

  3. 按一下 [Rebuild] (重新建構)

  4. 建構完成後,在瀏覽器中重新載入應用程式。 「Hello World!」會再次顯示。

清除所用資源

如要避免系統向您的 Google Cloud 帳戶收取本教學課程中所用資源的相關費用,請刪除含有該項資源的專案,或者保留專案但刪除個別資源。

  1. In the Google Cloud console, go to the Manage resources page.

    Go to Manage resources

  2. In the project list, select the project that you want to delete, and then click Delete.
  3. In the dialog, type the project ID, and then click Shut down to delete the project.

刪除資源

如要保留您在本教學課程中使用的 Google Cloud 專案,請刪除個別的資源:

  1. 刪除本機 Git 存放區。

    cd ~
    rm -rf ~/hello-cloudbuild-app
    rm -rf ~/hello-cloudbuild-env
    
  2. 在 Cloud Source Repositories 中刪除 Git 存放區。

    gcloud source repos delete hello-cloudbuild-app --quiet
    gcloud source repos delete hello-cloudbuild-env --quiet
    
  3. 刪除 Cloud Build 觸發條件。

    1. 開啟 Cloud Build 的「Triggers」(觸發條件) 頁面。

      前往「Triggers」(觸發條件)

    2. 針對每個觸發條件,依序按一下「更多」圖示 和「刪除」

  4. 刪除 Artifact Registry 中的 Docker 存放區。

    gcloud artifacts repositories delete my-repository \
        --location=us-central1
    
  5. 刪除允許 Cloud Build 與 GKE 連線的權限。

    PROJECT_NUMBER="$(gcloud projects describe ${PROJECT_ID} \
        --format='get(projectNumber)')"
    gcloud projects remove-iam-policy-binding ${PROJECT_NUMBER} \
        --member=serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com \
        --role=roles/container.developer
    
  6. 刪除 GKE 叢集。

    gcloud container clusters delete hello-cloudbuild \
       --region us-central1
    

後續步驟