見出し画像

Terraformを効率的に管理したい…そうだ、Terragruntを使おう!


今回のテーマ:Terragrunt


こんにちは、TOPPANデジタルで AWS Top Engineers を目指している加納です!
今回のテーマはIaCツールの一種である「Terragrunt」です。
新規プロジェクトにTerragruntを導入した経緯と、実際に導入して感じた「良かったこと、悪かったこと、導入後の課題」などについて共有していきます!

対象読者

基本的には下記のような方向けの記事です。

  1. Terraformの管理に限界を感じている方

  2. Terragruntを使ってみたがイマイチ利点がわからない方

普段TerraformなどのIaCツールを触らない方向けの補足も入れつつ説明していきますので、そういう仕組もあるんだなぁと思っていただけますと幸いです!

はじめに:Terragruntとは


導入

皆様はIaC(Infrastructure as Code)をご存知でしょうか?
AWSを始めとしたクラウドインフラなどを手動で管理するのではなく、コードで管理することを「IaC」と呼び、そのIaCを実現するためのツールとしてHashicorp社が公開している「Terraform」などがあります。
私が関わるプロジェクトではIaCを実現するためにTerraformを利用することが多いのですが、Terraformを利用するうえでいくつかの課題があり、その解決策としてGruntwork.io社が公開している「Terragrunt」を採用しました。

Terragurntとは

導入で説明した「Terraform」がIaCを実現するためのツールだったのに対し、公式によると「Terragrunt」は`Terragrunt is a flexible orchestration tool that allows Infrastructure as Code written in OpenTofu/Terraform to scale. Keep your OpenTofu/Terraform code DRY.`と説明されています。
要するにTerragruntは、Terraform(またはOpenTofu)で記述されたコードをDRY※1に保つためのオーケストレーションツールであるということです。
そのためTerragruntを利用すると、これまで利用してきたTerraformの利点を損なわず、更にTerraformを便利に活用することができます。

※1 DRY:`Don't repeat yourself`(コーディングの基本方針の一つであり「コードに(不必要な)重複を持たせてはいけない」という意味)

課題:Terraformに起因した複雑性や冗長性など


Terraform単体で利用していたときに感じていた課題が大きく分けて2つあります。
今回IaC化しようとしていたプロジェクトでは、Terraform単体の利用では下記問題にぶつかることが想定され、それがTerragruntを採用するきっかけになりました。

1. 巨大なtfstate問題

Terraformにはtfstateという、Terraformで管理しているリソースの状態が全て記録されるファイルがあります。
Terraformはその状態ファイルとコードを比較して、リソースを追加・変更・削除するわけですが、Terraformを適用する前に必ずtfstateとコードの比較処理が走ります。
管理しているリソースが少ない場合は特に問題にはならないのですが、管理しているリソースが数百~数千のオーダーになってくると、その比較処理の待ち時間が辛くなってきます。
これが「巨大なtfstate問題」です。

2. tfstateの同時編集問題

Terraformではtfstateというファイルでリソースの状態を管理します。
このtfstateファイルが同時編集されてしまうと、色々と不都合なことが起こるため、Terraformにはオプションとしてリレーショナルデータベースのように同時編集を防ぐ機能が搭載されています。
この機能があれば、同時編集には何ら問題がなさそうに見えると思うのですが、実際にはエンジニアが二人で開発する場合に、片方の変更適用を待つための無駄な時間が発生するなど、機能で守られるとはいえ同時編集は避けるべきことが多いです。
これが「tfstateの同時編集問題」です。

検証:Terragruntによって、どう良くなるか


Terragruntによる恩恵

「巨大なtfstate問題」と「tfstateの同時編集問題」の原因を突き詰めると、「Terraformではtfstateファイルが1つしか管理できない」ことに帰結します。
Terraformでも一応、複数のtfstateの情報を相互に参照することができる※2ため、tfstateファイルの分割も実現できなくはないのですが、人間が管理するにはなかなか大変な作業です。

そこで、Terragruntを導入します。
Terragruntを導入することによってtfstateの相互参照を比較的簡易な書き方で実現できるため、Terraformでのtfstateの複数手動管理と比べて圧倒的に管理が楽になります。
また、tfstateの複数管理以外の要素についても、Terraformと比べてDRYな記述が可能になるなど、Terragruntの恩恵を受けられる場面は多いです。

