部署到 Compute Engine


本指南介绍如何在 Google Cloud 控制台上执行零停机的蓝绿部署, 使用 Cloud Build 和 Terraform。

Cloud Build 可让您自动执行各种开发者流程 包括构建应用并将其部署到各种 Google Cloud 运行时 例如 Compute Engine Google Kubernetes EngineGKE Enterprise、 和 Cloud Run 函数

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

设计概览

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

蓝绿模型

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

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

蓝色和绿色虚拟机池是以 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 触发器附加到名为 main.tfvars 的 Terraform 文件, Cloud Source Repositories此文件包含代表蓝色和绿色负载均衡器的 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. 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. To initialize the gcloud CLI, run the following command:

    gcloud init
  4. 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.

  5. Make sure that billing is enabled for your Google Cloud project.

  6. Install the Google Cloud CLI.
  7. To initialize the gcloud CLI, run the following command:

    gcloud init
  8. 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.

  9. 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 的构建记录页面:

    打开“构建记录”页面

  4. 点击最新的 build。

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

  5. 打开 Cloud Source Repositories:

    打开 Cloud Source Repositories

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

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

  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

    您会在页面底部的历史记录标签页中看到描述为 Promote green 的提交。

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

    打开“构建记录”页面

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

    您将看到包含两个构建步骤的 apply 触发器流水线。第一个 构建步骤,执行 Terraform 应用,以创建 Compute Engine 并加载 为部署平衡资源第二个构建步骤会输出 IP 地址,您可以在其中看到正在运行的应用。

  14. 在浏览器中打开与绿色 MIG 对应的 IP 地址。您会看到 类似于以下所示部署的屏幕截图:

    部署

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

    打开“实例组”页面

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

    打开“虚拟机实例”页面

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

    打开“外部 IP 地址”页面

了解代码

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

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

设置脚本

setup.sh 是运行引导过程并创建 蓝绿部署的组件该脚本会执行以下操作 操作:

  • 启用 Cloud Build、Resource Manager Compute Engine API 和 Cloud Source Repositories API。
  • roles/editor IAM 角色授予项目中的 Cloud Build 服务账号。此角色是 让 Cloud Build 创建和设置必要的 用于部署的 GitOps 组件。
  • roles/source.admin IAM 角色授予项目中的 Cloud Build 服务账号。此角色是 创建 Cloud Build 服务账号所需的 在项目中使用 Cloud Source Repositories 并克隆示例的内容 添加到 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 流程的资源。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!"
}

以下代码段显示了 tf_destroy 中定义的函数 bash_utils.sh。它会调用 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/ 文件夹。

  • main.tf:这是 Terraform 配置文件
  • main.tfvars:此文件定义 Terraform 变量。
  • mig/splitter/:这些文件夹包含定义 负载平衡器mig/ 文件夹包含 Terraform 配置文件 为 Blue 和 Green 负载平衡器定义 MIG。The Blue and 绿色 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 中的以下代码段定义了两个相同的模块 代表蓝色和绿色 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)
    

删除项目

    Delete a Google Cloud project:

    gcloud projects delete PROJECT_ID

后续步骤