使用 Terraform、Cloud Build 和 GitOps 管理基礎架構式程式碼

本教學課程說明如何使用熱門的 GitOps 方法,透過 Terraform 和 Cloud Build 以程式碼的形式管理基礎架構。GitOps 一詞最初是由 Weaveworks 所提出,其主要概念是使用 Git 存放區儲存您想要的環境狀態。Terraform 是 HashiCorp 的工具,可讓您使用程式碼,以可預測的方式建立、變更及改善雲端基礎架構。在本教學課程中,您將使用 Cloud Build (持續整合服務 Cloud de Confiance by S3NS ) 自動將 Terraform 資訊清單套用至環境。

本教學課程適用於正在尋找一流的策略,以便以可預測的方式變更基礎架構的開發人員和操作人員。本文假設您熟悉 Cloud de Confiance、Linux 和 GitHub。

《DevOps 現狀》報告指出,有幾項能力可提升軟體推送效能。本教學課程將協助您瞭解下列功能:

架構

為了說明本教學課程如何將 GitOps 做法應用於管理 Terraform 的執行作業,請參考以下架構圖。請注意,這會使用 GitHub 分支版本 (dev 和 prod) 代表實際環境。這些環境分別由虛擬私有雲 (VPC) 網路 (dev 和 prod) 定義到 Cloud de Confiance 專案中。

包含開發和實際工作環境的基礎架構。

將 Terraform 程式碼推送至 dev 或 prod 分支時,程序就會啟動。在這個情境中,Cloud Build 會觸發並套用 Terraform 資訊清單,在相應環境中達成所需狀態。另一方面,如果您將 Terraform 程式碼推送至任何其他分支 (例如功能分支),Cloud Build 會執行 terraform plan,但不會將任何內容套用至任何環境。

理想情況下,開發人員或營運人員必須向非受保護的分支提出基礎架構提案,然後透過提取要求提交提案。本教學課程稍後會介紹 Cloud Build GitHub 應用程式,這個應用程式會自動觸發建構作業,並將 terraform plan 報表連結至這些提取要求。如此一來,您就可以在系統將變更合併至基本分支版本之前先和協作者討論及審查可能的變更,以及加入後續修訂版本。

如果沒有任何疑慮,您必須先將變更合併至 dev 分支。這項合併作業會觸發基礎架構部署至dev環境,方便您測試這個環境。測試完成並確認部署內容後,您必須將 dev 分支合併至 prod 分支,觸發基礎架構安裝程序,將內容部署至正式環境。

目標

  • 設定您的 GitHub 存放區。
  • 設定 Terraform,將狀態儲存在 Cloud Storage bucket 中。
  • 將權限授予您的 Cloud Build 服務帳戶。
  • 將 Cloud Build 連線至您的 GitHub 存放區。
  • 在功能分支版本中變更您的環境設定。
  • 將變更推行至開發環境。
  • 將變更升級至正式環境。

費用

在本文件中,您將使用下列 Cloud de Confiance by S3NS計費元件:

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

必要條件

  1. In the Cloud de Confiance console, on the project selector page, select or create a Cloud de Confiance project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  2. Verify that billing is enabled for your Cloud de Confiance project.

  3. 取得您剛選取專案的 ID:
    gcloud config get-value project
    如果這個指令未傳回專案 ID,請將本機殼層設定為使用專案。將 PROJECT_ID 替換為專案 ID。
    gcloud config set project PROJECT_ID
  4. 啟用所需的 API:
    gcloud services enable cloudbuild.googleapis.com compute.googleapis.com
    這個步驟可能需要幾分鐘才能完成。
  5. 使用您的姓名和電子郵件地址設定 Git 憑證:
    git config --global user.email "YOUR_EMAIL_ADDRESS"
    git config --global user.name "YOUR_NAME"
    
    Git 會使用這項資訊,將您識別為在本地殼層中建立的提交內容作者。

設定您的 GitHub 存放區

