🔥

テスタブル&DRYなTerraformプロジェクト 設計ドラフト v0.1.0

2024/03/03に公開

(前回からの続き)

今回はタイトル通りのTerraformプロジェクトを実際に作成していく。

Terraformではフォルダ構造やHCLファイルの配置が実装や運用に直接的な影響を与えるため、まずその点について初歩的な破綻がないことを確認する必要がある。

前回の記事でも書いたように、VPC + Cloud SQL(MySQL) + Cloud Runを使用したアーキテクチャを実装のお題としていて、今回はVPCとCloud SQLの実装まで検証を行った。

なおこの記事で説明するすべての内容をまとめたプロジェクトをGithubリポジトリで公開しているので、実際のソースコードを確認したい場合、以下のコマンドでローカルにチェックアウトすることができる。

$ cd /path/to/work-dir

# 以下のどちらかのコマンドでローカルにclone
$ git clone https://github.com/erueru-tech/infra-testing-google-sample.git
$ gh repo clone erueru-tech/infra-testing-google-sample

# 新規チェックアウトであれば不要だが、既にclone済みの場合必要
$ git fetch

# この記事に紐づくバージョンに切り替え
$ git checkout -b 0.1.1 refs/tags/0.1.1

それでは、infra-testing-google-sampleプロジェクトのフォルダ構造やファイルについて、どのような設計を行ったのか解説していく。

📂 infra-testing-google-sample

infra-testing-google-sampleプロジェクトのフォルダ構造は説明のためにかなり省略しているが、だいたいは以下のようになる。

$ cd /path/to/infra-testing-google-sample
$ tree -a
.
├── LICENSE
├── README.md
├── .gitignore
├── .pre-commit-config.yaml
├── scripts
│   ├── .gitignore
│   ├── README.md
│   ├── setup_gcp_project.sh
│   ├── _config.sh
│   ├── _utils.sh
│   ├── create_terraform_environment_template.sh
│   ├── create_terraform_module_template.sh
│   └── tflint.sh
└── terraform
    ├── globals.tf
    ├── environment-globals.tf
    ├── module-globals.tf
    ├── tier1-variables.tf
    ├── tier1-main.tf
    ├── tier1-outputs.tf
    ├── tier2-variables.tf
    ├── tier2-main.tf
    ├── tier2-outputs.tf
    ├── environments
    │   ├── sbx
    │   │   ├── .gitignore
    │   │   ├── tier1
    │   │   │   ├── tests
    │   │   │   ├── environment-globals.tf -> ../../../environment-globals.tf
    │   │   │   ├── globals.tf -> ../../../globals.tf
    │   │   │   ├── terraform.tf
    │   │   │   ├── variables.tf
    │   │   │   ├── sbx-tier1.auto.tfvars
    │   │   │   ├── main.tf
    │   │   │   ├── outputs.tf
    │   │   │   ├── tier1-main.tf -> ../../../tier1-main.tf
    │   │   │   ├── tier1-outputs.tf -> ../../../tier1-outputs.tf
    │   │   │   └── tier1-variables.tf -> ../../../tier1-variables.tf
    │   │   └── tier2
    │   │       ├── tests
    │   │       ├── environment-globals.tf -> ../../../environment-globals.tf
    │   │       ├── globals.tf -> ../../../globals.tf
    │   │       ├── terraform.tf
    │   │       ├── data.tf
    │   │       ├── variables.tf
    │   │       ├── sbx-tier2.auto.tfvars
    │   │       ├── main.tf
    │   │       ├── outputs.tf
    │   │       ├── tier2-main.tf -> ../../../tier2-main.tf
    │   │       ├── tier2-outputs.tf -> ../../../tier2-outputs.tf
    │   │       └── tier2-variables.tf -> ../../../tier2-variables.tf
    │   ├── test
    │   │   ├── ... (.gitignoreを除き、sbxと同じフォルダ、ファイル構成)
    │   ├── stg
    │   │   ├── ... (.gitignoreを除き、sbxと同じフォルダ、ファイル構成)
    │   └── prod
    │       ├── ... (.gitignoreを除き、sbxと同じフォルダ、ファイル構成)
    └── modules
        ├── .gitignore
        ├── network
        │   ├── tests
        │   ├── globals.tf -> ../../globals.tf
        │   ├── module-globals.tf -> ../../module-globals.tf
        │   ├── variables.tf
        │   ├── main.tf
        │   └── outputs.tf
        └── db
            ├── ... (networkモジュールと同じフォルダ、ファイル構成)

