第6回: Renovateによる依存関係の更新

※本記事は、技術評論社「Software Design」(2023年9月号)に寄稿した連載記事「Google Cloudで実践するSREプラクティス」からの転載です。発行元からの許可を得て掲載しております。

はじめに

前回はTerraformとGitHub Actionsで実践するインフラCI/CDについて解説しました。 今回はRenovate1を利用した、ツールやライブラリの依存関係更新について紹介します(図1)。 なぜ依存関係を更新する必要がある必要があるかという背景から、Renovateのしくみの解説と利用方法、更新の運用を手軽に行うためにキャディで取り組んでいることを紹介します。

▼図1 CADDiスタックにおける今回の位置付け

なぜ依存関係を更新するのか

現代のアプリケーション開発において、私たちエンジニアはさまざまなツールやライブラリの利用を通して、先人の知恵を借り、効率的な開発を進めています。また、前回までで紹介したTerraformやGitHub ActionsなどのインフラCI/CDの領域でも、なんらかの再利用のしくみを活用することで効率化しています。

しかし、ツールやライブラリは絶えずアップデートされています。機能の追加やバグの修正、脆弱性への対策など、その理由はさまざまです。その中でも筆者らが依存関係の更新を重視する理由は、セキュリティと対応コストの2点です。

セキュリティ観点では、ツールやライブラリの脆弱性やバグ修正の更新をいち早く検知・対応することが欠かせません。キャディでは、自社事業の基幹システムをフルクラウドで構築・運用しており、これらの放置は安定した価値提供を損ねることにつながるからです。

対応コスト観点では、頻繁な対応によって、バージョン間の差分が小さいうちに更新できることを重視しています。そのため、1回あたりの更新対応のコストを下げることが可能です。また、CHANGELOGにも常に目を通すことになるため、副次的に情報のキャッチアップにもつながります。

なぜRenovateを使うか

依存関係の更新をサポートしてくれる主要なツールとしては、RenovateのほかにGitHubで標準提供されているDependabot2があります。 キャディでは、2020年にRenovateを採用するまでは、Dependabotを一部で利用している程度でした。 本連載で紹介しているように、筆者の所属するPlatformグループでは、TerraformやGitHubActions を用いた IaC や CI/CDの高度化に取り組んでいます。これらにより依存するものが増えているため、前節のとおり依存関係の更新は必要です。一方で、事業の拡大を支えるための、本質的な価値提供にも集中する必要があります。 このような背景に対するトイル削減の一環で、高いカスタマイズ性を持つRenovateに魅力を感じ、利用を拡大しました。とくに、のちほど紹介するauto merge、Pull request(PR)のグループ化、正規表現を利用しながら更新ルールをカスタマイズできる点が効果的だったととらえています。 また、Dependabotは利用をやめているわけではありません。一部の開発チームではセキュリティアラートを活用するなどして、Renovateと共存しています。 Renovateでもセキュリティアラートを通知する設定はありますが、それぞれのツールでも得手不得手もあるため、開発者が最もメンテナンスしやすい方法を選択していく必要があると考えています。

Renovateのしくみ

ここからは、Renovateのしくみと設定方法について簡単に解説します(図2)。さらに理解を深めたい方は公式ドキュメント3を参照してください。

Renovateは依存関係を一元管理し、新しいバージョンがリリースされたときに自動的にファイルを更新します。そしてGitHubやGitLabなどのサポートされているプラットフォームにて、RenovateによってPRやMerge requestが作成されます。 まず、Renovateは依存関係の現状を把握するために各バージョンを確認します。JavaScriptの場合は package.json、Terraform の場合はterraform blockのprovider定義など、各言語やツールに応じたファイルから取得します。 次に、そのバージョンが最新であるかどうかをRenovateのルールに従って判定し、最新でない場合はバージョンを更新するPRを作成します。

▼図2 Renovateのしくみ

Renovateの設定

