こんにちは。 SRE の @suzuki-shunsuke です。 Terraform Monorepo に対する Renovate の大量の Pull Request を処理するための技術について紹介します。
背景
過去ブログで何度か紹介しているように、弊プロダクトでは Terraform の Monorepo を管理しています。 先日、 CI を AWS CodeBuild から GitHub Actions + tfaction に移行しました。
working directory (state) の数は 400 近くあり、 working directory ごとに以下のような tool のバージョンを管理しています。
これ以外に、 tfcmt などの tool のバージョンはリポジトリ単位で管理しています。
Renovate はデフォルトでは同じツールを一つの Pull Request で update しますが、 additionalBranchPrefix を設定することで working directory ごとに Pull Request を分けるようにしています。 そうするとあるツールが update されると 400 近い Pull Request をマージする必要が出てきます。 Terraform の AWS Provider などは毎週 update がありますし、 Terraform 本体もそこそこの頻度で update があります。 そのような大量の Pull Request を人間が一つ一つレビューするのは大変ですし、労力に見合いません。 そのため、 tflint などの CI が成功しており、かつ terraform plan の結果が No change である場合は、自動で merge されることが望ましいです。 一日あたりにマージできる Pull Request の数が少ないと Pull Request を処理しきれなくなり、いつまで経っても update されないような状態にもなってしまうため、大量の Pull Request を短時間で処理できる必要がありました。
解決方法
- automerge を有効にする
- platformAutomerge を有効にする
- prHourlyLimit を 0 にする
- prConcurrentLimit は 5 に制限する
- base branch が update されて automerge が disabled になったら、自動で feature branch を update して automerge を enabled にする
- CI が失敗したら直ぐに PR を close してブランチも削除する
- Terraform や Terraform Provider 以外の update では terraform plan, apply を skip する
- Renovate Approve だけでなく、 Renovate Approve 2 も install することで、 approve 漏れを防ぐ
- Terraform や AWS Provider のような更新頻度が多く、 working directory ごとにバージョン管理しているものの prPriority を下げる
- tfaction の inputs の
github_token
を、secrets.GITHUB_TOKEN
から GitHub App の token に切り替え、 rate limit を防ぐ
automerge を有効にする
automerge を有効にすることで Renovate が自動でマージしてくれます。 approve が必要な場合も Renovate approve という GitHub App を使うことで自動化出来ます。
しかし、 automerge によるマージには結構時間がかかることが知られています。 数時間かかることもあります。 そこで、 platformAutomerge を有効にします。
platformAutomerge を有効にする
platformAutomerge を有効にすると、 GitHub の場合 Automerge が有効にされ、条件を満たしたら直ぐにマージされます。
GitHub の Automerge の注意点
ただし、 GitHub の Automerge には注意が必要であり、気をつけないと CI が失敗しているのに Pull Request がマージされてしまいます。
- リポジトリの設定で Allow auto-merge を有効化する必要がある
- base branch に Branch Protection Rule を設定する必要がある
Status checks that are required.
で status checks を選択する。一つも選択しないと、 Automerge を有効化できない
Status checks that are required.
でチェックしたもの以外が失敗していても Pull Request はマージされてしまうことに気をつけてください。
GitHub Actions の job が if で skip されて実行されてない場合もマージされるようです。
弊プロダクトでは tfaction によって複数の state に対する CI を build matrix で実行していますが、これにより実行される job が動的に変わるため、 Status checks that are required.
に設定するのが難しいという問題があります。
そこで build matrix に依存する job を追加し、その job を Status checks that are required.
に追加しています。
それ以外の workflow が失敗してもマージされてしまいますが、滅多にそのようなことは起こらないし、起こったとしても直せばいいので許容しています。
prHourlyLimit を 0 にする
Renovate には Pull Request 作成を制限する幾つかの Limit があります。 注意が必要なのは、 default で無制限であっても、 config:base という Preset で制限されている場合があります。
config | default | config:base |
---|---|---|
prHourlyLimit | 0 | 2 |
prConcurrentLimit | 0 | 10 |
branchConcurrentLimit | prConcurrentLimit |
prHourlyLimit は config:base
で 2 に制限されており、 1 時間に 2 つしか Pull Request が作られないことになっています。
なので明示的に 0 に設定し、無制限に Pull Request が作られるようにします。
prConcurrentLimit は 5 に制限する
Renovate は上記の制限内で作れるだけ Pull Request を作ろうとします。 Terraform の CI で terraform plan, apply を実行する場合、同時に大量に実行すると高確率で API の rate limit に引っかかります。 また、 GitHub の Automerge は base branch が更新されると自動で disable されることがあります。
そのため、 prConcurrentLimit を 5 に制限しています。
branchConcurrentLimit も制限する
branchConcurrentLimit はブランチの数による制限です。別に branch の数で制限する必要性はないのではと思って一度 0 にしてみましたが、これは失敗でした。 Pull Request は作成されなくても branch は作成されるようで、 1000 以上の branch が無駄に作られてしまいました。 branchConcurrentLimit はデフォルトで prConcurrentLimit と同じになるので、 prConcurrentLimit だけ明示的に設定し branchConcurrentLimit は設定しないようにしました。
CI が失敗したら直ぐに PR を close してブランチも削除する
prConcurrentLimit, branchConcurrentLimit を制限している以上、 Renovate の Pull Request を open したままにしておくと、新たに作成される Pull Request の数が制限されてしまいます。 そこで、 automerge できなかった Pull Request を close するようにし、ブランチも削除するようにしました。
README のサンプルにあるように、 15 分おきに workflow を実行し、 10 分以上前に作成された pull request を close しています。
Close した Pull Request の一覧は簡単なクエリで取れますし、 Renovate の Dependency Dashboard にも出てくるので、後で確認して対応するようにしています。
is:pr is:unmerged author:app/renovate
base branch が update されて automerge が disabled になったら、自動で feature branch を update して automerge を enabled にする
GitHub の Automerge は base branch が更新されると自動で disable されてしまうことがあります。
そこで、 disable された event を元に GitHub Actions の Workflow を実行し、 自動で Pull Request の feature branch を update して automerge を enabled にするようにしました。
Terraform や Terraform Provider 以外の update では terraform plan, apply を skip する
tfaction では terraform apply によって危険な変更がされないよう、 Renovate の Pull Request で terraform plan の結果が No Change じゃない場合、 CI が失敗するようになっています。 しかしそうするとそれが blocker になって tfsec や tflint といったツールを update できないことがありました。
本来 tfsec や tflint は terraform plan, apply とは無関係で、それらを update する際に terraform plan, apply を実行する必要はないはずです。
tfaction v0.4.9 から Renovate の Pull Request で terraform plan, apply を skip できるようになったので、それを使っています。
terraform や terraform provider の update では plan, apply を実行し、それ以外の update では skip するようにしています。 こうすることで terraform plan の結果によって block されなくなるだけでなく、 CI が早くなりますし API rate limit にも引っかからなくなります。
Renovate Approve だけでなく、 Renovate Approve 2 も install することで、 approve 漏れを防ぐ
たまにですが、 Renovate Approve がなぜか approve してくれないことがあります。 1 approve を必須にしているため、 approve されないと自動でマージもされません。 そこで Renovate Approve 2 という GitHub App も install することで、 approve 漏れが起こりにくくしました。 この App は本来 2 approve が必要な場合に使うものだと思いますが、 approve 漏れを防ぐのにも使えるかと思います。 今の所 Renovate Approve 2 をインストールしてから approve 漏れは起こっていません。
更新頻度が多く、 working directory ごとにバージョン管理しているものの prPriority を下げる
Terraform や AWS Provider のように更新頻度が多く、かつ working directory ごとにバージョン管理しているものは他のツールの update を長い間 block してしまうことがあります。 そこでそういったツールの prPriority を下げる、あるいはそれ以外の prPriority を上げることによって block されるのを防ぐことが出来ます。
tfaction の inputs の github_token
を、 secrets.GITHUB_TOKEN
から GitHub App の token に切り替え、 rate limit を防ぐ
tfaction の幾つかの action は input として GitHub Access Token を渡すものがあります。
デフォルトでは secrets.GITHUB_TOKEN
が使われますが、 1 時間あたりの CI の回数が増えると rate limit に引っかかる可能性があります。
そこで secrets.GITHUB_TOKEN
よりも rate limit が厳しくない GitHub App の token に切り替えました。
切り替える際は GitHub App の permission を修正すること(issues: read が必要になるはず)と、 github-comment hide で古いコメントを非表示にしている場合はそちらの GitHub Access Token も切り替える必要があります(github-comment hide は同じユーザーのコメントしか非表示にしないため)。
- https://docs.github.com/en/rest/overview/resources-in-the-rest-api#requests-from-github-actions
- https://docs.github.com/en/developers/apps/building-github-apps/rate-limits-for-github-apps
結果
上記のような対応を行った結果、多いときには 1 つのリポジトリに 1 日に 500 近い Pull Request を作成・マージすることができるようになりました。 この 500 という数字はまだ改善の余地があります(最大 700 くらいはいける気がします)が、それでも現状十分な処理能力です。 今までは open なままの Pull Request を時々チェックして対応したりしてましたが、 自動化できる部分は自動化することによって本当に人間が対応しないといけない場合のみ対応すればよくなり、負担も減りました。
さいごに
以上、 Renovate によって大量の Pull Request を作成し、自動で処理する技術を紹介しました。
We are hiring!
スタディサプリ小・中・高校講座、大学受験講座では、 教育サービスを改革する自己完結型チームを実現する SRE を募集しています。
また、最近 Security に特化したポジションもオープンしたのでそちらもよろしくおねがいします。