みてね×gihyo.jpスペシャル

『みてね』Terraform構成~モノリポジトリとマルチリポジトリ~

株式会社MIXI『家族アルバム みてね』のSREをしているkohbisです。今回はTerraformのリポジトリ構成についてお話いたします。

Terraformとは

TerraformとはHashiCorpが提供するIaC(Infrastructure as Code) ツールであり、HCL(HashiCorp Configuration Language)という独自の文法でクラウドやオンプレミスのインフラリソースを宣言的に定義し、管理やプロビジョニングを行うことができます。

TerraformはAPIを通じてプラットフォームやサービス上にリソースを作成します。Terraformは対象とのAPIのやり取りを「プロバイダ」と呼ばれるプラグインに依存しており、Terraformを利用する場合はまずはじめにどのプロバイダを必要とするのかを定義することになります。

プロバイダの一覧はTerraform Registryから確認することができ、開発 / 管理元に応じたバッジが付与されています。HashiCorp(Official⁠⁠、サードパーティ企業(Partner⁠⁠、コミュニティ(Community)などがあり、この記事の執筆時点(2022年10月)で1,700以上が存在しています。

Terraformのプラクティス

Terraformは主にAWSやGCPなどクラウドサービスのリソースをプロビジョニングするために多くの企業やサービスに利用されており、その活用方法やプラクティスについてもブログや登壇などで多くの知見が共有されています。

複数のチームでTerraformを利用する場合には、Terraform公式サイトのTerraform Recommended PracticesやGoogleから公開されている『Terraform のベストプラクティス』が非常に役に立ちます。

たとえば、初期に悩みがちなディレクトリ構成などはGoogleのベストプラクティスを参考にすればまずは困らないでしょう。

コード 環境固有のサブディレクトリにアプリケーションを分割する より抜粋
-- SERVICE-DIRECTORY/
  -- OWNERS
  -- modules/
    -- <service-name>/
      -- main.tf
      -- variables.tf
      -- outputs.tf
      -- provider.tf
      -- README
    -- ...other…
  -- environments/
    -- dev/
      -- backend.tf
      -- main.tf

    -- qa/
      -- backend.tf
      -- main.tf

     -- prod/
      -- backend.tf
      -- main.tf

とはいえこれらは推奨事項なので、それぞれの組織やプロダクトに合わせて、個別の構成を取ったり改善していくことももちろん可能です。

Terraform構成はモノリポジトリかマルチリポジトリか

こういった議論の中に「Terraform の構成はモノリポジトリか? マルチリポジトリか?」というものがあります。

HashiCorpのブログ『Terraform Mono Repo vs. Multi Repo: The Great Debate』を読むと、それぞれのメリット / デメリットが解説されています。

モノリポジトリのメリット・デメリット

モノリポジトリとは、その名のとおりインフラ設定のすべての Terraform コードが同一のリポジトリ内に含まれている構成であり、個人プロジェクトや小規模なチームでの運用において有効です。

モノリポジトリを採用した場合のメリット / デメリットは以下のとおりです。

メリット

  • すべてのインフラ設定を取得するための、単一の信頼できるソースになる
  • データベースのパイプラインなどにとって重要な、テスト、デバッグ用のインフラ設定を一元化できる

デメリット

  • モジュールとプロバイダのバージョン管理と依存関係のメンテナンスコストが増加する
  • インフラ設定の変更を適用する場合、リポジトリ内の評価対象が多いため、CI(Continuous Integration)のパイプラインの実行時間が長くなる
  • Terraformのコードへのアクセス制御をしたい場合、ユーザやグループに特定のサブディレクトリへのアクセスのみを許可する必要がある

モノリポジトリは変更の影響範囲の大きさが問題

モノリポジトリのデメリットで最も大きいのは、一部の変更の影響範囲の大きさです。

Terraformは変更を適用するとき、すべての定義についての評価を行い、それぞれの依存関係の解決を行います。そのため一部のインフラ設定の変更によって、想定していなかったリソースへの影響が及ぶ可能性があります。

その内容については、変更計画を確認するterraform planコマンドの結果をよく確認していれば気づくことができますが、モノリポジトリで運用していると、インフラ設定の増加とともに運用負荷が増えることになります。

このほか、モノリポジトリを運用する中で問題になりやすいのはTerraformのState Locking機能を利用している場合です。この問題について説明します。

State Lockingは、Terraformが管理している現在のリソースの状態(以下、State)を変更する可能性があるすべての操作に対して、その名のとおりStateをロックする機能です。

この機能を使うことで、複数人で同じTerraformリポジトリを運用している場合などに、インフラ設定の変更作業の競合によってStateが壊れるのを防ぐことができます。Terraformコマンドを実行してロックが取得できなければ、そのコマンドは続行されません。

Terraformをチームで運用する場合にはほぼ必須とも言える機能なのですが、同時に、同じTerraformリポジトリについての並列での変更作業が難しくなるということでもあります。

どういうことかというと、Terraformの変更には、変更を適用するterraform applyコマンドと最新の設定を取得してStateを同期するterraform planコマンドがあります。

terraform applyが失敗することは複数人運用にはあまり影響がない一方で、terraform planコマンドでは、リソースの最新の設定を取得してStateを同期する「リフレッシュ」を伴ううため、失敗した場合、追加または更新したTerraformのコードが正しいのか、どこが間違っているかを確認しづらくなり非常に不便です。

それ以外にも、Terraformを有効に活用して多くのリソースを管理するようになればなるほど、CIの実行時間が増加していき、ロックを取得できない時間も増加していくことになります。

マルチリポジトリのメリット・デメリット

こうしたデメリットの影響は、モノリポジトリを複数のリポジトリに分割して、問題を解消することができます。

マルチリポジトリとは、特定の単位でリポジトリを分割する構成であり、複雑なインフラを複数または大規模なチームで運用している場合に有効です。

マルチリポジトリを採用した場合のメリット / デメリットは以下のとおりです。

メリット

  • それぞれのサービスごとにインフラ設定を管理することができる
  • リポジトリが独立しているため、個別にモジュールのセキュリティと機能をテストすることができる
  • リポジトリごとに、モジュールのバージョン管理を適用できる
  • リポジトリごとに、モジュールとディレクトリへのアクセス制御ができる
  • リポジトリへの変更の影響範囲を最小限に抑えることができる

デメリット

  • リポジトリ内で多くのリモートモジュールを参照する場合、それらのダウンロードに時間がかかる

もちろんモノリポジトリとマルチリポジトリのどちらが正しい / 間違っている、ということはないので、それぞれの組織やプロダクトに合わせて最適な構成を模索することが重要になります。

『家族アルバム みてね』におけるTerraform リポジトリの分割例

前述したHashiCorpのマルチリポジトリの例では、リポジトリはビジネスドメインやプロダクトのチームごとに分割されていました。

ここでは『家族アルバム みてね』⁠以下『みてね⁠⁠)におけるリポジトリの分割を1つの例として紹介します。

『みてね』のインフラはAWSクラウド上に構築しており、一般的なWebアプリケーションのインフラ構成と考えていただいて差し支えありません。

みてねのシステムは、Amazon EKSクラスタ上でRuby on Railsのアプリケーションが起動し、CloudFrontおよびALB(Application Load Balancer)で外部インターネットからのアクセス受け付ける構成となっています。

データベースにはAmazon RDS Aurora(MySQL⁠⁠、キャッシングにはAmazon ElastiCacheを採用しています。

『みてね』のTerraformリポジトリ分割

このようにシンプルな構成ですが、実際には下記のようにそれぞれの機能ごとに多くのリソースを作成するため、⁠みてね』ではTerraformリポジトリを「システム内での役割」単位で分割しています。

  • EKSクラスタに関連するさまざまなリソース
  • サブ・サービスごとのネットワーク設定やデータストア
  • ドメインごとのルーティング設定
  • 開発者のアカウント管理
  • その他、マネージドサービス

図1は簡素化した『みてね』のAWS構成図です。

図1 『みてね』のAWS構成図
図11

このインフラ設定を下記の Terraform リポジトリ構成で管理しています。

ちなみに商用 / 検証環境の分離には、Terraform Workspaces を活用しています。

-- SERVICE-DIRECTORY/
  # Kubernetes : EKS
  -- kubernetes/
    -- modules/
      -- <service-name>/
        -- main.tf
        -- variables.tf
        -- outputs.tf
      -- ...other...
    -- main.tf
    -- variables.tf
    # 商用環境の設定値
    -- prod.tfvars
    # 検証環境の設定値
    -- dev.tfvars

  # データストア : RDS Aurora, ElastiCache
  -- database/
    -- ...省略...

  # 外部との通信 : Route53, CloudFront, ALB
  -- ingress/
    -- ...省略...

  # その他のリソース : S3, SQS, SES など
  -- resources/
    -- ...省略...

みてねのTerraformマルチリポジトリのメリット・デメリット

この構成を採用した場合には、前述したマルチリポジトリのメリットをさらに効果的に享受することができます。

とくにKubernetesやデータストアのコンポーネントごとに適用ができるため、変更の影響範囲は最小限に抑え、複数人での変更作業もほとんど競合することなく並列して進めることができます。

一方、デメリットは以下のとおりです。

  • Terraformリソースの属性をリポジトリをまたいで参照することができない
  • リソースの依存関係を注入するために、適切に変数設定をする必要がある
  • インフラの構築には、リソースの依存関係に留意して段階的に行う必要がある

Terraformではリソースの属性を参照し、また別のリソースを作成するために利用することができます。

リポジトリをまたいでの属性の参照はできないため外部から適切に設定を行う必要があります。

しかし、サンドボックス環境のようにスクラップ & ビルドを繰り返さない限り、基本的には一度作成したリソースを削除することは少ないので、運用をするなかで問題にはなりにくいです。AWSの場合はSystems Manager Parameter Storeも利用できるので、属性の用途(例:Terraform外でも利用するか)や種別(例:秘匿性、データ型)に応じて設定方法を検討することになります。

最後に

Terraformのプラクティスについては多くの知見が共有されていますが、組織やプロダクトにおいて「ベスト」なプラクティスを採用し、さらに運用していくというのは大変な作業です。

それゆえに試行錯誤や特徴が見えるところでもあると思うので、皆さんが考える「ベストプラクティス」もぜひお聞かせください。

おすすめ記事

記事・ニュース一覧