Terraformの応用編 チームで活用

目次

チームで Terraform を運用する

さきほどは一人でリソースを作成しましたが、チームで利用する場合は環境別の構成や同一環境での競合の防止などを考慮する必要があります。

Terraformには環境の切り替え機能や競合を防止する仕組みが用意されています。

ここではそれらを紐解いていきましょう。

前提条件

  • OS: macOS
  • ツール: Homebrew
  • GCアカウントの作成
  • ✅ Terraformの基本を学ぶ

Terraform をチームで運用するための機能

さきほどは一人でリソースを作成しましたが、チームで利用する場合は環境別の構成や同一環境での競合の防止などを考慮する必要があります。

Terraformには環境の切り替え機能や競合を防止する仕組みが用意されています。

ここではそれらを紐解いていきましょう。

状態管理

Terraformにはリソースの管理状態を永続化する機能が備わっています。この機能により、複数人でリソースを競合することなく作成・変更が行えるようになります。

※ 初期状態ではローカル上にリソース状態が保存されます。またTerraform Cloudを利用する場合にはこの設定は不要です。

バックエンドブロック

バックエンドブロックは最上位のterraformブロック内にネストした形式で追加します。

バックエンドブロックは1つのみ宣言できます。また、このブロックでは変数やデータソース属性などの参照は利用できません。

terraform {
  backend "remote" {
    ...
  }
}

バックエンドは状態をリモートサービスに保存するため、複数の人がアクセスできるようになります。

アクセス認証について

状態データには非常に機密性の高い情報が含まれているため、アクセスには通常、資格情報が必要になります。 そのため、認証情報や機密データを提供するには環境変数を用いることを推奨します。バックエンドのリモートの種類に応じて必要なパラメータが異なります。詳しくは各バックエンドのタイプを参照。

  • .terraform/terraform.tfstate ファイルには、現在の作業ディレクトリのバックエンド構成が含まれています。
    • ハードコーディングされた値は .terraformサブディレクトリとプランファイルに書き込まれるので漏洩するリスクがあります。

GCP で状態管理を行う場合

バックエンドブロックの構成は以下になります。

terraform {
  backend "gcs" {
    bucket  = "tf-state-prod"
    prefix  = "terraform/state"
  }
}

認証にあたっては Google Cloud SDK をインストールし、Application Default Credentials による認証情報を取得する必要があります。

gcloud auth application-default login

詳細はこちら

ワークスペース

Terraform は default という名前のワークスペースから始まり、削除することはできません。

ワークスペース毎に作業ディレクトリは独立して機能します。そのためあるワークスペースで作成したリソースが別のワークスペースではまだ存在しないこともあり得ます。

作業ディレクトリに対して1つのワークスペースが選択できます。

ワークスペースの操作

ワークスペース一覧

terraform workspace list

ワークスペースの作成

terraform workspace new

ワークスペースの削除

terraform workspace delete

ワークスペースの管理

ワークスペースと状態管理

バックエンドに保存された状態管理データはワークスペースに属します。初期状態では1つのワークスペースに1つの状態が関連付けられています。

ただし、いくつかのバックエンドでは複数のワークスペース運用がサポートされています。そのため複数の状態管理が可能になります。新しいバックエンドの設定や認証情報を更新することなく利用することができます。

リソースにワークスペースの情報を適用する

ワークスペースの情報に基づいてリソースのパラメータを設定することができます。

例えば GC Cloud Run のリソースにてインスタンスの最大数の指定において、ワークスペース名に応じて数値を変更するなど。

resource "google_cloud_run_service" "default" {
  name     = "cloudrun-srv"
  location = "us-central1"

  template {
    metadata {
      annotations = {
        "autoscaling.knative.dev/maxScale" = "${terraform.workspace == "default" ? 100 : 5}"
      }
      ...
    }
  }
}

モジュール分割

管理するリソースが多くなったり、環境(dev/stg/prod)別の設定を行いたい場合はモジュール分割による運用が考えられます。 Terraformには設定ファイルやディレクトリの制限はないので、任意の運用が可能ですが、構成によっては運用のコストが増加することが考えられます。

ここではGoogle Cloudに環境別でリソースを扱う場合のモジュール分割の例を見てみましょう。

はじめの構成は以下の通りです。

# Terraformのルートディレクトリ
.
├── main.tf
├── variables.tf
├── outputs.tf

リソース・タイプ別のモジュール作成

管理するリソース・タイプ毎にディレクトリを作成します。

構成図としては以下のようになります。

.
├── main.tf
├── variables.tf
├── outputs.tf
└── modules
    └── <service-name>
        ├─ main.tf
        ├─ variables.tf
        ├─ outputs.tf
        ├─ ...other

service-name にはGCだと App Engine や Cloud Functions などのリソース・タイプを指定します。 その中のmain.tfにリソースブロックを定義して、変数はvariables.tfに定義して〜と、通常の作成方法に従って定義していきます。

なるべくリソースの定義をスリム化して余計な変更を加えないようにするのが良いですね。

例で全環境で利用されるIAMモジュールを定義するとします。

.
├── main.tf
├── variables.tf
├── outputs.tf
└── modules
    └── iam
        ├─ main.tf
        ├─ variables.tf
        ├─ outputs.tf

main.tf

resource "google_service_account" "web_server" {
  project      = var.project
  account_id   = "web_server"
  display_name = "Web server service account."
}

variables.tf

variable "project" {}

この変数の定義は、この後インポートする側から渡されてくる project 変数を受け取るために定義しています。

モジュールの呼び出し

Terraformは1つのディレクトリ(通常、作業ディレクトリ)にある設定ファイルのみを利用します。

しかし、設定ファイルにモジュールブロックを用いることで他のディレクトリにあるモジュールを呼び出し、読み込むことができます。(呼び出されたモジュールは子モジュールと言います。)

モジュールはローカルファイルに限らず、リモートのソースを指定することも可能です。Terraform Registryやバージョン管理システムなど。

※ Terraform Cloudを用いてモジュールを公開するには公式の命名規則に従う必要があります。

今回はローカル上で定義されたモジュールの設定をインポートしたいので、トップディレクトリのmain.tfに以下の設定を追記します。

module "iam" {
  source  = "../../modules/iam"
  project = terraform.workspace
}

こうすることでモジュールの設定ファイルを読み込むことができます。

環境別に作業ディレクトリを分割する

通常 リソースの反映は dev/stg/prod で分けて運用したいと考えます。

その場合に以下のディレクトリ構成を持ちます。

.
├── environments
   ├── dev
   ├── main.tf
   └── variables.tf
   ├── stg
   ├── main.tf
   └── variables.tf
   └── prod
       ├── main.tf
       └── variables.tf
└── modules
    └── <service-name>

devの環境でTerraformを利用する場合は ./environments/dev/ を作業ディレクトリとして用いることで、環境毎にリソースが管理できるようになります。

先ほどのIAMをdev環境で作成したい場合は、dev/main.tf のインポートを以下のように修正します。

./environments/dev/main.tf

module "iam" {
  source  = "../../modules/iam"
  project = terraform.workspace
}

iamモジュールが受け取る project 変数に terraform.workspace を渡しています。