クラウドネイティブ会議で登壇しました

こんにちは。最近は知床で世界自然遺産になっていました、バックエンド開発部のP山です。

クラウドネイティブ会議とは

クラウドネイティブ会議は、CloudNative Days・Platform Engineering Kaigi・SRE Kaigiの3つのカンファレンスが合同で開催する大規模テックカンファレンスです。2026年は5月14日〜15日の2日間、名古屋の中日ホール&カンファレンスにて開催されました。クラウドネイティブ技術、プラットフォームエンジニアリング、SREの3つの領域が交わることで、分野横断の学びと交流が生まれる場を目指したイベントです。

このクラウドネイティブ会議にて、「継続的な負荷検証を目指して」というタイトルで登壇しました。本記事では、発表内容をベースに、AIを活用して負荷検証シナリオの作成・メンテナンスを自動化した取り組みについてご紹介します。

きっかけとなった障害

以前、私たちのサービスは約1時間にわたるサービスダウンを経験しました。

当時すでに負荷検証は実施していたのですが、エラーが出ていたのは負荷検証の対象に含まれていないエンドポイントでした。平常時はそれほど負荷のかからないエンドポイントだったのですが、あるタイミングで大量のアクセスが集中した結果、READクエリがプライマリDBに大量に発行されていたことや、DBトランザクション内で外部APIをコールしていたことが重なり、ロック時間が伸びてサービスがダウンしてしまいました。

この障害には2つの側面がありました。

1つ目は負荷検証の網羅性の問題です。負荷をかけていたエンドポイントは問題なく動作していましたが、対象外のエンドポイントが落とし穴になりました。「やっていた」と「すべてをカバーしていた」の間には大きな差があります。

2つ目は平常時には問題が表れないコードの問題です。レプリカに向けるべきクエリがプライマリに向いたままだったり、GORMのauto reloadにより大量のリレーションが読み込まれていたり、トランザクション内で外部APIを呼んでいたりと、平常時のリクエスト数では問題にならないものが「長いロック × 多数の同時リクエスト」の掛け算で一気に悪化する状況でした。

DBはパフォーマンスが悪化すると芋蔓式に連鎖する特性があります。ロック待ちが増えると接続プールが枯渇し、タイムアウトが多発し、あるラインを超えると一気に崩壊する。こうした問題は負荷検証をしない限り気づけない類のものでした。

継続的な負荷検証の難しさ

この障害から得た学びは明確で、「実際のトラフィックをもとに負荷シナリオを作り、継続的にメンテナンスできる仕組みを作る」ことが必要だということでした。

しかし、継続的に開発されているサービスにおいて、すべてのエンドポイントを網羅した負荷検証シナリオを人手でメンテナンスし続けることは現実的ではありません。新しいエンドポイントが追加されるたびにシナリオを書き、APIのパラメータが変わればシナリオを更新し、認証フローが変わればシナリオ全体を見直す。こうした作業が積み重なると、シナリオのメンテナンスは徐々に後回しになっていきます。

そこで、AIに「人間がやるのと同じ手順」でシナリオを作らせるというアプローチを取りました。

人間がシナリオを作るときの手順

まず、人間のエンジニアが負荷検証シナリオを新しく追加するときの手順を整理しました。

  1. Grafana LokiなどでAPIのアクセスログを確認し、どのエンドポイントがどのような順序・頻度で呼ばれているかを把握する
  2. そのパターンをもとにシナリオのコードを書く
  3. 開発環境でシナリオを実際に実行して動作確認する
  4. 失敗したらログやレスポンスを確認して修正する
  5. 安定して動くことを確認したら本番の負荷検証に組み込む

この手順は当たり前のものに見えますが、逆に言えば「この手順をAIにそのままやらせればよい」という発想が今回のアプローチの出発点です。

runnによるシナリオ実行

今回のシナリオ実行には、k1LoW氏が開発しているOSSのAPIシナリオテストツールrunnを使っています。シナリオをYAML(runbook)で記述し、HTTPリクエストを順番に実行できるのが特徴です。

desc: ログイン→リソース作成→IDを使って操作
steps:
  login:
    include: setup_user.yml        # 認証などの共通処理をinclude
  create_item:
    req:
      /v1/items:
        post:
          headers:
            Authorization: "Bearer {{ steps.login.token }}"
          body:
            application/json:
              name: "{{ vars.item_name }}"
    test: current.res.status == 201
  get_item:
    req:
      /v1/items/{{ steps.create_item.res.body.id }}:
        get:
          headers:
            Authorization: "Bearer {{ steps.login.token }}"
    test: current.res.status == 200

ステップ間でレスポンスの値を参照できるため、「ログイン→リソース作成→IDを使って操作」といった状態を持つAPIフローを自然に表現できます。

Claude Codeスキルによる実装

今回の仕組みはClaude Codeのスキル機能を使って実装しています。スキルとは、AIに特定のツールと手順書を与えて特定のタスクをこなせるようにする仕組みです。2つのスキルがそれぞれ人間の作業フローのフェーズを担う形になっています。

本番Loki
  ↓ MCPで直接クエリ
[extract-scenariosスキル]
  ログを分析してAPIパターンを発見
  ↓ 既存シナリオとの重複チェック
  ↓ 人間が候補を承認
  runbook YAML を生成
  ↓
[run-stressスキル]
  dev環境で3回連続実行
  失敗→ログ確認→修正→再実行
  ↓
  dev Lokiでリクエスト到達を確認
  ↓
  stress.yml に登録
  ↓
継続的な負荷検証へ

