こんにちは。 SRE の @suzuki-shunsuke です。
Terraform Modules を Monorepo で versioning して管理するようにした話を紹介します。
先日紹介したとおり、弊プロダクトではインフラを Terraform の Monorepo で管理しており、 CI は GitHub Actions + tfaction で実行しています。
弊プロダクトの Terraform Modules の管理手法は、この 2.5 年の間に 2 回ほど変化がありました。
- Terraform Monorepo 内で管理し Local paths で Module を指定
- Module を versioning するために Module ごとにリポジトリを作成し、 Terraform Monorepo とは別リポジトリで管理
- Module のリポジトリの管理が煩雑になってきたため、 Terraform Monorepo に再び集約しつつ versioning を実現
1. Terraform Monorepo 内で管理し、 Local paths で Module を指定
Terraform は様々な Module Source をサポートしています。
Local paths は最もシンプルな Source で、ローカルにある Module を相対パスで指定するものです。
長い間 Local paths で Module を指定していましたが、以下のような課題がありました。
- Module が変更されたら、その Module に依存する全 working directory で CI を実行する必要がある
- 依存関係を CI で動的に検出する必要がある
- ビックバンリリースのようになる
- 段階的にリリースできない
- API rate limit に引っかかりやすくなる
- 特定の working directory で予期せぬ差分が出て Module を更新できなくなると、他のすべての working directory でも更新ができなくなってしまう
Terraform Monorepo で管理する working directory の数が増え、同じ Module に依存する working directory が増えるに連れ、 上記の問題が無視できなくなってきました。
2. Module を versioning するために Module ごとにリポジトリを作成し、 Terraform Monorepo とは別リポジトリで管理
そこで Module を versioning し、各 working directory で version を指定することで working directory ごとに update できるようにしたいと考えました。 Module を Monorepo で管理すると、 Module を Renovate で update するのが難しいなどの理由から Module ごとにリポジトリを分割して管理するようにしました。
弊プロダクトでは Monorepo というアーキテクチャは Terraform 以外でも至るところで採用されており、 その有用性はプロダクトチーム内で広く認識されています。 Module ごとにリポジトリを分割することは Monorepo を諦めることになるため、出来れば避けたかったのですが、当時は仕方がないと判断しました。
versioning することで Local paths の上記の課題は解決できましたが、リポジトリを分割したことで Repository Setting や CI, document の管理がとても煩雑になってしまいました。 Template Repository を用意することで Module の新規作成を多少改善しましたが、継続的にメンテしていくのはかなり難しいと感じました。
3. Module を Terraform Monorepo に再び集約しつつ versioning を実現
そこで再び Module を Terraform Monorepo に集約しつつ versioning をすることにしました。 ちょうど tfaction が v0.5.0 から Module 管理をサポートするようになったので、それをそのまま活用することにしました。
Module 用の Monorepo を別に用意するのではなく、 Terraform の Monorepo と同じリポジトリ直下に modules
というディレクトリを作成し、そこに Module を集約させています。
Module Source としては GitHub Source を採用し、 サブディレクトリを指定しています。
同じリポジトリなので、 GitHub Actions token や GitHub App token を使って HTTP で Module を download することができます。
Monorepo なので、 semver に従って versioning するのは難しく、 module_modules_hello_v0.1.0
のような、少々分かりにくい Git tag で versioning しています。
tfaction では Module を管理するために以下のような機能を提供しています。
- GitHub Actions による Module の Scaffold
- Module の CI (lint, format, ドキュメント自動生成)
- terraform fmt, terraform validate, tflint, tfsec, terraform-docs
- GitHub Actions による Module のリリース (versioning)
GitHub Actions によって Module を Scaffold する Pull Request が生成されます。
--
GitHub Actions によって GitHub Tag 及び Release が生成されます。
--
Module の作成・CI・リリースといった一連のフローがカバーされています。 tfaction で Module を管理するようにしたことで、 Module に対して CI がちゃんと行われるようになり、コードの品質が改善され、問題をより速く見つけることができるようになりました。 terraform-docs が自動で実行され差分があればコミット・プッシュされるため、 Module のドキュメントが自動で生成・更新されるようになっています。 元々 Module のドキュメントもちゃんと書かれていないものが多かったため、重宝しています。 また、同じリポジトリで管理しているため、一時的に Local paths で参照するようにして terraform plan を実行してみて動作確認するといったこともやりやすくなりました。
Module を Monorepo に集約して概ね良い感じにはなりましたが、 Module の update の自動化に関しては課題があります。 先述の通り、 Module の version のフォーマットが semver に従ってない上に Module 名も含んでいることから、 Renovate による update が少々難しいです。 やりようはあると思うので将来的に自動化したいと思っています。
移行手順
分割した Module の Repository を Terraform Monorepo に再び集約するための手順を説明します。
まず Module の Repository を全て Archive し、それ以上変更できないようにしました。 その上で、 Module 毎に以下の手順で移行しています(この記事の執筆時点で移行はまだ完了していません)。
- GitHub Actions で Module を Scaffold
- Module のコードをコピー
- tflint や tfsec のエラーを修正
- CODEOWNERS を設定
- GitHub Actions で Module を Release
- Module の
source
を新しい Module に変更
さいごに
以上、 Terraform Modules を Monorepo で versioning して管理するようにした話を紹介しました。 Module の管理方法は様々あり、どうするのが良いのか悩ましい部分もあるかと思いますが、 tfaction が提供するフローに従うことで良い感じに Module を管理できるようになりました。
We are hiring!
スタディサプリ小・中・高校講座、大学受験講座では、 教育サービスを改革する自己完結型チームを実現する SRE を募集しています。
また、最近 Security に特化したポジションもオープンしたのでそちらもよろしくおねがいします。