第21章:ローカルでの検証(負荷・失敗・遅延)🧪📈✨
この章は「メトリクスって…ほんとに“反応する”の?」を、手で壊して👊→目で見て👀→納得する😳ための回です!
今日のゴール🎯💖
次の3つを “自分の目で” 確認できるようになります👇
- 負荷(Traffic)が増えたとき:リクエスト数が上がる📈
- 失敗(Errors)が増えたとき:成功率が落ちる💥
- 遅延(Duration)が増えたとき:P90/P99が伸びる🐢➡️🚀
そして最後に、**「どのグラフを見ればいいか」**が迷子にならない型も作ります🧭✨
まずは可視化の“観測窓”を用意しよ🪟👀
ローカル検証は、最短でいくならこの2択がおすすめ!
A案:Aspire Dashboard(いちばん楽)🪄✨
Dockerで起動して、OTLPで送ったログ/メトリクス/トレースを一気に見れるやつです。 開発者向けで、セットアップ少なめなのが強い💪✨ (Microsoft Learn)
起動(PowerShell)👇(そのままコピペOK)
docker run --rm -it `
-p 18888:18888 `
-p 4317:18889 `
--name aspire-dashboard `
mcr.microsoft.com/dotnet/aspire-dashboard:latest
- 起動ログにログイン用トークンが出ます(毎回変わるよ)🔑
- URLの
0.0.0.0をlocalhostにして開けばOK、みたいな流れです (Microsoft Learn)
Aspire Dashboardは ヒストグラムを P50/P90/P99 で描いてくれるし、外れ値に「点(exemplar)」が出て クリックでトレースに飛べるのが神✨ (Microsoft Learn)
B案:Prometheus(収集)+Grafana(可視化)🧰📊
王道構成。後で運用に寄せるならこっちが筋良し。
.NET 側は Prometheus Exporterで /metrics を生やすのが基本ムーブです (Microsoft Learn)
(この章では、A案で進めつつ、B案の差分も最後に置きます🫶)
題材アプリに「反応するメトリクス」を入れる🧩📈
ここでは、わざと壊せるように、1つの /work で全部起こせる設計にします😈✨
ms:遅延を増やす(例:500ms)🐢failRate:失敗率を増やす(例:0.2で20%失敗)💥
1) NuGet(例)📦
Aspire Dashboard(OTLP)に投げるなら、OTLP Exporter入りが便利です (Microsoft Learn)
2) Program.cs(サンプル)🧑💻✨
using System.Diagnostics;
using System.Diagnostics.Metrics;
using OpenTelemetry.Metrics;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
var builder = WebApplication.CreateBuilder(args);
// OpenTelemetry(メトリクス+トレース)をまとめて設定
builder.Services.AddOpenTelemetry()
.ConfigureResource(r => r.AddService("DemoApi"))
.WithMetrics(m =>
{
m.AddAspNetCoreInstrumentation();
m.AddHttpClientInstrumentation();
// 自作メトリクス(Meter)
m.AddMeter(DemoMetrics.MeterName);
// ASP.NET Core 側のメトリクスも拾う(例)
m.AddMeter("Microsoft.AspNetCore.Hosting");
m.AddMeter("Microsoft.AspNetCore.Server.Kestrel"); // .NETの例でも使われます
})
.WithTracing(t =>
{
t.AddAspNetCoreInstrumentation();
t.AddHttpClientInstrumentation();
});
// OTLP exporter は “環境変数/設定” で指定した時だけON(例に合わせた形)
if (!string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]))
{
builder.Services.AddOpenTelemetry().UseOtlpExporter();
}
var app = builder.Build();
app.MapGet("/work", async (int? ms, double? failRate) =>
{
return await DemoMetrics.WorkAsync(ms ?? 30, failRate ?? 0);
});
app.Run();
static class DemoMetrics
{
public const string MeterName = "DemoApi";
private static readonly Meter Meter = new(MeterName);
private static readonly Counter<long> Requests =
Meter.CreateCounter<long>("work_requests", description: "Requests for /work");
private static readonly Counter<long> Failures =
Meter.CreateCounter<long>("work_failures", description: "Failures for /work");
private static readonly Histogram<double> DurationMs =
Meter.CreateHistogram<double>("work_duration_ms", unit: "ms", description: "Duration of /work");
public static async Task<IResult> WorkAsync(int ms, double failRate)
{
var start = Stopwatch.GetTimestamp();
Requests.Add(1);
await Task.Delay(ms);
var isFail = Random.Shared.NextDouble() < failRate;
if (isFail)
{
Failures.Add(1);
DurationMs.Record(Stopwatch.GetElapsedTime(start).TotalMilliseconds);
return Results.Problem("boom", statusCode: 500);
}
DurationMs.Record(Stopwatch.GetElapsedTime(start).TotalMilliseconds);
return Results.Ok(new { ok = true, ms, failRate });
}
}
3) OTLP の行き先を設定🧷
Aspire Dashboard コンテナを使う場合、例では OTEL_EXPORTER_OTLP_ENDPOINT を http://localhost:4317 にします (Microsoft Learn)
{
"OTEL_EXPORTER_OTLP_ENDPOINT": "http://localhost:4317",
"OTEL_SERVICE_NAME": "DemoApi"
}
負荷をかける道具:k6 を入れる🧨🧪

