Speee DEVELOPER BLOG

Speee開発陣による技術情報発信ブログです。 メディア開発・運用、スマートフォンアプリ開発、Webマーケティング、アドテクなどで培った技術ノウハウを発信していきます!

Terraformリポジトリがモノレポから分散レポになってもう一度モノレポに戻ってくるまで

本記事はterraformのカレンダー | Advent Calendar 2023 - Qiitaの3日目の記事です。
昨日の記事はgithub actions(w/z composite) + setup-terraform in 2023 #Terraform - Qiitaでした。


DX(デジタルトランスフォーメーション)事業本部DX共通開発部開発基盤グループのリーダー西田です。 最近、放送大学に入学して美術史を勉強しています。

今日は、僕が入社してから4年間のTerraformリポジトリの移り変わりと、それがなぜ出発地点のモノレポに戻ってきたのかを紹介しようと思います。

2019年

この頃はTerraform使い始めたばかりで、CloudFormation, itamae, ansibleなど多数のIaCが混在していた時代でした。
この頃のリポジトリ構成はほぼモノレポでCICDはCircleCIが使用されています。
当時はまだGitHub ActionsがGA前でした。

僕が入社したのが11月なのですが、僕が入社する前後に合わせて新しいシステム開発が多数始まります。
その中で

  • CircleCIがモノレポに対応していない(変更されたファイルのパスベースで実行するジョブを選べない)
  • 各開発チームが安心してTerraformを触れるようにしたい(そのためにチームごとにリポジトリを分けて間違って他システムを変更するリスクを0にしたい)
  • 既存リポジトリの構造が乱雑だったので新しいディレクトリ構造に仕切り直したい

などの理由からシステム毎にリポジトリを分割することにします。

2020年前半

Terraformのplan/applyをCICDで実行するようにします。
現在ではapplyを自動で行うというのはそこそこ一般的になっていますが、当時はまだインフラの変更を自動的にするということはかなり危険なことだという認識があり、僕自身もかなりの勇気が必要でした1

このCICDには従来のCircleCIではなくGitHub Actionsを採用しました。
これは、その直前にGitHub ActionsがGAとなっていたこと、ActionというCIコンポーネントの再利用の仕組みが非常に優れていると感じたことなどが理由です。

その時期に書いたのが次の記事だったりしました。

Github Actions 2年使ってみてわかったことまとめ #GitHubActions - Qiita

2020年中盤

この頃にはシステムの数が増え始め、共通の構造も頻出するようになってきました。
だいたい10個ぐらいのシステムとそれぞれごとのterraformリポジトリができることになります。

このままだと管理が大変なので、なんとか共通化しようとします。
そこで行ったのが次で紹介するテンプレート言語を使った管理でした。

speakerdeck.com

この方法は他社さんがTerraformコードをテンプレートで生成しているという事例を聞いてやってみた方法です。しかし、残念ながらうまくいかず半年程度で断念しました。

うちでうまくいかなかった理由としては、テンプレートによる生成を初期生成だけでなくその後の維持運用でも使用しようとしていたことにあったと思います。
DX事業本部のシステムはその構成がほとんど同じ(Rails + MySQL + redis + AWS)ではあったものの、細部は微妙にそれぞれ異なります。その違いを継続的に吸収するにはテンプレート言語では柔軟性が足りませんでした。

2020年後半

テンプレート言語による共通化がうまくいかなかったため、この頃terraformのモジュールを機能を利用しての共通化に挑戦します。

共通化を考えたとき、最初にTerraform自体の機能であるモジュール機能を使わずテンプレート言語を使用していたのには理由がありました。それは、Terraformのモジュールが本当によい機能であるか疑問があったためです。
具体例をあげるとTerraformはworkspaceという今では使わないほうがよいと思われている機能が当時は広く使われていたりして負債となっている例がありました。モジュールもまだどう使えばいいのかが広まっておらず、まだ海の物とも山の物ともつかない状態でした。

そんな中で改めてTerraformのモジュール機能について調査し、これなら行けそうとなったのがこの時期です。ちなみに、その調査結果をまとめたのが次の記事だったりします。

モジュールが行ける、と判断できたもう1つの理由は、次の公開モジュールの出来が非常に良く、またお手本として参考になったことでした。
github.com

最後の理由は、プライベートなモジュールをクロスリポジトリに参照する方法が見つかっていなかったことです。
前述の通り、当時私達はすでにシステムごとのリポジトリへ分散していました。その中でインターネットに公開せずプライベートのまま別リポジトリのモジュールを参照する方法がわかっていなかったのです。
ただ、ドキュメントをよく読むとgitパスでモジュールを使う方法が載っており、またCICD上でもssh鍵を設定することでterraform initが機能することがわかったことで解決しました。

その結果、取られたのが次の様な構成です。

モジュールを集約したリポジトリを新たに作成し、各システムのリポジトリはgitのURL指定でモジュールを参照しています。