Renovateの挙動を理解するために欠かせない概念として、設定ファイルとマネージャーがあります。

設定ファイル

Renovateは設定ファイルや環境変数によって挙動をカスタマイズできます。どんな依存関係にあるものをどんな頻度で更新するか、レビュアーを指定するか、PRのラベルを指定するかなど、さまざまな設定が可能です。設定ファイルは、renovate.json .github/renovate.json .renovatercとして配置できたり、コメントが記載できるようにも拡張されたjson5形式4でも記述できます。 リスト1の設定例をもとに、簡単に紹介します。より詳細を理解したい方はドキュメント5を参照ください。

▼リスト1 renovate.json

{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",    // ①
  "extends": [                                                                                        // ②
    "config:base",                                                                                   // ⑤
    ":label(renovate)",                                                                            // ⑥
    ":timezone(Asia/Tokyo)",                                                                   // ⑦
  ],
  "schedule": ["after 1am and before 9am every weekday"],                    // ③
  "reviewers": ["team:reviewer-team", "kei711"],                                    // ④
}

$schema(①)はJSON Schemaの指定です。この値により、エディタによっては設定名が補完されるようになります。extends(②)は設定値のプリセットを指定します。schedule(③)はcron形式で実行スケジュールを指定します。リスト1の例では、平日の午前1時から午前9時の間に実行されます。reviewers(④)はレビュアーを指定します。GitHubやGitLabなどの挙動に合わせて、グループや個人を指定できます。 また、リスト1の例ではRenovateで用意されているデフォルトプリセットの一部を指定しているため、こちらも紹介します。 config:base(⑤)はRenovateのデフォルト設定で、設定値はRenovate自体に組み込まれています6。:label(⑥)の設定により、作成されるPRに特定のラベルを指定します。ここでは、renovateというラベルを設定します。:timezone(⑦)の設定により、scheduleで指定された実行スケジュールのタイムゾーンを指定します。なお、デフォルトプリセットの詳細はドキュメント7を参照してください。 このように、設定ファイルによりRenovate自体の挙動を柔軟にカスタマイズできます。

マネージャー

Renovateのマネージャーとは、各言語やツールに応じた処理が定義されたモジュールのことを指します。このマネージャーを通して、Renovateが依存関係の解析や更新をします。たとえば、JavaScriptであればnpm、Terraformであればterraform や terraform-version などのマネージャーがあります。 Renovateは初期設定でも多くのマネージャーを利用する設定となっています。詳細はドキュメント8を参照してください。各マネージャーもドキュメントにて紹介されています。 また、未設定だと利用されないマネージャーもあります。たとえば、Argo CDはファイル構成が利用者に委ねられており正確な検知が難しいため、リスト2のように明示が必要です。

▼リスト2 Argo CD向け設定の抜粋

 ...
  "argocd": {
    "fileMatch": [
      "argocd/.+\\.ya?ml$",
      "applications/.+\\.ya?ml$"
    ]
  },

Argo CDは、本連載第1回(本誌2023年4月号)で紹介した、KubernetesマニフェストをGitOpsで管理するためのツールです。本連載でも以降の回で詳しく紹介する予定です。 最初から用意されているマネージャーのほかにも、正規表現を利用して振る舞いを定義できる、regex manager9が存在します。 こちらの詳細はキャディで利用している実例とともに、のちほど紹介します。

Renovateの組み込み方法

