部署至 Compute Engine


本指南說明如何使用 Cloud Build 和 Terraform,在 Compute Engine 代管執行個體群組 (MIG) 上執行藍綠部署,確保服務不中斷。

Cloud Build 可自動執行各種開發人員程序,包括建構應用程式,以及將應用程式部署至各種 Google Cloud 執行階段 ,例如 Compute Engine、Google Kubernetes EngineGKE EnterpriseCloud Run 函式

Compute Engine MIG 可讓您在多個相同的虛擬機器 (VM) 上執行應用程式。您可以利用自動調度資源、自動修復、區域 (多可用區) 部署以及自動更新等自動化 MIG 服務,讓工作負載具有可擴充性和高可用性。您將使用藍/綠持續部署模型,瞭解如何逐步將使用者流量從一個 MIG (藍色) 轉移至另一個 MIG (綠色),這兩個 MIG 都會在正式環境中執行。

設計總覽

下圖顯示本文所述程式碼範例使用的藍/綠部署模型:

藍綠模型

這個模型大致包含下列元件:

  • 兩個 Compute Engine VM 集區:藍色和綠色。
  • 三個外部 HTTP(S) 負載平衡器:
    • 藍/綠負載平衡器,可將使用者流量導向藍色或綠色 VM 執行個體集區。
    • 藍色負載平衡器,可將 QA 工程師和開發人員的流量,路由至藍色 VM 執行個體集區。
    • 綠色負載平衡器,可將 QA 工程師和開發人員的流量轉送至綠色執行個體集區。
  • 兩組使用者:
    • 可存取藍色/綠色負載平衡器的使用者,這會將他們導向藍色或綠色執行個體集區。
    • 需要存取兩組集區的品質確保工程師和開發人員,以進行開發和測試。他們可以存取藍色和綠色負載平衡器,分別將他們導向藍色執行個體集區和綠色執行個體集區。

藍色和綠色 VM 集區會以 Compute Engine MIG 實作,外部 IP 位址則會透過外部 HTTP(S) 負載平衡器,轉送至 MIG 中的 VM。本文所述的程式碼範例使用 Terraform 設定這項基礎架構。

下圖說明部署期間發生的開發人員作業:

開發人員營運流程

在上圖中,紅箭頭代表您首次設定部署基礎架構時發生的啟動程序流程,藍箭頭則代表每次部署時發生的 GitOps 流程。

如要設定這項基礎架構,請執行設定指令碼,啟動啟動程序並設定 GitOps 流程的元件。

設定指令碼會執行 Cloud Build 管道,執行下列作業:

  • Cloud Source Repositories 中建立名為 copy-of-gcp-mig-simple 的存放區,並將 GitHub 範例存放區的原始碼複製到 Cloud Source Repositories 的存放區。
  • 建立兩個名為 applydestroyCloud Build 觸發條件

apply 觸發條件會附加至 Cloud Source Repositories 中名為 main.tfvars 的 Terraform 檔案。這個檔案包含代表藍色和綠色負載平衡器的 Terraform 變數。

如要設定部署作業,請更新 main.tfvars 檔案中的變數。apply 觸發程序會執行 Cloud Build 管道,該管道會執行 tf_apply 並執行下列作業:

  • 建立兩個 Compute Engine MIG (一個用於綠色,一個用於藍色)、四個 Compute Engine VM 執行個體 (兩個用於綠色 MIG,兩個用於藍色 MIG)、三個負載平衡器 (藍色、綠色和分配器),以及三個公用 IP 位址。
  • 列印 IP 位址,您可以使用這些位址,在藍色和綠色執行個體中查看已部署的應用程式。

系統會手動觸發 destroy 觸發條件,刪除 apply 觸發條件建立的所有資源。

目標

  • 使用 Cloud Build 和 Terraform 設定外部 HTTP(S) 負載平衡器,後端為 Compute Engine VM 執行個體群組。

  • 在 VM 執行個體上執行藍綠部署。

費用

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

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

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

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

