ForgeVision Engineer Blog

フォージビジョン エンジニア ブログ

Terraform プロジェクトの効果的なディレクトリ構成パターン(ゼロから始める Terraform 講座~その2~)

こんにちは、こんばんわ!クラウドインテグレーション事業部の魚介系エンジニア松尾です。

前回は基本的な Terraform の使い方をご紹介させていただきましたが、今回は Terraform のディレクトリ構成についての記事を投稿させていただきます。
WEB には色んな方がディレクトリ構成に関する記事を投稿されていますが、内容はバラバラでどう構成しようかと悩まれることが多いのではないのでしょうか。
私自身も答えがあるわけではないのですが、いくつかのパターンをご用意しましたので、ご参考になれば幸いです!

なお、前回の投稿は以下の通りです!
ゼロから始める Terraform 講座~その1~ - ForgeVision Engineer Blog

注:本記事は AWS 利用者向けの記事となり、AWS 環境をベースにコードの記載をさせていただいております。

前提知識

ルートディレクトリの役割

まず Terraform のフォルダ構成を考える上で、ルートディレクトリ(ルートモジュール)の理解が必要不可欠です。

ルートディレクトリは通常、Terraform プロジェクトのエントリーポイントとして機能し、プロジェクト全体の構造や設定がこのディレクトリ内に定義されます。
基本的にローカル PC などからリソースの Apply を行う場合、ルートディレクトリと AWS アカウントが1対1の関係になります。
複数の AWS アカウントを管理する場合はやや工夫が必要となりますので、そちらに関しても後述させていただきます。

モジュールの役割

Terraform のモジュールはルートディレクトリと同様にディレクトリ構成を行うに当たって重要な要素となります。
モジュールは特定の機能やリソースを抽象化し、再利用可能なコードの単位として提供します。

一般的なモジュールの中身はresourceブロックで構成され、このモジュールを呼び出す際に必要な値を渡すことでリソースが作成できます。
このモジュールに記載しているresourceブロックを再利用することで、resourceブロックを重複して記載する必要がなくなり、コードの肥大化を抑えることができます。
なお、モジュールはルートディレクトリの中でだけでなく、ルートディレクトリの外にも配置でき、複数のルートディレクトリから参照させることも可能です。

ディレクトリ構成

あくまで個人的な見解となりますが、私が経験した中でパターンに応じた最適な構成をご説明します。
※細かいファイルの説明は割愛します。

ルートディレクトリが1つの場合

Apply を行う AWS アカウントが1つである場合は、コードの肥大化は抑えつつ可能な限りシンプルな作りにすることをお勧めします。
構成がシンプルなほど可読性は高くなりますので、複数人で管理する場合の引き継ぎもスムーズに済みます。
以下がルートディレクトリが1つの場合の構成例です。

├─ main.tf
├─ providor.tf
├─ locals.tf
└─ modules
      ├─ alb
      ├─ ec2
      ├─ security_group
      ├─ network
      │  ├─ main.tf
      │  ├─ output.tf
      │  └─ variable.tf
      └─ rds

繰り返し処理が発生するリソース、または今後発生しそうなリソースはモジュール化を行っておきます。
変更の可能性があるパラメータはlocals.tfで定数を指定しておきます。変更が必要なパラメータは纏めていた方が効率的ですし、resourceブロックのパラメータを直接編集した際のオペミス防止にもつながります。
main.tflocals.tf内のパラメータを参照し、モジュールの呼び出しを行います。この役割のファイル(今回の構成ではmain.tf)は肥大化しがちなので、可読性を高めるために複数分けることをお勧めします。(ここだと、alb.tf、ec2.tf など・・・)

なお、前回のブログでも記載をさせていただいた通り、「.tf」の拡張子を持つファイルはファイル名に関わらず全て読み込まれるので、1ファイルに全て記述をしてもよいですし、10ファイルに分けても動作に変わりありません。

ルートディレクトリが複数の場合 パターン①

複数の AWS アカウントに対して Apply を行う場合、ディレクトリ構成に工夫が必要です。
Terraform の追加機能に頼らないシンプルな構成でかつモジュールを上手く活用する構成例をご紹介します。

├─ dev
├─ stg
├─ prod
│    ├─ main.tf
│    ├─ providor.tf
│    └─ locals.tf
└─ modules
      ├─ alb
      ├─ ec2
      ├─ security_group
      ├─ network
      │  ├─ main.tf
      │  ├─ output.tf
      │  └─ variable.tf
      └─ rds

各ファイルの役割については単一のルートディレクトリ構成時と変わりありませんが、モジュールを複数の環境(dev、stg、prod)で利用できるようにルートディレクトリ外部に配置します。
そうすることで、コードの肥大化を抑え、Terraform 実行環境全体を軽量化することが可能です。
なお、ルートディレクトリ外部にモジュールを配置したとしても、main.tfのモジュール指定先のディレクトリが変わるのみで、特別考慮が必要なことはありません。

また、基本的には各環境のディレクトリで環境設定(認証情報、バージョンなど)を行うため、他の環境への影響はほぼ無いこともメリットです。