ここからは、実際の使用方法を説明します。 大きく分けて「GitHub Appの利用」「ローカルで実行」「GitHub Actionsなどの環境で実行」の3パターンがありますが、ここでは最も手軽なGitHub Appによる方法を紹介します。 RenovateはMend社により、無償のGitHub Appとしても提供されています。ソースコードの管理にGitHubを利用している場合には、GitHubのMarketplace10からGitHub Appを導入して利用することで、手軽にRenovateを利用できます。 MarketplaceからGitHub Appをインストールし、依存関係を自動更新させたいリポジトリを選択します。そうすると、Renovateの更新対象として選択したリポジトリにて、Renovateの設定ファイルであるrenovate.jsonを作成するPRが自動作成されます。設定ファイルをリポジトリに配置することで、Renovateの設定が完了し、自動で依存関係の更新が行われるようになります。 GitHub Appの各リポジトリにおけるGitHub Appの動作状況は、ポータルサイト11から確認できます。GitHub Organizationを選択すると、Installed Repositoriesとして、Renovateをインストールしたリポジトリの一覧と、それぞれのインストール日や最終実行時刻が表示されます。 次に、リポジトリの行をクリックするとRecentJobsのページが表示され、リポジトリ単位の実行状況が表示されます。 さらにJobごとの行をクリックすると、Renovateが実行された際のログを、ログレベルや詳細情報の表示切り替えをしながら確認できます。もしRenovateの設定変更がうまくPRに反映されていない場合は、このログから状況を確認できます。

応用的な使い方

ここからは、更新の運用を手軽に行うために取り組んでいることをピックアップして紹介します。

共通設定の定義と利用

Renovateの設定はrenovate.jsonに記述しますが、リポジトリそれぞれで定義すると管理コストが非常に高くなります。実際、キャディでは管理するリポジトリが多く、設定の共通化で管理コストを下げています。 共通設定の共有方法は複数ありますが、今回は手軽なGitHubで公開する方法を紹介します。 ほかの共有方法や、GitHubで公開する方法の詳細はドキュメント12を参照してください。 まず、renovateの設定ファイルを共有するためのリポジトリを作成します。キャディでは、renovate-configという名前で作成しています。 次に、後述するプリセット名を省略した場合のため、default.jsonにrenovateの設定を記述します。また、言語や開発チームごとに共通設定を用意したい場合はpreset name.jsonというような命名をします。たとえば、go.jsonやteam-platform.json5のような形です。 このように共通設定を用意したら、利用したいリポジトリのrenovate.jsonにて、リスト3のように記述します。GITHUB_ORGはcaddijpのような組織名やkei711のようなアカウント名に書き換えてください。

▼リスト3 共通設定の利用例

{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "extends": [
    "github>GITHUB_ORG/renovate-config",                                 // ①
    "github>GITHUB_ORG/renovate-config:go",                            // ②
    "github>GITHUB_ORG/renovate-config:team-platform.json5", // ③
  ]
}

リスト3の設定ファイルでは、次のように共通設定を参照します。 ①リポジトリ上のdefault.jsonを利用 ②プリセット名を指定し、go.jsonを利用 ③platform.json5を利用。JSON5形式の場合はプリセット名に拡張子を含める必要がある また、Gitのタグやファイルパスも指定できます。詳細な例はドキュメント13のGitHubの項目を参照してください。 なお、プライベートリポジトリでGitHub AppのRenovateを利用する場合には、renovate-configリポジトリもプライベートリポジトリにし、GitHub Appの導入も必要となる点に注意してください。

[Column] Renovate の GitHub Appを利用する際の注意点

GitHub Appは手軽に使える反面、注意すべき点が2点あります。 1点目は、セキュリティ観点です。GitHub Appを導入するということは、アプリケーションを作成する場合のサプライチェーン攻撃の攻撃面が増えることを意味します。RenovateのGitHub Appをインストールすると、自分たちが管理するソースコードへのアクセス権を与えることになります。そのため、このアクセス権が奪われた場合や、GitHub Appに不正なコードが含まれている場合、それが自分たちのソースコードにも影響を及ぼす可能性があることを理解する必要があります。 2点目は、作業コストの観点です。全リポジトリを対象にRenovateを導入するオプションがありますが、リポジトリに導入したぶん、Renovateにより依存関係の更新PRが作成されます。作成されるPRが多過ぎるとメンテナンスを行いにくくなります。そのため、前述のように導入するリポジトリを選択することをお勧めします。また、セキュリティリスクにおいても、リスクを取れるリポジトリと取れないリポジトリもあるため、適切に選択をする必要があります。

