SREチームの島です。最近Terraformを書いてるのですがまだまだ理解不足なところもあり日々試行錯誤しています。
今日は開発環境をTerraformで作成した話をします。
背景
- 複数人でGCP上で動くアプリを開発したい
- 各自がローカルで変更したソースを動作確認できる環境を作りたい
やりたいこと
- 各自がローカルで変更したソースを動かせる環境をGCP上に作りたい
- 全部ローカルに作れたら良かったが作るのが大変なので手っ取り早く動く環境を作りたかった
- 各自が自由に自分用の環境を作成、削除できるようにしたい。また環境の作成、削除をしても他の環境には影響を与えないようにしたい。
前提
ブランチ戦略
developブランチを親ブランチとしてそこから派生して子ブランチ(feature/X)を作成し、featureブランチで個人開発するようなイメージです。
以下、developブランチが動く環境をdevelop環境、featureブランチが動く環境をfeature環境、まとめて開発環境と呼びます。
クラウド構成概略
- niftyというGCPプロジェクトの中にdevelop環境とfeature環境(N個)を作ります。feature環境は自由に作成、削除することを想定していますので数は増減します。
- 詳細は省きますがPub/SubでBigQueryに溜め込んだデータをCloudRunでグラフ化するようなアプリ「serviceA(仮名)」を想定しています。
- 各環境用のリソースの他に全環境共用のワークロードID、develop環境用、feature環境用のサービスアカウントの作成が必要です。これらは環境ごとに増減するものではないので一度作成すればいいものになります。
ポイント
- 「環境ごとに作るもの」はリソース名被りがないように作る必要がある
- 「共通設定」はTerraformでどのように定義すればいいのか
作ったもの
Terraformのディレクトリ構造は以下のようにしました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
. ├── serviceA │ ├── envs │ │ ├── develop │ │ │ ├── main.tf │ │ │ └── variables.tf │ │ └── feature │ │ ├── main.tf │ │ └── variables.tf │ └── modules // 環境ごとに作るリソース │ ├── bigquery.tf │ ├── dashboard.tf │ ├── iam.tf │ ├── locals.tf │ ├── outputs.tf │ ├── parsers.tf │ ├── provider.tf │ ├── services.tf │ └── variables.tf └── project-common // 共通設定 ├── projects/nifty │ ├── main.tf │ ├── output.tf │ └── variables.tf └── modules ├── oidc-github-pool-provider // ワークロードID │ ├── main.tf │ ├── output.tf │ ├── provider.tf │ └── variables.tf └── oidc-github-sa // develop、feature環境のサービスアカウント ├── main.tf ├── output.tf ├── provider.tf └── variables.tf |
解説
環境ごとに作るリソースの定義
develop環境はserviceA/envs/develop、feature環境はserviceA/envs/feature配下でterraform applyすると、serviceA/modulesで定義した「環境ごとに作るリソース」を作成します。
このとき、変数でブランチ名をモジュールに渡して、各リソース名にブランチ名を付けることでリソース名が被らないようにしました。
serviceA/envs/feature/main.tf
1 2 3 4 5 6 |
module "serviceA" { source = "../../modules/serviceA" project_id = var.project_id (中略) branch_name = var.branch_name // ブランチ名を変数で渡す } |
serviceA/modules/dashboard.tf
1 2 3 4 5 |
resource "google_cloud_run_service" "dashboard" { name = "dashboard-${var.branch_name}" // リソース名にブランチ名をつける location = var.region project = var.project_id (略) |
ステートファイルの管理
ステートファイルの管理についてはGCSで管理する設定にしました。
develop環境のステートファイルは下記のようにprefixを固定で設定しました。
serviceA/envs/develop/main.tf
1 2 3 4 5 6 |
terraform { backend "gcs" { bucket = "tf-state-project-nifty" prefix = "service-a-develop" // prefixを固定で設定 } } |
feature環境については環境ごとにステート管理する必要があるので、固定で設定せずterraform initをする際に
terraform init -backend-config="prefix=service-a-${TF_VAR_branch_name}"
といった感じでブランチ名を付けた値をprefixに設定するようにしました。
serviceA/envs/feature/main.tf
1 2 3 4 5 6 |
terraform { backend "gcs" { bucket = "tf-state-project-nifty" // prefixはここには設定しない } } |
これで環境ごとにステートファイル管理ができます。
共通設定の定義
次に、共通設定は/project-common配下に定義しました。
project-common/projects/nifty/main.tf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// develop環境用のサービスアカウント module "oidc-sa-develop" { source = "../../modules/oidc-github-sa" project_id = var.project_id env_name = "develop" } // feature環境用のサービスアカウント module "oidc-sa-feature" { source = "../../modules/oidc-github-sa" project_id = var.project_id env_name = "feature" } // ワークロードID module "oidc-github-pool-provider" { source = "../../modules/oidc-github-pool-provider" project_id = var.project_id } |
/modules/oidc-github-saをenv_nameの値を変えて2回実行しています。ここで「develop環境用のサービスアカウント」と「feature環境用のサービスアカウント」が作られます。
/modules/oidc-github-pool-providerは「ワークロードID」を定義しています。
さきほどの図に当てはめると以下のようなイメージです。
まとめると
- develop環境構築時(一度だけ実行):「serviceA/envs/develop」、「/project-common/projects/nifty」配下でterraform applyしてdevelop環境と共通設定を作成
- feature環境構築時(環境数分実行):「serviceA/envs/feature」配下でterraform applyしてfeature環境を作る。共通設定は上記で作成済みなのでfeature環境を増やそうが消そうが影響を受けない
といった状態を作ることができました。
これがベストな状態かは定かではないですが、ひとまず開発環境の動作確認ができる状態を作ることができました。
共通設定と環境ごとのモジュールの定義の分け方など実際に実装してみて良い学びになりました。