これらのファイル、ディレクトリについては、順を追って1つずつ説明していく。

まずプロジェクトルート直下にはREADME.md、LICENSE、.gitignore、.pre-commit-config.yamlといったファイルがある。

README.mdとLICENSEについては一般的なものなので、特に説明の必要はない。

.gitignoreはGithubリポジトリ作成時にAdd .gitignoreの設定でTerraformを選択して生成されたものに対して、以下の設定をコメントアウトしたものを使用している(理由は後述)。

.gitignore
#*.tfvars
#*.tfvars.json

.pre-commit-config.yamlは、Gitコミット前に余計なスペースが設定ファイルに含まれているのを削除したり、TFLintやterraform fmtコマンドを実行するためのツールであるpre-commitの設定ファイルとなる。

📁 scripts

プロジェクトルート配下のscriptsディレクトリにはこのインフラプロジェクトを開発するのに役立つスクリプトをまとめている.

各スクリプトの説明は以下で行うが、このディレクトリ内にあるREADME.mdにも説明がまとめられているので、そちらも確認していただきたい。

setup_gcp_project.sh, _config.sh, _utils.sh, .gitignore

setup_gcp_project.shは、このインフラプロジェクトで使用するGoogle Cloudプロジェクトの作成およびセットアップを行うためのスクリプトである。

README.mdにも記載している通り、Google Cloudプロジェクトは個人/会社ごとに作成方法やポリシーが異なるため、汎用的な実装を行うことはできない。

そのため、このスクリプトは基本的に自分の開発環境に依存した実装となっており、第三者の利用はまったく想定していないスクリプトとなっていて、ほぼ確実と言ってよい確率で他の組織では正しく動作しない。

それでも試してみたい人がいるなら、README.mdに実行のための前提条件や漠然とした実行方法を記載したので、そちらを参考にしてほしい。

ちなみにsetup_gcp_project.shを実行する前に_config.shに自身の組織情報、Billing情報、サービス名、環境名(prod, stg, e.g.)といった情報を以下のように指定する必要がある。

scripts/_config.sh
$ vi scripts/_config.sh
# 以下の設定を記述
#!/bin/bash

# 組織のID
readonly ORG="123456789012"
# 組織のドメイン
readonly ORG_DOMAIN="foo-bar-12345.com"
# 以下のコマンドで確認できるBilling ID
# $ gcloud billing accounts list
readonly BILLING_ACCOUNT_ID="123456-7890AB-CDEF01"
# サービス名
readonly SERVICE="infra-testing-google-sample"
# 環境名(prod, stg, e.g.)
readonly ENV="sbx-e"

この情報を元にsetup_gcp_project.shはGoogle Cloudプロジェクトのセットアップを行うようになっている。

なおセンシティブな値を設定することになるため、_config.shは同じディレクトリ内にある.gitignoreでコミット対象から除外する設定を行なっている。

_utils.shは各スクリプト内で使用される汎用的な関数をまとめているが、特に読む必要はない。

create_terraform_environment_template.sh

create_terraform_environment_template.shはterraform/environmentsディレクトリ配下に、引数で指定した環境用の設定ファイルを自動的に作成するためのスクリプトとなる。

なおこのプロジェクトでは、ファイルやフォルダ構成に対してかなり厳しい制約を課しているため、基本的に新しい環境を構築する場合は必ずこのスクリプトを使う必要がある。

create_terraform_module_template.sh

create_terraform_module_template.shはterraform/modulesディレクトリ配下に、引数で指定したTerraformモジュール用の設定ファイルを自動的に作成するためのスクリプトとなる。

モジュールについても、ファイルやフォルダ構成に対してかなり厳しい制約を課しているため、基本的に新しいモジュールを構築する場合は必ずこのスクリプトを使う必要がある。

