Tech Learning Daily

2026-08-21 (Fri) — 第 36 号
AI が毎朝届ける、ソフトウェア技術の基礎解説
Obs 🌿 基礎 ⏱ 約 7 分

同じ「アラート」なのに、なぜ叩き起こされる時とチケットで済む時があるのか?

深夜にアラートで叩き起こされたことがあるはずだ。しかし同じくらいの一時的なエラー増加でも、ただ Slack にチケットが起票されるだけで済む日もある。この差を決めているのは担当者の主観ではなく、決められた「ものさし」をどれだけ速く使い切っているかという計算だ。

🎯 3 行まとめ
  • SLI(サービスレベル指標)は「良いイベント数 ÷ 全イベント数」の比率として定義され、これに対する目標値が SLO。SLO は意図的に 100% 未満に設定する
  • SLO の残り(1 − SLO)がエラーバジェットで、「あとどれだけ不安定でいられるか」の残高。使い切るとリリースを一時停止し信頼性回復に専念するポリシーへ切り替わる
  • バジェットの消費速度(バーンレート)が高いほど緊急度が高い。長い窓と短い窓を同時に見るマルチウィンドウ方式が「即ページ」と「チケットで十分」を切り分けている

「動いているか」をどう数値にするか

サービスが動いているかを一言で言えても、それをアラートの根拠にするには数値に落とす必要がある。Google の SRE ワークブックは SLI(Service Level Indicator)を「良いイベントの数を全イベント数で割った比率」として定義することを勧めている。HTTP なら成功したリクエスト数 ÷ 総リクエスト数、レイテンシなら 100ms 未満で完了した gRPC 呼び出し数 ÷ 総呼び出し数、といった具合だ。

比率で表すと 0%〜100% の範囲に収まり、0% が「何も動いていない」、100% が「何も壊れていない」を意味するため、エンジニア以外にも直感的に伝わる。ここでいう「良いイベント」の定義がそのまま監視の基準になる。何をカウントするかを間違えると、実際にはユーザーが困っているのに数値上は健全に見える、という逆転が起きる。

なぜ SLO は 100% にしないのか

SLI に対して目標値を定めたものが SLO(Service Level Objective)だ。ここで引っかかりやすいのは「目標なら 100% を目指すべきでは」という直感だが、SRE ワークブックは 100% にしない理由を明確に述べている。冗長構成やヘルスチェック、自動フェイルオーバーをどれだけ積み重ねても、複数コンポーネントのどれかが故障する確率をゼロにはできない。

さらに 100% を目指すこと自体が別の問題を生む。仮に 100% の信頼性という体験を作れたとしても、それ以降のデプロイは壊すリスクしか持たないため、サービスを一切更新できなくなる。SLO を意図的に 100% 未満に設定するのは妥協ではなく、更新し続けるためにあらかじめ失敗できる余地を確保する設計判断だ。なお、ユーザーへの補償を伴う対外的な契約である SLA と SLO は別物で、SLA は「ユーザーが十分に不満を感じたときに補償が発生する契約」、SLO は日々の意思決定に使う社内向けの目標という違うレイヤーの数字になる。

エラーバジェットは「使ってよい失敗の残高」

SLO を 99.9% と決めると、残りの 0.1% がそのままエラーバジェットになる。SRE ワークブックはこれを「1 からその期間の SLO を引いた値」と定義し、直近 30 日などの一定期間について、あとどれだけ不安定であることが許されているかを示す残高として扱う。実際の稼働率が SLO を上回っている限りバジェットは残っており、その間はリリースを止める理由にならない。

バジェットが尽きると聞くと、サービスがそのまま止まるように思える。しかし SRE ワークブックが紹介するエラーバジェットポリシーの一例が定めているのは、直近 4 週間でバジェットを使い切ったサービスについて、P0 障害対応とセキュリティ修正を除くすべてのリリースを一時停止し、信頼性の回復に専念することだ。バジェットの枯渇はサービスを落とす合図ではなく、「これ以上リスクを積み増す変更をしない」という開発プロセス側の制約への切り替えを意味する。

