KENTEM TechBlog

建設業のDXを実現するKENTEMの技術ブログです。

Azure SRE Agent でアラートから自動 Issues 投稿を試してみた

Azure SRE Agent を使ってサーバーのエラーや遅延を AI に自動分析させてみました。

Azure SRE Agent に「アラート検知 → ログとソースコードで原因分析 → GitHub Issues 起票」まで任せる

マイクロサービス構成の Web アプリでエラーが出ると、いつも同じことをやっています。アラートを見て、Application Insights を開いて、例外のスタックトレースからソースコードを追い、分かったことを Issue に書く。この一連を Azure SRE Agent に任せ、人が操作しないまま Issue が起票されるところまで動かしました。構成と手順、そしてハマった点をまとめます。

サービス構成

2026 年 8 月時点の情報です(Azure SRE Agent は 2026 年 3 月 10 日に GA)。ポータルの UI 名称は変わりやすいので、画面遷移は各節のリンクを正としてください。

全体の流れ

# 起点 何が起きるか
① public-app の App Service エラー発生。テレメトリが Application Insights に記録され、メトリックのしきい値超過でアラートが発火する
② Azure SRE Agent Azure Monitor をポーリングし、対応プランの条件に一致したアラートを収集する
③ Azure SRE Agent マネージド ID(Reader)で両リソースグループのログ・例外・リソース構成を取得し、両リポジトリのソースコードと突き合わせて原因を分析する
④ GitHub 分析結果を Issue として起票する

ポイントは ② がポーリング型であることです。アラート側にアクショングループや Webhook は要らず、エージェントのスキャナーが見に来ます。既存のアラートルールに手を入れずに済みます。

そして ③ では、複数のリソースグループと複数のリポジトリを 1 つのエージェントに横断させられます。マイクロサービスの障害は「呼び出す側のログ」と「呼ばれる側のコード」を往復しないと原因に届かないので、ここが効きます。

前提とする環境

この記事は、次の状態からスタートします。

対象 内容
アプリ App Service 上の Web アプリ(public-app)と、それが呼ぶ内部向けマイクロサービス(private-api)。リソースグループは別々
テレメトリ 両サービスに Application Insights を構成済みで、要求・依存関係・例外が流れている
アラート Web アプリの 5xx を拾う Azure Monitor のメトリックアラート(無ければ手順 3 で作ります)
ソースコード 2 つのサービスを GitHub の別リポジトリで管理している
Issue 起票先のリポジトリと、区別するためのラベル(sre-agent)

テレメトリとソースコードが両方揃っていることが肝です。エージェントは「実際に起きたエラーのテレメトリ」を起点に調査し、それを「リポジトリのコード」と突き合わせて原因をファイル・行番号まで特定します。テレメトリだけなら原因の推測まで、コードだけなら一般的なコードレビューまでで止まります。

手順

1. エージェントを作成する

sre.azure.com → Create agent。リソースグループはエージェント専用に新規作成します。エージェント自身の Application Insights が一緒に作られるので製品のテレメトリと混ざらないようにするためと、常時課金がエージェント単位なので1 つのエージェントで複数製品を見るほうが安いためです。つまりこのリソースグループは特定の製品ではなく、SRE Agent という基盤に属します。

作成する人には、エージェントのマネージド ID にロールを割り当てるための権限(Owner または User Access Administrator 相当)が必要です。リージョンは 2026 年 8 月時点で 18 か所(Japan East も対応済み)。監視対象は任意のリージョン・サブスクリプションでよいので、エージェントとアプリのリージョンを揃える必要はありません。

参考: エージェントを作成する / エージェントのアクセス許可 / サポートされているリージョン

2. 監視対象リソースグループを追加する

Settings → Managed resources で public-app と private-api の両方を追加します。ここがエージェントの権限そのものです。追加するとエージェントのマネージド ID に Azure RBAC ロールが割り当てられ、外すと失効します。ロールを個別に外すことはできず、リソースグループ単位でしか制御できません。

ロールの強さは Reader(読み取りと監視のみ)と Privileged(変更もできる)の 2 段階で、調査して Issue を書くだけなら Reader で足ります。実際に何が付いたかは、監視対象リソースグループのアクセス制御 (IAM) で確認できます。今回は Reader / Log Analytics Reader / Monitoring Reader がリソースグループに、アラートの Acknowledge・Close に使う Monitoring Contributor がサブスクリプションに付いていました。

3. メトリックアラートを用意する

App Service の Http 5xx に対する、ごく普通のメトリックアラートを 1 本作ります。

