tjinjin's blog

インフラ要素多めの個人メモ

CloudformationからTerraformに移行した話

About

この記事は LITALICO Engineers Advent Calendar 2021 2つ目 2日目の記事です。

株式会社LITALICOでSREグループのマネージャをしております、 tjinjinです。 今回はSREグループで取り組んでいたIaCの変更(CloudformationからTerraform)にした話を書きたいと思います。

なぜTerraformに変更したのか

Terraformに移行した観点としては以下があります。

  • 元々あったCloudformationがちゃんと運用されていなかった
    • 当時Cloudformationを書いたメンバーは既におらず、設計意図が不明確だった
    • Cloudformationの書き方で冗長なものが多く、リファクタリングするにしても大規模な改修が必要そうだった
    • stack名等が追番になっていてどのリソースを変更したものなのかぱっとわからなかった…
    • 適用方法もUIからポチポチするようなやり方になっていた
  • 私がTerraformをずっと使ってきており、他のメンバーもTerraformへの興味もあった
  • 当時の印象としてterraformのほうがimport機能や各種共通化の仕組みが整っており、今から書くならTerraformのほうがスムーズなのではと判断した
  • CloudformationのchangeSetやdriftがあまり信用できず、Terraformのplanで差分を確認するほうがトラブルが少なそうと判断した※個人の判断です

どういうステップで進めたのか

Terraformにしようと行ったものの各メンバーもキャッチアップする必要だったり、どういう設計にするのがよいのか?だったり、いきなりかっちり設計するのはよくないという判断があったので、「terraform化議論シリーズ」というものを立ち上げ各観点で議論を進めていきました。

f:id:cross_black777:20211201135539p:plain

最終的に9回まで到達しました。

各議論では下記のようなお題にそって設計を決めていきました。

  • tfファイルの分け方
  • 環境の分け方
  • terraformで管理する・しないの区分け
  • moduleの作り方、使い方
  • 外部module/共通moduleの利用
  • リソースのネーミングルール・タグ付け
  • local/variablesの使い分け

議論にあたって前提を揃えるため各社の技術ブログに乗っている構成やスライドなどを参照させていただきました。

どういう設計にしたのか

すべてを載せることは大変なので、ディレクトリ構成に関しての設計のみ記載したいと思います。

議論の結果決まった構成は下記のようになっています。

├── README.md
├── env
│   ├── common
│   ├── dev
│   ├── prd
│   ├── shared    ※ 環境を跨いで利用するリソースをここに配置する。
│   ├── stg
│   └── test
│       ├── backend-config.yml         ※ terragruntで利用する環境設定ファイル。
│       ├── terragrunt.hcl -> terragrunt/common/terragrunt.hcl
│       └── vpc        ※コンポーネント毎に実行ディレクトリを用意する。
│           ├── (backend.tf)
│           ├── (env_locals.tf)
│           ├── locals.tf -> project-resource/vpc/locals.tf
│           ├── main.tf -> project-resource/vpc/main.tf
│           ├── outputs.tf -> project-resource/vpc/outputs.tf
│           ├── override_main.tf       ※ 環境特有のリソースを記述する。
│           ├── override_outputs.tf    ※ 環境特有のoutputを記述する。
│           ├── (provider.tf)
│           ├── terragrunt.hcl -> terragrunt/component/terragrunt.hcl
│           └── (version.tf)
├── make_env_dir.sh    ※ シンボリックリンクを作成するスクリプト。
├── modules
├── project-resource
│   └── vpc
│       ├── locals.tf  ※ 変数を記述するtfファイル。map形式で各環境の変数を記載する。env/stage/componentにシンボリックリンクファイルを配置する。
│       ├── main.tf    ※ リソースを記述するtfファイル。env/stage/componentにシンボリックリンクファイルを配置する。
│       └── outputs.tf ※ outputを記述するtfファイル。env/stage/componentにシンボリックリンクファイルを配置する。
└── terragrunt
    ├── common
    │   └── terragrunt.hcl  ※ terragruntの実設定ファイル。env/stageにシンボリックリンクファイルを配置する。
    └── component
        └── terragrunt.hcl  ※ 上記を参照するためのファイル。env/stage/componentにシンボリックリンクファイルを配置する。

各要素毎に説明していきます。

envディレクト

ここはいわゆる各環境ごとの設定を置いておく場所になり、terraformの実行はこのディレクトリ内で行います。