🍱 たとえるなら

エラーバジェットは家計簿の「今月使ってよい交際費」に近い。上限を月 1 万円と決めたら、1 日 300 円ほどのペースで使えば月末にちょうど使い切る。ところが月の初めに一気に 5,000 円を使ってしまえば、そのペースのまま使い続けた場合に数日で使い切ってしまうと分かる。見るべきなのは「今いくら残っているか」だけでなく「このペースだと何日で尽きるか」であり、エラーバジェットのアラートも残量そのものより、この消費速度を監視している。

同じ 1% のエラー率でも、緊急度が変わる理由

バジェットの残量だけを見張っていると、じわじわ長期間にわたって侵食されるケースと、急激に食い潰されるケースを区別できない。そこで使われるのがバーンレートで、通常のペースで消費し続けたときにちょうど期間の終わりでバジェットを使い切る速度を「1x」とする。SRE ワークブックの例では、99.9% の SLO(バジェット 0.1%)に対してエラー率が定常的に 0.1% であれば、30 日かけてぴったりバジェットを使い切るのでバーンレート 1x になる。

ここでエラー率が 1% まで跳ね上がると、バーンレートは 10x になり、本来 30 日かけて使うはずのバジェットをわずか 3 日で使い切る計算になる。バーンレートが高いほど今すぐ対応すべきインシデントである可能性が高く、低いバーンレートが延々と続く方は緊急招集ではなくチケットで十分、という切り分けがここから生まれる。

「5 分でも確認、1 時間でも確認」——二つの窓を同時に見る理由

バーンレートが分かっても、単純に「1 つの閾値を超えたらアラート」という設計には弱点がある。SRE ワークブックはこれを、緩やかで低いレートの障害を拾い損ねる再現率の低さと、障害が収まったあともアラートが鳴り続ける解除の遅さという二つの欠陥として指摘している。単一の窓・単一の閾値では、深刻な障害と一時的な揺らぎを区別できない。

そこで実装では、長い窓でバジェット消費の兆候を検知し、短い窓でその消費が今も進行中かを確認する二重の窓(マルチウィンドウ)を組み合わせる。SRE ワークブックが示す 99.9% SLO の例では、1 時間窓でバーンレート 14.4x(バジェットの 2% 消費)かつ直近 5 分窓でも同じ傾向が続いていれば緊急招集、6 時間窓でバーンレート 6x が続けば同じく緊急招集、3 日窓でバーンレート 1x が続く程度ならチケット起票にとどめる。窓の長さと倍率の組み合わせが変わるのは、深刻さの体感ではなく、バジェット消費の速度計算がそのまま基準になっているからだ。

エラー率 1%(定常運転は0.1%)
        │
        │ バーンレート = 1% ÷ 0.1% = 10x
        ▼
  30日分のバジェットを
   3日で使い切る計算
        │
        │ 1時間窓+5分窓の両方で検知
        ▼
     緊急招集(ページ)
💼 実務でどう出会うか

Datadog や Grafana、Google Cloud の SLO 監視機能に触れたことがあるなら、ダッシュボードの「バジェット消費率」や「バーンレートアラート」はここで説明した計算そのものだ。PagerDuty や Opsgenie で緊急招集用と通常チケット用にエスカレーションが分かれているサービスでは、裏でこのようなバーンレートの閾値がアラートルールとして設定されていることが多い。リリース判定に「エラーバジェットが残っているか」をゲートとして組み込み、canary リリースの自動ロールバック条件に使うチームもある。

⌨️ 手を動かす(5 分)

SLO 99.9%(バジェット 0.1%)のサービスで、直近のエラー率が 1% に跳ね上がった場合のバーンレートと、このペースが続くと何日でバジェットを使い切るかを計算してみる。

