tfactionを使ってTerraformの実行を自動化した!!
この記事は、tacoms Advent Calendar 2024の16日目です!
他メンバーのAdvent Calendarはこちらからご覧ください!👇
はじめに
どうもー!株式会社tacomsのSREのMorixです!
入社して1ヶ月経ちました。毎日学ぶことが多くあっという間の1ヶ月でした!
この1ヶ月で色々やったのですが、その中のひとつである「tfactionを使ったTerraform自動化」についてお話したいと思います。
tfactionとは?
tfactionは @shunsuke_suzuki氏が開発している、GitHub Actionsを使っていい感じのTerraform Workflowを構築するためのactionです。
単一のactionではなく複数のactionが用意されており、それらを組み合わせて使用します。
プルリクエストを作ると変更のあったディレクトリに対してterraform plan
を行い、差分をプルリクエストにコメントしてくれます。
レビューが済みmainブランチへマージされるとterraform apply
をしてくれます。
tfactionを採用するまでの状況
まずtacomsのTerraform環境がどんな感じだったのかを説明したいと思います。
tacomsのTerraformのplanやapplyはGitHub Actionsで実行するようになっていました。
しかしworkflow_dispatchを使った手動実行のみのサポートで、以下のような手順でapplyまで持っていってました。
- Terraformのコード修正・プルリク作成
- Terraform FormatチェックがCI(GitHub Actions)で実行
- Plan用Workflowを手動実行
- 3の結果をプルリクエストに貼り付けてレビュー依頼
- レビュワーは3の結果を見に行き問題ないことを確認する
- プルリクエストをマージ
- Apply用Workflowを手動実行
applyするまでにやることが多く、作業の漏れもあったりしました。
この状況を改善するために、Terraformのplan/applyの実行を自動化したいと考えました。
tfactionを選定する意思決定の流れ
前述のような状況だったので、まずはどうやってplan/applyの実行の自動化をするかを検討しました。
tacomsではArchitectural Decision Records(ADR)で技術選定の意思決定を行なっているため、ADRを作成しました。
案としては次のものが出ました。
- Atlantisを使う
- GitHub Actionsで実現する(プルリクマージ前にapplyできる)
- GitHub Actionsで実現する(プルリクマージ後にapplyできる)
候補1.Atlantisを使う
Atlantisはプルリク上で特定のコメントするとplanやapplyをしてくれるセルフホスティング型のアプリケーションです。
これを使った場合のTerraformリリースフローは以下のようになります。
- プルリク作成
- 自動でplanされ、差分がプルリクにコメントされる
- approveもらったらプルリクコメントに
terraform apply
と書く - apply結果がプルリクにコメントされる
- マージする
非常にわかりやすいです。
ディレクトリ単位のロック機構もあるので、他の人が別プルリクで同じディレクトを触っててもそのplan/applyの実施を抑制してくれます。
またマージ前にapplyできるのも良いです。Terraformではplanが実行できたからといってapplyできるわけではないので・・・。その試行錯誤をマージしなくてもできるのはすごくポイントが高いです。
しかしこれはアプリケーションをセルフホストしないといけないので、動かすインフラが必要になったり、運用保守も必要になってくるため見送りました。
候補2.GitHub Actionsで実現する(プルリクマージ前にapplyできる)
Finatextグループ様が寄稿されたGitHub ActionsのみでAtlantis likeなTerraform CICDの仕組みを構築するという記事では、Atlantisを使わずGitHub ActionsのみでAtlantisライクなことを実現するというのを紹介されていました。
これは大変素晴らしいアプローチで、インフラ運用不要で同じようなことができるのは大変魅力的です。
リリースフローはAtlantisと同様です。
メリットもAtlantisと同様で、マージ前にapplyできるのがポイント高いです。
しかしロック機構が複雑になりそうで、保守の観点で不安がありました。
候補3.GitHub Actionsで実現する(プルリクマージ後にapplyできる)
プルリクマージ前にapplyするのを諦めれば、複雑なロック機構は不要だと判断しました。
なぜ複雑なロック機構が不要かと判断したかというと、mainブランチにマージされたらapplyをするので、mainブランチをロックしておけば簡単に実現できると思ったからです。
さて、この場合次のようなリリースフローになります。
- プルリク作成
- 自動でplanされ、差分がプルリクにコメントされる
- プルリクがapproveされたらマージする
- apply結果がプルリクにコメントされる
ということでこの案で行くことにしました!
この流れ、当初はすべて自作しようとしてました。理由は前職で同じようなものを作っていたので簡単に作れると思ったからです。
しかし調べてみると作ろうと思ってた次の機能がすべてtfactionを使えば簡単に実現できそうだったので、tfactionを導入することにしました!
- 変更があったディレクトリのみにplan/applyを実行
- 変更があったディレクトリにplan/applyを並列実行
- planの差分を整形してプルリクエストにコメント
- ロック機能
複数Terraformリポジトリへのtfactionの導入方法
tfactionの導入はドキュメントにしっかり書いてくださっているので非常に簡単でした。
しかしtacomsにはTerraformのコードがあるGitリポジトリが複数あります。
この手順を1つ1つのリポジトリにやるには少々時間がかかります。
この問題をどう解決したのかをご紹介します。
どうやったかというと、Terraform plan/applyをするWorkflowを1つのGitリポジトリに集約し、Terraform管理リポジトリはそのWorkflowを呼び出す です。
Terraform plan/applyするWorkflowを集約するGitリポジトリの構成
tfactionを使ったplanやapplyは複数のReusable Workflowを使って実現します。
tfaction-exampleを見るとコード構成がイメージしやすいです。
このworkflow群を1つのGitリポジトリで管理します。
実際の構成は次のようになってます。
workflowsにあるplan.yaml
とapply.yaml
が他のGitリポジトリから呼び出されるReusable Workflowです。
.aqua
├── aqua-checksums.json
├── aqua.yaml
└── imports
├── github-comment.yaml
├── reviewdog.yaml
└── etc...
.github/workflows
├── apply.yaml
├── plan.yaml
├── wc-hide-comment.yaml
├── wc-path-filter.yaml
├── wc-plan.yaml
├── wc-setup.yaml
├── wc-test-module.yaml
└── wc-test.yaml
ここのポイントはaquaの設定ファイルです。
aquaはコマンドラインツールの宣言型のバージョン管理ツールです。tfactionを使う場合このaquaを導入する必要があります。
tfactionは複数のコマンドラインツールに依存しています。reviewdogとかtrivyなどです。
これらのツールはtfactionには必要ですが、通常のローカルで実行するときのTerraformのplanには必要ありません。
なのでTerraform管理リポジトリでこれらの管理をしたくありません。
そのためtfactionでしか使わないコマンドラインツールのバージョン管理はこの集約リポジトリのみで行うことにしました。
しかし集約リポジトリのReusable Workflowが呼び出されたとして、集約リポジトリにあるaquaの設定ファイルは参照できません。
そのためplanとapplyのWorkflowの先頭に、集約リポジトリのaqua設定をダウンロードする処理を入れました。
こうすることでTerraformリポジトリではTerraformのaqua設定だけ入れておけばいい状態にできました!
Terraform管理リポジトリからplan/applyのWorkflowの呼び出し方
集約リポジトリにあるplan/applyをTerraform管理リポジトリから呼び出すだけで簡単にTerraform自動化ができます!
もちろんaquaの設定やtfaction-root.yaml
やtfaction.yaml
の配置は必要ですが!
name: plan
on:
pull_request_target:
branches:
- main
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref }}
cancel-in-progress: true
permissions: {}
jobs:
plan:
uses: xxx/xxx(集約リポジトリ名)/.github/workflows/plan.yaml@main
permissions:
id-token: write
contents: read
secrets:
gh_app_id: ${{secrets.xxx }}
gh_app_private_key: ${{secrets.xxx}}
tfactionを採用した感想
まだ採用して日が浅いですが、よかった点を書いてみます!
- めちゃくちゃ簡単に導入できる
- 上述したようにチュートリアルが充実してるので簡単に実現できます。ただplanを実行するだけでいいなら2〜3時間で出来ました!
- 差分結果がめちゃくちゃわかりやすい
- ただplan結果を出すんじゃなくて、いい感じに整形したうえでコメントしてくれるので読みやすいです
- ロック機能が簡単に実現できる
- tfactionの作者さまがlock機構を実現するActionを作ってくれますし、tfaction-exampleでも使われてるので参考にしやすいし、簡単にロック機構を実現できました!
- https://zenn.dev/shunsuke_suzuki/articles/github-lock-action
- apply失敗したらフォローアッププルリクエストを作ってくる
- apply失敗したらほとんどの場合、修正するプルリクエストを作る羽目になりますが、それを自動でやってくれます。これ便利です。
Discussion