設定 値
対象(スコープ) public-app の App Service のみ(両方に張ると Issue が重複する。ハマりどころ #5)
シグナル / 条件 メトリック Http 5xx の合計が 0 より大きい(検証用に敏感にしている)
評価頻度 / 期間 1 分ごと / 直近 5 分
重大度 Sev2(Warning)
アクショングループ 不要

重大度は手順 7 のフィルターと必ず合わせてください。ズレているとアラートは発火するのに調査が始まりません。

4. Azure Monitor を接続する

Builder → Incident platform で Azure Monitor を選んで保存。認証はマネージド ID がそのまま使われるので資格情報の設定は不要です。数分でアラートが流れ始めます。流れてこないときは、マネージド ID にサブスクリプションスコープの Monitoring Contributor があるかを確認します。

参考: Azure Monitor アラート

5. ソースコードを接続する

Builder → Knowledge base → Add repository で両リポジトリを追加します。認証は OAuth か PAT(Personal Access Token)。企業プロキシが *.azuresre.ai を止めていると OAuth が Invalid state で失敗するので、そのときは PAT が確実です。これで、エラーをファイル・行番号に対応付けた原因分析ができるようになります。

参考: ソース コードを接続する

6. Issue を書く経路を用意する(=「書く」経路)

手順 5 の接続は読み取り専用で、Issue は書けません。 Builder → Connectors → Add connector で GitHub Connector、または MCP(Model Context Protocol)版の GitHub MCP コネクタを別途追加します。MCP 版なら https://api.githubcopilot.com/mcp/ に、Issues: Read and write を含む PAT を渡します。これが無いと、原因分析はできても起票が 401 Bad credentials で落ちます。

参考: GitHub コネクタ

7. インシデント対応プランを作る

Builder → Incident response plans。決めるのは「どのインシデントを拾うか(重大度・タイトル)」と「どこまで自律にやらせるか」の 2 つです。

モード 挙動
Review 調査はするが、Azure の書き込み操作の前に承認を求める。まずはこちら
Autonomous 承認なしで実行して結果を報告する

対応プラン自体にプロンプトを書く欄はありません。「どう調査して、何をするか」を決めているのは、担当サブエージェントに与えるプロンプトです。起票先・ラベル・出力言語を確実に効かせたいなら、Builder → Agent Canvas → Create → Custom Agent でカスタムサブエージェントを作り、その Instructions 欄にプロンプトを書いて、対応プランの担当をそれに差し替えます。

↓ その Instructions 欄に貼っているプロンプト(要約版。YAML 定義では system_prompt にあたる部分です)

SCOPE (strict): Your ONLY write action is creating a GitHub issue or commenting on an
existing one. Do NOT modify source code, create/merge pull requests, push commits, run
write commands, or restart/reconfigure any Azure resource.

OUTPUT LANGUAGE: Always write the entire issue (title and body) in Japanese.

On an HTTP 5xx incident:
1. Query Application Insights (AppRequests, AppDependencies, AppExceptions) to trace it.
2. Correlate the telemetry with the source code in the connected repositories and identify
   the root cause down to the file and line, including the code excerpt.
3. Create an issue summarizing the root cause, the excerpt, and a proposed fix.
   Always add the label "sre-agent".

Distinguish between what telemetry confirms and what you infer.
Do not assert unverified facts.

英語で書いているのは、モデルへの指示は英語のほうが安定するからです。そのぶん、Issue を日本語で書かせるために OUTPUT LANGUAGE の 1 行を入れています。差し替えを忘れると既定のサブエージェントが動き、起票先やラベルの指示は効きません。

参考: 実行モード / インシデント対応を自動化する

8. 動作確認

障害を注入して 5xx を出し、アラート発火 → 自動で調査開始 → 起票、を追います。実測でアラートから起票まで約 2 分でした。

起票された Issue(抜粋)

実際に自動起票された Issue です(検証環境に意図的に障害を注入して出したもので、本番の障害ではありません)。サービス名・パス・traceId をこの記事の構成に合わせて置き換え、長い節は省略しています。

見てほしいのは後半の 「問題箇所」と「コード抜粋」 です。ファイル名・行番号・そして実際のソースコードの引用は、テレメトリをいくら眺めても出てきません。GitHub のリポジトリを読んで、例外と突き合わせて初めて書ける部分です。

タイトル: GET /orders/{id} が HTTP 500 になる(private-api の creditLimit デシリアライズ失敗)

## 概要
2026-08-04 00:00-00:30 UTC に `public-app` で `GET /orders/{id}` の HTTP 500 が発生しました。
Application Insights の分散トレースでは、`private-api` が外部 API のレスポンスをデシリアライズ
する箇所で失敗し、その 500 が `public-app` まで連鎖していました。

## 影響
- アラート発火時刻: 2026-08-04 00:20:14 UTC
- `public-app` の失敗: 252 / 253 リクエスト(失敗率 99.6%)
- 代表 traceId: `6965d417…`

## 失敗経路
1. `public-app` の `GET /orders/{id}` が 500
2. 同一 trace で `private-api` への依存呼び出しが 500
3. `private-api` から外部 API への依存呼び出しは 200
4. その直後に `System.Text.Json.JsonException` / `System.FormatException` が発生

## 根本原因
`private-api` が外部 API レスポンスの `creditLimit` を `decimal` 前提でデシリアライズしており、
実際には `"上限なし"` という文字列が返ったため `JsonException` が発生して 500 になっています。

- メッセージ: `The JSON value could not be converted to DeptCustomer. Path: $.creditLimit`
- innermost: `System.FormatException: The JSON value is either too large or too small for a Decimal.`
- 生レスポンス: `{"customerId":"1001","customerName":"山田太郎","creditLimit":"上限なし"}`

## 問題箇所
- `private-api/Program.cs:70`
- `public-app/Program.cs:57`

### コード抜粋
`private-api/Program.cs:70`
```csharp
var customer = JsonSerializer.Deserialize<DeptCustomer>(raw, jsonOptions);
record DeptCustomer(string CustomerId, string CustomerName, decimal CreditLimit);
```

`public-app/Program.cs:57`
```csharp
var response = await client.GetAsync($"/internal/orders/{id}");
response.EnsureSuccessStatusCode();
```

## 提案する修正
1. `creditLimit` を文字列と数値の両方で受けられる DTO かカスタムコンバーターに変える
2. 契約逸脱を未処理の 500 にせず、制御されたエラーに変換する
3. 外部 API とのスキーマ契約テストを追加し、この形を回帰テストにする

この Issue の情報は、2 つの出どころに分かれています。

Issue のこの部分 どこから来たか
失敗率・traceId・失敗経路・例外メッセージ・生レスポンス Application Insights のテレメトリ
ファイル名・行番号・コード抜粋・提案する修正 GitHub のリポジトリ(手順 5 で繋いだソースコード)

つまり、コードを繋いでいなければ下の行は全部出てきません。「500 が出ている」「デシリアライズで失敗している」までは分かっても、decimal CreditLimit という宣言を指して「ここが "上限なし" を受けられない」とは書けない。逆にコードだけあってもテレメトリが無ければ、どの経路がいつ何回落ちたのかが分かりません。

さらに言えば、外部 API 側のテレメトリは存在しません。外部 API への呼び出しは 200 で返っていて、異常はレスポンスの中身だけです。それでも private-api の例外と生レスポンスとコードを突き合わせて、型の不一致を行番号まで当てています。

ただしこれは当たった回です。同じ構成の別の実行では、正確な分析の中に存在しないコードの断定が混ざっていました(ハマりどころ #6)。

ハマりどころ

1. 「読む」接続と「書く」接続は別(手順 6)。1 回 GitHub を認証すれば全部使える、ではありません。

2. Code Access が読むのはリモートのブランチ(既定 main)。 未 push のコードは見えません。さらに、未コミットのままビルド・デプロイすると、バイナリのデバッグ情報ファイル(PDB)がビルド時のコミットを指すため、あとで main にマージしてもファイルが見つからず、エージェントは逆コンパイルでコードを得ようとします。コード引用まで出したいなら、コミット済みのコードからデプロイします。

3. 既定の autonomy level は Autonomous。 しかもサブエージェントのツールを未選択にすると、本番操作やコード書き込み系まで含む全ツールを継承します。ツール選択は「追加」ではなく「置換」なので、必要なものだけチェックしてホワイトリストにするのが一番効きます。権限レベル Reader なら書き込みは 403 で止まるので、二重で守れます。

4. 重複起票の抑止は「記憶ベース」。 同じ原因と判断されると、既存 Issue をクローズしても削除しても新規作成されないことがあります。同一ルールの再発火も、再調査のクールダウンが効いて既存インシデントに統合されます。毎回起票させたいなら instructions で明示します。

5. アラートを呼び出す側と呼ばれる側の両方に張ると Issue が重複します。 インシデントが 2 つ立ち、同じエージェントが 2 回動くためです。入口のサービスだけに絞れば 1 障害 = 1 件。出力言語も instructions で固定しないと、日本語と英語が混ざります。

6. 原因分析はハルシネーションします。 行番号や例外連鎖の指摘が正確な Issue の中に、「main には修正済みの実装がある」という存在しないコードの断定が混ざっていました。エージェントの自己申告も、git や Azure と突き合わせると事実でないことがあります。自動起票された Issue は下書き扱いで、人のレビューを前提に。

コスト

エージェントは存在するだけで課金されます(4 AAU / agent-hour。停止しても止まらず、削除で止まります)。実働分はトークンベースで別途かかります。Settings → Agent consumption で月間 AAU 上限を入れておくと、暴走時の保険になります。

参考: 価格と課金

まとめ

  • 人の操作なしで、アラートから Issue 起票まで到達できた。リソースグループとリポジトリを両方繋げば、マイクロサービスをまたぐ原因分析も成立する。
  • 配線でつまずくのは「読む接続と書く接続が別」「Code Access はリモートのブランチを読む」の 2 点。
  • 自律化の前に、ツールのホワイトリスト化と権限レベル Reader。既定が Autonomous なので、何もしないと一番広い状態から始まる。
  • 出力は下書き扱い。それでも初動の調査を肩代わりしてくれる価値は大きい。

おわりに

KENTEMでは、様々な拠点でエンジニアを大募集しています! 建設×ITにご興味頂いた方は、是非下記のリンクからご応募ください。 recruit.kentem.jp career.kentem.jp