使用 Cloud Build 以 Git 運作方式持續推送軟體更新

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

Google 工程師長期以來一直在我們的主要原始碼存放區中儲存設定檔案和部署檔案。此方法在網站穩定性工程,第 8 章 (Beyer 等人,2016 年) 一書中有所描述,並由 Kelsey Hightower 在 2017 年 Google Cloud Next 大會的主題演講中進行了展示。「GitOps」一詞本身是由 Weaveworks 所創造。「環境式程式碼」概念是 GitOps 的關鍵要素,亦即使用儲存在 Git 存放區中的檔案 (例如,Kubernetes 資訊清單),以宣告的方式說明您的部署。

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

CI/CD 管道的架構

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

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

當您將變更推送到「app」存放區時,Cloud Build 管道會執行測試、建構容器映像檔,並將其推送到 Container 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」存放區中使用單一生產環境,但您也可以視需要將其部署到多個環境。

費用

事前準備

  1. 選取或建立一個 GCP 專案。

    前往「MANAGE RESOURCES」(管理資源) 頁面

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

    啟用計費功能

  3. 打開 Cloud Shell 以執行本教學課程中列出的指令。

    前往 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 \
        containeranalysis.googleapis.com
    
  6. 在 Cloud Shell 中建立一個 GKE 叢集,您將用此叢集來部署本教學課程的範例應用程式。

    gcloud container clusters create hello-cloudbuild \
        --num-nodes 1 --zone us-central1-b
    
  7. 如果您從未在 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/gke-gitops-tutorial-cloudbuild \
        hello-cloudbuild-app
    
  3. 將 Cloud Source Repositories 設定為遠端。

    cd ~/hello-cloudbuild-app
    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.7-slim
RUN pip install flask
WORKDIR /app
COPY app.py /app/app.py
ENTRYPOINT ["python"]
CMD ["/app/app.py"]

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

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

    cd ~/hello-cloudbuild-app
    COMMIT_ID="$(git rev-parse --short=7 HEAD)"
    gcloud builds submit --tag="gcr.io/${PROJECT_ID}/hello-cloudbuild:${COMMIT_ID}" .
    

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

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

    前往 CONTAINER REGISTRY

    Container Registry 中的 hello-cloudbuild 映像檔

建立持續整合管道

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

steps:
# This step runs the unit tests on the app
- name: 'python:3.7-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'
  - 'gcr.io/$PROJECT_ID/hello-cloudbuild:$SHORT_SHA'
  - '.'

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

    前往「TRIGGERS」(觸發條件)

  2. 按一下 [Create trigger] (建立觸發條件)

  3. 選擇 [Cloud Source Repositories] 做為來源,然後按一下 [Continue] (繼續)

  4. 選擇「hello-cloudbuild-app」存放區,然後按一下 [Continue] (繼續)

  5. 在「Triggers settings」(觸發條件設定) 畫面中,輸入以下參數:

    • 「Name」(名稱):hello-cloudbuild
    • 「Branch (regex)」(分支版本 (規則運算式)):master
    • 「Build configuration」(建構作業設定):cloudbuild.yaml
  6. 按一下 [Create trigger] (建立觸發條件)

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

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

    cd ~/hello-cloudbuild-app
    git push google master
    
  8. 開啟 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 需要容器開發人員 IAM 角色。在 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

初始化「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 ~/hello-cloudbuild-env
    git checkout -b production
    
  2. 複製「hello-cloudbuild-app」存放區中提供的 cloudbuild-delivery.yaml 檔案並確認變更。

    cd ~/hello-cloudbuild-env
    cp ~/hello-cloudbuild-app/cloudbuild-delivery.yaml ~/hello-cloudbuild-env/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_ZONE=us-central1-b'
      - '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. 將原始碼存放區寫入者 IAM 角色授予「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. 按一下 [Add trigger] (新增觸發條件)。

  3. 選擇 [Cloud Source Repositories] 做為來源,然後按一下 [Continue] (繼續)

  4. 選擇「hello-cloudbuild-app」存放區,然後按一下 [Continue] (繼續)

  5. 在「Triggers settings」(觸發條件設定) 畫面中,輸入以下參數:

    • 「Name」(名稱):hello-cloudbuild-deploy
    • 「Branch (regex)」(分支版本 (規則運算式)):candidate
    • 「Build configuration」(建構作業設定):cloudbuild.yaml
  6. 按一下 [Create trigger] (建立觸發條件)

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

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

  1. 複製「app」存放區的 cloudbuild.yaml 檔案擴充版本。

    cd ~/hello-cloudbuild-app
    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 gcr.io/${PROJECT_ID}/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 ~/hello-cloudbuild-app
    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 服務頁面。

    前往 GOOGLE KUBERNETES ENGINE 服務

    清單中應該有一個名為「hello-cloudbuild」的服務。它是由剛剛執行的持續推送軟體更新建構作業所建立的。

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

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

    cd ~/hello-cloudbuild-app
    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 Platform 帳戶收取您在本教學課程中使用資源的相關費用:

  1. 前往 GCP 主控台的「Projects」(專案) 頁面。

    前往專案頁面

  2. 在專案清單中選取要刪除的專案,然後按一下 [Delete] (刪除)
  3. 在對話方塊中輸入專案 ID,按一下 [Shut down] (關閉) 即可刪除專案。

刪除資源

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

  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. 針對每個觸發條件按一下右側的三個垂直點,然後按一下 [Delete] (刪除)

  4. 刪除 Container Registry 中的映像檔。

    gcloud beta container images list-tags \
        gcr.io/${PROJECT_ID}/hello-cloudbuild \
        --format="value(tags)" | \
        xargs -I {} gcloud beta container images delete \
        --force-delete-tags --quiet \
        gcr.io/${PROJECT_ID}/hello-cloudbuild:{}
    
  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 \
        --zone us-central1-b
    

後續步驟

本頁內容對您是否有任何幫助?請提供意見:

傳送您對下列選項的寶貴意見...

這個網頁
Kubernetes Engine 教學課程