在本教學課程中,您將使用單一 Git 存放區來定義您的雲端基礎架構。您可以讓不同的分支版本對應至不同的環境,藉此來自動化調度管理這個基礎架構:

  • dev 分支包含套用至開發環境的最新變更。
  • prod 分支包含套用至正式環境的最新變更。

有了這項基礎架構,您隨時可以參照存放區,瞭解每個環境中預期的設定,並先將變更合併至 dev 環境,然後提議新的變更。接著,將 dev 分支合併到後續的 prod 分支,即可升級變更。

如要開始使用,請分叉 solutions-terraform-cloudbuild-gitops 存放區。

  1. 在 GitHub 中,前往 https://github.com/GoogleCloudPlatform/solutions-terraform-cloudbuild-gitops.git。
  2. 按一下頁面右上角的 [Fork] (分支)。

    建立存放區的分支。

    現在您已擁有存放區副本和來源檔案。solutions-terraform-cloudbuild-gitops

  1. 複製這個分支存放區,並將 YOUR_GITHUB_USERNAME 換成你的 GitHub 使用者名稱:

    cd ~
    git clone https://github.com/YOUR_GITHUB_USERNAME/solutions-terraform-cloudbuild-gitops.git
    cd ~/solutions-terraform-cloudbuild-gitops
    

這個存放區中的程式碼結構如下:

  • environments/ 資料夾包含代表環境的子資料夾,例如 dev 和 prod,分別在成熟度、開發和生產的不同階段,提供工作負載之間的邏輯分隔。雖然建議盡可能讓這些環境相似,但每個子資料夾都有自己的 Terraform 設定,確保可以視需要使用專屬設定。

  • modules/ 資料夾包含內嵌的 Terraform 模組。這些模組代表相關資源的邏輯群組,可用於跨不同環境共用程式碼。

  • cloudbuild.yaml 檔案是建構設定檔,內含 Cloud Build 的操作說明,例如如何根據一組步驟執行工作。這個檔案會根據 Cloud Build 擷取程式碼的來源分支版本來指定條件式執行作業,例如:

    • 對於 dev 和 prod 分支,系統會執行下列步驟:

      1. terraform init
      2. terraform plan
      3. terraform apply
    • 如果是任何其他分支版本,系統會執行以下步驟:

      1. 所有 environments 子資料夾的 terraform init
      2. 所有 environments 子資料夾的 terraform plan

為確保所提議的變更適用於所有環境,請針對所有 environments 子資料夾執行 terraform init 和 terraform plan。合併提取要求前,您可以查看方案,確保存取權不會授予未經授權的實體。

設定 Terraform,將狀態儲存在 Cloud Storage bucket 中

根據預設,Terraform 會將狀態儲存在名為 terraform.tfstate 的本機檔案中。這個預設設定會使得團隊難以使用 Terraform,尤其是當有許多使用者同時執行 Terraform,且每部機器對於目前的基礎架構都有自己的理解時。

為協助您避免這類問題,本節將設定指向 Cloud Storage bucket 的遠端狀態。遠端狀態是後端的功能,在本教學課程中,遠端狀態是在 backend.tf 檔案中設定,例如:

# Copyright 2019 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


terraform {
  backend "gcs" {
    bucket = "PROJECT_ID-tfstate"
    prefix = "env/dev"
  }
}

