ENECHANGEの白坂です。普段はバックエンド開発をメインでしています。
今回の記事では、ライブラリアップデートの具体的な実施手順と、継続的にアップデートを実施していくための工夫を紹介します。
ライブラリアップデートはなぜ必要なのか?
ライブラリアップデートが必要な理由
ライブラリアップデートってついつい後回しになってしまいますよね。
私たちのチームのプロダクトは運用が始まって10年程なのですが、ライブラリアップデートは後回しにされがちで、普段開発していても、「ライブラリが古すぎてある日急にコードが動かなくなる」ということが定期的に起こっていました。
こうした状況をなんとかして変えたいという思いがあり、まずはライブラリアップデートがなぜ必要なのかを言語化してみました。
- 開発が止まるリスクを避けるため
- 脆弱性に対応するため
- 迅速に脆弱性に対応できるようにするため
- ライブラリの新機能を使うため
1. 開発が止まるリスクを避けるため
ライブラリが古いままだと、開発が想定外のトラブルで停止するリスクがあります。
実際に私たちのチームで起こっていたので、体感としてすぐ理解できました。
具体的には「昨日まで動いていたコードが動かなくなった」、「新しいライブラリを追加したくてもできない」等です。
新機能を開発するために工数を見積もっても、このような事態が起こると予定工数やスケジュールが崩れます。
そのため、工数見積もりやスケジュールの精度を上げたり、開発者体験向上のためにも、別工数でライブラリアップデートを定期的に実施して、開発が止まるリスクを避けるべきだと思いました。
2. 脆弱性に対応するため
ライブラリは常に新しい脆弱性が発見されています。
そのまま放置すれば、セキュリティリスクが高まり、システムが攻撃される可能性が大きくなります。
私たちのチームでは、ライブラリの脆弱性管理にDependabotを使っているのですが、結構な頻度で脆弱性の通知が発生するので、定期的なライブラリアップデートがいかに必要か実感しています。
3. 迅速に脆弱性に対応できるようにするため
2だけだと、「脆弱性対応が必要なライブラリだけアップデートすればいい」という考えになりがちですが、そうではありません。
他のライブラリが古いままだと、重大な脆弱性を修正したバージョンに依存関係が対応できず、結果として脆弱性対応が遅れることがあります。
そのため全てのライブラリを定期的に最新に近づける必要があります。
4. ライブラリの新機能を使うため
ライブラリの新しいバージョンには、新機能や改善が含まれています。
これらを活用することで、より効率的で保守性の高いコードを書けるようになります。
場合によっては、パフォーマンスの向上も期待できます。 zenn.dev
脆弱性対応が遅れると発生するリスク
重大な脆弱性対応が遅れることがどのくらいリスクなのか、実はあまりよく分かっていないので調べてみました。
過去には下記のようなインシデントが起こっています。
Equifax社のデータ漏洩(2017年)
米国の信用情報会社Equifaxは、Apache Strutsの脆弱性(CVE-2017-5638)を悪用され、約1.5億件の個人情報が漏洩しました。
原因は、サードパーティライブラリであるApache Strutsの脆弱性が公表されていたにもかかわらず、Equifaxが人為的ミスでパッチバージョンを適用しなかったためです。
このインシデントによって、多額の補償金が発生し、株価も暴落しました。
Log4Shell(2021年)
Javaのロギングライブラリ「Log4j」における重大な脆弱性(CVE-2021-44228)が発見され、世界中の多くのシステムやサービスに影響を与えました。
ライブラリアップデートどうやって進める?
上記理由をメンバーに説明し、これまで後回しにされてきたライブラリアップデートに着手することができました。
以下に具体的な実施手順をまとめます。
実施手順
私たちのプロダクトはPythonコードで書かれており、poetryを使っています。
そのため、poetryでの具体的な手順をまとめます。
- 全てのtestをpassさせて、warningsを0に近づける
- 使っていないライブラリの削除
- 使っていないコードと関連ライブラリの削除
- 最も古い&影響範囲の狭いライブラリのバージョンアップ
- その他のライブラリのバージョンアップ
- poetry update
1. 全てのtestをpassさせて、warningsを0に近づける
現状のプロダクトだと、testが全てpassせず、warningsが数万件出ている状態だったので、いったんこれらを解消しました。
failedやwarningsが0に近ければ近いほど、ライブラリアップデートの影響を早期に発見しやすくなります。
2. 使っていないライブラリの削除
poetryファイルに記載されているものの、コード上では使用されていないライブラリがあったため削除しました。
コード上で使われていないことを確認するために deptry が便利です。
poetry add deptry poetry run deptry . pyproject.toml: DEP002 'simplejson' defined as a dependency but not used in the codebase pyproject.toml: DEP002 'ipython' defined as a dependency but not used in the codebase pyproject.toml: DEP002 'deptry' defined as a dependency but not used in the codebase
こんな感じでコード上で使われていないライブラリを一発で洗い出してくれます。
※コード上では使われていなくても、デバッグ用などで使われている場合もあるので、ライブラリを実際に削除する際は慎重に確認しましょう。
3. 使っていないコードと関連ライブラリの削除
コード上ではライブラリが使われていても、そもそもそのコード自体が使われていないということがあったので、そういったコードやライブラリもこれを機に削除しました。
4. 最も古い&影響範囲の狭いライブラリのバージョンアップ
いまだにライブラリのアップデートができていませんが、ようやくここでライブラリのアップデートです。
アップデートする際の順番は、バージョンが最も古いもの、かつ、影響範囲の狭い(使われている箇所が少ない)ライブラリを優先すべきだと思いました。
バージョンが新しいライブラリから先に上げようとすると、古いライブラリとの依存関係がなくてエラーになることが多かったためです。
また、使われている箇所が少ないライブラリは、3に繋がる可能性が高かったため優先しました。
アップデートの仕方は、上げたいライブラリを最新バージョンに書き換えて、
poetry update ライブラリ名
で、一個ずつ手動で上げていきました。
その後に下記を確認しました。
- テストが全てpassすること
- warningsが出ていないこと
- リリースノートに記載された破壊的な変更点に影響ありそうなコードがないこと
5. その他のライブラリのバージョンアップ
4と同様の手順で全てのライブラリのバージョンを上げていきます。
pandasやnumpyのアップデートに特に苦労したので、今後は高頻度でアップデートをしていきたいと思いました。
6. poetry update
最後に、依存関係全体を最新の状態にします。
poetry update
継続的に実施するための取り組み
ライブラリアップデートは、1回実施すれば終わりではなく、継続的に実施していく必要があります。
しかし、仕組みがないと継続的に実施することは難しいです。
そのため、下記の取り組みを行いました。
1. タスクと工数を可視化する
今回ほぼ全てのライブラリアップデートを実施したので、定期的に実施する場合にどのくらいの工数が必要なのかを可視化することができました。
また、poetryファイルやnpmファイルなど、ライブラリアップデートが必要なものをドキュメントにまとめることで、タスクの可視化もできました。
2. Dependabotの導入
Dependabotを導入することで、ライブラリアップデートのタスクの一部を自動化することができました。
以下が自動化したタスクです。
- ライブラリの脆弱性検知の自動化
- ライブラリの脆弱性アップデートPRの自動作成
- ライブラリの脆弱性アップデートPRがパッチバージョンだった場合にオートマージする
詳細は長くなるので次の章でまとめます。
Dependabotでライブラリアップデートを一部自動化しよう
Dependabotとは?
Dependabotとは、GitHubが提供しているサービスで、ライブラリアップデートを一部自動化することができます。
機能は下記3つです。
1. Dependabot alerts
GitHubリポジトリのライブラリに脆弱性が発見された場合、アラート通知を行います
2. Dependabot security updates
GitHubリポジトリのライブラリに脆弱性が発見された場合、脆弱性に対応したバージョンに修正するPRを自動作成します
3. Dependabot version updates
GitHubリポジトリのライブラリが最新でない場合、最新のバージョンに修正するPRを自動作成します
設定方法
どの機能も無料で使えますが、導入するには設定が必要です。
GitHubのSettingsタブにあるCode securityで機能をオンにできます。 デフォルトは全てオフになっていますが、ボタンを押すだけで簡単に設定できます。
ライブラリの脆弱性アップデートPRがパッチバージョンだった場合にオートマージする
私たちのチームでは、1のDependabot alertsと、2のDependabot security updatesを取り入れることにしました。
3のDependabot version updatesを取り入れなかった理由は、PRが溢れて管理コストが現状より増大すると考えたからです。
その代わり、ライブラリ全体のバージョンアップは定期的に手動で実施する予定です。
1と2を取り入れたことで、今まで実施されていなかった脆弱性の検知を自動化することができました。
また、脆弱性のバージョンアップについてはDependabotがPRを自動作成してくれます。
さらに、もっと自動化できないかと思っていたところ、下記の記事を見つけました。
こちらの記事内のコードを参考に、以下の条件を満たした場合、GitHubActionsを使ってDependabotが作成したPRをオートマージするようにしました。
- CIのテストが全てpassしていること。
- パッチバージョンの更新であること(マイナー・メジャーバージョンは対象外)。
マイナーバージョンやメジャーバージョンは、Dependabotがやってくれない破壊的な変更点のコード修正を別途手動で実施する必要があるため、オートマージにするのは危険そうでした。
Dependabpotを使ってライブラリアップデートを全て自動化することは現状難しそうですが、一部を自動化することは工夫次第でいろいろできそうです。
最後に
ライブラリアップデートを進めるなかで、普段から不要なコードを削除することの重要性を改めて実感しました。