部署到 Compute Engine


本指南介绍如何使用 Cloud Build 和 Terraform 在 Compute Engine 代管式实例组 (MIG) 上执行零停机蓝绿部署。

借助 Cloud Build,您可以自动执行各种开发者流程,包括构建应用并将其部署到各种 Google Cloud 运行时,例如 Compute Engine、Google Kubernetes EngineGKE EnterpriseCloud Functions

借助 Compute Engine MIG,您可以在多个相同的虚拟机 (VM) 上运行应用。您可以利用自动化 MIG 服务使工作负载具有可伸缩性和高可用性,这些服务包括自动伸缩、自动修复、区域(多可用区)部署和自动更新。使用蓝绿持续部署模型,您将学习如何将用户流量从一个 MIG(蓝色)逐步转移到另一个 MIG(绿色),这两个 MIG 都在生产环境中运行。

设计概览

下图显示了本文档中描述的代码示例使用的蓝绿部署模型:

蓝/绿型号

概括来讲,此模型包括以下组成部分:

  • 两个 Compute Engine 虚拟机池:蓝色和绿色。
  • 三个外部 HTTP(S) 负载平衡器:
    • 蓝/绿负载均衡器,用于将来自最终用户的流量路由到虚拟机实例的蓝色或绿色池。
    • 一个蓝色负载均衡器,用于将来自质量检查工程师和开发者的流量路由到蓝色虚拟机实例池。
    • 一个绿色负载均衡器,用于将来自 QA 工程师和开发者的流量路由到绿色实例池。
  • 两组用户:
    • 有权访问蓝/绿负载均衡器的最终用户,该负载均衡器会将他们指向蓝/绿实例池。
    • 需要访问这两组数据池以进行开发和测试的质量检查工程师和开发者。他们可以访问蓝色负载平衡器和绿色负载平衡器,后者分别路由到蓝色实例池和绿色实例池。

蓝色虚拟机池和绿色虚拟机池以 Compute Engine MIG 的形式实现,外部 IP 地址使用外部 HTTP(S) 负载平衡器路由到 MIG 中的虚拟机。本文档中描述的代码示例使用 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 虚拟机实例(两个用于绿色 MIG,两个用于蓝色 MIG)、三个负载平衡器(蓝色、绿色和拆分器)和三个公共 IP 地址。
  • 输出可用于查看蓝色和绿色实例中已部署应用的 IP 地址。

销毁触发器会手动触发,以删除应用触发器创建的所有资源。

目标

  • 使用 Cloud Build 和 Terraform 通过 Compute Engine 虚拟机实例组后端设置外部 HTTP(S) 负载平衡器。

  • 在虚拟机实例上执行蓝绿部署。

费用

在本文档中,您将使用 Google Cloud 的以下收费组件:

您可使用价格计算器根据您的预计使用情况来估算费用。 Google Cloud 新用户可能有资格申请免费试用

完成本文档中描述的任务后,您可以通过删除所创建的资源来避免继续计费。如需了解详情,请参阅清理

准备工作

  1. 登录您的 Google Cloud 账号。如果您是 Google Cloud 新手,请创建一个账号来评估我们的产品在实际场景中的表现。新客户还可获享 $300 赠金,用于运行、测试和部署工作负载。
  2. 安装 Google Cloud CLI。
  3. 如需初始化 gcloud CLI,请运行以下命令:

    gcloud init
  4. 创建或选择 Google Cloud 项目

    • 创建 Google Cloud 项目:

      gcloud projects create PROJECT_ID

      PROJECT_ID 替换为您要创建的 Google Cloud 项目的名称。

    • 选择您创建的 Google Cloud 项目:

      gcloud config set project PROJECT_ID

      PROJECT_ID 替换为您的 Google Cloud 项目 名称。

  5. 确保您的 Google Cloud 项目已启用结算功能

  6. 安装 Google Cloud CLI。
  7. 如需初始化 gcloud CLI,请运行以下命令:

    gcloud init
  8. 创建或选择 Google Cloud 项目

    • 创建 Google Cloud 项目:

      gcloud projects create PROJECT_ID

      PROJECT_ID 替换为您要创建的 Google Cloud 项目的名称。

    • 选择您创建的 Google Cloud 项目:

      gcloud config set project PROJECT_ID

      PROJECT_ID 替换为您的 Google Cloud 项目 名称。

  9. 确保您的 Google Cloud 项目已启用结算功能