さらにこの構成に加えて、各環境で同じ内容の設定ファイルをルートディレクトリ外に配置し、各環境ディレクトリ配下でシンボリックリンクを行うことで重複ファイルを一本化し、さらに軽量化を図ることも可能です。

ルートディレクトリが複数の場合 パターン②

パターン①のデメリットとして、各環境で作成されるリソースがほぼ同じ場合、同様のファイルが複数存在することが考えられます。
前述の方法でファイルを全てシンボリックリンクを行っていくと構成が複雑になるだけでなく、特定の Terraform 実行環境に依存してしまう事態が発生してしまいます。
そういった際には Terraform の Workspace 機能を利用することをお勧めします。
※Terraform Workspace の細かい利用方法の説明は割愛します。

Terraform Workspace は単一のルートディレクトリを用いて、Workspace を切り替えることで複数の環境または複数の AWS アカウントの管理を可能にします。
ディレクトリの構成は前述した「ルートディレクトリが1つの場合」と同様ですが、Workspace ごとにパラメータを別にするため、locals.tfを以下のような記載に変更します。
例:Workspace 名が「dev」、「stg」、「prod」の場合

  • locals.tf
locals {
  environment_setting = tomap({
    prod= {
      name = "production"
    },
    stg = {
      env_name = "staging"
    },
    dev = {
      env_name = "development"
    }
  })[terraform.workspace]
}

terraform.workspaceは現在選択している Workspace から Workspace 名が取り出せる変数となります。
上記のlocals.tfではterraform.workspaceの値に応じたパラメータを取り出すことが可能です。
この方法により、単一のルートディレクトリで複数の環境に応じたパラメータの指定が可能になります。

なお、各 AWS アカウントで利用する認証情報も予め Workspace 名を認証プロファイル名の一部として用いることで、terraform.workspace変数を活用した環境ごとの Apply も可能です。
※こちらはあくまで一例となりますのでご参考程度にいただければ幸いです。

ルートディレクトリが複数の場合 EX

有償のプランも含まれますが、パターン②のセキュリティ面、運用面を強化する方法をご紹介します。
パターン②までの方法では認証情報やtfstateファイルなどのセンシティブな情報をユーザで管理する必要があるため、保管場所・記載場所に十分に気を付ける必要があります。
そういった問題に対して Terraform Cloud(以降 TFC) 及び Terraform Enterprise(以降 TFE) を用いることで解決が可能です。

ディレクトリ構成、ファイル構成はパターン②と同様になりますが、認証情報やtfstateファイルの管理を TFC 上の Workspace に任せることが可能です。
また、Github、GitLab などのバージョン管理ツールとも連携ができ、特定のブランチに Push された際にブランチに紐づいた TFC 上の Workspace に Plan/Apply を実行するパイプラインを構築することもできるため、運用の幅が圧倒的に広がります。
なお、プランによっては TFC 上で変数やモジュールを定義することも可能で、それをローカルの Terraform 実行環境からも呼び出すことで、パラメータの秘匿化、コードの軽量化を行うことも可能です。

さらに、大多数のワークロードが稼働するようなエンタープライズ規模のシステムで、高性能で堅牢なセキュリティが求められる場合は、TFE と呼ばれるエンタープライズ向けのプランもあるため、利用も検討してみてもよいかもしれません。(お値段もそれなりとなりますが・・・)

おまけ~Terraform 触りたての頃の失敗例~

Terraform を触り始めた頃はファイル間の依存関係などの理解がなかったため、単純に管理や可読性だけを考慮した以下の構成を検討していました。

├─ env
│  ├─ dev
│  ├─ prod
│  │  ├─ alb
│  │  ├─ ec2
│  │  ├─ efs
│  │  ├─ network
│  │  │  ├─ main.tf
│  │  │  └─ output.tf
│  │  │
│  │  └─ rds
│  └─ stg
└─ modules
    ├─ alb
    ├─ ec2
    ├─ efs
    ├─ network
    │  ├─ main.tf
    │  ├─ output.tf
    │  └─ variable.tf
    └─ rds

Terraform を既に利用されている方はお気づきかもしれませんが、この構成では Apply が一気にできません
ルートディレクトリが dev、prod などの環境ディレクトリならよかったのですが、さらに AWS リソースでディレクトリを分けているため、リソースごとのディレクトリがルートディレクトリとなってしまいます。
ある意味設定の変更が他のリソースに影響を与えることが無い点では堅牢な構成なのかもしれませんが、Apply に順番付けが必要ですし、Apply されたリソースを参照する場合に毎回dataブロックの追記が必要となります。
当時はここの理解に手間取った経験があるので、まずは Terraform ディレクトリの基本からしっかり学びましょう。

最後に

今回は Terraform のディレクトリ構成についてご紹介させていただきました!
適切なディレクトリ構成は、Terraform プロジェクトの可読性やメンテナンス性を向上させる重要な要素です。
本記事で紹介した手法はあくまで一例となりますが、皆様のご参考になれれば幸いです!!

次回は本記事内で説明を省かせていただいたモジュールの利用方法について詳しく説明したいと思います!!

では次回もお楽しみに!!!