Terraform で GCP のリソースを管理する
Terraform で GCP のリソースを管理する
概要
GKE, Cloud SQL, Cloud Storage, Cloud Memorystore など、gcloud コマンドで構築していたが、 今後、本番、負荷試験、開発環境を増やす、といった際に、すぐに構築できるように Terraform の導入を検討してみた。
Terraform
Install
自分は homebrew で入れた
$ brew install terraform $ terraform -v Terraform v0.11.7
使う
方針
- terraform 用のサービスアカウントは作成せず、owner 権限を持った個人が terraform コマンドを実行する。
- .tfstate は GCS で管理する。
- workspace を切り替えて、環境ごとに操作する。
- 今回は Cloud SQL, Cloud Storage, Cloud Memorystore のコードをのせ、GKE については別でまとめたい。 ※GKE は同じ cluster に namespace で環境を用意しようと思っているが、負荷試験環境では新しく cluster を作らればならないと考えている。cluster のスペックの検証のために。
terraform ディレクトリを作成し、そこでリソース操作を行う。
$ mkdir terraform $ cd terraform
フォルダ構成をどうするか
構成1
- tarraform のルートディレクトリで workspace を操作し、
- tarraform のルートディレクトリで
terraform init <サブディレクトリ>
で state ファイルを gcs で管理すると、 - tarraform のルートディレクトリで workspace を切り替えられないエラーに直面した。
具体的にいうと、下記のフォルダ構成において
$ tree . ├── environments │ ├── production │ │ ├── backend.tf │ │ ├── main.tf │ │ ├── provider.tf │ │ └── variables.tf │ └── staging │ ├── backend.tf │ ├── main.tf │ ├── provider.tf │ └── variables.tf ├── modules │ ├── db │ │ ├── main.tf │ │ └── variables.tf │ ├── redis │ │ ├── main.tf │ │ └── variables.tf │ └── storage │ ├── main.tf │ └── variables.tf
tarraform のルートディレクトリで workspace を操作
$ pwd <ディレクトリ>/terraform $ terraform workspace show staging
tarraform のルートディレクトリでサブディレクトリを init
$ terraform init environments/staging/ 略 Terraform has been successfully initialized!
workspace 切り替えるとエラー
$ terraform workspace select production Backend reinitialization required. Please run "terraform init". Reason: Unsetting the previously set backend "gcs" The "backend" is the interface that Terraform uses to store state, perform operations, etc. If this message is showing up, it means that the Terraform configuration you're using is using a custom configuration for the Terraform backend. Changes to backend configurations require reinitialization. This allows Terraform to setup the new configuration, copy existing state, etc. This is only done during "terraform init". Please run that command now then try again. If the change reason above is incorrect, please verify your configuration hasn't changed and try again. At this point, no changes to your existing configuration or state have been made. Failed to load backend: Initialization required. Please see the error message above.
同じ内容の issue を見つけた。
For now the recommended workflow is to always run terraform from the root of the configuration for consistency, which also has the benefit of making it explicit which configuration the working directory has been initialized for.
どうやら設定ファイル(.tf)のルートディレクトリで terraform コマンドを実行することを想定していて、terraform init <サブディレクトリ>
は考慮されていないらしい...
公式で推奨している構成と矛盾している気がするが、仕方ない。。。
構成2
├── cluster(GKE用) │ ├── backend.tf │ ├── main.tf │ ├── provider.tf │ └── variables.tf ├── datastores │ ├── backend.tf │ ├── main.tf │ ├── modules │ │ ├── db │ │ │ ├── main.tf │ │ │ └── variables.tf │ │ ├── redis │ │ │ ├── main.tf │ │ │ └── variables.tf │ │ └── storage │ │ ├── main.tf │ │ └── variables.tf │ ├── provider.tf │ └── variables.tf
このようなフォルダ構成にし、datastores ディレクトリで workspace (staging, production, stress) を作成し、cloud_sql, cloud_memorystore, cloud_storage リソースを操作することにする。
terraform/resource ディレクトリで、 workspace を切り替えて操作する。
$ cd terraform/<resource>
workspace を切り替え
$ terraform workspace list $ terraform workspace select staging
init。module を更新した際も初期化が必要
$ terraform init
リソース作成の計画を表示する
$ terraform plan
リソース作成を行う
$ terraform apply
コードフォーマットを整える
$ terraform fmt
.tfstate を表示する
$ terraform state pull
コード
コードもどんな感じか載せておく。
datastores/backend.tf
terraform { backend "gcs" { bucket = "<バケット名>" prefix = "datastores" } } data "terraform_remote_state" "backend" { backend = "gcs" workspace = "${terraform.workspace}" config { bucket = "<バケット名>" prefix = "datastores" } }
datastores/main.tf
module "db" { source = "./modules/db" common = "${var.common}" db = "${var.db}" }
datastores/provider.tf
provider "google" { project = "<GCPプロジェクト名>" }
datastores/variables.tf
variable "common" { default = { default.name = "development" default.region = "asia-northeast1" # workspace ごとの設定 staging.name = "staging" } } variable "db" { default = { default.tier = "db-n1-standard-1" # workspace ごとの設定 staging.tier = "db-n1-standard-1" } }
datastores/modules/db/main.tf
resource "google_sql_database_instance" "db" { name = "${lookup(var.common, "${terraform.workspace}.name", var.common["default.name"])}" database_version = "MYSQL_5_7" region = "${lookup(var.common, "${terraform.workspace}.region", var.common["default.region"])}" settings { tier = "${lookup(var.db, "${terraform.workspace}.tier", var.db["default.tier"])}" replication_type = "SYNCHRONOUS" database_flags { name = "character_set_server" value = "utf8mb4" } database_flags { name = "slow_query_log" value = "on" } database_flags { name = "long_query_time" value = "0.2" } database_flags { name = "lock_wait_timeout" value = "1" } } } resource "google_sql_database" "database" { name = "<データベース名>" instance = "${google_sql_database_instance.db.name}" } resource "google_sql_user" "user" { name = "<ユーザ名>" instance = "${google_sql_database_instance.db.name}" host = "cloudsqlproxy~%" } resource "google_service_account" "cloud_sql_proxy_service_account" { account_id = "${lookup(var.common, "${terraform.workspace}.name", var.common["default.name"])}-cloud-sql-proxy" display_name = "${lookup(var.common, "${terraform.workspace}.name", var.common["default.name"])} Cloud SQL Proxy" } resource "google_project_iam_member" "cloud_sql_proxy_service_account" { role = "roles/cloudsql.client" member = "serviceAccount:${google_service_account.cloud_sql_proxy_service_account.email}" }
datastores/modules/db/variables.tf
variable "common" { default = {} } variable "db" { default = {} }