extract-scenariosスキル: ログを読んでシナリオを書く

このスキルでは、AIにGrafana LokiへのアクセスをMCP経由で与えています。AIは本番環境のLokiに直接クエリを投げて実際のアクセスログを取得し、ユーザーがどのAPIをどんな順番で呼んでいるかを分析します。

# 外部からの着信リクエストだけを取り出す
{app="<your-app>", container="istio-proxy"} |= `inbound` | json | path != ""

# 特定ユーザーの一連のリクエストを時系列で追跡
{app="<your-app>", container="istio-proxy"} |= `inbound` |= `:<user_id>:` | json

AIはこうしたクエリを自分で組み立てて投げ、エンドポイントのパターン・呼び出し順・頻度を把握します。発見したパターンをもとにrunbook YAMLを生成し、既存シナリオとの重複チェックも行って、新規性のあるシナリオ候補のみを人間に提案します。

run-stressスキル: dev環境で実際に実行して確認する

シナリオを書いたら終わりではありません。このスキルでは、AIがdev環境でrunnを使って実際にシナリオを動かします。

重要なのは、3回連続で成功するまで繰り返すという規約です。タイミング依存のバグは1回の実行では見つからないことが多い。失敗した場合はレスポンスのHTTPステータスコードとボディを確認し、ポーリングの間隔や回数を調整するなどして修正を加え、再度検証します。

さらに、dev環境のLokiにもクエリを投げて、リクエストがAPIサーバに実際に届いているかを確認するステップも含まれています。シナリオが動いているように見えてもリクエストが届いていないというケースを防ぐためです。

テスト環境の設計

シナリオの品質だけでなく、テスト環境の設計も負荷検証の精度に直結します。

まず、テストに使うデータは本番相当の量を用意しています。データ量が少ないとインデックスが効きすぎて本番では起きないほど速く応答してしまったり、ロック競合が再現しないといった問題が生じます。

次に、テスト実行時の外部サービスへの通信はすべてモックしています。負荷検証の目的はあくまでも自分たちのサービスのボトルネックを見つけることなので、外部APIの応答速度に結果が左右されないよう、計測対象を自分たちのコードに絞っています。

また、テストに使うデータは配車数の多いものを選んでいます。配車数が多いということは関連するデータのリレーションが多く、クエリの負荷が高くなりやすい。意図的に「重い」データを使うことで、平常時には見えないN+1や遅いクエリが顕在化しやすくなります。このデータの選択もDBから自動的に取得する仕組みにしており、常に最新の本番相当データを使って検証できる状態を保っています。

運用してみてわかったこと

カバレッジの向上

仕組みを導入する前は、明示的にシナリオを書いていたエンドポイントしか検証できていませんでした。導入後は実トラフィックベースでシナリオが追加されるため、開発者が見落としていたエンドポイントにもカバレッジが広がりました。

特に、「平常時は負荷が低いが大量アクセスで問題が顕在化するエンドポイント」はまさに今回の障害の教訓そのものです。AIがLokiを眺めて「このエンドポイント、意外とアクセスがあるな」と気づくことで、人間の思い込みによる見落としを防げるようになりました。

スキルという設計の重要性

単にAIにシナリオ生成を依頼するだけでは、品質のばらつきが大きく実用に耐えませんでした。スキルとして手順書とツールをセットで定義することで、AIが毎回同じ品質で作業できるようになりました。

特に「3回連続成功」という規約をスキルに組み込んだことは大きかったです。この規約がなければ、AIは1回動いただけで完了と判断してしまいます。人間が経験則として知っている「1回成功しても信用するな」というノウハウをスキルに落とし込むことで、AIの出力品質を担保できました。

人間のレビューが残る部分

完全自動化を目指したわけではなく、AIが提案したシナリオ候補を人間が承認するステップを意図的に残しています。AIが「動く」シナリオを作ることはできますが、「ビジネス的に意味のある負荷パターンになっているか」という判断は人間が行います。AIと人間の役割分担を明確にしたことで、全体として品質と効率のバランスが取れました。

発表を終えて

セッション中のXポストや懇親会では、「うちでもトランザクションの長期化でサイトダウンしたことがある」「負荷検証で普段の◯倍のトラフィックはかけるけど、パターンの網羅性が漏れがち」といった声をいただきました。「負荷検証をやっていた」と「必要なパターンをカバーしていた」の間にある差は、多くの現場で共通の課題なのだと改めて感じました。

一方で「新規開発に追われてそれどころじゃない」という声もあり、それはまさにこの仕組みを作った動機そのものです。人手では回らない部分をAIに任せることで、開発を止めずに負荷検証のカバレッジを広げていけるのがこのアプローチの強みだと思っています。

最後に

AIに何かを任せるときは、人間がその作業をどうやっているかを分解し、その手順とツールをそのままAIに渡すことが重要だと感じています。「AIが自動生成する」という発想より、「AIが人間の代わりに手順を踏む」という発想のほうが、実用的な品質につながりました。

今回の障害で得た「やっていた」と「すべてをカバーしていた」は別物だという教訓を、仕組みで解決できたことが一番の学びです。

今回のセッションにお越しいただいた方、会場でお話しさせていただいた方、ありがとうございました。また、CloudNative Days・Platform Engineering Kaigi・SRE Kaigiの3カンファレンス合同という大規模なイベントを実現してくださった運営スタッフの皆さんに感謝いたします。素晴らしい会場と運営のおかげで、多くの方と交流し、刺激をいただくことができました。

トークの詳細についてはスライドもあわせてご覧ください。