事前準備

  1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. Install the Google Cloud CLI.

  3. 如果您使用外部識別資訊提供者 (IdP),請先 使用聯合身分登入 gcloud CLI

  4. 如要初始化 gcloud CLI,請執行下列指令:

    gcloud init
  5. Create or select a Google Cloud project.

    • Create a Google Cloud project:

      gcloud projects create PROJECT_ID

      Replace PROJECT_ID with a name for the Google Cloud project you are creating.

    • Select the Google Cloud project that you created:

      gcloud config set project PROJECT_ID

      Replace PROJECT_ID with your Google Cloud project name.

  6. Verify that billing is enabled for your Google Cloud project.

  7. Install the Google Cloud CLI.

  8. 如果您使用外部識別資訊提供者 (IdP),請先 使用聯合身分登入 gcloud CLI

  9. 如要初始化 gcloud CLI,請執行下列指令:

    gcloud init
  10. Create or select a Google Cloud project.

    • Create a Google Cloud project:

      gcloud projects create PROJECT_ID

      Replace PROJECT_ID with a name for the Google Cloud project you are creating.

    • Select the Google Cloud project that you created:

      gcloud config set project PROJECT_ID

      Replace PROJECT_ID with your Google Cloud project name.

  11. Verify that billing is enabled for your Google Cloud project.

  12. 立即體驗

    1. 從 Google 程式碼範例存放區執行設定指令碼:

      bash <(curl https://raw.githubusercontent.com/GoogleCloudPlatform/cloud-build-samples/main/mig-blue-green/setup.sh)
      
    2. 當設定指令碼要求使用者同意時,請輸入「yes」

      指令碼會在幾秒內完成執行。

    3. 在 Google Cloud 控制台中,開啟 Cloud Build 的「建構記錄」頁面:

      開啟「建構記錄」頁面

    4. 按一下最新版本。

      您會看到「建構詳細資料」頁面,其中顯示 Cloud Build 管道,包含三個建構步驟:第一個建構步驟會在 Cloud Source Repositories 中建立存放區;第二個步驟會將 GitHub 中範例存放區的內容複製到 Cloud Source Repositories;第三個步驟則會新增兩個建構觸發條件。

    5. 開啟 Cloud Source Repositories:

      開啟 Cloud Source Repositories

    6. 在存放區清單中,按一下 copy-of-gcp-mig-simple

      在頁面底部的「History」(記錄) 分頁中,您會看到一個含有說明的提交內容, A copy of https://github.com/GoogleCloudPlatform/cloud-build-samples.git 這是 Cloud Build 為了建立名為 copy-of-gcp-mig-simple 的存放區而提交的內容。

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

      開啟「觸發條件」頁面

    8. 您會看到兩個名為 applydestroy 的建構觸發條件。apply 觸發程序會附加至 main 分支中的 infra/main.tfvars 檔案。每當檔案更新時,就會執行這項觸發條件。destroy 觸發條件是手動觸發條件。

    9. 如要開始部署程序,請更新 infra/main.tfvars 檔案:

      1. 在終端機視窗中,建立並前往名為 deploy-compute-engine 的資料夾:

        mkdir ~/deploy-compute-engine
        cd ~/deploy-compute-engine
        
      2. 複製 copy-of-gcp-mig-simple 存放區:

        gcloud source repos clone copy-of-mig-blue-green
        
      3. 前往複製的目錄:

        cd ./copy-of-mig-blue-green
        
      4. 更新 infra/main.tfvars,將藍色換成綠色:

        sed -i'' -e 's/blue/green/g' infra/main.tfvars
        
      5. 新增更新後的檔案:

        git add .
        
      6. 修訂檔案:

        git commit -m "Promote green"
        
      7. 推送檔案:

        git push
        

        變更 infra/main.tfvars 會觸發 apply 觸發程序,進而啟動部署作業。

    10. 開啟 Cloud Source Repositories:

      開啟 Cloud Source Repositories

    11. 在存放區清單中,按一下 copy-of-gcp-mig-simple

      頁面底部的「記錄」分頁會顯示含有 Promote green 說明的提交。

    12. 如要查看 apply 觸發條件的執行情況,請在 Google Cloud 控制台中開啟「建構記錄」頁面:

      開啟「建構記錄」頁面

    13. 按一下第一個建構作業,開啟「Build details」(建構作業詳細資料) 頁面。

      您會看到 apply 觸發管道,其中包含兩個建構步驟。第一個建構步驟會執行 Terraform apply,為部署作業建立 Compute Engine 和負載平衡資源。第二個建構步驟會列印出 IP 位址,您可以在該處查看執行中的應用程式。

    14. 在瀏覽器中開啟與綠色 MIG 對應的 IP 位址。您會看到類似下方的螢幕截圖,顯示部署作業:

      部署作業

    15. 前往 Compute Engine 的「Instance group」(執行個體群組) 頁面,查看藍色和綠色執行個體群組:

      開啟「執行個體群組」頁面

    16. 開啟「VM instances」(VM 執行個體) 頁面,查看四個 VM 執行個體:

      開啟「VM Instance」(VM 執行個體) 頁面

    17. 開啟「外部 IP 位址」頁面,查看三個負載平衡器:

      開啟「外部 IP 位址」頁面

    瞭解程式碼

    這個程式碼範例的原始碼包括:

    • 與設定指令碼相關的原始碼。
    • 與 Cloud Build 管道相關的原始碼。
    • 與 Terraform 範本相關的原始碼。

    設定指令碼

    setup.sh 是設定指令碼,可執行啟動程序,並為藍/綠部署建立元件。指令碼會執行下列作業:

    • 啟用 Cloud Build、Resource Manager、Compute Engine 和 Cloud Source Repositories API。
    • roles/editor 身分與存取權管理角色授予專案中的 Cloud Build 服務帳戶。Cloud Build 必須具備這個角色,才能建立及設定部署作業所需的 GitOps 元件。
    • roles/source.admin 身分與存取權管理角色授予專案中的 Cloud Build 服務帳戶。Cloud Build 服務帳戶必須具備這個角色,才能在專案中建立 Cloud Source Repositories,並將範例 GitHub 存放區的內容複製到 Cloud Source Repositories。
    • 產生名為 bootstrap.cloudbuild.yaml 的內嵌 Cloud Build 管道,該管道會執行下列作業:

      • 在 Cloud Source Repositories 中建立新的存放區。
      • 將範例 GitHub 存放區中的原始碼複製到 Cloud Source Repositories 中的新存放區。
      • 建立套用和刪除建構觸發條件。
    set -e
    
    BLUE='\033[1;34m'
    RED='\033[1;31m'
    GREEN='\033[1;32m'
    NC='\033[0m'
    
    echo -e "\n${GREEN}######################################################"
    echo -e "#                                                    #"
    echo -e "#  Zero-Downtime Blue/Green VM Deployments Using     #"
    echo -e "#  Managed Instance Groups, Cloud Build & Terraform  #"
    echo -e "#                                                    #"
    echo -e "######################################################${NC}\n"
    
    echo -e "\nSTARTED ${GREEN}setup.sh:${NC}"
    
    echo -e "\nIt's ${RED}safe to re-run${NC} this script to ${RED}recreate${NC} all resources.\n"
    echo "> Checking GCP CLI tool is installed"
    gcloud --version > /dev/null 2>&1
    
    readonly EXPLICIT_PROJECT_ID="$1"
    readonly EXPLICIT_CONSENT="$2"
    
    if [ -z "$EXPLICIT_PROJECT_ID" ]; then
        echo "> No explicit project id provided, trying to infer"
        PROJECT_ID="$(gcloud config get-value project)"
    else
        PROJECT_ID="$EXPLICIT_PROJECT_ID"
    fi
    
    if [ -z "$PROJECT_ID" ]; then
        echo "ERROR: GCP project id was not provided as parameter and could not be inferred"
        exit 1
    else
        readonly PROJECT_NUM="$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')"
        if [ -z "$PROJECT_NUM" ]; then
            echo "ERROR: GCP project number could not be determined"
            exit 1
        fi
        echo -e "\nYou are about to:"
        echo -e "  * modify project ${RED}${PROJECT_ID}/${PROJECT_NUM}${NC}"
        echo -e "  * ${RED}enable${NC} various GCP APIs"
        echo -e "  * make Cloud Build ${RED}editor${NC} of your project"
        echo -e "  * ${RED}execute${NC} Cloud Builds and Terraform plans to create"
        echo -e "  * ${RED}4 VMs${NC}, ${RED}3 load balancers${NC}, ${RED}3 public IP addresses${NC}"
        echo -e "  * incur ${RED}charges${NC} in your billing account as a result\n"
    fi
    
    if [ "$EXPLICIT_CONSENT" == "yes" ]; then
      echo "Proceeding under explicit consent"
      readonly CONSENT="$EXPLICIT_CONSENT"
    else
        echo -e "Enter ${BLUE}'yes'${NC} if you want to proceed:"
        read CONSENT
    fi
    
    if [ "$CONSENT" != "yes" ]; then
        echo -e "\nERROR: Aborted by user"
        exit 1
    else
        echo -e "\n......................................................"
        echo -e "\n> Received user consent"
    fi
    
    #
    # Executes action with one randomly delayed retry.
    #
    function do_with_retry {
        COMMAND="$@"
        echo "Trying $COMMAND"
        (eval $COMMAND && echo "Success on first try") || ( \
            echo "Waiting few seconds to retry" &&
            sleep 10 && \
            echo "Retrying $COMMAND" && \
            eval $COMMAND \
        )
    }
    
    echo "> Enabling required APIs"
    # Some of these can be enabled later with Terraform, but I personally
    # prefer to do all API enablement in one place with gcloud.
    gcloud services enable \
        --project=$PROJECT_ID \
        cloudbuild.googleapis.com \
        cloudresourcemanager.googleapis.com \
        compute.googleapis.com \
        sourcerepo.googleapis.com \
        --no-user-output-enabled \
        --quiet
    
    echo "> Adding Cloud Build to roles/editor"
    gcloud projects add-iam-policy-binding \
        "$PROJECT_ID" \
        --member="serviceAccount:$PROJECT_NUM@cloudbuild.gserviceaccount.com" \
        --role='roles/editor' \
        --condition=None \
        --no-user-output-enabled \
        --quiet
    
    echo "> Adding Cloud Build to roles/source.admin"
    gcloud projects add-iam-policy-binding \
        "$PROJECT_ID" \
        --member="serviceAccount:$PROJECT_NUM@cloudbuild.gserviceaccount.com" \
        --condition=None \
        --role='roles/source.admin' \
        --no-user-output-enabled \
        --quiet
    
    echo "> Configuring bootstrap job"
    rm -rf "./bootstrap.cloudbuild.yaml"
    cat <<'EOT_BOOT' > "./bootstrap.cloudbuild.yaml"
    tags:
    - "mig-blue-green-bootstrapping"
    steps:
    - id: create_new_cloud_source_repo
      name: "gcr.io/cloud-builders/gcloud"
      script: |
        #!/bin/bash
        set -e
    
        echo "(Re)Creating source code repository"
    
        gcloud source repos delete \
            "copy-of-mig-blue-green" \
            --quiet || true
    
        gcloud source repos create \
            "copy-of-mig-blue-green" \
            --quiet
    
    - id: copy_demo_source_into_new_cloud_source_repo
      name: "gcr.io/cloud-builders/gcloud"
      env:
        - "PROJECT_ID=$PROJECT_ID"
        - "PROJECT_NUMBER=$PROJECT_NUMBER"
      script: |
        #!/bin/bash
        set -e
    
        readonly GIT_REPO="https://github.com/GoogleCloudPlatform/cloud-build-samples.git"
    
        echo "Cloning demo source repo"
        mkdir /workspace/from/
        cd /workspace/from/
        git clone $GIT_REPO ./original
        cd ./original
    
        echo "Cloning new empty repo"
        mkdir /workspace/to/
        cd /workspace/to/
        gcloud source repos clone \
            "copy-of-mig-blue-green"
        cd ./copy-of-mig-blue-green
    
        echo "Making a copy"
        cp -r /workspace/from/original/mig-blue-green/* ./
    
        echo "Setting git identity"
        git config user.email \
            "$PROJECT_NUMBER@cloudbuild.gserviceaccount.com"
        git config user.name \
            "Cloud Build"
    
        echo "Commit & push"
        git add .
        git commit \
            -m "A copy of $GIT_REPO"
        git push
    
    - id: add_pipeline_triggers
      name: "gcr.io/cloud-builders/gcloud"
      env:
        - "PROJECT_ID=$PROJECT_ID"
      script: |
        #!/bin/bash
        set -e
    
        echo "(Re)Creating destroy trigger"
        gcloud builds triggers delete "destroy" --quiet || true
        gcloud builds triggers create manual \
            --name="destroy" \
            --repo="https://source.developers.google.com/p/$PROJECT_ID/r/copy-of-mig-blue-green" \
            --branch="master" \
            --build-config="pipelines/destroy.cloudbuild.yaml" \
            --repo-type=CLOUD_SOURCE_REPOSITORIES \
            --quiet
    
        echo "(Re)Creating apply trigger"
        gcloud builds triggers delete "apply" --quiet || true
        gcloud builds triggers create cloud-source-repositories \
            --name="apply" \
            --repo="copy-of-mig-blue-green" \
            --branch-pattern="master" \
            --build-config="pipelines/apply.cloudbuild.yaml" \
            --included-files="infra/main.tfvars" \
            --quiet
    
    EOT_BOOT
    
    echo "> Waiting API enablement propagation"
    do_with_retry "(gcloud builds list --project "$PROJECT_ID" --quiet && gcloud compute instances list --project "$PROJECT_ID" --quiet && gcloud source repos list --project "$PROJECT_ID" --quiet) > /dev/null 2>&1" > /dev/null 2>&1
    
    echo "> Executing bootstrap job"
    gcloud beta builds submit \
        --project "$PROJECT_ID" \
        --config ./bootstrap.cloudbuild.yaml \
        --no-source \
        --no-user-output-enabled \
        --quiet
    rm ./bootstrap.cloudbuild.yaml
    
    echo -e "\n${GREEN}All done. Now you can:${NC}"
    echo -e "  * manually run 'apply' and 'destroy' triggers to manage deployment lifecycle"
    echo -e "  * commit change to 'infra/main.tfvars' and see 'apply' pipeline trigger automatically"
    
    echo -e "\n${GREEN}Few key links:${NC}"
    echo -e "  * Dashboard: https://console.cloud.google.com/home/dashboard?project=$PROJECT_ID"
    echo -e "  * Repo: https://source.cloud.google.com/$PROJECT_ID/copy-of-mig-blue-green"
    echo -e "  * Cloud Build Triggers: https://console.cloud.google.com/cloud-build/triggers;region=global?project=$PROJECT_ID"
    echo -e "  * Cloud Build History: https://console.cloud.google.com/cloud-build/builds?project=$PROJECT_ID"
    
    echo -e "\n............................."
    
    echo -e "\n${GREEN}COMPLETED!${NC}"

    Cloud Build 管道

    apply.cloudbuild.yamldestroy.cloudbuild.yaml 是設定指令碼用來設定 GitOps 流程資源的 Cloud Build 設定檔。apply.cloudbuild.yaml 包含兩個建構步驟:

    • tf_apply build 建構步驟,呼叫 tf_install_in_cloud_build_step 函式來安裝 Terraform。tf_apply ,用於建立 GitOps 流程中使用的資源。函式 tf_install_in_cloud_build_steptf_apply 定義於 bash_utils.sh,建構步驟會使用 source 指令呼叫這些函式。
    • describe_deployment 建構步驟,呼叫 describe_deployment 函式,輸出負載平衡器的 IP 位址。

    destroy.cloudbuild.yaml 呼叫 tf_destroy,刪除 tf_apply 建立的所有資源。

    函式 tf_install_in_cloud_build_steptf_applydescribe_deploymenttf_destroy 是在 bash_utils.sh 檔案中定義。建構設定檔會使用 source 指令呼叫函式。

    steps:
      - id: run-terraform-apply
        name: "gcr.io/cloud-builders/gcloud"
        env:
          - "PROJECT_ID=$PROJECT_ID"
        script: |
          #!/bin/bash
          set -e
          source /workspace/lib/bash_utils.sh
          tf_install_in_cloud_build_step
          tf_apply
    
      - id: describe-deployment
        name: "gcr.io/cloud-builders/gcloud"
        env:
          - "PROJECT_ID=$PROJECT_ID"
        script: |
          #!/bin/bash
          set -e
          source /workspace/lib/bash_utils.sh
          describe_deployment
    
    tags:
      - "mig-blue-green-apply"
    steps:
      - id: run-terraform-destroy
        name: "gcr.io/cloud-builders/gcloud"
        env:
          - "PROJECT_ID=$PROJECT_ID"
        script: |
          #!/bin/bash
          set -e
          source /workspace/lib/bash_utils.sh
          tf_install_in_cloud_build_step
          tf_destroy
    
    tags:
      - "mig-blue-green-destroy"

    下列程式碼顯示 bash_utils.sh 中定義的 tf_install_in_cloud_build_step 函式。建構設定檔會呼叫這個函式,即時安裝 Terraform。這會建立 Cloud Storage 值區,用於記錄 Terraform 狀態。

    function tf_install_in_cloud_build_step {
        echo "Installing deps"
        apt update
        apt install \
            unzip \
            wget \
            -y
    
        echo "Manually installing Terraform"
        wget https://releases.hashicorp.com/terraform/1.3.4/terraform_1.3.4_linux_386.zip
        unzip -q terraform_1.3.4_linux_386.zip
        mv ./terraform /usr/bin/
        rm -rf terraform_1.3.4_linux_386.zip
    
        echo "Verifying installation"
        terraform -v
    
        echo "Creating Terraform state storage bucket $BUCKET_NAME"
        gcloud storage buckets create \
            "gs://$BUCKET_NAME" || echo "Already exists..."
    
        echo "Configure Terraform provider and state bucket"
    cat <<EOT_PROVIDER_TF > "/workspace/infra/provider.tf"
    terraform {
      required_version = ">= 0.13"
      backend "gcs" {
        bucket = "$BUCKET_NAME"
      }
      required_providers {
        google = {
          source  = "hashicorp/google"
          version = ">= 3.77, < 5.0"
        }
      }
    }
    EOT_PROVIDER_TF
    
        echo "$(cat /workspace/infra/provider.tf)"
    }

    下列程式碼片段顯示 bash_utils.sh 中定義的 tf_apply 函式。這個函式會先呼叫 terraform init 載入所有模組和自訂程式庫,然後執行 terraform applymain.tfvars 檔案載入變數。

    function tf_apply {
        echo "Running Terraform init"
        terraform \
            -chdir="$TF_CHDIR" \
            init
    
        echo "Running Terraform apply"
        terraform \
            -chdir="$TF_CHDIR" \
            apply \
            -auto-approve \
            -var project="$PROJECT_ID" \
            -var-file="main.tfvars"
    }

    下列程式碼片段顯示 bash_utils.sh 中定義的 describe_deployment 函式。這個指令會使用 gcloud compute addresses describe,依名稱擷取負載平衡器的 IP 位址,然後列印出來。

    function describe_deployment {
        NS="ns1-"
        echo -e "Deployment configuration:\n$(cat infra/main.tfvars)"
        echo -e \
          "Here is how to connect to:" \
          "\n\t* active color MIG: http://$(gcloud compute addresses describe ${NS}splitter-address-name --region=us-west1 --format='value(address)')/" \
          "\n\t* blue color MIG: http://$(gcloud compute addresses describe ${NS}blue-address-name --region=us-west1 --format='value(address)')/" \
          "\n\t* green color MIG: http://$(gcloud compute addresses describe ${NS}green-address-name --region=us-west1 --format='value(address)')/"
        echo "Good luck!"
    }

    下列程式碼片段顯示 bash_utils.sh 中定義的 tf_destroy 函式。這個函式會呼叫 terraform init,載入所有模組和自訂程式庫,然後執行 terraform destroy,卸載 Terraform 變數。

    function tf_destroy {
        echo "Running Terraform init"
        terraform \
            -chdir="$TF_CHDIR" \
            init
    
        echo "Running Terraform destroy"
        terraform \
            -chdir="$TF_CHDIR" \
            destroy \
            -auto-approve \
            -var project="$PROJECT_ID" \
            -var-file="main.tfvars"
    }

    Terraform 範本

    您可以在 copy-of-gcp-mig-simple/infra/ 資料夾中找到所有 Terraform 設定檔和變數。

    • main.tf:這是 Terraform 設定檔
    • main.tfvars:這個檔案會定義 Terraform 變數。
    • mig/splitter/:這些資料夾包含定義負載平衡器的模組。mig/ 資料夾包含 Terraform 設定檔,用於定義藍色和綠色負載平衡器的 MIG。藍色和綠色 MIG 完全相同,因此只需定義一次,並針對藍色和綠色物件例項化。分割器負載平衡器的 Terraform 設定檔位於 splitter/ 資料夾中。

    以下程式碼片段顯示 infra/main.tfvars 的內容。其中包含三個變數:兩個變數用於決定要將哪個應用程式版本部署到藍色和綠色集區,另一個變數則用於決定有效顏色:藍色或綠色。變更這個檔案會觸發部署作業。

    MIG_VER_BLUE     = "v1"
    MIG_VER_GREEN    = "v1"
    MIG_ACTIVE_COLOR = "blue"

    以下是 infra/main.tf 的程式碼片段。這個程式碼片段:

    • 為專案定義變數 Google Cloud 。
    • Google 已設為 Terraform 供應商。
    • 系統會為命名空間定義變數。Terraform 建立的所有物件都會加上這個變數的前置字元,因此應用程式的多個版本可以部署在同一個專案中,且物件名稱不會彼此衝突。
    • 變數 MIG_VER_BLUEMIG_VER_BLUEMIG_ACTIVE_COLORinfra/main.tfvars 檔案中變數的繫結。
    variable "project" {
      type        = string
      description = "GCP project we are working in."
    }
    
    provider "google" {
      project = var.project
      region  = "us-west1"
      zone    = "us-west1-a"
    }
    
    variable "ns" {
      type        = string
      default     = "ns1-"
      description = "The namespace used for all resources in this plan."
    }
    
    variable "MIG_VER_BLUE" {
      type        = string
      description = "Version tag for 'blue' deployment."
    }
    
    variable "MIG_VER_GREEN" {
      type        = string
      description = "Version tag for 'green' deployment."
    }
    
    variable "MIG_ACTIVE_COLOR" {
      type        = string
      description = "Active color (blue | green)."
    }

    下列 infra/main.tf 的程式碼片段顯示了分割器模組的例項。這個模組會接收有效顏色,讓分割器負載平衡器知道要將應用程式部署到哪個 MIG。

    module "splitter-lb" {
      source               = "./splitter"
      project              = var.project
      ns                   = "${var.ns}splitter-"
      active_color         = var.MIG_ACTIVE_COLOR
      instance_group_blue  = module.blue.google_compute_instance_group_manager_default.instance_group
      instance_group_green = module.green.google_compute_instance_group_manager_default.instance_group
    }

    infra/main.tf 中的下列程式碼片段會為 Blue 和 Green MIG 定義兩個相同的模組。這個函式會接收顏色、網路和子網路,這些項目是在分割器模組中定義。

    module "blue" {
      source                               = "./mig"
      project                              = var.project
      app_version                          = var.MIG_VER_BLUE
      ns                                   = var.ns
      color                                = "blue"
      google_compute_network               = module.splitter-lb.google_compute_network
      google_compute_subnetwork            = module.splitter-lb.google_compute_subnetwork_default
      google_compute_subnetwork_proxy_only = module.splitter-lb.google_compute_subnetwork_proxy_only
    }
    
    module "green" {
      source                               = "./mig"
      project                              = var.project
      app_version                          = var.MIG_VER_GREEN
      ns                                   = var.ns
      color                                = "green"
      google_compute_network               = module.splitter-lb.google_compute_network
      google_compute_subnetwork            = module.splitter-lb.google_compute_subnetwork_default
      google_compute_subnetwork_proxy_only = module.splitter-lb.google_compute_subnetwork_proxy_only
    }

    檔案 splitter/main.tf 會定義為分割器 MIG 建立的物件。以下是 splitter/main.tf 的程式碼片段,其中包含在綠色和藍色 MIG 之間切換的邏輯。這項服務以 google_compute_region_backend_service 為後端,可將流量導向兩個後端區域:var.instance_group_bluevar.instance_group_greencapacity_scaler:定義要轉送的流量比例。

    以下程式碼會將 100% 的流量導向指定顏色,但您可以更新此程式碼,進行 Canary 部署,將流量導向部分使用者。

    resource "google_compute_region_backend_service" "default" {
      name                  = local.l7-xlb-backend-service
      region                = "us-west1"
      load_balancing_scheme = "EXTERNAL_MANAGED"
      health_checks         = [google_compute_region_health_check.default.id]
      protocol              = "HTTP"
      session_affinity      = "NONE"
      timeout_sec           = 30
      backend {
        group           = var.instance_group_blue
        balancing_mode  = "UTILIZATION"
        capacity_scaler = var.active_color == "blue" ? 1 : 0
      }
      backend {
        group           = var.instance_group_green
        balancing_mode  = "UTILIZATION"
        capacity_scaler = var.active_color == "green" ? 1 : 0
      }
    }

    mig/main.tf 檔案會定義與 Blue 和 Green MIG 相關的物件。這個檔案中的下列程式碼片段定義了用於建立 VM 集區的 Compute Engine 執行個體範本。請注意,這個執行個體範本的 Terraform 生命週期屬性已設為 create_before_destroy。 這是因為更新集區版本時,如果舊版集區仍在使用範本,您就無法使用範本建立新版集區。但如果在新範本建立前,舊版集區已遭毀損,集區就會有一段時間無法運作。為避免這種情況,我們將 Terraform 生命週期設為 create_before_destroy,以便先建立較新版本的 VM 集區,再刪除舊版。

    resource "google_compute_instance_template" "default" {
      name = local.l7-xlb-backend-template
      disk {
        auto_delete  = true
        boot         = true
        device_name  = "persistent-disk-0"
        mode         = "READ_WRITE"
        source_image = "projects/debian-cloud/global/images/family/debian-10"
        type         = "PERSISTENT"
      }
      labels = {
        managed-by-cnrm = "true"
      }
      machine_type = "n1-standard-1"
      metadata = {
        startup-script = <<EOF
        #! /bin/bash
        sudo apt-get update
        sudo apt-get install apache2 -y
        sudo a2ensite default-ssl
        sudo a2enmod ssl
        vm_hostname="$(curl -H "Metadata-Flavor:Google" \
        http://169.254.169.254/computeMetadata/v1/instance/name)"
        sudo echo "<html><body style='font-family: Arial; margin: 64px; background-color: light${var.color};'><h3>Hello, World!<br><br>version: ${var.app_version}<br>ns: ${var.ns}<br>hostname: $vm_hostname</h3></body></html>" | \
        tee /var/www/html/index.html
        sudo systemctl restart apache2
        EOF
      }
      network_interface {
        access_config {
          network_tier = "PREMIUM"
        }
        network    = var.google_compute_network.id
        subnetwork = var.google_compute_subnetwork.id
      }
      region = "us-west1"
      scheduling {
        automatic_restart   = true
        on_host_maintenance = "MIGRATE"
        provisioning_model  = "STANDARD"
      }
      tags = ["load-balanced-backend"]
    
      # NOTE: the name of this resource must be unique for every update;
      #       this is wy we have a app_version in the name; this way
      #       new resource has a different name vs old one and both can
      #       exists at the same time
      lifecycle {
        create_before_destroy = true
      }
    }

    清除所用資源

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

    刪除個別資源

    1. 刪除套用觸發條件建立的 Compute Engine 資源:

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

        開啟「觸發條件」頁面

      2. 在「觸發條件」表格中,找到對應於「destroy」觸發條件的資料列,然後按一下「執行」。觸發程序執行完畢後,系統會刪除 apply 觸發程序建立的資源。

    2. 在終端機視窗中執行下列指令,刪除在啟動程序中建立的資源:

      bash <(curl https://raw.githubusercontent.com/GoogleCloudPlatform/cloud-build-samples/main/mig-blue-green/teardown.sh)
      

    刪除專案

      Delete a Google Cloud project:

      gcloud projects delete PROJECT_ID

    後續步驟