環境固有のもの(たとえばtest環境ではIPアドレス制限をしたいなど)で変数だけで制御が難しいものに関してはoverride_main.tfという形で独自リソースを定義できるようにしました。

独自リソースをどこで定義するのか?が難しいポイントかと思いますが、環境側に書くことで共通リソースであるproject-resource(後述)を複雑にしすぎないようにしています。以前workspaceの機能を使ったこともありましたが、環境ごとの差分を埋める苦労が多かったため今回の設計はバランスがいいのではと勝手に思っております。

moduleディレクト

こちらは名前の通りterraform moduleを置いておく場所になります。現代ではterraform moduleを使っておらず、必要になったタイミングでこちらに展開予定です。VPCなどプロジェクトを跨いでも使えるようなものを配置する予定です。初期設計段階でここまでやり込むのは危険と判断し、当面こちらをフル活用する予定はありません。

project-resourceディレクト

こちらは1つのプロジェクトで共有するresourceを定義する場所にしています。各envディレクトリから、こちらのディレクトリのコンポーネントシンボリックリンクを使ってenvディレクトリから呼び出せるようにしています。

project-resource
└── vpc
    ├── locals.tf  ※ 変数を記述するtfファイル。map形式で各環境の変数を記載する。env/stage/componentにシンボリックリンクファイルを配置する。
    ├── main.tf    ※ リソースを記述するtfファイル。env/stage/componentにシンボリックリンクファイルを配置する。
    └── outputs.tf ※ outputを記述するtfファイル。env/stage/componentにシンボリックリンクファイルを配置する。

当初議論で、env毎に共通なものは変数だけで挙動変えるようにしたいよね、という話があり様々検討した結果こちらの方式に落ち着きました。

terragruntディレクト

terraformをより良い感じに書けるツールとして Terragruntを採用しております。

今回設計した構成だとどうしても冗長になってしまう部分があるので、terragruntを使って省力化を図っています。

├── terragrunt
│   ├── common
│   │   └── (1)terragrunt.hcl
│   └── component
│       └── (2)terragrunt.hcl  ※ (1)を参照するためのファイル。env/stage/componentにシンボリックリンクファイルを配置する。
└── env
    └── test
        ├── (3)backend-config.yml         ※ 環境設定ファイル。(1)が参照する。
        ├── (1)terragrunt.hcl -> terragrunt/common/terragrunt.hcl
        └── vpc        ※コンポーネント毎に実行ディレクトリを用意する。
            ├── (2)terragrunt.hcl -> terragrunt/component/terragrunt.hcl
            └── (backend.tf, provider.tf, version.tf, env_locals.tf)
  • (1) terragrant/common/terragrunt.hcl terragruntのメイン設定ファイル。 backend.tfなどのgenerate情報を記載している。 以下へシンボリックリンクを貼る env/{stage}/
  • (2) terragrant/component/terragrunt.hcl 上記(1)の設定を参照させるためのファイル。 以下へシンボリックリンクを貼る env/{stage}/{component}/
  • (3) env/{stage}/backend-config.yml stageの差異を記載する設定ファイル。 上記(1)が参照する。 実ファイルとして各env/{stage}/に存在する。

若干複雑な気もしますが初期に作ってしまえばそれほど苦ではないので、現時点では大きな問題にはなっておりません。

設計を進める上で大切にしたこと

ガイドライン化を意識して作る

どのような構成にするにしても、プロジェクトの初期構成構築時や新規メンバー参入時などある程度コンテキストが高くなってしまうことが懸念されました。よって、議論シリーズにしつつ最終的にガイドラインを作る方向で話を進めました(この記事の多くはガイドラインから引っ張ってきています)

実運用を意識して作る

IaCは初期構築して終わりではなく、一度やると決めたら運用し続ける覚悟が必要かと思っています(もちろん初期だけ作る設定というのもあると思っています)。初期の設計と変わることはざらなので、変更容易性を担保することが大切かと思います。変更箇所を限定しつつ、同じ修正を各env毎に実施することはしたくない、ということで今回の設計となっています。

まとめ

terraform化を進めた話を書かせていただきました。こちらに関しては私個人の力ではなく、チームで進めた成果ですので一緒に議論してくださったチームメンバーの皆さんありがとうございました!!!引き続きより良い設計を模索していきます。