設定ファイルの検証

Renovateには設定ファイルの構文が正常かどうかを確認するコマンドが用意されています。 RENOVATE_CONFIG_FILE=renovate.jsonnpx renovate-config-validator のように実行することで確認できます。共通設定を管理するリポジトリのCIに設定しておくと安心して編集できるでしょう。

GitHubでオートマージを利用する

Renovateのautomergeを活用する場合は、設定ファイルの変更と、GitHubやGitLabなどにて設定しているマージ条件が満たされている必要があります。

・Allow auto-mergeが有効になっている ・ Branch Protection Rulesで設定されたマージ条件が満たされている

たとえば、CODEOWNERSのレビューを必須にしている場合には、更新対象のファイルをCODEOWNERSから除外するか、ルールをバイパスする設定を追加する必要があります。また、Branch Protection RulesでPRのapproveを必須としている場合には、RenovateのPRを自動approveしてくれる GitHub App である「renovate-approve」を導入します。approveの数などの必要に応じて「renovate-approve2」のGitHub Appsも導入してください。 リスト4は、セマンティックバージョニングされたツールで、minorpatchの更新をオートマージする例です。設定ファイルでは、packageRulesブロックで依存関係ごとに上書きできます。matchPackageNamesでは対象を指定することで、特定の依存関係のみオートマージできます。また、必要に応じてignoreTestsでテストの実行を無視できます。

▼リスト4 オートマージの設定例

{
  "platformAutomerge": true,
  "packageRules": [
    {
      "automerge": true,
      "matchUpdateTypes": ["minor", "patch"],
      "matchPackageNames": [
        "kubernetes-sigs/kustomize",
        "mikefarah/yq"
      ],
      "ignoreTests": true
    }
  ]
}

グループ化により、依存関係をまとめて更新する

同じ用途のバージョンは、一度に更新したいことが多いかと思います。キャディでは前回までの連載で紹介しているとおり、TerraformでGoogle Cloudの設定をIaC化しています。 Google Cloudの設定をするためのTerraformproviderには、googleとgoogle-betaの2種類があります。早く技術検証したい場合にはgoogle-beta providerを利用することがあります。 筆者らはこれらのproviderをまとめて更新するためのリスト5の設定を利用しています。

▼リスト5 PRをグループ化する設定例

{
  "packageRules": [
    {
      "matchManagers": ["terraform"],
      "matchPackageNames": ["google", "google-beta"],
      "groupName": "Google Terraform providers"
    }
  ]
}

matchManagersとmatchPackageNamesで対象となる依存関係を指定します。そしてgroupNameを指定することで、1つのPRの中で一度にバージョンが更新されるようになります。

regexManagersによる独自の更新ルール定義

筆者らは、TerraformによるGoogle Cloudの設定処理を共通化するため、GitHub ActionsのComposite Actionを作成しています。このとき、terraform_versionを渡す必要がありますが、このバージョンの指定方法ではRenovateが更新してくれません(リスト6)。

▼リスト6 標準では更新対象外となる独自定義

...
      - name: Setup Terraform and Auth Google Cloud
        uses: caddijp/gh-actions/terraform/setup_terraform@v0.21.0
        with:
          workload_identity_provider: ${{ vars.GCP_WI_PROVIDER }}
          service_account: ${{ vars.GCP_WI_SERVICE_ACCOUNT }}
          working_directory: ./terraform
          terraform_version: 1.4.6

そこで登場するのがregex managerです。対象ファイルと正規表現をもとに更新すべき対象を絞り込みし、依存するバージョンの公開先を指定することで一緒に更新してくれるようになります。 リスト7の設定は、キャディで実際に利用している共通定義の一部を抜粋したものです。

▼リスト7 独自の更新ルールを指示する設定