tflint.sh

tflint.shはGitコミット時にpre-commitでTFLintを実行する場合と、CIでTFLintを実行する場合の処理を一元管理するために用意したものだが、怪しい挙動が見受けられるので近い将来に改修、あるいは削除される予定。

📁 terraform

プロジェクトルート配下のterraformディレクトリにはインフラストラクチャを構築するために必要なすべてのHCLファイルがまとめられている。

このプロジェクトではHCLファイルをmodule、environment、tierという単位で管理を行なっていて、Terraformのstateのパーティショニングを設計する際にはこれらの単位を踏まえて考える必要がある。

まずmoduleだが、これは一般的な意味のTerraformモジュールと同義となる。

今回はVPCとCloud SQLを作成するが、VPCを作成する設定はnetworkモジュール、Cloud SQLを作成する設定はdbモジュールといったように、Terraformの設定を意図的な単位で部品化したものがモジュールになる。

HCLファイルを適切な単位のモジュールで分けることによって、モジュールの再利用やテストコードの実装が容易になるというメリットがある。

そしてこのmoduleを組み合わせて各環境のインフラストラクチャ上にリソースを作成していくことになる。

environmentとは本番環境や開発環境といった各環境をあらわす単位となる。

なお、このインフラプロジェクトで構築するサービス(infra-testing-google-sample)では以下のような4つの環境を持つと仮定している。

環境 CIDR
prod 10.1.0.0/16
stg 10.2.0.0/16
test 10.3.0.0/16
sbx-e 10.4.0.0/16

prod環境やstg環境は一般的な環境名であるため特に説明はしないが、test環境はCI/CD時にTerraformのテストコードの実行を行うためだけに存在する環境として扱う。

sandbox環境については前回の記事でも説明したが、これはインフラ開発者1人1人に割り当てられる開発やテストを行うための専用環境となる。

所有者を識別するためのサフィックスをsbxの後に付与していて、開発者がerueru-techの場合、sbx-eといった環境名になる。

後ほど詳細を説明するものの、これら4つの環境はそれぞれenvironmentsのディレクトリとして定義されているのが分かる。

なおこのプロジェクトでは基本的にディレクトリが分かれた時点でstateの管理も分かれるといった設計となっているため、それぞれの環境間でリソースが共有されることはない。

そして各環境のディレクトリ内にはtier1とtier2というディレクトリが存在する。

tierとはこのプロジェクトが独自に定義したHCLファイルの管理単位であって、一般的なプラクティスではない。

tier1とtier2それぞれにHCLファイルを定義することになるが、分け方としてはテストコード実行後のterraform destroyを回避したいリソースかどうかが一番の判断基準となる。

tier1にはGCPサービスAPIの有効化設定やVPCなどのネットワークリソースといった、テストコード実行のたびに削除->再作成を行いたくない、いわゆるdestroyを回避したいリソースを定義する。

一方tier2にはCloud SQLなどの常時稼働させておくとコストがかかるような、テストコードの実行が完了したら必ず削除を行いたいリソースを定義する。

このようにtier1とtier2でディレクトリを分けることでstateの管理が分離され、結果tier2内に定義されているリソースのみをdestroyすることが可能となる。

ちなみに、Cloud SQLインスタンスの起動には20分程度かかり非常に低速であるため、destroyされないtier1に配置した方がいいのではないかといった、各リソースの適切な分類方法については今後見直しの余地がある。

また当初はtier1をperm(=permanent)、tier2をtemp(=temporary)と名づける予定だったが、prod環境やstg環境にとっては、どちらもpermanentであるため、より役割をぼかした名前であるtierとすることにした。

つまりprod、stg環境にとっては意味のないパーティションとはなるが、各環境間でディレクトリとファイルの構造--つまりはオペレーション方法を統一したいことから、全環境でtierディレクトリを持たせるようにしている。

以上がこのプロジェクトにおける、HCLファイルの管理単位の説明となる。

これらの内容を踏まえて、次はterraformディレクトリ直下に存在する以下のファイルの意図について、それぞれ説明していく。

