1. はじめに
はじめまして、サインSREのfujiyaです。現在はサイン全体のインフラを担当しており、基盤の構築や監視などをメインに働いています。今回はサインのインフラ管理で利用しているTerragruntというツールについて、紹介したいと思います。
対象読者
- Terragruntってなんだ?という人
- Terragruntについて聞いたことがあるが、詳細はわからない人
- Terraformの知識があり、Terragruntの基本的な利用方法を学びたい人
ゴール
- Terragruntの基礎を学ぶ:Terragruntの基本的な概念、主要機能と使用方法について理解する。
- インフラ管理の効率化を学ぶ:Terragruntを用いてインフラコードをDRYに書く方法を紹介。
2. Terragruntとは?
概要
Terragrunt は Gruntwork 社が公開している Terraform のラッパーツールです。Terraformが提供する機能を拡張し、重複を減らしたり、設定管理を簡素化したりすることで、DRYに保ち、インフラコードをより効率的に管理できるようになります。
また、TerragruntはTerraformの構文をそのまま利用できるため、Terraformに慣れている開発者にとっては、非常に使いやすいツールなのも特徴です。
インストール
インストールは公式サイトのインストールページを参考にインストールしましょう。
使い方
CLIでの使い方は簡単で、コマンドを terraform のかわりに terragrunt に置き換えるだけです。Terragrunt特有のコマンド以外は直接Terraformに転送するため、Terraformのコマンドをそのまま利用することができます。Terragrunt特有のコマンドについては後の章で説明します。
terraform plan ↓ terragrunt plan
参考
3. プロジェクトのフォルダ構造と実装の基本
ここでは、Terragruntプロジェクトの基本について説明します。
基本のフォルダ構造
一般的なTerragruntプロジェクトのフォルダ構造は次のようになります。
my-terraform-project/
├── terragrunt.hcl # ルートレベルの設定
├── environments/
│ ├── production/
│ │ ├── app/
│ │ │ └── terragrunt.hcl # 本番環境のアプリ用設定ファイル
│ │ ├── database/
│ │ │ └── terragrunt.hcl # 本番環境のデータベース用設定ファイル
│ │ └── network/
│ │ └── terragrunt.hcl # 本番環境のネットワーク用設定ファイル
│ └── staging/
│ ├── app/
│ │ └── terragrunt.hcl # ステージング環境のアプリ用設定ファイル
│ ├── database/
│ │ └── terragrunt.hcl # ステージング環境のデータベース用設定ファイル
│ └── network/
│ └── terragrunt.hcl # ステージング環境のネットワーク用設定ファイル
└── modules/
├── app/
├── database/
└── network/
この構造では、modules ディレクトリが実際のTerraformコードを含み、environments ディレクトリが特定の環境(例:production、staging)に対するTerragruntの設定を持っています。
terragrunt.hcl ファイル
terragrunt.hcl ファイルは、TerragruntとTerraformの設定を定義します。基本的な設定では、以下の要素が含まれます。
- リモートステートの設定:Terraformのステートファイルを保存する場所(例:S3バケット)を定義
- 入力変数:Terraformモジュールに必要な入力変数を記述
- 依存関係:他のTerraformモジュールへの依存関係を定義
以下はterragrunt.hclファイルの記述例です。
# terragrunt.hcl (例:production/app/terragrunt.hcl)
terraform {
source = "../../modules/app" # Terraformモジュールへのパス
# リモートステート設定
backend "s3" {
bucket = "my-terraform-state"
key = "production/app/terraform.tfstate"
region = "us-east-1"
encrypt = true
}
}
# 入力変数
inputs = {
region = "us-east-1"
name = "production-app"
}
# 依存関係
dependencies {
paths = ["../database"]
}
その他の設定項目については公式のConfigurationのページを参考にしてください。
4. 設定をDRYに保つ
Terragruntでは、共通の設定をルートレベルのterragrunt.hclファイルに定義し、サブディレクトリのterragrunt.hclファイルでこれらの設定を参照することで、設定をDRYに保つことができます。
先に紹介した、一般的なTerragruntプロジェクトのフォルダ構造を例に説明します。
my-terraform-project/
├── terragrunt.hcl # ルートレベルの設定
├── environments/
│ ├── production/
│ │ ├── app/
│ │ │ └── terragrunt.hcl # 本番環境のアプリ用設定ファイル
│ │ ├── database/
│ │ │ └── terragrunt.hcl # 本番環境のデータベース用設定ファイル
│ │ └── network/
│ │ └── terragrunt.hcl # 本番環境のネットワーク用設定ファイル
│ └── staging/
│ ├── app/
│ │ └── terragrunt.hcl # ステージング環境のアプリ用設定ファイル
│ ├── database/
│ │ └── terragrunt.hcl # ステージング環境のデータベース用設定ファイル
│ └── network/
│ └── terragrunt.hcl # ステージング環境のネットワーク用設定ファイル
└── modules/
├── app/
├── database/
└── network/
ルートレベルの terragrunt.hcl
このファイルでは、全ての環境で共通する設定を定義します。
以下では、共通の設定の例としてリージョンとリモートステートの設定を定義しています。
# my-terraform-project/terragrunt.hcl
remote_state {
backend = "s3"
config = {
bucket = "my-terraform-state"
encrypt = true
region = "us-east-1"
key = "${path_relative_to_include()}/terraform.tfstate"
}
}
inputs = {
region = "us-east-1"
}
path_relative_to_include()は、サブディレクトリのパスを動的に生成し、各環境のステートファイルを別々に保持します。これにより、各フォルダで定義しなければならなかったリモートステートを一箇所で定義することができます。
サブディレクトリの terragrunt.hcl
サブディレクトリのterragrunt.hclファイルでは、ルートのterragrunt.hclファイルを参照し、その他の固有の設定を追加します。
# my-terraform-project/environments/production/app/terragrunt.hcl
include {
path = find_in_parent_folders()
}
terraform {
source = "../../../modules/app"
}
inputs = {
name = "my-app-production"
}
この設定では、includeブロックを使用してルートレベルのterragrunt.hclを含めています。これにより、リモートステートの設定が引き継がれます。また、terraformブロックでモジュールのソースを指定し、inputsブロックで固有の入力変数を設定します。
includeブロック内で使用しているfind_in_parent_folders() は Terragrunt のビルトイン関数で、現在のディレクトリから親ディレクトリを遡って、特定のファイルを探します。
find_in_parent_folders() は以下のような特徴があります。
- 初めて見つかる
terragrunt.hclファイルのパスをincludeブロックで使用 - 引数を渡すことで他のファイル名を指定することも可能
そのほかの設定をDRYに保つ
ここで紹介した設定項目以外に、プロバイダー構成を DRYに保つ なども存在します。公式ドキュメントを参考にしてみてください。
参考
- その他の設定項目 ⇒ Configuration - Terragrunt
- プロバイダー構成を DRYに保つ
5. TerraformとTerragruntを比較する
4 設定をDRYに保つで示したように、ルートレベルのterragrunt.hcl に inputsブロックで共通の入力変数を設定することでDRYに保つことができます。ここでは、素のTerraformで書いた場合と比較しながらDRYに保つメリットを紹介します。
素のTerraformで書いた場合
以下はappモジュールの記述例です。このように共通の設定であるregionやリモートステートは各種モジュールに記述が必要になってしまいます。
# my-terraform-project/environments/production/app/main.tf
module "app" {
source = "../../../modules/app"
name = "production-app"
region = "us-east-1"
}
# リモートステートの設定
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "production/app/terraform.tfstate"
region = "us-east-1"
}
}
Terragruntで書いた場合
ルートレベルのterragrunt.hclにregionとリモートステートを記述しておけるため、appモジュールの記述は以下のように簡潔に済ませることができます。
# my-terraform-project/environments/production/app/terragrunt.hcl
include {
path = find_in_parent_folders()
}
terraform {
source = "../../../modules/app"
}
inputs = {
name = "production-app"
}
比較
- DRY原則の適用
- 素のTerraform:各環境でTerraformの
backend設定を繰り返し記述する必要がある。これは設定が重複し、変更があった場合に複数のファイルを更新する必要がある。 - Terragrunt:共通の設定(リモートステートの設定など)をルートの
terragrunt.hclに一度だけ記述し、includeを使用して各環境で継承できる。
- 素のTerraform:各環境でTerraformの
- コードの管理と再利用性
- 素のTerraform:各環境でモジュールを呼び出す際に、モジュールのソースと変数を指定する必要がある。
- Terragrunt:モジュール(
app、database、network)を一度定義し、異なる環境で再利用。
- 保守性
- 素のTerraform:共通設定の変更には、各環境やモジュールのTerraformファイルを個別に編集する必要がある。また、誤ってほかのフォルダのステートファイルを指定してしまった場合、環境を破壊する可能性も出てくる。
- Terragrunt:共通設定の変更はルートの
terragrunt.hclで一度だけ行えば良いため、保守が容易
Terragruntを使用するとDRYに記述ができるため、設定の変更が容易になり、全体的なコードベースの保守性が向上します。
6. Terragruntのコマンド
ここでは、Terragrunt特有のコマンドを紹介します。
これより前の章でも書きましたが、Terragrunt特有のコマンド以外は直接Terraformに転送します。
| Terragrunt Command | 説明 |
|---|---|
| aws-provider-patch | ネストされたAWSプロバイダーの設定を上書きして、Terraformのバグを回避 |
| graph-dependencies | terragruntの依存関係グラフを標準出力に出力 |
| hclfmt | 再帰的にhclファイルを検索し、それらを正規形式に書き換える |
| output-module-groups | コマンド(applyまたはdestroy)で順序付けられたモジュールのグループをJSONのリストとして出力(CIでに役立つ)。 |
| render-json | すべての変数、インクルード、および機能が解決された最終的なterragrunt設定をjsonとしてレンダリング |
| run-all | 指定されたコマンドを各サブフォルダで実行することにより、'stack'に対してterraformコマンドを実行 |
| terragrunt-info | 限定的なterragruntの状態を標準出力に出力し、終了 |
| validate-inputs | terragruntで設定された入力がterraformで定義された変数と整合しているかを確認 |
| * | Terragruntは他のすべてのコマンドを直接Terraformに転送 |
コマンドやオプションに関しては以下で確認することができます。
terragrunt --help
ドキュメントはこちら
run-allコマンド
run-all コマンドは、複数の Terraform モジュールが含まれるディレクトリ構造において、指定された Terraform コマンドをすべてのサブモジュールに対して一度に実行することができます。
先に示したTerragruntProjectのフォルダ構造の一部を参考に説明します。
environmensts/production/
├── app/
│ └── terragrunt.hcl
├── database/
│ └── terragrunt.hcl
└── network/
└── terragrunt.hcl
run-all コマンドの使い方
run-all コマンドを使用するには、コマンドラインで environments/production ディレクトリに移動し、以下のようにコマンドを実行することで利用できます。
terragrunt run-all apply
このコマンドは、my-terraform-infra ディレクトリ内のすべてのサブディレクトリ(この場合はapp、 database、network)に対して terraform apply を実行します。
依存関係の管理
run-all コマンドは、サブディレクトリ間の依存関係も考慮します。例えば、app モジュールが database モジュールに依存している場合、Terragrunt は database の apply が完了するまで app の apply を開始しません。これは terragrunt.hcl ファイル内で依存関係を設定できます。
7. Terragruntについてより深く学ぶ
Terragruntの知識を深めたい方々に向けて、Terragruntについて学ぶための情報源を紹介します。
- 公式ドキュメント
- TerragruntのGitHub Actions(terragrunt-action)も存在し、CI/CDでの利用もできます。
- その他
- 書籍はTerragruntに特化した書籍は見当たりませんでした。
- 動画はいくつかコンテンツを見つけました。
8. さいごに
この記事では、Terragruntについての基礎とこれを用いてインフラコードをDRYに書く方法を紹介しました。Terragruntを用いることでTerraformのコードをDRYに保ち、インフラの設定管理を効率化できます。快適なTerraformライフを!
