为了Terraform团队的运营,我们所采取的措施
概述
在这篇文章中,我们将总结如何在Terraform团队运作中实现持续apply。我们的基础设施环境是AWS,主要的做法与实践Terraform的第27章”持续apply和Terraform在哪里执行”(Continuous apply and where to execute Terraform)中所总结的几乎相同。在实际环境建设中,我们非常参考了这篇文章,希望它能作为一个样例对您有所帮助。
前提 tí)
当前使用的是实际的服务,AWS平台上有两个账户,分别用于生产环境和开发环境。生产环境包括production和staging,而开发环境则是development。terraform的源代码与应用代码一起通过GitHub进行管理,采用GitFlow进行运维。tf文件则通过terraform cloud进行管理,计划和应用则基本上由一人在本地进行。
文件夹的结构如下所示。
app/ ... アプリケーションのコード
terraform/prod/ ... 本番AWSアカウント用のディレクトリ
/common ... 本番AWSアカウントで共通に使うもののtfファイル群
/prod ... production環境で使うもののtfファイル群
/stg ... staging環境で使うもののtfファイル群
/dev/ ... 開発AWSアカウント用のディレクトリ
/common ... 開発AWSアカウントで共通に使うもののtfファイル群
/dev ... develop環境で使うもののtfファイル群
我們希望此次能夠實現的目標。
-
- AWS上でplan,applyをさせたい
実行に時間がかかるものをローカルで実行されるのは途中で落ちないか不安
複数人運用しても事故が起こらないようにしたい
常に最新の状態でapplyがされるようにしたい
terraformの内容をレビューし合えるようしたい
本番環境以外はある程度柔軟に操作できるようにしたい
開発環境ではトライアンドエラーがしやすいようにしたい。
# 最后成功实现的事物
-
- GitHubでプルリク時にCodeBuild上でplan実行、マージ時にapply実行
-
- plan,apply実行時にはGitHubのコメントとSlack両方に通知する
GitHubだけだとマージ後にapplyを行った際、終わったかを見に行かなければいけないのが手間なので
本番と本番共通のterraformはmasterのマージ時にそれ以外はdevelopのマージ時に行う。
ローカルではapplyとplan両方が実行できてしまうが、applyは実行しないというルールにする
planができないのは不便なため。applyができるのは塞ぎたかったがうまくできず今後の検討事項
实施细节
请注意以下事项
请根据您的环境和需求来修改并经过充分的操作确认后使用所提供的代码。此外,由于此操作还没有完全成熟,所以未来可能需要进行修正。如果有关更好的方法的指导意见,将非常感激!
文件夹结构
在制定这次方案时,我们添加了以下内容。
terraform/continuous_apply/script/apply.sh ... apply用のシェル
/build.sh ... applyかplanかで振り分ける用のシェル
/install.sh ... tfnotifyのインストール用
/plan.sh ... plan用のシェル
/_terraformrc ... terraform cloudの設定ファイル用のテンプレート
/buildspec.yml ... CodeBuild用のyaml
/tfnotify_github.yml ... tfnotifyの設定ファイル、github用
/tfnotify_slack.yml ... tfnotifyの設定ファイル、slack用
我会逐个查看每个文件。
在CodeBuild中执行的Shell脚本
在install.sh脚本中,我们正在安装tfnotify。如果已经有一个包含tfnotify的容器镜像,那就很好了,我们计划以后会做相应的适配。需要注意的是,由于我们安装的是最新版本的tfnotify,所以我们已经修改了实践Terraform中的示例脚本。
#!/bin/sh
VERSION="v0.6.1"
BASE_URL=https://github.com/mercari/tfnotify/releases/download
DOWNLOAD_URL="${BASE_URL}/${VERSION}/tfnotify_linux_amd64.tar.gz"
wget ${DOWNLOAD_URL} -P /tmp
mkdir -p /tmp/tfnotify_linux_amd64
tar zxvf /tmp/tfnotify_linux_amd64.tar.gz -C /tmp/tfnotify_linux_amd64
mv /tmp/tfnotify_linux_amd64/tfnotify /usr/local/bin/tfnotify
在 build.sh 脚本中,我们根据是否进行合并和合并时是否选择生产环境来进行分流。然而,目前还无法将计划分流。
#!/bin/sh
set -x
if [[ ${CODEBUILD_WEBHOOK_TRIGGER} = 'branch/master' ]]; then
${CODEBUILD_SRC_DIR}/terraform/continuous_apply/scripts/apply.sh master
elif [[ ${CODEBUILD_WEBHOOK_TRIGGER} = 'branch/develop' ]]; then
${CODEBUILD_SRC_DIR}/terraform/continuous_apply/scripts/apply.sh develop
else
${CODEBUILD_SRC_DIR}/terraform/continuous_apply/scripts/plan.sh
fi
计划将获取到有更改的文件夹,并执行terraform apply。为了通知GitHub和Slack,我将使用双重管道进行两次通知。
#!/bin/sh
DIRS=$(git --no-pager diff origin/develop..HEAD --name-only | xargs -I {} dirname {} | grep "terraform" | uniq)
if [ -z "$DIRS" ]; then
echo "No directories for apply."
exit 0
fi
for dir in $DIRS
do
if [ ! -e $dir/terraform.tf ]; then
continue
fi
echo $dir
(cd $dir && terraform init -input=false -no-color)
(cd $dir && terraform plan -input=false -no-color | tfnotify --config ${CODEBUILD_SRC_DIR}/terraform/continuous_apply/tfnotify_github.yml plan --message "$dir" | tfnotify --config ${CODEBUILD_SRC_DIR}/terraform/continuous_apply/tfnotify_slack.yml plan --message "$dir" )
done
在apply的shell中,基本流程与plan相同,但区分了是用于生产环境还是其他环境。
#!/bin/sh
MODE=$1
PROD_DIRS='terraform/prod/prod|terraform/prod/common'
echo "Mode: ${MODE}"
MESSAGE=$(git log ${CODEBUILD_SOURCE_VERSION} -1 --pretty=format:"%s")
CODEBUILD_SOURCE_VERSION=$(echo ${MESSAGE} | cut -f4 -d' ' | sed 's/#/pr\//')
get_dir_list () {
if [ $1 = 'master' ]; then
git --no-pager diff HEAD^..HEAD --name-only | xargs -I {} dirname {} | grep "terraform" | egrep "$2" | uniq
else
git --no-pager diff HEAD^..HEAD --name-only | xargs -I {} dirname {} | grep "terraform" | egrep -v "$2" | uniq
fi
}
DIRS=$(get_dir_list $MODE $PROD_DIRS)
echo $DIRS
if [ -z "$DIRS" ]; then
echo "No directories for apply."
exit 0
fi
for dir in $DIRS
do
if [ ! -e $dir/terraform.tf ]; then
continue
fi
echo $dir
(cd $dir && terraform init -input=false -no-color)
(cd $dir && terraform apply -input=false -no-color -auto-approve | tfnotify --config ${CODEBUILD_SRC_DIR}/terraform/continuous_apply/tfnotify_github.yml apply --message "$dir" | tfnotify --config ${CODEBUILD_SRC_DIR}/terraform/continuous_apply/tfnotify_slack.yml apply --message "$dir" )
done
构建规范文件 buildspec.yml 和 tfnotify 的配置
以下是用于在CodeBuild中实现的构建规范(builspec)。
重点是在安装时安装了tfnotify,并添加了存放Terraform Cloud配置文件的处理过程。
version: 0.2
env:
parameter-store:
GITHUB_TOKEN: "/continuous_apply/github_token"
TERRAFROM_CLOUD_TOKEN: "/continuous_apply/terraform_cloud_token"
SLACK_TOKEN: "/continuous_apply/slack_token"
phases:
install:
commands:
- ${CODEBUILD_SRC_DIR}/terraform/continuous_apply/scripts/install.sh
- sed -e "s/_TOKEN_/${TERRAFROM_CLOUD_TOKEN}/" ${CODEBUILD_SRC_DIR}/terraform/continuous_apply/_terraformrc > ~/.terraformrc
build:
commands:
- ${CODEBUILD_SRC_DIR}/terraform/continuous_apply/scripts/build.sh
credentials "app.terraform.io" {
token = "_TOKEN_"
}
以下是tfnotify的設定。為了易讀性,我們在GitHub和Slack上更改了格式。
ci: codebuild
notifier:
github:
token: $GITHUB_TOKEN
repository:
owner: "(__secret__)"
name: "(__secret__)"
terraform:
plan:
template: |
{{ .Title }}
{{ .Message }}
{{if .Result}}<pre><code> {{ .Result }} </pre></code>{{end}}
<details><summary>Details (Click me)</summary>
<pre><code> {{ .Body }} </pre></code></details>
apply:
template: |
{{ .Title }}
{{ .Message }}
{{if .Result}}<pre><code> {{ .Result }} </pre></code>{{end}}
<details><summary>Details (Click me)</summary>
<pre><code> {{ .Body }} </pre></code></details>
ci: codebuild
notifier:
slack:
token: $SLACK_TOKEN
channel: "(__secret__)"
bot: "(__secret__)"
terraform:
plan:
template: |
{{ .Title }}
{{ .Message }}
{{if .Result}}
```
{{ .Result }}
```
{{end}}
```
{{ .Body }}
```
apply:
template: |
{{ .Title }}
{{ .Message }}
{{if .Result}}
```
{{ .Result }}
```
{{end}}
```
{{ .Body }}
```
AWS的配置
我正在使用Terraform进行编写。实际上,我们需要一个用于生产和一个用于开发的环境,以下是用于开发环境的部分。
为了避免不必要的构建,在这里也通过修改文件夹进行过滤。
resource "aws_codebuild_project" "continuous_apply" {
name = "${var.project}-common-continuous-apply"
service_role = module.continuous_apply_codebuild_role.iam_role_arn
source {
type = "GITHUB"
location = "(_secret_)"
buildspec = "terraform/continuous_apply/buildspec.yml"
}
artifacts {
type = "NO_ARTIFACTS"
}
environment {
type = "LINUX_CONTAINER"
compute_type = "BUILD_GENERAL1_SMALL"
image = "hashicorp/terraform:0.12.25"
privileged_mode = false
}
provisioner "local-exec" {
command = <<-EOT
aws codebuild import-source-credentials \
--server-type GITHUB \
--auth-type PERSONAL_ACCESS_TOKEN \
--token $GITHUB_TOKEN
EOT
environment = {
GITHUB_TOKEN = data.aws_ssm_parameter.github_token.value
}
}
}
resource "aws_codebuild_webhook" "continuous_apply" {
project_name = aws_codebuild_project.continuous_apply.name
filter_group {
filter {
type = "EVENT"
pattern = "PULL_REQUEST_CREATED"
}
filter {
exclude_matched_pattern = false
pattern = "^terraform/dev/"
type = "FILE_PATH"
}
}
filter_group {
filter {
type = "EVENT"
pattern = "PULL_REQUEST_UPDATED"
}
filter {
exclude_matched_pattern = false
pattern = "^terraform/dev/"
type = "FILE_PATH"
}
}
filter_group {
filter {
type = "EVENT"
pattern = "PULL_REQUEST_REOPENED"
}
filter {
exclude_matched_pattern = false
pattern = "^terraform/dev/"
type = "FILE_PATH"
}
}
filter_group {
filter {
type = "EVENT"
pattern = "PUSH"
}
filter {
type = "HEAD_REF"
pattern = "develop"
}
filter {
exclude_matched_pattern = false
pattern = "^terraform/dev/"
type = "FILE_PATH"
}
}
}
GitHub的配置设置
不会提供详细说明,但是为了事故防止,我们在分支中进行了以下设置。
-
- Require pull request reviews before merging
- Require branches to be up to date before merging
总结
经过以上的工作,我们已经成功地建立起了一个持续申请的机制。虽然不能完全称之为牢固的运作和框架,但与之前只能由一个人来处理的情况相比,现在运用起来变得更加便利。希望您能参考并帮助我们构建这个机制。
请参考
我之前已经提到过,但是再次总结一下。这些内容非常有参考价值。谢谢!
実践Terraform 第27章 継続的apply
Terraform どこで実行していますか?
メルカリ Microservices Team による Terraform 運用とその中で開発したOSSの紹介
github.com/mercari/tfnotify