🕌

アルダグラムのTerragruntのディレクトリ構成について

2024/09/19に公開

こんにちは!アルダグラムのSREエンジニアの田中です。

アルダグラムではインフラ環境をTerraformを使って管理しています。
最近アルダグラムではメインのプロダクトであるKANNAプロジェクトのほかにKANNAレポートといった新しいプロダクトが生まれています。

また、それに伴い複数の開発環境が必要となり、効率的なインフラ環境の構成管理も求められるようになってきました。

開発環境やプロダクトが増えるごとにTerraformのコードが冗長になり始めたため、Terragruntを使用し、効率的に管理できるようにしました。

今回は、Terragruntを用いてどのようなディレクトリ構成で構成管理を行っているかをご紹介します。

Terraformについて

Terraformとは、HashiCorp社が開発したオープンソースのIaCツールです。
https://www.terraform.io/

主な特徴は以下のとおりです。

  • マルチクラウド対応: AWS、Azure、Google Cloudなど、複数のクラウドプロバイダーを一貫した方法で管理できます。
  • プランニング機能: terraform plan コマンドで変更内容を事前に確認できるため、安全なデプロイが可能です。
  • 状態管理: インフラの現状を状態ファイルで管理し、変更の追跡やチームでの協業が容易になります。

Terraformを利用することで、手作業での設定ミスを減らし、インフラ環境の再現性やスケーラビリティを向上させることができます。

Terragruntについて

一方、Terragruntとは、Terraformをより効率的に運用するための薄いラッパーとして機能するオープンソースのツールです。
https://terragrunt.gruntwork.io/

Gruntwork社が開発しており、Terraformでのインフラストラクチャコードの管理を簡素化し、再利用性や保守性を向上させるための機能を提供しています。

主な特徴は以下のとおりです。

  • コードをDRYに: 共通の設定や変数を再利用しやすくし、コードの重複を減らします。
  • モジュールの依存関係管理: 複数のTerraformモジュール間の依存関係を明確に定義し、適切な順序でのデプロイを可能にします。
  • 複数環境の管理: 開発、ステージング、本番など、異なる環境ごとの設定をシンプルに管理できます。

Terragruntを利用することで、Terraformのコードをよりクリーンに保ち、複雑なインフラストラクチャの管理を効率化できます。特に大規模なプロジェクトや複数の環境を扱う場合に、その真価を発揮します。

アルダグラムのインフラ環境について

アルダグラムではメインとしてAWS上にシステムを構築しています。
また、開発ステージに応じた環境が複数存在します。
具体的には以下の表のようになっています。

アカウント 環境名 用途
A QA1~12 各エンジニアチームに割り振られている検証環境
A dev 全エンジニアチーム共通の検証環境
A dev-ex 外部プロダクトとの連携検証用の環境
B loadtest 負荷検証用の環境
B pre-prod 本番相当のインフラスペックを有したの検証環境
B prod 本番環境

これがプロダクトの数だけあります。

改めて表にしてみると、なかなか多くの環境がありますね。

検証環境ではコスト削減のため、RDSやElastiCacheなどが共通で使われていたりと、環境ごとに差異もあります。

また、将来的にはさらなるマルチプロダクトや海外リージョンにもインフラを構築することが見込まれています。

そのため、中長期的なスケールを見据えて、拡張しやすいディレクトリ構成にする必要があります。

実際のディレクトリ構成について

  • 複数の環境の差異に対応する
  • マルチプロダクト化に対応する
  • 海外リージョン化に対応する

といった要件を満たすために、アルダグラムでは以下のようなディレクトリ構成で管理を行っています(一部簡略化しています)。

kanna-infra
├─ gcp
│  └─ ...
└─ aws
   ├─ envrionment
   │  ├─ accountA
   │  │  └─ ap-northeast-1
   │  │     ├─ dev
   │  │     ├─ qa1
   │  │     ├─ ...
   │  │     ├─ qa12
   │  │     │  ├─ kanna-project
   │  │     │  │  ├─ alb
   │  │     │  │  │  └─ terragrunt.hcl
   │  │     │  │  ├─ ecs
   │  │     │  │  │  └─ terragrunt.hcl
   │  │     │  │  ├─ ecr
   │  │     │  │  │  └─ terragrunt.hcl
   │  │     │  │  └─ ...
   │  │     │  ├─ product.hcl
   │  │     │  └─ kanna-report
   │  │     │     ├─ ...
   │  │     │     └─ product.hcl
   │  │     └─ shared
   │  │           ├─ kanna-project
   │  │           │  ├─ rds
   │  │           │  │  └─ terragrunt.hcl
   │  │           │  └─ ...
   │  │           └─ kanna-report
   │  │              └─ ...
   │  └─ accountB
   │     └─ ap-northeast-1
   │        ├─ loadtest
   │        │  └─ ...
   │        ├─ pre-prod
   │        │  └─ ...
   │        └─ prod
   │           └─ ...
   ├─ modules
   │  ├─ alb
   │  │  ├─ main.tf
   │  │  ├─ variables.tf
   │  │  └─ output.tf
   │  ├─ ecs
   │  │  └─ ...
   │  ├─ ecr
   │  │  └─ ...
   │  ├─ rds
   │  │  └─ ...
   │  └─ ...
   └─ root.hcl

