Terraformにおけるディレクトリ構成のベストプラクティス

Terraformはコードをどう配置するかというルールがありません。 だからといって自由にコードを書いてしまうと、コードを管理しづらくなったり、メンバーがコードの変更をキャッチアップしづらくなってしまいます。

Terraformのコードを扱うときは、どう管理するかというルールを定めることが大切です。 この記事では、Terraformにおけるディレクトリ構成の例を示します。

この記事の目的

この記事の読者の対象はTerraformの基礎を理解している方で、Terraformのコードをどう設計または改善するかについて考えている方です。 ディレクトリの構成例にふれることで、プロダクトにあった構造をみつける参考になればと思います。

背景

Terraformはインフラをコードで定義し構築の自動化を行うソフトウェアです。 公式なプロバイダでAWSやGCPのリソース定義をできるのはもちろん、コミュニティプロバイダを使うことでSentryやStripeといったサービスの定義も行えます。 Terraformを使うことで、インフラを宣言的に管理できます。 バージョン管理を行えば差分がわかりやすくなり、コードを共有するメンバーもキャッチアップしやすくなります。

一方でTerraformを使うサービスや環境、プロバイダやリソースがふえたときに、基準となる指針がないと、コードがちらばりわかりづらくなってしまいます。 再利用できそうな、同じ責務をもつモジュールの存在に気づけないかもしれません。 こういった問題を起こさないような、チームにあった指針を決めることが大切になります。

Terraformにおけるディレクトリ構成とは

この記事でいうディレクトリ構成とは「Terraformのコードをどう配置するか」をいいます。 main.tfvariables.tfoutputs.tfなどのtfファイルやモジュールの構成をどうするか。 サービスや環境の種類をどう定義するか。 この課題をディレクトリ構成といいます。

ディレクトリ構成の要件

この記事では、次の要件を満たすディレクトリ構成について示しています。

  • メンバーがルールを把握しやすいこと
  • コードを変更しやすいこと
  • 複数のサービスに対応できること
  • 複数の環境に対応できること
  • 再利用性があること

この記事の前提

この記事の前提として「Terraformのコードをひとつのチームで管理していること」を想定しています。 ひとつのチームで管理しきれない規模になるようであれば、リポジトリを分割するなどの戦略が必要になると思います。

また、TerraformにはWorkspacesというしくみがあります。 ただ、この記事ではWorkspacesには言及しません。 Workspacesは差分を吸収するためのコードが必要になり、わかりづらくなると考えているためです。

次章でディレクトリ構成を示していますが、これは例にすぎません。 最適なディレクトリ構成はサービスの種類やチームの状況によって異なり、ただひとつの正解はないと考えています。 重要なのはチーム内で指針をもつことです。 チームにあった指針を決めるときの参考になればと思います。

ディレクトリ構成のベストプラクティス

ここではa.example.comとb.example.comという2つのサービスがあり、それぞれステージングと本番環境をもっていると想定します。 このときのディレクトリ構成を示します。

動作環境

この記事にあるコードは、次のバージョンで動作を確認しています。

名前バージョン
Terraform0.12.29

ディレクトリ構成

まずディレクトリ構成を示し、次節でこの構成をもとにした設計指針を示します。 モジュールはAWS S3やHerokuなど、エントリポイントから参照されるものをいくつか示しています。

▼ modules
  ▼ aws-s3-bucket
      main.tf
      outputs.tf
      variables.tf
  ▶ heroku
  ▶ sentry
  ▶ stripe
▼ services
  ▼ a.example.com
    ▼ staging
        main.tf
    ▼ production
        main.tf
  ▶ b.example.com

ディレクトリ構成の設計指針

上記ディレクトリ構成をもとに、設計指針の例を示します。

最小限のモジュールをひとつの単位としてmodules下に配置する。 たとえば「S3のバケットをつくる」「HerokuのWeb Dynoを起動する」など。 このときバケット名などは変数として定義します。 こうすることで再利用性が高まり、各サービスから参照しやすくなります。

サービスをservices下に、各環境をサービスの下に配置する。 たとえばservices/a.example.com/staging。 サービス×環境のエントリポイントがすぐわかります。

モジュールは3つのtfファイルのみをもつmain.tfoutputs.tfvariables.tfの3つ。 これは公式ドキュメントにある最小限の構成で、名前の決め方やファイルのもつ役割がわかりやすくなります。

エントリポイントにはバックエンドやプロバイダ、モジュール定義のみを行う。 できるだけモジュールとして定義し、エントリポイントから使用することで、定義場所のゆらぎがなくなり、再利用性をもたせることができます。

備考

上記のディレクトリ構成例において、a.example.comとb.example.comを管理するメンバーが大きく異なる場合はリポジトリをわけた方がよいと思います。 またa.example.comとb.example.comで技術スタックが大きく異なる場合もわけた方がよいかもしれません。 ステートの規模も分割の基準になると思います。

まとめ

モジュールをわけ、サービス×環境ごとにエントリポイントをもたせるディレクトリ構成例を示しました。 この構成にすることでサービスや環境、モジュールがふえても柔軟に対応することができます。

チーム内でディレクトリ構成の設計指針をもつことで、コードを変更するときの指針になり、またキャッチアップしやすくなります。 チームにあった構成をみつける参考になればと思います。


Author
著者Hiroki Zenigami

プロダクト開発者。妻と娘、猫とのんびり暮らしています。


Publishing
現場で使えるRuby on Rails 5

共著で「現場で使えるRuby on Rails 5(マイナビ出版)」を出版しました