测试

  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. 点击最新 build。

    您会看到构建详情页面,其中显示了一个包含三个构建步骤的 Cloud Build 流水线:第一个构建步骤在 Cloud Source Repositories 中创建代码库,第二步将 GitHub 中示例代码库的内容克隆到 Cloud Source Repositories,第三步添加两个构建触发器。

  5. 打开 Cloud Source Repositories:

    打开 Cloud Source Repositories

  6. 在代码库列表中,点击 copy-of-gcp-mig-simple

    在页面底部的历史记录标签页中,您将看到一项提交内容,其中包含 Cloud Build 为创建名为 copy-of-gcp-mig-simple 的代码库所做的说明 A copy of https://github.com/GoogleCloudPlatform/cloud-build-samples.git

  7. 打开 Cloud Build 触发器页面:

    打开“触发器”页面

  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

    您会在页面底部的 History(历史记录)标签页中看到带有 Promote green 说明的提交。

  12. 如需查看 apply 触发器的执行情况,请打开 Google Cloud 控制台中的构建记录页面:

    打开“构建记录”页面

  13. 点击第一个构建,打开构建详情页面。

    您会看到包含两个构建步骤的 apply 触发器流水线。第一个构建步骤执行 Terraform apply,为部署创建 Compute Engine 和负载均衡资源。第二个构建步骤会输出显示应用正在运行的 IP 地址。

  14. 在浏览器中打开与绿色 MIG 对应的 IP 地址。您将看到类似如下所示的屏幕截图,其中显示了该部署:

    部署

  15. 转到 Compute Engine 实例组页面,查看蓝色和绿色实例组:

    打开“实例组”页面

  16. 打开虚拟机实例页面,查看四个虚拟机实例:

    打开“虚拟机实例”页面

  17. 打开外部 IP 地址页面,查看三个负载平衡器:

    打开“外部 IP 地址”页面

了解代码

此代码示例的源代码包含:

  • 与设置脚本相关的源代码。
  • 与 Cloud Build 流水线相关的源代码。
  • 与 Terraform 模板相关的源代码。

设置脚本

setup.sh 是设置脚本,用于运行引导过程并为蓝绿部署创建组件。该脚本将执行以下操作:

  • 启用 Cloud Build、Resource Manager、Compute Engine 和 Cloud Source Repositories API。
  • roles/editor IAM 角色授予项目中的 Cloud Build 服务帐号。Cloud Build 需要此角色来为部署创建和设置必要的 GitOps 组件。
  • roles/source.admin IAM 角色授予项目中的 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_applybash_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 和绿色 MIG 是相同的,因此它们只定义一次,并针对蓝色和绿色对象进行实例化。拆分器负载均衡器的 Terraform 配置文件位于 splitter/ 文件夹中。

以下代码段展示了 infra/main.tfvars 的内容。它包含三个变量:两个用于确定要部署到 Blue 和 Green 池的应用版本,以及一个表示活动颜色(蓝色或绿色)的变量。对此文件的更改会触发部署。

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 中的以下代码段为蓝色 MIG 和绿色 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 和蓝色 MIG 之间切换的逻辑。它由 google_compute_region_backend_service 服务提供支持,该服务可以将流量路由到两个后端区域:var.instance_group_bluevar.instance_group_greencapacity_scaler 定义要路由多少流量。

以下代码会将所有流量路由到指定颜色,但您可以为 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 相关的对象。此文件中的以下代码段定义了用于创建虚拟机池的 Compute Engine 实例模板。请注意,此实例模板已将 Terraform 生命周期属性设置为 create_before_destroy。这是因为,在更新池的版本时,如果上一版本的池仍在使用新池,则您无法使用模板来创建新版本的池。但是,如果在创建新模板之前销毁了池的旧版本,则池会在一段时间内宕机。为避免这种情况,我们将 Terraform 生命周期设置为 create_before_destroy,以便在销毁旧版本之前,先创建虚拟机池的较新版本。

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 触发器页面:

      打开“触发器”页面

    2. 触发器表中,找到 destroy 触发器对应的行,然后点击运行。该触发器完成执行后,系统会删除 apply 触发器创建的资源。

  2. 在终端窗口中运行以下命令,删除引导期间创建的资源:

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

删除项目

    删除 Google Cloud 项目:

    gcloud projects delete PROJECT_ID

后续步骤