.
└── terraform
    ├── globals.tf
    ├── environment-globals.tf
    ├── module-globals.tf
    ├── tier1-variables.tf
    ├── tier1-main.tf
    ├── tier1-outputs.tf
    ├── tier2-variables.tf
    ├── tier2-main.tf
    ├── tier2-outputs.tf
...

globals.tf, environment-globals.tf, module-globals.tf

まずglobals.tfだが、これは全moduleディレクトリおよび全environmentの全tierディレクトリ内で必ず使用する定義を記述するファイルとなる。

具体的には、terraformブロックやproviderブロックの宣言だったり、serviceやregion、project_idなどといった常に必要な変数などを定義している。

このファイルのシンボリックリンクを各moduleディレクトリやtierディレクトリ内に配置することでDRYな定義を実現している。

なおシンボリックリンクはcreate_terraform_(environment|module)_template.shスクリプトを使うことで自動的に作成されるため、開発者が追加の設定をする必要はない。

次にenvironment-globals.tfだが、これはenvironments配下の全ディレクトリ内で必須となる定義を記述するファイルで、globals.tfの役割からmodule部分を差し引いたような設定ファイルとなっている。

module-globals.tfはenvironment-globals.tfの逆で、全moduleディレクトリ内で必須となる定義を記述するファイルとなる。

environment-globals.tfとmodule-globals.tfについても、create_terraform_(environment|module)_template.shスクリプトを使用して各ディレクトリすることで、自動的に必要な箇所にシンボリックリンクが作成される。

tierN-variables.tf, tierN-main.tf, tierN-outputs.tf

globals.tfの説明で大体想像はつくだろうが、tierN-variables.tftierN-main.tftierN-outputs.tfは環境を横断して各tierディレクトリ内で共通の設定を記述するファイルである。

これもcreate_terraform_(environment|module)_template.shスクリプトを使用することで、自動的に各tierディレクトリ内にシンボリックリンクが作成される。

後ほど説明することになるが、運用に必要なインフラリソースの定義は、基本的にこの3つのファイル(2つのtierで6ファイル)内に集中するような気がしている。

📁 terraform/modules

modules内にはinfra-testing-google-sampleサービス内で使用するnetworkとdbというTerraformモジュールが作られている。

networkモジュールは現時点ではVPCネットワークやサブネット、ルートなどの作成のみを行なっているが、将来的にネットワーク関連リソースが増えた場合にはこのモジュール内に設定を追加していく。

ちなみに運用やstate管理の粒度次第では、別のモジュール名にしたり、細分化した方が良い可能性があるので、その点は各自のプロジェクト内で適切なものを選択する必要がある。

もう一つのモジュールであるdbモジュールでは、Cloud SQL上にMySQLインスタンスの作成と、Private IPアドレスの付与設定を行っている。

ネットワークの作成とMySQLインスタンスの作成は、基本的にGoogleが公開しているブループリントモジュールnetworkモジュールとsql-dbモジュールを又呼びしているだけである。

(ちなみにブループリントモジュールの中にはthree-tier-web-appのような汎用性や拡張性が低いモジュールがあるため、必ず中身を調査してから使う必要がある点には注意)

設定の詳細について興味があるなら実際のコードを確認してもらうのが一番だが、ここでも一応モジュールディレクトリ内について説明する。

networkモジュールの内容は以下のようになる。

.
└── terraform
    └── modules
        ├── network
        │   ├── tests
        │   ├── globals.tf -> ../../globals.tf
        │   ├── module-globals.tf -> ../../module-globals.tf
        │   ├── variables.tf
        │   ├── main.tf
        │   └── outputs.tf

tests

testsディレクトリはTerraform Testのテストコードを格納する予定のディレクトリとなる。

なおTerraform Cloudの調査でも書いたが、Terraformのテストはモジュール単位で行うことになるため、テストの際には以下のようなコマンドでモジュールディレクトリに移動してテストを実行することになる。

$ cd /path/to/infra-testing-google-sample/terraform/modules/network
$ terraform init
$ TF_VAR_service=infra-testing-google-sample \
TF_VAR_env=sbx-e \
... \
terraform test