在後續步驟中,您將建立 Cloud Storage bucket,並變更幾個檔案,指向新的 bucket 和專案。 Cloud de Confiance

  • 建立 Cloud Storage bucket:

    PROJECT_ID=$(gcloud config get-value project)
    gcloud storage buckets create gs://${PROJECT_ID}-tfstate
    
  • 1. 啟用物件版本管理功能,保留部署作業的歷史記錄:

    ```sh
    gcloud storage buckets update gs://${PROJECT_ID}-tfstate --versioning
    ```
    
    Enabling Object Versioning increases
    [storage costs](https://cloud.google.com/storage/pricing){: track-type="tutorial" track-name="internalLink" track-metadata-position="body" },
    which you can mitigate by configuring
    [Object Lifecycle Management](/storage/docs/lifecycle){: track-type="tutorial" track-name="internalLink" track-metadata-position="body" } 
    to delete old state versions.
    
    1. 在 terraform.tfvars 和 backend.tf 檔案中,將 PROJECT_ID 預留位置文字替換為專案 ID:

      cd ~/solutions-terraform-cloudbuild-gitops
      sed -i s/PROJECT_ID/$PROJECT_ID/g environments/*/terraform.tfvars
      sed -i s/PROJECT_ID/$PROJECT_ID/g environments/*/backend.tf
      

      在 OS X/macOS 上,您可能需要在 sed -i 後方加上兩個半形雙引號 (""),如下所示:

      cd ~/solutions-terraform-cloudbuild-gitops
      sed -i "" s/PROJECT_ID/$PROJECT_ID/g environments/*/terraform.tfvars
      sed -i "" s/PROJECT_ID/$PROJECT_ID/g environments/*/backend.tf
      
    2. 檢查是否已更新所有檔案:

      git status
      

      輸出如下所示:

      On branch dev
      Your branch is up-to-date with 'origin/dev'.
      Changes not staged for commit:
       (use "git add <file>..." to update what will be committed)
       (use "git checkout -- <file>..." to discard changes in working directory)
             modified:   environments/dev/backend.tf
             modified:   environments/dev/terraform.tfvars
             modified:   environments/prod/backend.tf
             modified:   environments/prod/terraform.tfvars
      no changes added to commit (use "git add" and/or "git commit -a")
      
    3. 修訂並推送您的變更:

      git add --all
      git commit -m "Update project IDs and buckets"
      git push origin dev
      

      視您的 GitHub 設定而定,您將必須進行驗證才能推送上述變更。

    將權限授予您的 Cloud Build 服務帳戶

    如要允許 Cloud Build 服務帳戶執行 Terraform 指令碼來管理 Cloud de Confiance 資源,請授予該帳戶專案的適當存取權。為簡化作業,本教學課程會授予專案編輯者存取權。但如果專案編輯者角色具有廣泛的權限,在正式環境中,您必須遵循貴公司的 IT 安全性最佳做法,通常是提供最低權限存取權。如需安全最佳做法,請參閱「明確驗證每次存取嘗試」。

    1. 在本地殼層中,擷取專案的 Cloud Build 服務帳戶電子郵件地址:

      CLOUDBUILD_SA="$(gcloud projects describe $PROJECT_ID \
          --format 'value(projectNumber)')@cloudbuild.s3ns-system.iam.gserviceaccount.com"
      
    2. 將所需的存取權授予您的 Cloud Build 服務帳戶:

      gcloud projects add-iam-policy-binding $PROJECT_ID \
          --member serviceAccount:$CLOUDBUILD_SA --role roles/editor
      

    將 Cloud Build 直接連線至您的 GitHub 存放區

    本節說明如何安裝 Cloud Build GitHub 應用程式。安裝完成後,您就能將 GitHub 存放區連結至Cloud de Confiance 專案,這樣每當您建立新分支或將程式碼推送至 GitHub 時,Cloud Build 就會自動套用 Terraform 資訊清單。

    以下步驟說明如何只為 solutions-terraform-cloudbuild-gitops 存放區安裝應用程式,但您也可以選擇為更多或所有存放區安裝應用程式。

    1. 前往 Cloud Build 應用程式的「GitHub Marketplace」(GitHub 市集) 頁面:

      開啟 Cloud Build 應用程式頁面

      • 如果是第一次在 GitHub 中設定應用程式,請按一下頁面底部的「Setup with Google Cloud Build」(使用 Google Cloud Build 設定)。然後按一下「Grant this app access to your GitHub account」(授予這個應用程式 GitHub 帳戶存取權)。
      • 如果不是第一次在 GitHub 中設定應用程式:請點選「設定存取權」。個人帳戶的「應用程式」頁面隨即開啟。
    2. 按一下 Cloud Build 資料列中的「設定」。

    3. 選取「Only select repositories」,然後選取「solutions-terraform-cloudbuild-gitops」連結至存放區。

    4. 按一下「儲存」或「安裝」,按鈕標籤會視您的工作流程而異。系統會將你重新導向至「 Cloud de Confiance by S3NS 」,繼續安裝程序。

    5. 使用 Cloud de Confiance by S3NS 帳戶登入。如果系統要求,請授權 Cloud Build 與 GitHub 整合。

    6. 在「Cloud Build」頁面中,選取您的專案。系統會顯示精靈。

    7. 在「Select repository」(選取存放區) 專區中,選取 GitHub 帳戶和 solutions-terraform-cloudbuild-gitops 存放區。

    8. 如果同意條款及細則,請勾選核取方塊,然後按一下「連線」。

    9. 在「建立觸發條件」部分,按一下「建立觸發條件」:

      1. 新增觸發條件名稱,例如 push-to-branch。請記下這個觸發條件名稱,後續步驟會用到。
      2. 在「事件」部分,選取「推送至分支版本」。
      3. 在「來源」部分,選取「分支」欄位中的 .*。
      4. 點選「建立」。

    Cloud Build GitHub 應用程式已設定完成,GitHub 存放區也已連結至 Cloud de Confiance 專案。從現在起,GitHub 存放區的變更會觸發 Cloud Build 執行作業,並使用 GitHub Checks 將結果回報給 GitHub。

    在新的功能分支版本中變更您的環境設定

    到目前為止,您已完成了大部分的環境設定。現在請在您的開發環境中進行一些程式碼變更。

    1. 在 GitHub 中,前往分支的存放區的主頁面。

      https://github.com/YOUR_GITHUB_USERNAME/solutions-terraform-cloudbuild-gitops
      
    2. 確認您位於 dev 分支。

    3. 如要開啟檔案進行編輯,請前往 modules/firewall/main.tf 檔案,然後按一下鉛筆圖示。

    4. 在第 30 行,修正 target_tags 欄位中的 "http-server2" 錯字。

      此值必須是 "http-server"。

    5. 在頁面底部新增提交訊息,例如「Fixing http firewall target」,然後選取「Create a new branch for this commit and start a pull request」(為這次提交建立新分支並啟動提取要求)。

    6. 按一下「提議變更」。

    7. 在下一頁中,按一下 [Create pull request] (建立提取要求) 以開啟包含您的變更的新提取要求。

      開啟提取要求後,系統會自動啟動 Cloud Build 工作。

    8. 按一下 [Show all checks] (顯示所有檢查結果),然後等待檢查作業變成綠色。

      在提取要求中顯示所有檢查結果。

    9. 按一下「詳細資料」即可查看更多資訊,包括 terraform plan 的輸出內容 (位於「在 Google Cloud Build 中查看更多詳細資料」連結)。

    請勿合併提取要求。

    請注意,Cloud Build 工作執行了 cloudbuild.yaml 檔案中定義的管道。如先前所述,這個管道會根據要擷取的分支版本而有不同的行為。建構作業會檢查 $BRANCH_NAME 變數是否與任何環境資料夾相符。如果是,Cloud Build 會為該環境執行 terraform plan。否則,Cloud Build 會針對所有環境執行 terraform plan,確保建議的變更適用於所有環境。如果其中任何一項計畫無法執行,建構作業就會失敗。

    - id: 'tf plan'
      name: 'hashicorp/terraform:1.0.0'
      entrypoint: 'sh'
      args: 
      - '-c'
      - | 
          if [ -d "environments/$BRANCH_NAME/" ]; then
            cd environments/$BRANCH_NAME
            terraform plan
          else
            for dir in environments/*/
            do 
              cd ${dir}   
              env=${dir%*/}
              env=${env#*/}  
              echo ""
              echo "*************** TERRAFORM PLAN ******************"
              echo "******* At environment: ${env} ********"
              echo "*************************************************"
              terraform plan || exit 1
              cd ../../
            done
          fi 

    同樣地,terraform apply 指令會針對環境分支執行,但會完全忽略任何其他情況。在本節中,您已將程式碼變更提交至新分支版本,因此基礎架構部署作業未套用至 Cloud de Confiance 專案。

    - id: 'tf apply'
      name: 'hashicorp/terraform:1.0.0'
      entrypoint: 'sh'
      args: 
      - '-c'
      - | 
          if [ -d "environments/$BRANCH_NAME/" ]; then
            cd environments/$BRANCH_NAME      
            terraform apply -auto-approve
          else
            echo "***************************** SKIPPING APPLYING *******************************"
            echo "Branch '$BRANCH_NAME' does not represent an official environment."
            echo "*******************************************************************************"
          fi

    強制執行 Cloud Build,確保建構作業成功後再合併分支版本

    如要確保只有在個別 Cloud Build 作業成功執行時才能進行合併,請繼續執行以下步驟:

    1. 在 GitHub 中,前往分支的存放區的主頁面。

      https://github.com/YOUR_GITHUB_USERNAME/solutions-terraform-cloudbuild-gitops
      
    2. 在您的存放區名稱之下,按一下 [Settings] (設定)。

    3. 按一下左選單中的「分店」。

    4. 在「Branch protection rules」(分支版本保護規則) 之下,按一下 [Add rule] (新增規則)。

    5. 在「Branch name pattern」(分支名稱模式) 中輸入 dev。

    6. 在「保護相符分支版本」部分,選取「必須通過狀態檢查才能合併」。

    7. 搜尋先前建立的 Cloud Build 自動建構觸發條件名稱。

    8. 點選「建立」。

    9. 重複步驟 3 到 7,將「Branch name pattern」(分支名稱模式) 設為 prod。

    這項設定對於保護 dev 和 prod 分支都非常重要。也就是說,必須先將修訂版本推送至另一個分支版本,然後才能將它們合併至受保護的分支版本。在本教學課程中,保護措施要求 Cloud Build 執行作業必須成功,才能允許合併。

    將變更推行至開發環境

    您有一個正在等待合併的提取要求。現在請將所需狀態套用至 dev 環境。

    1. 在 GitHub 中,前往分支的存放區的主頁面。

      https://github.com/YOUR_GITHUB_USERNAME/solutions-terraform-cloudbuild-gitops
      
    2. 在您的存放區名稱之下,按一下 [Pull requests] (提取要求)。

    3. 按一下您剛剛建立的提取要求。

    4. 按一下 [Merge pull request] (合併提取要求),然後按一下 [Confirm merge] (確認合併)。

      確認合併。

    5. 檢查是否已觸發新的 Cloud Build:

      前往 Cloud Build 頁面

    6. 開啟版本並檢查記錄檔。

      當該版本完成後,您會看到如下的內容:

      Step #3 - "tf apply": external_ip = EXTERNAL_IP_VALUE
      Step #3 - "tf apply": firewall_rule = dev-allow-http
      Step #3 - "tf apply": instance_name = dev-apache2-instance
      Step #3 - "tf apply": network = dev
      Step #3 - "tf apply": subnet = dev-subnet-01
      
    7. 複製 EXTERNAL_IP_VALUE,然後在網路瀏覽器中開啟該地址。

      http://EXTERNAL_IP_VALUE
      

      佈建程序可能需要幾秒鐘才能啟動 VM 並傳播防火牆規則。最後,網頁瀏覽器會顯示「Environment: dev」。

    8. 前往 Cloud Storage bucket 中的 Terraform 狀態檔案。

      https://storage.cloud.google.com/PROJECT_ID-tfstate/env/dev/default.tfstate
      

    將變更推送至正式環境

    開發環境測試完成後,即可將基礎架構程式碼升級至正式版。

    1. 在 GitHub 中,前往分支的存放區的主頁面。

      https://github.com/YOUR_GITHUB_USERNAME/solutions-terraform-cloudbuild-gitops
      
    2. 在您的存放區名稱之下,按一下 [Pull requests] (提取要求)。

    3. 按一下 [New pull request] (新的提取要求)。

    4. 針對「base repository」(基本存放區) 選取您剛剛分支的存放區。

    5. 在「base」部分,從自己的基礎存放區選取 prod。如要比較,請選取 dev。

      比較變更。

    6. 按一下「建立提取要求」。

    7. 在「title」中輸入標題 (例如 Promoting networking changes),然後按一下「Create pull request」。

    8. 查看建議的變更,包括 Cloud Build 的 terraform plan詳細資料,然後按一下「Merge pull request」(合併提取要求)。

    9. 按一下 [Confirm merge] (確認合併)。

    10. 在 Cloud de Confiance 控制台中開啟「建構記錄」頁面,查看變更是否已套用至正式環境:

      前往 Cloud Build 頁面

    11. 等待版本完成,然後檢查記錄檔。

      在記錄檔的末尾,您會看到如下的內容:

      Step #3 - "tf apply": external_ip = EXTERNAL_IP_VALUE
      Step #3 - "tf apply": firewall_rule = prod-allow-http
      Step #3 - "tf apply": instance_name = prod-apache2-instance
      Step #3 - "tf apply": network = prod
      Step #3 - "tf apply": subnet = prod-subnet-01
      
    12. 複製 EXTERNAL_IP_VALUE,然後在網路瀏覽器中開啟該地址。

      http://EXTERNAL_IP_VALUE
      

      佈建程序可能需要幾秒鐘才能啟動 VM 並傳播防火牆規則。最後,網路瀏覽器會顯示「Environment: prod」。

    13. 前往 Cloud Storage bucket 中的 Terraform 狀態檔案。

      https://storage.cloud.google.com/PROJECT_ID-tfstate/env/prod/default.tfstate
      

    您已成功在 Cloud Build 中設定無伺服器基礎架構即程式碼管道。日後,您可以嘗試以下做法:

    • 針對不同的用途新增部署。
    • 建立其他環境以反映您的需求。
    • 針對每個環境使用一個專案,而不是針對每個環境使用一個虛擬私人雲端。

    清除所用資源

    完成本教學課程後,請清除您在Cloud de Confiance 上建立的資源,以免日後產生相關費用。

    刪除專案

    1. 前往 Cloud de Confiance 控制台的「Manage resources」(管理資源) 頁面。

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

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

    刪除 GitHub 存放區

    如要避免封鎖 GitHub 存放區中的新提取要求,可以刪除分支保護規則:

    1. 在 GitHub 中,前往您建立分支的存放區主頁面。
    2. 在您的存放區名稱之下,按一下 [Settings] (設定)。
    3. 按一下左選單中的「分店」。
    4. 在「Branch protection rules」(分支保護規則) 部分下方,點選 dev 和 prod 列的「Delete」(刪除) 按鈕。

    您也可以從 GitHub 完全解除安裝 Cloud Build 應用程式:

    1. 前往 GitHub 的「Applications」設定。

      前往 GitHub 應用程式頁面

    2. 在「Installed GitHub Apps」分頁中,點選「Cloud Build」列的「Configure」。然後在「危險區域」部分,點按「解除安裝 Google Cloud Builder」列中的「解除安裝」按鈕。

      頁面頂端會顯示「一切就緒。系統已將工作加入佇列,準備解除安裝 Google Cloud Build。」

    3. 在「已授權的 GitHub 應用程式」分頁中,按一下「Google Cloud Build」列的「撤銷」按鈕,然後在彈出式視窗中點選「我瞭解,要撤銷存取權」。

    如要刪除 GitHub 存放區:

    1. 在 GitHub 中,前往您建立分支的存放區主頁面。
    2. 在您的存放區名稱之下,按一下 [Settings] (設定)。
    3. 向下捲動至「危險區域」。
    4. 按一下「Delete this repository」(刪除這個存放區),然後按照確認步驟操作。

    後續步驟