本教程介绍如何使用常用的 GitOps 方法,通过 Terraform 和 Cloud Build 以代码形式管理基础设施。术语 GitOps 一词由 Weaveworks 首先提出,其主要概念是使用 Git 代码库来存储您所需的环境状态。Terraform 是一种 HashiCorp 开源工具,可让您使用代码以可预测的方式创建、更改和改进您的云基础架构。在本教程中,您将使用 Cloud Build(一个 Google Cloud 持续集成服务)自动将 Terraform 清单应用到您的环境。
本教程适用于正在寻求一种出色的解决方案来以可预测的方式对基础架构做出更改的开发者和运维人员。本文假设您熟悉 Google Cloud和 Linux。
DevOps 现状报告确定了可提高软件交付方面表现的功能。本教程将帮助您使用以下功能:
架构
本教程应用 GitOps 实践来管理 Terraform 执行。请注意,它使用 Secure Source Manager 分支 dev
和 prod
来表示实际环境。这些环境分别由Google Cloud 项目中的虚拟私有云 (VPC) 网络 dev
和 prod
定义。
当您将 Terraform 代码推送到 dev
或 prod
分支时,该过程就会启动。在这种情况下,Cloud Build 会触发,然后会应用 Terraform 清单在相应环境中实现所需状态。另一方面,当您将 Terraform 代码推送到任何其他分支(例如,某个功能分支)时,Cloud Build 会运行以执行 terraform plan
,但不会对任何环境应用任何内容。
理想情况下,开发者或运维人员必须向开发分支或功能分支提出基础设施提案,然后通过拉取请求提交这些提案。这样,您就可以与协作者讨论和查看潜在更改,并在将更改合并到基本分支之前添加后续提交。
如果不存在任何问题,您必须先将更改合并到 dev
分支。此合并会触发将基础架构部署到 dev
环境的操作,如此您便可以测试此环境。在测试所部署的内容并确信其没有问题之后,您必须将 dev
分支合并到 prod
分支,以触发在生产环境中安装基础架构的操作。
目标
- 设置 Secure Source Manager 实例和代码库。
- 将 Terraform 配置为在 Cloud Storage 存储桶中存储状态。
- 向您的 Cloud Build 服务账号授予权限。
- 将 Cloud Build 连接到 Secure Source Manager 代码库。
- 更改功能分支中的环境配置。
- 将更改升级到开发环境。
- 推广对生产环境的更改。
费用
在本文档中,您将使用 Google Cloud的以下收费组件:
您可使用价格计算器,根据您的预计使用情况生成费用估算。
完成本文档中描述的任务后,您可以通过删除所创建的资源来避免继续计费。如需了解详情,请参阅清理。
准备工作
- 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.
-
In the Google Cloud console, on the project selector page, select or create a Google Cloud project.
-
Verify that billing is enabled for your Google Cloud project.
-
In the Google Cloud console, on the project selector page, select or create a Google Cloud project.
-
Verify that billing is enabled for your Google Cloud project.
-
In the Google Cloud console, activate Cloud Shell.
At the bottom of the Google Cloud console, a Cloud Shell session starts and displays a command-line prompt. Cloud Shell is a shell environment with the Google Cloud CLI already installed and with values already set for your current project. It can take a few seconds for the session to initialize.
- 在 Cloud Shell 中,获取您刚刚选择的项目的 ID:
如果此命令未返回项目 ID,请将 Cloud Shell 配置为使用您的项目。请将gcloud config get-value project
PROJECT_ID
替换为您的项目 ID。gcloud config set project PROJECT_ID
- 启用所需的 API:
此步骤可能需要几分钟才能完成。gcloud services enable cloudbuild.googleapis.com compute.googleapis.com securesourcemanager.googleapis.com
- 如果您从未在 Cloud Shell 中使用过 Git,请使用您的姓名和电子邮件地址配置 Cloud Shell:
Git 根据这些信息确定您就是在 Cloud Shell 中创建的提交的作者本人。git config --global user.email "YOUR_EMAIL_ADDRESS" git config --global user.name "YOUR_NAME"
dev
分支包含应用于开发环境的最新更改。prod
分支包含应用于生产环境的最新更改。- 类似于
feature_x
的功能分支用于在推送到dev
或prod
分支之前进行更改。 - 创建空的 Secure Source Manager 代码库 - 请勿初始化代码库。
通过运行以下命令,将 Secure Source Manager 身份验证帮助程序添加到全局
git config
:git config --global credential.'https://*.*.sourcemanager.dev'.helper gcloud.sh
当您将 Git 命令与 Secure Source Manager 搭配使用时,身份验证帮助程序会使用 gcloud CLI 来获取您的Google Cloud 凭据。
如需在初始凭据设置后重新进行身份验证,请运行以下 gcloud CLI 命令:
gcloud auth login
将 solutions-terraform-cloudbuild-gitops 代码库克隆到本地 shell 或工作环境:
git clone https://github.com/GoogleCloudPlatform/solutions-terraform-cloudbuild-gitops.git
将 Secure Source Manager 代码库添加为上游。
git remote add google HTTPS_REPO_URL
其中,
HTTPS_REP_URL
是 Secure Source Manager 代码库的 HTTPS 网址。您可以在 Secure Source Manager 网页界面中,在代码库页面顶部找到该网址。创建并切换到
dev
分支。git checkout dev
使用以下命令将克隆的代码库推送到您的代码库:
git push -u google --all
针对
prod
分支重复前两个步骤。environments/
文件夹包含代表环境(例如dev
和prod
)的子文件夹,它们将处于不同成熟阶段(分别为开发和生产阶段)的工作负载进行逻辑分隔。尽管让这些环境尽可能保持相似是不错的做法,但每个子文件夹都有自己的 Terraform 配置,以确保它们可以根据需要采用独特的设置。modules/
文件夹包含内嵌 Terraform 模块。这些模块代表相关资源的逻辑分组,用于跨不同环境共享代码。cloudbuild.yaml
文件是包含 Cloud Build 说明的 build 配置文件,例如如何根据一系列步骤执行任务。此文件根据 Cloud Build 从中提取代码的分支指定条件执行,例如:对于
dev
和prod
分支,执行以下步骤:terraform init
terraform plan
terraform apply
对于任何其他分支,执行以下步骤:
- 对于所有
environments
子文件夹,执行terraform init
- 对于所有
environments
子文件夹,执行terraform plan
- 对于所有
- 添加一个克隆代码库的步骤。
- 添加一个步骤来获取分支名称并将其分配给一个变量。
切换到
dev
分支:git checkout dev
打开
cloudbuild.yaml
文件,然后将其内容替换为以下内容:# Copyright 2019 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. steps: - id: 'clone repository' name: 'gcr.io/cloud-builders/git' args: - clone - '${_REPO_URL}' - . - id: 'branch name' name: gcr.io/cloud-builders/git entrypoint: 'sh' args: - '-c' - | branch=$(basename "$_REF") git checkout ${branch} echo "***********************" git branch --show-current echo "***********************" - id: 'tf init' name: 'hashicorp/terraform:1.0.0' entrypoint: 'sh' args: - '-c' - | branch=$(basename "$_REF") if [ -d "environments/${branch}/" ]; then cd environments/${branch} terraform init else for dir in environments/*/ do cd ${dir} env=${dir%*/} env=${env#*/} echo "" echo "*************** TERRAFORM INIT ******************" echo "******* At environment: ${env} ********" echo "*************************************************" terraform init || exit 1 cd ../../ done fi - id: 'tf plan' name: 'hashicorp/terraform:1.0.0' entrypoint: 'sh' args: - '-c' - | branch=$(basename "$_REF") if [ -d "environments/${branch}/" ]; then cd environments/${branch} terraform plan else for dir in environments/*/ do cd ${dir} env=${dir%*/} env=${env#*/} echo "" echo "*************** TERRAFOM PLAN ******************" echo "******* At environment: ${env} ********" echo "*************************************************" terraform plan || exit 1 cd ../../ done fi - id: 'tf apply' name: 'hashicorp/terraform:1.0.0' entrypoint: 'sh' args: - '-c' - | branch=$(basename "$_REF") if [ -d "environments/${branch}/" ]; then cd environments/${branch} terraform apply -auto-approve else echo "***************************** SKIPPING APPLYING *******************************" echo "Branch '${branch}' does not represent an official environment." echo "*******************************************************************************" fi
检查文件是否已修改。
git status
提交并推送您的更改:
git add --all git commit -m "Modify build config file." git push google dev
打开拉取请求,以便快速将更改提升到
prod
分支。- 在 Secure Source Manager 网页界面中,前往您的代码库。
- 点击 Pull request 标签页。
- 点击 New pull request。
- 在合并到:字段中,选择
prod
分支。 - 在 pull from: 字段中,选择
dev
分支。 - 查看更改,然后点击新建拉取请求。
- 点击创建拉取请求。
- 点击合并拉取请求。
再次点击合并拉取请求。
相应更改会合并到您的
prod
分支中。
在 Cloud Shell 中,创建 Cloud Storage 存储分区:
PROJECT_ID=$(gcloud config get-value project) gcloud storage buckets create gs://${PROJECT_ID}-tfstate
启用对象版本控制,以保留部署历史记录:
gcloud storage buckets update gs://${PROJECT_ID}-tfstate --versioning
创建一个新的
cloud-storage-bucket
分支以在其中进行更改:cd ~/solutions-terraform-cloudbuild-gitops git checkout -b cloud-storage-bucket
将
PROJECT_ID
占位符替换为terraform.tfvars
和backend.tf
文件中的项目 ID:sed -i s/PROJECT_ID/$PROJECT_ID/g environments/*/terraform.tfvars sed -i s/PROJECT_ID/$PROJECT_ID/g environments/*/backend.tf
在 OS X 或 macOS 上,您可能需要在
sed -i
后添加两个引号 (""
),如下所示:sed -i "" s/PROJECT_ID/$PROJECT_ID/g environments/*/terraform.tfvars sed -i "" s/PROJECT_ID/$PROJECT_ID/g environments/*/backend.tf
检查是否所有文件都已更新:
git status
输出类似于以下内容:
On branch cloud-storage-bucket Changes not staged for commit: (use "git add
..." to update what will be committed) (use "git restore ..." to discard changes in working directory) modified: environments/dev/backend.tf modified: environments/dev/terraform.tfvars modified: environments/prod/backend.tf modified: environments/prod/terraform.tfvars no changes added to commit (use "git add" and/or "git commit -a") 提交并推送您的更改:
git add --all git commit -m "Update project IDs and buckets" git push google -u cloud-storage-bucket
您的新
cloud-storage-bucket
分支已推送到代码库。通过为每个分支打开并提交合并请求,将
cloud-storage-bucket
更改合并到dev
和prod
分支中。如需查找 Cloud Build 服务账号电子邮件地址,请在 Cloud Build 页面中前往设置。
复制服务账号电子邮件地址的值。
向 Cloud Build 服务账号授予所需的访问权限:
gcloud projects add-iam-policy-binding PROJECT_ID \ --member serviceAccount:CLOUDBUILD_SA --role roles/editor
替换以下内容:
- PROJECT_ID 替换为您的项目 ID。
- 将 CLOUDBUILD_SA 替换为 Cloud Build 服务账号电子邮件地址。
在项目中启用并设置 Cloud Build。
在 Google Cloud 控制台中打开触发器页面。
从页面顶部的项目选择器下拉菜单中选择您的项目。
点击打开。
点击创建触发器。
输入以下触发器设置:
名称:
trigger-on-push
区域:选择触发器的区域。如果与触发器关联的 build 配置文件指定了专用池,则您为触发器选择的区域必须与专用池的区域一致。
如果您选择
global
作为区域,Cloud Build 会使用 build 配置文件中指定的区域来运行构建。如果您在 build 配置文件中指定了专用池,则此值可以是专用池的区域;如果您未指定专用池,则此值可以是全局默认池。说明(可选):输入触发器的说明。
事件:选择 Webhook 事件作为用于调用触发器的代码库事件。
如果未安装 Secret Manager,系统会提示您启用 Secret Manager。
Webhook 网址:选择以下选项之一:
- 如果您想使用 Cloud Build 生成新的 Secret,请选择使用新的 Secret。点击创建密钥以创建密钥。
- 如果您想使用现有密文,请使用现有密文或创建自己的密文。在下拉选择框中输入密文和版本。
如果您使用现有 Secret,则可能需要手动向 Cloud Build 服务代理
service-PROJECT_NUMBER@gcp-sa-cloudbuild.iam.gserviceaccount.com
授予 Secret Manager Secret Accessor 角色。如需了解详情,请参阅向 Cloud Build 服务代理授予角色。
点击显示网址预览,然后记录该网址。您需要此网址才能在 Secure Source Manager 中设置网络钩子。
- 配置:对于类型,选择 Cloud Build 配置文件(YAML 或 JSON);对于位置,选择内嵌。
点击打开编辑器按钮,修改您的 build 配置文件。
将
cloudbuild.yaml
文件的内容复制到编辑器中。如前所述,根据所提取的分支,此流水线的行为会有所不同。该 build 会检查
${branch}
变量是否与任何环境文件夹匹配。如果匹配,Cloud Build 会针对该环境执行terraform plan
。否则,Cloud Build 会针对所有环境执行terraform plan
,以确保建议的更改适用于所有环境。如果这些方案中的任一方案未能执行,构建会失败。- id: 'tf plan' name: 'hashicorp/terraform:1.0.0' entrypoint: 'sh' args: - '-c' - | branch=$(basename "$_REF") if [ -d "environments/${branch}/" ]; then cd environments/${branch} terraform plan else for dir in environments/*/ do cd ${dir} env=${dir%*/} env=${env#*/} echo "" echo "*************** TERRAFOM PLAN ******************" echo "******* At environment: ${env} ********" echo "*************************************************" terraform plan || exit 1 cd ../../ done fi
terraform apply
命令会针对各环境分支运行,但它在任何其他情况下都会被完全忽略。点击 + 添加变量,然后添加以下两个替换变量:
- 变量:
_REPO_URL
,值:$(body.repository.clone_url)
- 变量:
_REF
,值:$(body.ref)
- 变量:
点击创建。
- 在 Secure Source Manager 网页界面中,找到要为其创建 Webhook 的代码库。
- 点击设置。
- 点击 Webhooks,然后点击 Add webhook。
在 Hook ID 字段中,输入 Webhook 的 ID。
在目标网址字段中,输入您在 Cloud Build 中设置网络钩子触发器时复制的网络钩子网址。
如需查找 Webhook 网址,请执行以下操作:
在 Google Cloud 控制台中打开触发器页面。
点击相应触发器。
在 Webhook 网址 部分,点击显示网址预览,然后复制网址。
网络钩子网址包含您在创建 Cloud Build 触发器时输入的密钥和密钥值。为防止这些值泄露,请从目标网址末尾移除这些值,然后将其复制到敏感查询字符串字段。
如需在网络钩子网址中找到您的密钥和密钥,请查找以
key=
开头的文本例如,假设有以下网址:
https://cloudbuild.googleapis.com/v1/projects/my-project/triggers/test-trigger:webhook?key=eitIfKhYnv0LrkdsyHqIros8fbsheKRIslfsdngf&secret=Hello%20Secret%20Manager
复制并移除目标网址字段中以问号
?key=...
开头的部分。然后移除初始问号,将剩余部分key=...
移至敏感查询字符串字段。点击 Add webhook。
Webhook 会显示在 Webhook 页面中。
确保您位于
dev
分支中:cd ~/solutions-terraform-cloudbuild-gitops git checkout dev
拉取最新更改:
git pull
创建
bug-fix
分支以更改环境配置。git checkout -b bug-fix
打开
modules/firewall/main.tf
进行修改。在第 30 行,更正
target_tags
字段中的"http-server2"
拼写错误。该值必须为
"http-server"
。提交并推送您的更改:
git add --all git commit -m "Fix typo." git push google -u bug-fix
在 Google Cloud 控制台中打开 Cloud Build 历史记录页面:
点击相应build 可查看更多信息,包括
terraform plan
的输出。- 在 Secure Source Manager 网页界面中,前往您的代码库。
- 点击 New pull request(新建拉取请求)
- 在合并到:字段中,选择您的
dev
分支。 - 在 pull from: 字段中,选择您的
bug-fix
分支。 - 点击 New pull request。
- 点击创建拉取请求。
- 点击 Merge pull request,然后再次点击 Merge pull request。
检查是否已触发新的 Cloud Build:
打开该构建并检查日志。
构建完成后,您会看到如下内容:
Step #3 - "tf apply": external_ip = EXTERNAL_IP_VALUE Step #3 - "tf apply": firewall_rule = dev-allow-http Step #3 - "tf apply": instance_name = dev-apache2-instance Step #3 - "tf apply": network = dev Step #3 - "tf apply": subnet = dev-subnet-01
复制
EXTERNAL_IP_VALUE
,并在网络浏览器中打开该地址。http://EXTERNAL_IP_VALUE
此配置可能需要几秒钟时间来启动虚拟机并传播防火墙规则。最终,您将在网络浏览器中看到 Environment: dev。
前往 Cloud Storage:
选择您的项目。
点击您的 Terraform 状态存储桶。存储桶名称如下所示:
PROJECT_ID-tfstate
依次点击 env 和 dev,以查看您的 Terraform 状态文件。
- 在 Secure Source Manager 网页界面中,前往您的代码库。
- 点击 Pull request 标签页。
- 点击 New pull request。
- 对于合并到:,请选择您的代码库
prod
分支。 - 对于 pull from:,请选择您的代码库
dev
分支。 - 点击 New pull request。
- 对于 title,请输入一个标题(如“Promoting networking changes”),然后点击 Create pull request。
查看建议的更改,然后点击合并拉取请求。
日期和代码库网址会添加到评论字段中。
再次点击合并拉取请求进行确认。
在 Google Cloud 控制台中,打开 Build 历史记录页面,查看应用到生产环境的更改:
等待构建完成,然后检查日志。
日志末尾会显示如下内容:
Step #3 - "tf apply": external_ip = EXTERNAL_IP_VALUE Step #3 - "tf apply": firewall_rule = prod-allow-http Step #3 - "tf apply": instance_name = prod-apache2-instance Step #3 - "tf apply": network = prod Step #3 - "tf apply": subnet = prod-subnet-01
复制
EXTERNAL_IP_VALUE
,并在网络浏览器中打开该地址。http://EXTERNAL_IP_VALUE
此配置可能需要几秒钟时间来启动虚拟机并传播防火墙规则。最终,您将在网络浏览器中看到 Environment: prod。
前往 Cloud Storage:
选择您的项目。
点击您的 Terraform 状态存储桶。存储桶名称如下所示:
PROJECT_ID-tfstate
依次点击 env 和 prod,以查看您的 Terraform 状态文件。
- 针对不同的使用情形添加部署。
- 创建其他环境以满足您的需求。
- 每个环境使用一个项目,而非每个环境使用一个 VPC。
- In the Google Cloud console, go to the Manage resources page.
- In the project list, select the project that you want to delete, and then click Delete.
- In the dialog, type the project ID, and then click Shut down to delete the project.
- 考虑使用 Cloud Foundation Toolkit 模板在Google Cloud中快速构可重复的企业级基础环境。
- 观看 Next' 19 视频借助 Cloud Build 的基础架构即代码流水线规模化创建可重复的 Google Cloud 环境,了解本教程中介绍的 GitOps 工作流。
- 查看使用 Cloud Build 实现 GitOps 形式的持续交付教程。
- 查看更高级的 Cloud Build 功能:配置构建步骤的顺序、构建、测试和部署工件以及创建自定义构建步骤。
- 查看使用 Cloud Build 确保 Terraform 部署的规模和合规性中的博客。
- 阅读我们关于 DevOps 的资源。
- 详细了解与本教程相关的 DevOps 功能:
- 进行 DevOps 快速检查,了解您与业界其他公司相比所处的位置。
设置 Secure Source Manager 代码库
在本教程中,您将使用一个 Secure Source Manager 代码库来定义您的云基础架构。您还可以通过使不同的分支对应于不同的环境,来编排此基础架构:
借助此基础架构,您可以随时参考代码库以了解每个环境中的预期配置,并通过先将它们合并到 dev
环境中来提出新的更改建议。然后,您可以通过将 dev
分支合并到后续的 prod
分支来推广更改。
此代码库中代码的结构如下所示:
为了确保建议的更改适用于每种环境,请为所有 environments
子文件夹运行 terraform init
和 terraform plan
。例如,在合并拉取请求之前,您可以查看这些方案,以确保不会对未经授权的实体授予访问权限等等。
修改 build 配置文件
如需使示例 build 配置文件与 Secure Source Manager 搭配使用,您需要进行以下修改:
修改 dev
分支中的 build 配置文件:
将 Terraform 配置为在 Cloud Storage 存储桶中存储状态
默认情况下,Terraform 会将状态存储在名为 terraform.tfstate
的本地文件中。采用此默认配置,使用 Terraform 对团队而言可能会很困难,尤其是当许多用户同时运行 Terraform 并且每个机器都对当前基础架构有自己的理解时。
为帮助您避免此类问题,本部分将配置指向 Cloud Storage 存储桶的远程状态。远程状态是一项backends功能,在本教程中,该功能在 backend.tf
文件中进行配置,例如:
在以下步骤中,您将创建一个 Cloud Storage 存储桶,并将一些文件更改为指向新存储桶和您的项目。 Google Cloud
向您的 Cloud Build 服务账号授予权限
如需允许 Cloud Build 服务账号运行 Terraform 脚本以管理 Google Cloud 资源,您需要向其授予项目的相应访问权限。为简单起见,本教程中授予了 Project Editor 访问权限。对于生产环境,请遵循公司的 IT 安全最佳实践,此类最佳实践通常提供的是最低访问权限。
连接到 Cloud Build
如需在向任何分支推送时触发 Cloud Build,请设置 Secure Source Manager Webhook。build 配置文件将检查分支名称,以确定是否需要对 dev
或 prod
环境进行更改。
在 Secure Source Manager 中设置 webhook
创建 Webhook 以在向 dev
或 prod
分支推送时触发构建。
更改新功能分支中的环境配置
请注意,Cloud Build 作业运行了 cloudbuild.yaml
文件中定义的流水线。如前所述,根据所提取的分支,此流水线的行为会有所不同。该构建会检查 ${branch}
变量是否与任何环境文件夹匹配。如果匹配,Cloud Build 会针对该环境执行 terraform plan
。否则,Cloud Build 会针对所有环境执行 terraform plan
,以确保建议的更改适用于所有环境。如果这些方案中的任一方案未能执行,构建会失败。
- id: 'tf plan' name: 'hashicorp/terraform:1.0.0' entrypoint: 'sh' args: - '-c' - | branch=$(basename "$_REF") if [ -d "environments/${branch}/" ]; then cd environments/${branch} terraform plan else for dir in environments/*/ do cd ${dir} env=${dir%*/} env=${env#*/} echo "" echo "*************** TERRAFOM PLAN ******************" echo "******* At environment: ${env} ********" echo "*************************************************" terraform plan || exit 1 cd ../../ done fi
同样,terraform apply
命令会针对各环境分支运行,但它在任何其他情况下都会被完全忽略。在此部分中,您是向新分支提交的代码更改,因此未对您的 Google Cloud 项目应用任何基础架构部署。
推广对开发环境的更改
现在可以对您的 dev
环境应用所需的状态。
推广对生产环境的更改
现在,您已经对开发环境进行了全面测试,可以将您的基础架构代码推广到生产环境。
您已在 Cloud Build 上成功配置了无服务器基础架构即代码流水线。以后,您可能想要尝试以下操作:
清理
完成本教程后,请清理在Google Cloud 上创建的资源,避免日后再为这些资源付费。