手動テストについても同様にこのディレクトリに移動して、planやapplyコマンドを実行したのち、Google Cloudのコンソールから変更が正しく反映されたか確認するといった流れで行うことになる。

ちなみに上記terraform initコマンドでは-backend-configを指定しないため、このディレクトリ内にstateファイルが作成される。

つまりはコンソール上から直接変更操作が行われたドリフトしたリソースと同じような状態になる。

これについては個人環境(sbx-e)のGCSバックエンドでstate管理するべきかもしれないが、Terraformのモジュールのテストコードは並列実行を行いたいという要件があり、その場合モジュール間でstateを共有すると今度はテスト実行の独立性の問題が懸念されることから、現在は判断を保留している。

他にもTF_VAR_env=prodを指定してapply&destroyを実行すると、本番環境にダイレクトにモジュール内の定義を反映できる点について対策を施す必要があるなど、割と課題が多い点となっている。

globals.tf, module-globals.tf

長くなってしまったが、話を各ファイルの説明に戻す。

globals.tfとmodule-globals.tfは先ほど説明した全モジュールで共通の設定が上の階層から天下りしてきたものである。

variables.tf, main.tf, outputs.tf

variables.tf、main.tf、outputs.tfはTerraformモジュールの基本構成要素なので特に説明の必要はないかと思われる。

dbモジュールのフォルダ構造はnetworkモジュールのものと全く同じであるため、説明は省略する。

(ともにcreate_terraform_module_template.shから作られたこともあり)

.gitignore

最後にmodulesディレクトリ直下に.gitignoreファイルが存在するが、これはモジュール内でテストコードや手動テストを実行した際に作成される.terraform.lock.hclを、VCS(Github, Gitlab, e.g.)で管理しないようにするための設定となる。

他にもモジュール内でapplyコマンドを実行するとstateファイルが作成されるが、これについてはルートディレクトリ直下の.gitignoreでignore対象として指定されているため、これもVCSで管理されることはない。

📁 terraform/environments

environments内にはinfra-testing-google-sampleサービス内で使用する各環境ごとにディレクトリが分けられている。

どのディレクトリがどの環境用なのかは一目瞭然で、その中身のフォルダ構成も全環境間で同じである(後述するsbx配下の.gitignoreを除く)。

よって、1つの環境の構造さえ理解できれば他のすべての環境についても理解出来るようになるため、ここではsandbox環境用ディレクトリを例に挙げて、ファイルやフォルダについて詳細を説明する。

sandbox環境用ディレクトリの内容は以下のようになる。

.
└── terraform
    ├── environments
    │   ├── sbx
    │   │   ├── .gitignore
    │   │   ├── tier1
    │   │   │   ├── tests
    │   │   │   ├── environment-globals.tf -> ../../../environment-globals.tf
    │   │   │   ├── globals.tf -> ../../../globals.tf
    │   │   │   ├── terraform.tf
    │   │   │   ├── variables.tf
    │   │   │   ├── sbx-tier1.auto.tfvars
    │   │   │   ├── main.tf
    │   │   │   ├── outputs.tf
    │   │   │   ├── tier1-main.tf -> ../../../tier1-main.tf
    │   │   │   ├── tier1-outputs.tf -> ../../../tier1-outputs.tf
    │   │   │   └── tier1-variables.tf -> ../../../tier1-variables.tf
    │   │   └── tier2
    │   │       ├── tests
    │   │       ├── environment-globals.tf -> ../../../environment-globals.tf
    │   │       ├── globals.tf -> ../../../globals.tf
    │   │       ├── terraform.tf
    │   │       ├── data.tf
    │   │       ├── variables.tf
    │   │       ├── sbx-tier2.auto.tfvars
    │   │       ├── main.tf
    │   │       ├── outputs.tf
    │   │       ├── tier2-main.tf -> ../../../tier2-main.tf
    │   │       ├── tier2-outputs.tf -> ../../../tier2-outputs.tf
    │   │       └── tier2-variables.tf -> ../../../tier2-variables.tf

まずはtier1について説明していく。

tests

testsディレクトリはmodule同様、Terraform Test用のディレクトリだが、環境側でテストコードの実行を行う可能性は低い気がしていて、もしかすると将来的には不要となるかもしれない。