{
  "regexManagers": [
    {
      "fileMatch": [                                                                            // ①
        "^\\.github/workflows/.*\\.ya?ml$",
        "^\\.circleci/config\\.ya?ml$"
      ],
      "matchStrings": [                                                                       // ②
        "terraform_version: +['\"]?(?<currentValue>[^'\" \\n]+?)['\"]?\\n"  // ③
      ],
      "depNameTemplate": "hashicorp/terraform",                              // ④
      "datasourceTemplate": "github-releases",                                   // ⑤
      "extractVersionTemplate": "^v(?<version>.*)$"                             // ⑥
    }
  ]
}

まず、Renovateの更新対象となるファイルを①fileMatchで指定します。キャディではGitHubActionsのほか、CircleCIも利用しているので、両方を指定しています。次の②matchStringsでは正規表現を指定し、fileMatchで指定したファイルの中からマッチするものを探します。③がRenovate中で特殊に扱われているキャプチャグループ名です。terraform_version: 1.4.6という表記のほかにもterraform_version: '1.4.6'のような表記、terraform_version: 1.4.6 # comment のような表記のブレも吸収できるようにしています。

次の④⑤⑥は、データソースの設定です。④depNameTemplateと⑤datasourceTemplate により、TerraformのGitHub Release14の情報をもとに最新バージョンを取得します。最後の⑥extractVersionTemplateは、バージョンの表現を指定しています。Terraformのバージョンはv1.4.6のように先頭がvから始まるタグの命名ルールです。ですが、キャディではworkflow中のバージョンではvを除いているため、記述方法に合わせるように先頭のvを除外しています。ほかにも特殊なキャプチャグループ名がありますので、興味がある方はregex managerのドキュメント15を参照してください。

Renovateの更新運用の工夫

Renovateが自動的にPRを作成してくれるとはいえ、リポジトリ数が増えると差分を確認しながらマージするだけでも一苦労です。依存関係の更新が形骸化しないように、Platformグループ設立から2年間試行錯誤してきました。ここからは筆者らが現在行っている運用の一部を紹介します。

依存関係更新の運用

Renovateの更新PRが溜まってしまうことを防ぎつつも、依存関係を更新していくには、習慣化するのが一番です。そこで筆者らは毎週1回30分カレンダーにRenovate用の予定を登録しました。この時間内は必ず依存関係を更新するルールにしています。 また、作業開始前にRenovateによるPRがあるリポジトリのURLをSlackに通知しています。このしくみを作ることにより、対象PRを探しに行く手間をなくすようにしました。図3のようにリポジトリ単位でリポジトリのURLを投稿されるため、それぞれにリアクションができるようになります。筆者らは作業開始するリポジトリに対して「やります」のリアクションをしながら分担して作業を進めています。

▼図3 Slackを利用した運用

CIのみで利用するツールのpatch、minorは極力automergeする

CIのみで利用するテスト、Lint、静的解析に関連するツールを自動更新しても大きく壊れることがなかったため、極力automergeを利用するようにしています。 ただし、CDでも利用しているツールは自動でデプロイされると影響が大きく困るため、automergeの対象から外しています。

Renovate経由で作られたPRの通知を削減

筆者らはSlackのGitHub Appを経由して、担当するリポジトリのPRを定期的にSlackに通知し、PRマージまでのリードタイムを短くする取り組みをしています。この通知設定にて、renovateのラベルが付いているPRを除外することで、Renovateによる通知疲れを低減させています。 Platformグループは横断組織であることから、認知負荷が高くなりがちなため、日ごろから通知を減らす努力をしています。

おわりに

今回は依存関係の更新が必要な背景、Renovateの解説、キャディでの取り組みについて紹介しました。連載の流れから、TerraformとRenovateの組み合わせを中心に紹介をしてきましたが、今回紹介したものはアプリケーション開発でも同様に使えるものばかりです。みなさんの開発においても、依存関係の更新が楽になることを願っています。来月はArgo CDを利用したKubernetesのCDについて、キャディの事例をまじえながら紹介する予定です。