※2 terraform_remote_state:The terraform_remote_state Data Source | Terraform | HashiCorp Developer

実際のコード例

実際のプロジェクトで使用した構成をベースとして、terragruntのサンプルコードを作成しました。
Codespacesで実行することができるようにしているため、ご興味有る方は是非お試しください!

一部補足

上記サンプルコードでは、AWSリソース名のprefixとして「`terragrunt.hcl`ファイルと`entrypoint.hcl`ファイルの相対パスから取得できる文字列」を使用しています。
一方で、公式のUsing read_terragrunt_config to DRY parent configurationsによると、`env.hcl`ファイルに環境名などのAWSリソース名のprefixに使用する文字列を記載し、それを`terragrunt.hcl`から参照する方法が紹介されています。
どちらのパターンも良し悪しあると思いますが、個人的にはパスベースの命名規則のほうが修正漏れが起こりづらくてより良いと思うので、パスベースの命名規則でAWSリソース名のprefixを設定できるようにしています。

terragrunt-sample/environments/entrypoint.hcl

locals {
  service = "replace_me"

  # terragrunt.hclのあるディレクトリとentrypoint.hclの相対パスから、環境名、テナント名、モジュール名を取得する
  relpath       = replace(get_original_terragrunt_dir(), get_terragrunt_dir(), "")
  path_segments = slice(split("/", local.relpath), 1, length(split("/", local.relpath)))
  env           = length(local.path_segments) > 0 ? local.path_segments[0] : ""
  tenant        = length(local.path_segments) > 1 ? local.path_segments[1] : ""
  module        = length(local.path_segments) > 2 ? local.path_segments[2] : ""

  env_name_prefix     = "${local.service}-${local.env}"
  feature_name_prefix = "${local.service}-${local.env}-${local.tenant}"
  source_suffix       = "${local.module}"

  regions = {
    tokyo = "ap-northeast-1"
  }
}

terragrunt-sample/environments/dev/tenant/app-source/terragrunt.hcl

include "root" {
  path = find_in_parent_folders()
}

locals {
  common_vars = read_terragrunt_config(find_in_parent_folders("entrypoint.hcl")).locals
}

terraform {
  source = "${path_relative_from_include()}/../../modules/${local.common_vars.source_suffix}"
}

inputs = {
  common_vars = local.common_vars
}

所感:比較的大きいプロジェクトでは是非導入すべき


良かった点

今回のプロジェクトで構築したAWSリソースは、機能毎に大きく3つのセクションに分かれており、作成するリソースも多く、従来のTerraform管理では開発・保守がし辛いことが予想されました。
Terragrunt導入によって、当初の目論見通り「巨大なtfstate問題」と「tfstateの同時編集問題」にぶつかることなく開発をすることができ、改めてTerragruntを導入して良かったと感じました。

悪かった点

Terragruntの使用感的には特に不満はないです。
一方で、Terragruntを導入すること自体については一考の余地ありかなと思います。
Terragrunt自体の学習コストがあること※3や、Terraform単体で利用する場合よりも依存を増やしていると捉えることもできるため、プロジェクトの規模※4やエンジニアの習熟度などと相談して導入を決めるのがよさそうです。

※3:ただしTerragruntの学習コストは、Terragruntが「Terraformのオーケストレーションツール(ラッパーツール)」であるという性質上、Terraform自体の学習コストと比較して小さいです。
※4:プロジェクトで管理するリソースが200~300を超える場合、Terragruntを導入したほうが幸せになれそうです。

導入後の課題

Terragruntを利用した開発ができるようになった一方で、これまでTerraform単体で構築していたCICDパイプラインの知見を直接的には活かせないため、改めてTerragrunt用のパイプライン構築しつつ、開発・保守を進めていく必要があります。
また、Terragruntを扱えるエンジニアについても育成を進めていく必要があるため、これらの課題については引き続き取り組んでいく想定です。

〆:さいごに


最後までお読みいただき、ありがとうございました!

Terragruntを導入することによって、これまでTerraformを利用したときに感じていた不満点を解消することができました。
今回新しくTerragruntを導入したわけですが、IaCの選択肢はTerraformやTerragrunt以外にもたくさんあるため、これからも引き続き検証を重ねて、よりよい開発手法を随時取り入れていきたいと考えています!