KAKEHASHI Tech Blog

カケハシのEngineer Teamによるブログです。

採用したTerraformのディレクトリ構成について

こちらの記事はカケハシ Advent Calendar 2021の3日目の記事になります。

こんにちは。 この秋リリースしたMusubi AI在庫管理の開発を担当している平松です。

上記プロダクトのインフラ構成管理にTerraformを用いています。 今回は採用しているTerraformのディレクトリ構成について紹介したいと思います。

背景

カケハシではインフラ構成管理ツールとして何を採用するかは、各チームの判断に委ねられています。 現在では、TerraformとAWS CDKを採用しているチームが多く、おおよそ半々くらいです。

今回開発を行ったMusubi AI在庫管理は、

  • 新規プロダクトでより早く立ち上げる必要があった
  • チームメンバーにTerraform経験者がいた

といった状況であったので、Terraformを採用しました。 今回はAWS CDKを採用しませんでしたが、インフラをTypeScriptで記述するといった開発体験は非常に魅力的なのでかなり悩みました。 ただ、当時(2020年)AWS CDKはまだまだ安定しているとはいえず、プロダクトの立ち上げ速度を優先し、より安定しているTerraformを採用しました。

前提

プロダクト要件によって、最適なTerraformのディレクトリ構成は異なります。 ですので、一例として参考にしていただければと思います。

採用しているディレクトリ構成

いきなりですが、採用しているTerraformのディレクトリ構成です。(便宜上簡略化しています)

├── envs
│   ├── dev
│   │   ├── app
│   │   │   ├── db
│   │   │   │   ├── main.tf
│   │   │   │   ├── outputs.tf
│   │   │   │   └── provider.tf
│   │   │   ├── network
│   │   │   ├── fargate
│   │   │   ├── iam
│   │   │   ︙
│   │   ├── etl
│   │   ︙
│   ├── stg
│   ├── prod
│   ︙
├── usecases
│   ├── app
│   │   ├── db
│   │   │   ├── main.tf
│   │   │   ├── outputs.tf
│   │   │   └── variables.tf
│   │   ├── bastion
│   │   ├── network
│   │   ︙
│   ├── etl
│   │   ├── airflow
│   │   │   ├── network
│   │   │   ︙
│   │   ├── embulk
│   │   ├── glue
│   ︙   ︙
└── modules
    ├── s3_bucket
    │   ├── main.tf
    │   ├── outputs.tf
    │   └── variables.tf
    ├── glue_job
    ︙

Terraformのディレクトリ構成に関しての記事は多々ありますが、以下の記事を参考にさせていただきました。

大きく

  • envs
  • usecases
  • modules

のディレクトリに分かれており、各ディレクトリの依存関係は、

envs -> usecases -> modules

のようになっています。

各ディレクトリについて詳しく見ていきます。

envs

今回の設計では、モノリシックなtfstateファイルにするのではなく、適切な粒度でtfstateファイルを細かく分割する方針をとっています。 便宜上、以降はtfstateファイルの単位をコンポーネントと呼ぶことにします。

envsディレクトリ配下には、各コンポーネントの設定が配置されます。 コンポーネントは、

  • dev-app-db
  • dev-app-network

などのようにディレクトリに対応する形で命名し、tfstateファイル名に利用しています。

各コンポーネントのmain.tfには、usecasesディレクトリに定義したmoduleを記述するだけにしています。

module "network" {
  source = "../../../../usecases/app/network"

  app_name       = local.app_name
  vpc_cidr_block = local.vpc_cidr_block
  availability_zones = [
    "ap-northeast-1a",
    "ap-northeast-1c",
    "ap-northeast-1d",
  ]
}

tfstateファイルを細かく分割する方針は、以下Terraform CloudのWorkspacesのドキュメントの考え方を参考にしています。現状Terraform Cloudは使っていませんが、参考にできる内容です。 https://www.terraform.io/docs/cloud/workspaces/index.html#planning-and-organizing-workspaces

また、先日のAWS DEV DAYの以下の動画で、envsディレクトリに分ける構成について紹介されていましたので、よくある構成かと思います。

usecases

envsディレクトリの各コンポーネントから利用されるmodule定義が配置されます。

複数のコンポーネントから利用できるよう汎用的に作る、ということはそこまで意識していません。 たとえば、VPCネットワーク設定に関して、APIアプリケーションのVPCとETLアプリケーションのVPCを構築する必要がありましたが、

  • usecases/app/network
  • usecases/etl/airflow/network

のようにmodule定義を共通化せず、それぞれ定義しました。 似たような記述が増えてしまいますが、moduleの記述が複雑になりませんし、moduleの影響範囲を限定できます。 (余談ですが、自分はReactコンポーネントの設計でも同じような方針を取ることが多いです。無理に共通化しようとすることで痛い目に合ったことがあるので。。)

modules

usecasesディレクトリと同様に、moduleが配置されます。 usecasesディレクトリのmoduleから呼ばれる、よく再利用するmoduleを配置しています。

ただ、usecasesディレクトリのmoduleと同様に、無理に共通化しないような方針をとっています。

module機能について

usecases, modules ディレクトリではTerraformのmodule機能を活用しています。 module機能は便利ですが、注意点とアンチパターンを事前に把握しておくと良いでしょう。 ハマらないためにも公式ドキュメントのNesting modulesの項目に関しては、一読をオススメします。

ふりかえり

上記ディレクトリ構成で一年ほどプロダクトの開発、運用をしてきましたが、比較的うまく回っているように思います。 カケハシでは各プロダクト開発チームにインフラ専任エンジニアがいるわけではなく、必要に応じて各領域のエンジニアがインフラを構築していますが、多くのメンバーがインフラリポジトリにコミットして、インフラリソースの作成、管理をすることができています。 (プロダクト横断のSREチームがサポートしてくれますが、実際の開発や運用は各プロダクトチームに任されています。)

モノリシックなtfstateファイルを分割することはコストがかかるので、最初から見通しの良い設計にしておくことは重要です。 チームやプロダクトによって最適なTerraformのディレクトリ構成は異なりますが、今回紹介したTerraformのディレクトリ構成が誰かの参考になれば幸いです。

最後まで読んでいただきありがとうございました。

最後に

カケハシでアドベントカレンダーやっています。他の記事もぜひ確認してみてください。