globals.tf, environment-globals.tf

次にglobals.tfとenvironment-globals.tfだが、これは全環境で共通の設定が上の階層から天下りしてきたものとなる。

terraform.tf

terraform.tfではstate管理のバックエンドとしてGCSを使用する定義を以下のように行っていて、prefixについてはstateのパスを指定しているが、bucketについてはyour-terraform-bucket-nameといったように、そのままterraform initを実行するとエラーが発生するバケット名を指定している。

terraform/environments/sbx/tier1/terraform.tf
terraform {
  backend "gcs" {
    bucket  = "your-terraform-bucket-name"
    prefix  = "terraform/tier1-state"
  }
}

これはソースコードが公開されているものであるため自分のバケット名を書けないかつ、terraformブロック内では変数を利用できないため、仮置きでこのような定義をしている。

各自のprivateリポジトリであれば、この値を自分の環境にあったバケット名に変更して、VCSで管理しても問題ない。

ただしprod環境では簡単にterraform destroyできないようにするためにこのままにするなどして、CI/CDパイプラインで本番リリースする時に限り以下のようにコマンドでバックエンドを指定して利用するような運用を想定している。

$ terraform init -backend-config="bucket=infra-testing-google-sample-sbx-e-terraform"

sandbox環境についても開発者が1人だけならbucket名をterraform.tfに直接設定してVCS上で管理しても問題ないが、複数人で開発を行う場合はprod環境同様に-backend-configオプションを使用してバケットを指定するか、.gitignoreでこのファイルをVCSの管理対象から除外して、bucketの値を変更するなどの対応が必要になる。

variables.tf, main.tf, outputs.tf

variables.tf、main.tf、outputs.tfは基本的なHCLファイルのセットだが、ここにはsandbox環境のtier1でのみ使用する定義を記述する。

ちなみにインフラを構築するためのリソース定義はほとんどのケースにおいて、次に説明するtierN-variables.tf、tierN-main.tf、tierN-outputs.tf側で全環境共通で設定することになるかと思われる。

また仮にsandbox環境およびtier1環境だけでリソースの作成が必要な要件が発生したとしても、count引数を使用して、count = startswith(var.env, "sbx-") ? 1 : 0のように特定の環境でのみリソースを作る条件は指定可能なので、もしかするとこれらの設定ファイルを必要とするケースはほとんどないのかもしれない。

今後開発を進めていって、長期にわたって不要の状態が続いた場合は削除対象となる。

ただしvariables.tfだけは以下の重要なenv変数の宣言が行われているため削除してはいけない。

terraform/environments/sbx/tier1/variables.tf
variable "env" {
  type    = string
  default = null
  validation {
    condition     =  startswith(var.env, "sbx-")
    error_message = "The value of var.env must start with 'sbx-', but it is '${var.env}'."
  }
}

env変数はアサーションの定義が環境ごとにかなり異なるため共通化が難しく、この変数に関してだけは個別のvariables.tfで宣言している。

なお、sbx/tier1とsbx/tier2でまったく同じ定義となっていて、シンボリックリンクを使用してDRYに宣言することも可能である。

しかし、複数階層での天下りを行うとどこに値を定義すればいいのかを考える上でさらに混乱を招くような気がしたので、いったんは重複して定義を行なっている。

tier1-variables.tf, tier1-main.tf, tier1-outputs.tf, sbx-tier1.auto.tfvars

既に説明したが、基本的にはインフラ上に構築するリソースは、天下りしてきたこれらのHCLファイルに定義することになる。

例えばGCPサービスAPIの有効化やVPCモジュールの作成の定義はすべてtier1-main.tfに定義されていて、これはシンボリックリンクであるため全環境およびtier間でDRYに宣言される。

DRYなリソース定義に対して、環境ごとに異なる値(サブネットの範囲など)を渡す必要がある場合は、変数化を行ったのち、その変数の値をsbx-tier1.auto.tfvarsに定義することで、terraform apply時に自動的に値が埋め込まれる。

(.auto.tfvarsについては、公式ドキュメントを参照)