Windowsなら、winget か choco でOKです✨ (Grafana Labs)
winget install k6 --source winget
## または
choco install k6
3つの検証シナリオ🔥(この章のメイン!)
シナリオ0:ベースライン(平常運転)🌤️
まず「何もしてない時」の形を見ます👀
failRate=0、ms=30くらいでOK
k6スクリプト(baseline.js)👇
import http from "k6/http";
import { sleep } from "k6";
export const options = { vus: 5, duration: "20s" };
export default function () {
http.get("http://localhost:5000/work?ms=30&failRate=0");
sleep(0.1);
}
実行👇
k6 run baseline.js
見るところ(Aspire Dashboard想定)👀✨
work_requests:右肩上がり📈work_failures:ほぼ0work_duration_ms:P50/P90/P99 が「30ms付近」に集まる(だいたい)
Aspire Dashboardはヒストグラムを P50/P90/P99 で描いてくれます (Microsoft Learn)
シナリオ1:負荷(Traffic)を増やす📈💨
「ユーザー増えた!」を再現✨
- VU(仮想ユーザー)を増やすだけでOK!
// load.js
import http from "k6/http";
export const options = { vus: 30, duration: "20s" };
export default function () {
http.get("http://localhost:5000/work?ms=30&failRate=0");
}
k6 run load.js
期待する見え方👀✨
work_requests:増える📈📈work_failures:0のまま😊work_duration_ms:もしCPU/スレッドが詰まると、P90/P99がじわっと上がる🐢(上がらないなら健康👍)
📌ここで覚える“型”🧭
- Trafficが上がったのに Duration が上がらない → まだ余裕あり💪
- Trafficが上がったら Duration も上がる → 飽和近いかも⚠️(次の章の話につながる✨)
シナリオ2:失敗(Errors)を増やす💥🚨
「外部API死んだ」「DB接続落ちた」みたいなのを再現😈
// errors.js
import http from "k6/http";
export const options = { vus: 10, duration: "20s" };
export default function () {
http.get("http://localhost:5000/work?ms=30&failRate=0.3"); // 30%失敗
}
k6 run errors.js
期待する見え方👀✨
work_failures:増える📈💥work_requests:増える(失敗でもリクエスト自体は来てる)work_duration_ms:大きくは変わらないことも多い(失敗が即死なら)- もし「失敗=リトライ地獄」だと Duration も上がることあり🔥
💡ポイント:Errorsは「率」で見ると気持ちいい
- 例:
Failures / Requestsを自分で計算して「失敗率」を作ると、アラート設計に直結します🚨✨
シナリオ3:遅延(Duration)を増やす🐢⏱️
「なんか遅い…」を再現😇(これが一番“体感”しやすい!)
// latency.js
import http from "k6/http";
export const options = { vus: 10, duration: "20s" };
export default function () {
http.get("http://localhost:5000/work?ms=500&failRate=0");
}
k6 run latency.js
期待する見え方👀✨
work_duration_ms:P90/P99がグワッと伸びる🐢➡️🚀work_requests:同じVUでも「処理が遅いと回転数が落ちる」ので、増えにくいこともある- Aspire Dashboardなら、ヒストグラムがP50/P90/P99で見えて分かりやすいです (Microsoft Learn)
- 点(exemplar)が出たら、クリックして「この遅いやつ何?」をトレースで追えるのが最高✨ (Microsoft Learn)
“見る順番”テンプレ(迷子防止)🧭✨
検証でも障害でも、この順が強いです👇
- Traffic(量):来てる?増えた?減った?📈
- Errors(失敗):失敗率が跳ねた?💥
- Duration(遅延):P90/P99が伸びた?🐢
- (余裕あれば)Saturation(詰まり):CPU/スレッド/コネクションが限界?⚠️
よくある「出ない😵」チェックリスト✅
- Dashboard側:コンテナ起動できてる?ポート合ってる?(OTLPは
localhost:4317の形が例) (Microsoft Learn) - アプリ側:
OTEL_EXPORTER_OTLP_ENDPOINT入ってる?(設定がDevelopment側に入ってる?) (Microsoft Learn) - メトリクス:
work_requestsみたいなCounterは「叩けば増える」ので、まずそこから確認👀
(おまけ)Prometheus でやる場合の差分🧰📊
「/metrics を生やして Prometheus が scrape」する形です (Microsoft Learn)
.NET 側の例(要点だけ)👇
var otel = builder.Services.AddOpenTelemetry();
otel.WithMetrics(metrics =>
{
metrics.AddAspNetCoreInstrumentation();
metrics.AddPrometheusExporter(); // /metrics に出す
});
var app = builder.Build();
app.MapPrometheusScrapingEndpoint(); // /metrics
app.Run();
この “AddPrometheusExporter + MapPrometheusScrapingEndpoint” の流れが基本になります (Microsoft Learn)
ミニ課題🎒✨(やると定着するやつ)
failRate=0.05(5%)→0.2(20%)にして、失敗率が跳ねる瞬間をスクショ📸ms=30→ms=1000にして、P99だけが暴れるのを観察👀- 「Traffic↑なのにDuration↑しない」ケースを作る(VU少なめ)→ 「Traffic↑でDuration↑」になるまでVUを上げて境界を探す🎯
AI活用プロンプト(コピペOK)🤖💬✨
- 「k6で“徐々にVUを増やす(ramp up)”シナリオを書いて。
/workに当てたい」 - 「
work_failures / work_requestsをメトリクスとして出す設計案を出して(ラベル爆発しない形で)」 - 「P50/P90/P99 の読み方を、1枚のチートシートにして」
次の章(22章)は、この章で選んだ指標を “アラート向き/向かない” に仕分けして、鳴らし方まで型にします🚨💖