泥沼のタイムアウトと「見えない」ボトルネック
「キャッシュを入れたのに、なぜ速くならないのか?」――多くのエンジニアが一度は直面する、この絶望的な問い。株式会社イエソドのバックエンドエンジニアが直面した事象は、まさにCI/CDの現場における典型的な「負のループ」でした。OWASP Dependency-Checkを用いた脆弱性スキャンが、55分という長時間枠を確保してもなおタイムアウトで落ちる。この状況は、単なる設定ミスではなく、CIの実行環境と外部API、そしてキャッシュ戦略が複雑に絡み合った「技術的負債」の顕在化に他なりません。直近100回の実行のうち65回が打ち切りという事実は、もはや「不安定なジョブ」という言葉では片付けられない、システムとしての致命的な欠陥です。
筆者が最初に行ったのは、体感ではなく「数字」による現状把握でした。成功したわずか2回の実行時間は32分と34分。タイムアウト設定が35分であったことを考えれば、これは「運が良ければ通る」という極めて危険な状態です。ここで重要なのは、原因を単一の要素に求めず、多層的に分解した点です。脆弱性データベースの取得が毎回フルダウンロードされていたこと、NVD APIのレート制限に抵触していたこと、そしてGitHub Actionsのキャッシュ枠が他のテストジョブによって圧迫されていたこと。これらが複合的に作用し、スキャンを「終わらないタスク」へと変貌させていたのです。
我々エンジニアが肝に銘じるべきは、CIの実行環境は「クリーンな状態から毎回構築される」という前提を過信してはならないという点です。特に外部データベースを伴うスキャン処理において、キャッシュの保存条件を「ジョブの成功」に依存させる設計は、失敗時にキャッシュが育たないというデッドロックを招きます。この「成功しないとキャッシュが保存されない」という鶏と卵のジレンマを、保存と復元を分離し、キャンセル時以外は保存を試みるという戦略で突破した点は、実務における非常に鋭い洞察です。
キャッシュ戦略の再構築とAPIの最適化
キャッシュを導入しても効果が出ない場合、多くのエンジニアは「キャッシュのキーが間違っている」と考えがちですが、真の敵は「キャッシュの生存期間」と「リポジトリのキャッシュ枠」にありました。GitHub Actionsのキャッシュは、リポジトリ単位で10GBという制限があり、それを超えると古いものから自動的に退避されます。テストジョブが生成する巨大なキャッシュ群の中で、脆弱性データベースのキャッシュは「1日1回しか更新されない」という特性上、最も優先順位が低く、真っ先に押し出される運命にあったのです。この「キャッシュが消える」という不可視の事象を、ログと実行履歴から論理的に導き出したプロセスは、まさにシニアエンジニアの矜持と言えます。
また、NVD APIキーの活用についても、単なる「レート制限の緩和」以上の意味があります。キーなしの8秒間隔と、キーありの3.5秒間隔。この差は単なる速度向上ではなく、NVD側の推奨する「差分取得」という作法への適応です。APIキーを環境変数として適切に管理し、未設定時でもフォールバックを設けるという実装は、CIの堅牢性を高めるための必須要件です。さらに、Gradleデーモンのヒープ不足という、一見するとスキャンとは無関係に思えるメモリ問題が、実は36万件を超えるCVEデータのH2データベースへの書き込みという「重い処理」に起因していたという事実は、JVMベースのツールを扱う際の教訓として深く刻むべきでしょう。
| 原因 | 対策 |
|---|---|
| 脆弱性DBがキャッシュ対象外 | キャッシュ保存・復元ステップの分離と永続化 |
| NVD APIのレート制限 | APIキーの取得と設定によるリクエスト間隔の最適化 |
| キャッシュ枠の枯渇 | キャッシュ上限の引き上げと管理 |
| Gradleデーモンのヒープ不足 | ランナーの増強とJVM引数の最適化 |
これらの対策を講じた結果、スキャン時間は2分まで短縮されました。これは単なる高速化ではなく、CIの信頼性を回復し、開発者が「脆弱性スキャンが落ちるから」という理由でデプロイを躊躇する心理的障壁を取り除いたことを意味します。技術的な最適化は、常に開発者の生産性と直結しているのです。
自動化の罠とエンジニアが問うべき本質
今回の事例で最も注目すべきは、技術的な解決策そのものよりも、「なぜこれまで放置されていたのか」という問いです。多くの現場では、CIが落ちるたびに「再実行」ボタンを押すことが日常化し、それが「不安定なジョブ」というラベルで正当化されています。しかし、その裏側ではNVDへの無駄なリクエストが繰り返され、リソースが浪費され、何より開発者の貴重な集中力が削がれています。CIのタイムアウトを「環境のせい」にするのではなく、自らのコードと設定の不備として捉え直す姿勢こそが、真のエンジニアリングです。
読者であるあなたに問いたい。あなたのプロジェクトのCIは、本当に「信頼できる」ものですか?「たまに落ちる」という事象を、あなたは「運」で片付けていませんか?もしそうなら、それは技術的な課題ではなく、あなたのキャリアにおける「改善の機会」をドブに捨てているのと同じです。明日から取るべきアクションは明確です。まずはCIの実行ログを詳細に分析し、どのステップが「なぜ」時間を食っているのか、そのボトルネックを数値化することから始めてください。キャッシュのヒット率、APIのレスポンス時間、ランナーのメモリ使用量。これらを可視化するだけで、解決すべき課題は自ずと浮き彫りになります。
最後に、脆弱性検査という文脈において「古いキャッシュで検査が通ってしまう」というリスクをどう管理するかという視点は極めて重要です。デフォルト設定に依存するだけでなく、更新失敗を確実にビルドエラーとして検知する仕組みを維持すること。この「壊れるなら正しく壊れる」という設計思想こそが、セキュリティツールを運用する上での生命線です。自動化は魔法ではありません。それは、あなたが書いたコードと同じように、メンテナンスされ、監視され、進化し続けるべき「プロダクト」なのです。あなたのCIは、今日も正しく動いていますか?それとも、再実行ボタンのクリックを待っていますか?


コメント