data.tf

以上でtier1の内容について全て説明した。

tier2についてもtier1と各ファイルの意味は同じなのでその点については説明を省略するが、唯一異なる点として、以下のdata.tfが存在することが挙げられる。

terraform/environments/sbx/tier2/data.tf
data "terraform_remote_state" "tier1" {
  backend = "gcs"
  config = {
    bucket  = "${local.project_id}-terraform"
    prefix  = "terraform/tier1-state"
  }
}

これはtier2内でCloud SQLインスタンスを作成する際に、tier1側のstateで管理されているVPCのIDや名前などを参照できるようにするための設定となる。

参照は以下のようにして行う。

terraform/environments/sbx/tier2/tier2-main.tf
module "db" {
  source       = "../../../modules/db"
  service      = var.service
  env          = var.env
  vpc_id = data.terraform_remote_state.tier1.outputs.vpc_id
  vpc_name = data.terraform_remote_state.tier1.outputs.vpc_name
  cloudsql_network_address = var.cloudsql_network_address
  availability_type = var.availability_type
}

ちなみにtier2に存在するリソースをtier1が参照する可能性も考えられるが、循環参照やデッドロックのような問題が想起されるので、いったんはtier2のみにdata.tfを定義して運用を行っていく。

.gitignore

最後に、sbxディレクトリ限定(他の環境にはない)で、.gitignoreが宣言されているが、これは複数人でこのディレクトリを利用することを想定した場合、sbx-tierN.auto.tfvarsに定義する値(例えばサブネットの範囲)が各人で異なることから、sbx-tierN.auto.tfvarsの変更をVCS管理対象外とするために用意している。

ただしサンプルを提供する側としては、sbx-tierN.auto.tfvarsもVCSで管理したいので、いったんはコメントアウトしている。

terraform/environments/sbx/.gitignore
#sbx-tier*.auto.tfvars

ちなみにsandbox環境の数が限定されるなら、sbxでまとめずに1つずつsbx-a、sbx-bのようなディレクトリを作って管理した方がシンプルな実装になる。

しかしプロジェクト内のファイル数が肥大していくことになり、管理対象が多くなることで発生する問題とのトレードオフになるかと思われる。

ロードマップ

以上でサンプルプロジェクトの設計について一通り説明してきた。

設計は引き続き行なっていくことになるが、infra-testing-google-sampleプロジェクトの今後の設計/実装予定は以下のようになっている。

  • Terraform Testによるunit、integrationテストの書き方を体系化
  • terraform-execによるunit、integrationテストの書き方を体系化
  • Terraformのcheckによるe2eテスト、continuous validationの体系化
  • terraform-execによるe2eテスト、continuous validationの体系化
  • project_cleanupによる、 テスト時に削除し損ねたリソースをクリーンアップする方法の体系化
  • Conftestによるポリシーテスト導入
  • Trivyによるセキュリティテストの導入
  • drift detectionの実装
  • prod、stg環境といった実稼働環境への誤ったdestroy実行対策の確立
  • リソースを意図的なドリフト、tier1、tier2のいずれに分類するかといった定義基準の確立
  • GHAによるCI/CDパイプラインの構築、リポジトリのtag打ち自動化
  • 各モジュールの仕様ドキュメントなど追加

おわり

上記ロードマップの課題がすべて解決されれば、プロトタイプ版の完成となり、次いで実際のサービスへの投入といった流れとなる。

(導入/学習コストとリスクが高いため、まったく重要ではない、最悪障害などが発生しても問題ないようなサービスへの投入となる予定)

今回は設計の概要についてのみ話したが、実際にこのプロジェクトを使用して、どのように手動テストや自動テストを行うのかといった具体的な開発フローについて、次回は説明したい。

(その前にTerraform Testのテスト実装体系化を先にやるかも)

追記

上記ロードマップでproject_cleanupを紹介したが、これはリソースではなくGCPプロジェクト丸ごと削除するツールのようで、さらにApp Engineというリージョンを一度設定したら二度と変更できない問題や、Cloud RunやFunctionsに比べて様々な制約を抱えるサービスを使用しているため、利用を断念。

Discussion