部署至 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 (綠色),兩者皆在實際工作環境中執行。

設計總覽

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

藍綠模型

整體來說,這個模型包含下列元件:

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

藍色和綠色 VM 資源池會做為 Compute Engine MIG 實作,並使用外部 HTTP(s) 負載平衡器,將外部 IP 位址轉送至 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 位址,您可以使用這些 IP 位址查看藍色和綠色執行個體中已部署的應用程式。

手動觸發 destroy 觸發條件,藉此刪除 apply 觸發條件建立的所有資源。

目標

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

  • 在 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. If you're using an external identity provider (IdP), you must first sign in to the gcloud CLI with your federated identity.

  4. To initialize the gcloud CLI, run the following command:

    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. Make sure that billing is enabled for your Google Cloud project.

  7. Install the Google Cloud CLI.

  8. If you're using an external identity provider (IdP), you must first sign in to the gcloud CLI with your federated identity.

  9. To initialize the gcloud CLI, run the following command:

    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. Make sure that billing is enabled for your Google Cloud project.

立即體驗

  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 的「Build history」(建構歷史記錄) 頁面:

    開啟「Build History」(版本記錄) 頁面

  4. 按一下最新版本。

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

  5. 開啟 Cloud Source Repositories:

    Open 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:

    Open Cloud Source Repositories

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

    您會在頁面底部的「歷史記錄」分頁中,看到描述為 Promote green 的提交記錄。

  12. 如要查看 apply 觸發事件的執行情形,請在 Google Cloud 控制台中開啟「Build history」(建構歷史記錄) 頁面:

    開啟「Build History」(版本記錄) 頁面

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

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

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

    部署作業

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

    開啟「Instance group」(執行個體群組) 頁面

  16. 開啟「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 是 Cloud Build 設定檔,可讓設定指令碼為 GitOps 流程設定資源。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 apply 來從 main.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 的以下程式碼片段顯示了 splitter 模組的例項化。這個模組會擷取有效顏色,讓分割器負載平衡器知道要部署哪個 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 程式碼片段會為藍色和綠色 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 定義了與藍色和綠色 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. 在「Triggers」表格中找出對應於「destroy」觸發事件的資料列,然後按一下「Run」。觸發事件執行完畢後,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

後續步驟