同時にデプロイフローをそれまでのmasterブランチ一つの方式からproductionブランチとstagingブランチを使う方式に変えました。
以前の方式では本番環境もステージング環境も同じタイミングでapplyされていましたが、ステージング環境はstagingブランチが変更されたとき、本番環境はproductionブランチが変更されたときにapplyされるようにしました。これにより本番環境でのapply前にかならずステージング環境でapplyされるため実行テストが強制され、安全性が格段に上がりました。

2021~2022年

この頃にはterraformモジュールによる共通化の成功ははっきりわかっていました。
そのためterraformモジュールは急速に増えていきます(2023年末現在は60個)。
特筆するべきは数が増えただけではなく、インフラに関する高度なノウハウがモジュールに込められたことです。
例えば、CloudFront + S3を使って、各ディレクトリパスへのアクセスのとき index.html を表示しつつS3への直接のアクセスを禁止したい場合、 CloudFrontとS3で作成する静的サイト構成の私的まとめ | DevelopersIOのような構成が必要になります。これは複雑かつ高度なナレッジが必要なのですが、うちの事業部ではモジュールを一つ参照するだけでこの構成が実現できるようになっています。

また、社外の公開モジュールの利用も増え、現在10種類前後のモジュールを使用しています。

一方で、共通モジュールが独立したリポジトリにあるため、モジュールの更新時にplanが壊れないよう、共通モジュールの参照にはgitタグを用いていました。そのために、モジュールの更新には次のような手順が必要でした。

  1. 共通モジュールを変更するPRを作成、テストとレビューを通してマージする
  2. moduleリポジトリで新たなバージョンをgitタグで作る
  3. モジュールを参照している各terraformリポジトリでバージョン番号を更新するPRを作成、変更される内容をレビューしてからマージする。もし問題があれば追加でコードを修正する

特に3番の各リポジトリでの参照箇所のバージョン更新が非常に大変でした。10前後もリポジトリがあるためです。2022年頃にはこれをrenovatebotが半自動的にやってくれる様になりましたが、それでもすべてのリポジトリでPRをチェックし、エラーがないことを確認してからマージするのはなかなかの手間でした。

また、モジュール化されていないあるリソースについてすべてのシステムに対して一律で変更したいような場合の手間は悪夢でした。
ちょっとした修正でも、10個のリポジトリで10個のPRを作り、10個のテストと10個のレビューとapproveと10個のマージと場合によっては本番デプロイまで必要でした。これにより横断的な修正の作業効率は極めて低くなっていました。
このような作業は、2020年ごろはそれほどないと予測していたのですが、各コンポーネントのバージョンアップやS3・ECRなど各要素のベストプラクティスの発見・更新・仕様変更による対応が想定よりずっと頻繁に発生したため、かなり困ったことになってしまいました。

加えて、パフォーマンス上の問題もありました。リポジトリ外のモジュールに多数依存しているため、terraform init が非常に遅くなってしまいました(1~3分)。

2023年

これらの問題を解決するために、terraformリポジトリを再びモノレポ化することにしました。
実はモノレポ化自体は常に選択肢には上がっていたものの、多数の障害があり、この選択は難しいだろうと思っていました。
しかし、

  • CircleCIがパスベースでのジョブ実行判定に対応していない問題 → だいぶ前にGitHub Actionsに移行しており、GHAはモノレポのためのパスベースのジョブ実行ができるため問題なし
  • 開発チームがterraformを触りにくくなる問題 → そもそもtfファイルの変更の95%は開発基盤グループだったため、むしろ開発基盤の作業の効率を優先したほうがよい
  • 1リポジトリに多数のシステムのtfファイルが含まれることによる爆発半径の増加 → ディレクトリごとのapprove必須者の設定などで爆発半径を十分に小さくできることがわかった
  • stagingブランチの問題 - GitOpsからIssueOpsに切り替えることで対処可能とわかった(最も解決が難しかった)

などですべて解決できることがわかりました(ちなみにIssueOpsについては同じ12/06に公開予定の記事で詳しく説明します)。

モノレポ化作業

モノレポ化はかなり慎重に行いました。
まず、どうやったらできるかの脳内試行錯誤・情報収集を1年以上かけて行っており、IssueOpsにすれば行けそうというのが2023年2月ぐらいにわかりました。
次に、そのIssueOpsをどうやって実装すれば現在のterraform運用品質を維持しつつ移行できるかを1ヶ月ぐらいかけてテストしました。

2023年12月(現在)

検証が終わって行けることを確信し、その方法も確立できたので、分散していたリポジトリを1箇所に統合している途中です。

まだ統合作業の進捗は15%ぐらいですが、今まで苦労していた横断的な修正が5倍、10倍の速度でできるようになっており非常にワクワクしています。これによりインフラの改善はさらに加速できます。

いつもの

Speeeでは一緒にサービス開発を推進してくれる仲間を大募集しています!
こちらのFormよりカジュアル面談も気軽にお申し込みいただけます!

Speeeでは様々なポジションで募集中なので「どんなポジションがあるの?」と気になってくれてた方は、こちらチェックしてみてください!もちろんオープンポジション的に上記に限らず積極採用中です!!!


  1. 実際、これがきっかけで本番であわや大事故というヒヤリが一度起こったため、安全のためデプロイフローを見直したりplan結果をapplyへ引き回す、などの安全対策が後年取られます