お題
以下の記事をトレースするだけ。
https://cloud.google.com/solutions/managing-infrastructure-as-code?hl=ja
前提
以下の記事でのセットアップが完了していること。
https://qiita.com/sky0621/items/c488e8adb2a51870ad22
以下については知識があるものとしてやり方や概念など説明しない。
- GitHubでのフォークのやり方
- GCS(Cloud Storage)
開発環境
# OS - Linux(Ubuntu)
$ cat /etc/os-release
NAME="Ubuntu"
VERSION="18.04.4 LTS (Bionic Beaver)"
# Git
$ git version
git version 2.26.2
# Terraform
$ terraform version
Terraform v0.12.25
# gcloud
$ gcloud version
Google Cloud SDK 294.0.0
実践
■ アーキテクチャ
以下に記載の通り、「dev
」と「prod
」という(実際の開発現場ならもうひとつ「stg
」もあるかも)構成。
https://cloud.google.com/solutions/managing-infrastructure-as-code?hl=ja#architecture
■ 使用リポジトリ
以下をフォーク。
https://github.com/GoogleCloudPlatform/solutions-terraform-cloudbuild-gitops
で、ローカル(作業端末)にクローンしておく。
ディレクトリ構成は以下の通り。
$ tree
.
├── cloudbuild.yaml
├── environments
│ ├── dev
│ │ ├── backend.tf
│ │ ├── main.tf
│ │ ├── outputs.tf
│ │ ├── terraform.tfvars
│ │ ├── variables.tf
│ │ └── versions.tf
│ └── prod
│ ├── backend.tf
│ ├── main.tf
│ ├── outputs.tf
│ ├── terraform.tfvars
│ ├── variables.tf
│ └── versions.tf
└── modules
├── firewall
│ ├── main.tf
│ ├── outputs.tf
│ ├── variables.tf
│ └── versions.tf
├── http_server
│ ├── main.tf
│ ├── outputs.tf
│ ├── variables.tf
│ └── versions.tf
└── vpc
├── main.tf
├── outputs.tf
├── variables.tf
└── versions.tf
以降、上記のディレクトリ構造であることを前提とする。
■ 状態の保存先をGCSバケットに変更
terraform.tfstate
(インフラ構築状態が保存されているファイル)の格納先をローカルからGCSバケット内に変更。
GCSバケット作成
GCPプロジェクトIDを名前の一部に含むバケット名を作るとバケット名の重複が防げる。
$ env | grep GOOGLE
GOOGLE_CREDENTIALS=/home/sky0621/.config/gcloud/my-gcp-prj-01-terraform-credential.json
GOOGLE_PROJECT=my-gcp-prj-01
$
$ gsutil mb -l asia-northeast1 gs://${GOOGLE_PROJECT}-tfstate
Creating gs://my-gcp-prj-01-tfstate/...
あと、デプロイ履歴を保持できるようにしておく。
$ gsutil versioning set on gs://${GOOGLE_PROJECT}-tfstate
Enabling versioning for gs://my-gcp-prj-01-tfstate/...
backend.tf
ファイルの修正
terraform.tfstate
の格納先の指定はbackend.tf
ファイルで行っているので、dev と prod それぞれの環境別に置いてある当該ファイルを修正。
修正後は以下のようになる。
terraform {
backend "gcs" {
bucket = "${GOOGLE_PROJECT}-tfstate"
prefix = "env/dev"
}
}
※ environments/prod/backend.tf
の方も同様に修正。
terraform.tfvars
ファイルの修正
dev と prod それぞれの環境において、定義時にGCPプロジェクトIDの指定が必要になる箇所が複数ある。
そのため、変数として定義しておいて、必要になる箇所ではその変数を参照する記述にしてある。
変数が定義されているのは、terraform.tfvars
ファイル。
この中も以下のように環境変数からGCPプロジェクトIDを参照するように修正する。
project="${GOOGLE_PROJECT}"
ここまでの状況をGitHub上のリポジトリにPush
$ git status
ブランチ dev
Your branch is up to date with 'origin/dev'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." 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
$ git add -A
$ git status
ブランチ dev
Your branch is up to date with 'origin/dev'.
コミット予定の変更点:
(use "git restore --staged <file>..." to unstage)
modified: environments/dev/backend.tf
modified: environments/dev/terraform.tfvars
modified: environments/prod/backend.tf
modified: environments/prod/terraform.tfvars
$ git commit -m "Update project IDs and buckets"
[dev 6c54e5f] Update project IDs and buckets
5 files changed, 8 insertions(+), 5 deletions(-)
$ git status
ブランチ dev
このブランチは 'origin/dev' よりも1コミット進んでいます。
(use "git push" to publish your local commits)
nothing to commit, working tree clean
$ git push origin dev
Enumerating objects: 17, done.
Counting objects: 100% (17/17), done.
Delta compression using up to 4 threads
Compressing objects: 100% (7/7), done.
Writing objects: 100% (9/9), 750 bytes | 375.00 KiB/s, done.
Total 9 (delta 5), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (5/5), completed with 4 local objects.
To github.com:sky0621/solutions-terraform-cloudbuild-gitops.git
bf83e89..6c54e5f dev -> dev
■ Cloud Build 関連の設定
API使えるようにしておく
$ gcloud services enable cloudbuild.googleapis.com
Operation "operations/xxx.99999999-xxxx-999-xxxx-99999999" finished successfully.
Cloud Build サービスアカウントに権限を付与
元記事でも記載があるけど、「とりあえず roles/editor
をつけているものの本来なら必要最小限なロールを付与すべき」とあるので実際の開発現場では留意すべき。
サービスアカウントの情報を得る
プロジェクト自体の情報
$ gcloud projects describe ${GOOGLE_PROJECT}
createTime: '2020-05-27T15:19:59.167Z'
lifecycleState: ACTIVE
name: My Project 19162
projectId: my-gcp-prj-01
projectNumber: '999999999999'
プロジェクトナンバー部分を抜粋
$ gcloud projects describe ${GOOGLE_PROJECT} --format 'value(projectNumber)'
999999999999
上記ナンバーを含むCloud Build サービスアカウントのメアドを環境変数にセット
$ export CLOUDBUILD_SA="$(gcloud projects describe ${GOOGLE_PROJECT} --format 'value(projectNumber)')@cloudbuild.gserviceaccount.com"
$
$ env | grep CLOUDBUILD_SA
CLOUDBUILD_SA=999999999999@cloudbuild.gserviceaccount.com
ロール付与
$ gcloud projects add-iam-policy-binding ${GOOGLE_PROJECT} --member serviceAccount:${CLOUDBUILD_SA} --role roles/editor
Updated IAM policy for project [my-gcp-prj-01].
bindings:
〜〜〜
- serviceAccount:999999999999@cloudbuild.gserviceaccount.com
role: roles/editor
〜〜〜
Cloud Build を GitHub リポジトリに接続
Cloud Build アプリの GitHub Marketplace ページにアクセス
ちなみに、下記とのこと。
Cloud Buildは、無料枠を超えるビルド1分あたりの料金です。
1日120分の無料ビルド分。その後$ 0.003 /分。
最大10の同時ビルドが含まれます。
ビルドされたイメージをGoogle Container Registryに自動的にプッシュします(ストレージコストがかかる場合があります)。
詳細については、Cloud Buildの料金ページをご覧ください。
https://cloud.google.com/cloud-build/pricing
終わった様子。
■ ソースの変更によるCloud Build発動を確認
ローカルでソース修正しリモートにPush
$ pwd
/home/sky0621/work/src/github.com/sky0621/solutions-terraform-cloudbuild-gitops
$
$ git branch
* dev
$
フォーク元リポジトリで(学習用に)タイプミスされていたファイルを修正。
$ git diff
diff --git a/modules/firewall/main.tf b/modules/firewall/main.tf
index 5e40f70..1d2e549 100644
--- a/modules/firewall/main.tf
+++ b/modules/firewall/main.tf
@@ -27,6 +27,6 @@ resource "google_compute_firewall" "allow-http" {
ports = ["80"]
}
- target_tags = ["http-server2"]
+ target_tags = ["http-server"]
source_ranges = ["0.0.0.0/0"]
}
上記バグ修正のコミット用にブランチを切って、上記の内容を反映。
$ git checkout -b bugfix origin/dev
M modules/firewall/main.tf
Branch 'bugfix' set up to track remote branch 'dev' from 'origin'.
Switched to a new branch 'bugfix'
$
$ git branch
* bugfix
dev
$
$ git add .
$
$ git status
ブランチ bugfix
Your branch is up to date with 'origin/dev'.
コミット予定の変更点:
(use "git restore --staged <file>..." to unstage)
modified: modules/firewall/main.tf
$
$ git commit -m "http ファイアウォール ターゲットの修正"
[bugfix 82e283a] http ファイアウォール ターゲットの修正
1 file changed, 1 insertion(+), 1 deletion(-)
$
$ git status
ブランチ bugfix
このブランチは 'origin/dev' よりも1コミット進んでいます。
(use "git push" to publish your local commits)
nothing to commit, working tree clean
$
$ git push -u origin bugfix
Enumerating objects: 9, done.
〜〜〜
To github.com:sky0621/solutions-terraform-cloudbuild-gitops.git
* [new branch] bugfix -> bugfix
Branch 'bugfix' set up to track remote branch 'bugfix' from 'origin'.
作成したバグ修正ブランチを dev
ブランチにマージ
あれっ、Cloud Buildのとこで失敗してる?
というわけで、詳細を見に行く。
これでもよくわからないので、Cloud Build側のページへ飛ぶ。
terraform init
で失敗してるのかな。
「生データを表示」してみると、
Step #1 - "tf init": [0m[1mInitializing the backend...[0m
Step #1 - "tf init": [31mError loading backend config: 1 error occurred:
Step #1 - "tf init": * terraform.backend: configuration cannot contain interpolations
Step #1 - "tf init":
Step #1 - "tf init": The backend configuration is loaded by Terraform extremely early, before
Step #1 - "tf init": the core of Terraform can be initialized. This is necessary because the backend
Step #1 - "tf init": dictates the behavior of that core. The core is what handles interpolation
Step #1 - "tf init": processing. Because of this, interpolations cannot be used in backend
Step #1 - "tf init": configuration.
Step #1 - "tf init":
Step #1 - "tf init": If you'd like to parameterize backend configuration, we recommend using
Step #1 - "tf init": partial configuration with the "-backend-config" flag to "terraform init".
Step #1 - "tf init":
Step #1 - "tf init": [0m[0m
Finished Step #1 - "tf init"
ERROR
ERROR: build step 1 "hashicorp/terraform:0.11.14" failed: step exited with non-zero status: 1
「terraform.backend: configuration cannot contain interpolations
」とのこと。
ググる。
https://qiita.com/aoshimash/items/f0be7dd81c856d335460
backend.tf
で変数使えない様子。。。
しょうがないので変数使わず、再度Push。
お、先ほどはすぐに失敗したけど、しばらく継続してる様子。
成功した。
詳細を見てみる。
失敗時にも出ていたけど、以下に記載の4つのステップが実行されたログとなっている。
https://github.com/GoogleCloudPlatform/solutions-terraform-cloudbuild-gitops/blob/master/cloudbuild.yaml
つまり、terraform apply
までされているので、うまくいっているなら、適用対象のGCPプロジェクトにTerraformの定義が反映されているはず。
そう、今回、bugfix
ブランチから dev
ブランチへのプルリクは作ったけど、まだマージしてない。
このCloud Buildログは、あくまで bugfix
ブランチが出来たから流されたもので、設定としては dev
と prod
以外は terraform apply
はしないものとなっているので、実際にGCP環境には適用されていない。
では、プルリクをマージして、dev
ブランチを更新してみる。
はい、マージ完了。
Cloud Build側でビルド状況を確認してみる。
すると、dev
ブランチに更新がかかったゆえの実行中ビルドが増えている。
完了後のログ詳細を見てみると、
今度は、スキップされずに terraform apply
が実行されている。
■ 反映結果の確認
まずは、Terraform実行による状態管理をCloud Storage上で行う設定にしたので、その確認。
うん、tfstate
ファイルが格納されている。
うまくいっているなら、以下が出来ているはず。
- VPC
- Firewall
- Compute Engine
それぞれ出来ておる。
まとめ
参考記事では、このあと、「Cloud Build の実行が成功した場合にのみマージを適用できるようにする」対応や本番環境の方への適用などもやっているけど、ちょっと力尽きたので今回はこのへんでおしまい。