python3 - <<'EOF'
slo = 0.999                 # 99.9% SLO
observed_error_rate = 0.01  # 直近で観測されたエラー率 1%
budget = 1 - slo
burn_rate = observed_error_rate / budget
days_to_exhaust = 30 / burn_rate
print(f"burn rate: {burn_rate:.1f}x")
print(f"30日分のバジェットを使い切るまで: {days_to_exhaust:.1f}日")
EOF

burn rate: 10.0x30日分のバジェットを使い切るまで: 3.0日 が出力されるはずだ。これは「エラー率 1% は 30 日分のバジェットを 3 日で使い切る」という SRE ワークブックの例と一致する。observed_error_rate0.002 のように小さい値に変えて再実行すると、バーンレートが 1 に近づき、日数が 30 日近くまで伸びることも確認できる。

🙅 よくある誤解
  • SLA と SLO は同じ数値の言い換えにすぎない — SLA はユーザーへの補償を伴う対外的な契約、SLO は日々のリリース判断に使う社内向けの目標であり、別のレイヤーの数字だ。
  • SLO は高ければ高いほど良いので 100% を目指すべきだ — 故障確率をゼロにはできないうえ、100% を維持しようとすると更新のたびに壊すリスクしか無くなり、サービスを改善できなくなる。
  • エラーバジェットを使い切ったら、サービスが停止するか罰則が科される — 実際に起きるのは P0 障害対応やセキュリティ修正を除くリリースの一時停止であり、信頼性の回復にリソースを振り向けるための開発プロセス上の制約にすぎない。
📖 用語ミニ辞典
SLI(Service Level Indicator)
良いイベント数 ÷ 全イベント数として定義される、サービス状態を表す比率の指標。
SLO(Service Level Objective)
SLI に対して設定する目標値。意図的に 100% 未満に設定する。
SLA(Service Level Agreement)
ユーザーへの補償を伴う対外的な契約上の数値。SLO とは別レイヤーの数字。
エラーバジェット
1 から SLO を引いた値。特定期間内にあとどれだけ不安定でいられるかを示す残高。
バーンレート
エラーバジェットの消費速度。1x は期間終了時にちょうど使い切るペース。
🔗 もっと深く
最近の記事

ClusterIP に ping が通らないのに、なぜ curl は届くのか?

ClusterIPは実在しない仮想IPで、実体は各ノードのkube-proxyが書き込むiptablesのDNATルールにすぎない。EndpointSliceとreadiness probeの関係、TCPは通りICMP(ping)だけが失敗する理由を解説。
Container🌿 基礎⏱ 約 7 分2026-08-19

「1分100リクエストまで」のはずが、なぜ一瞬で200件通ってしまうのか?

固定ウィンドウカウンタはウィンドウ境界をまたぐと定義の最大2倍のリクエストを通してしまう。トークンバケット・リーキーバケット・スライディングウィンドウカウンタの違いと、分散環境でのレースコンディション対策を解説。
Arch🌿 基礎⏱ 約 8 分2026-08-18

HTTP/2 は接続を1本にまとめたのに、なぜ遅くなることがあるのか?

HTTP/1.1はアプリ層のHead-of-Line Blockingをブラウザが複数TCP接続で回避してきたが、HTTP/2はストリーム多重化で1本の接続に集約した。その代償として生まれたTCP層のHead-of-Line BlockingとHTTP/3による解決を解説。
Network🌿 基礎⏱ 約 7 分2026-08-17

ローカルでは一瞬なのに、CI の docker build はなぜ毎回フルビルドになるのか?

COPY/ADDはファイル内容のチェックサム、RUNはコマンド文字列でキャッシュを判定し、一度ミスすると以降の全レイヤーが連鎖的に無効化される。CIランナーは前回ビルドを保持しないため常にフルビルドになる理由とレジストリキャッシュでの対処を解説。
Container🌿 基礎⏱ 約 7 分2026-08-16