ディレクトリ構成は

<provider>/environment/<aws_account>/<aws_region>/<env>/<product_type>/product.hcl

という形式にしており、環境やプロダクトが増えても柔軟に対応できるようにしています。

また、アカウントAは前述のとおり共通のリソースを使っているため、shared というディレクトリを作り、こちらで共通リソースの管理を行っています。

moduleについては、

  • planやapplyの高速化
  • 更新による影響範囲の最小化
  • 複数人の同時作業の円滑化
    を期待し、なるべく小さいモジュールにするようにしました(基本的にはAWSのサービス単位で分割しています)。

設定の共通化

Terragruntを用いることで一部設定を共通化でき、コードをDRYに保つことができます。 アルダグラムでは、root.hcl(全環境共通の設定)やproduct.hcl(プロダクトで共通の設定)を用いて、設定の共通化を行っています。

具体的には以下のようになっています(一部簡略化しています)。

root.hcl
locals {
  # バージョン指定
  terraform_version    = "1.9.1"
  aws_provider_version = "5.57.0"

  # Terraform Cloudの設定
  tfc_organization = "Organization Name"
  tfc_project      = "KANNA"

  # workspace名はリポジトリのルートから子のterragrunt.hclファイルまでの相対パスを元に自動生成
  path_parts_reversed = reverse(split("/", find_in_parent_folders("product.hcl")))

  product_type = local.path_parts_reversed[1]
  env_name     = local.path_parts_reversed[2]
  aws_region   = local.path_parts_reversed[3]
  project_type = local.path_parts_reversed[6]

  workspace_name = join("_", compact(split("/", get_path_from_repo_root())))
}

inputs = {
  env_name        = local.env_name
  aws_region      = local.aws_region
  product_type    = local.product_type
  project_type    = local.project_type
  tag_name_prefix = "${local.project_type}-${local.env_name}-${local.product_type}"
}

generate "common" {
  path      = "common.tf"
  if_exists = "overwrite_terragrunt"
  contents  = <<-EOF
    terraform {
      required_version = "${local.terraform_version}"
      required_providers {
        aws = {
          version = "${local.aws_provider_version}"
        }
      }
      cloud {
        organization = "${local.tfc_organization}"
        workspaces {
          name    = "${local.workspace_name}"
          project = "${local.tfc_project}"
        }
      }
    }
    locals {
      common_tags = {
        "Env"         = "${local.env_name}"
        "ProductType" = "${local.product_type}"
        "ManagedBy"   = "terraform"
      }
    }
    provider "aws" {
      region = "${local.aws_region}"
      default_tags {
        tags = local.common_tags
      }
    }
    provider "aws" {
      alias  = "global"
      region = "us-east-1"
      default_tags {
        tags = local.common_tags
      }
    }
  EOF
}
product.hcl
# プロダクト内で共有したい値を管理
locals {
  hogehoge = 'hogehoge'
}

inputs = {
  hogehoge = local.hogehoge
}
terragrunt.hcl
include "root" {
  path   = find_in_parent_folders("root.hcl")
  expose = true
}

include "product" {
  path = find_in_parent_folders("product.hcl")
}

terraform {
  source = "${get_parent_terragrunt_dir("root")}/modules/alb"
}

inputs = {
 hogehoge_arn = "hogehoge"
}
# root.hclやproduct.hclのinputsは暗黙的にmodulesに渡されます

root.hclでは共通のtagやproviderの設定、workspace名を自動生成を行なっています。
これによりコードをDRYに保ち、効率的で保守性の高い構成管理を行っています。

最後に

今回は、アルダグラムのTerragruntのディレクトリ構成についてご紹介しました。

ディレクトリ構成は自由度が高いため、会社やプロダクトの規模、そしてエンジニアの組織構成によって様々な最適解があると思います。

これからもより良いインフラの構成管理を行い、プロダクトや事業の成長に寄与していきます。

もっとアルダグラムエンジニア組織を知りたい人、ぜひ下記の情報をチェックしてみてください!

アルダグラム Tech Blog

Discussion