Article

オブザーバビリティ(可観測性)入門:システム障害を未然に防ぐには

高田晃太郎
オブザーバビリティ(可観測性)入門:システム障害を未然に防ぐには

99.9%の可用性は年間約8.76時間の停止を意味し、99.99%では約52.6分にまで短縮されます。¹ この一桁の差が、失注、SLA(Service Level Agreement)ペナルティ、ブランド毀損のカーブを大きく変えます。さらにIBMのレポートでは、セキュリティ起因のインシデント平均コストが数百万ドル規模に達すると示され²、障害一回あたりの経営インパクトは年々増大しています。クラウドネイティブ化と分散アーキテクチャの浸透で、単純なメトリクス中心の監視(Monitoring)だけでは因果を辿れず、復旧までの時間が読めないという声が業界で増えました³。だからこそ、未知の障害にも仮説検証で迫れる土台──すなわち「可観測性(オブザーバビリティ)」──が、経営と現場の共通言語になりつつあります。監視が既知の異常を検知する仕組みだとすれば、可観測性は未知の問いに答えられるようにシステムを設計する態度です⁴。指標を結び、因果を追跡し、再現可能な学習ループに載せることが、結果としてMTTR(平均復旧時間)短縮、MTBF(平均故障間隔)延伸、ページャの健全化に直結します。

可観測性とは何か:監視との違いと経営インパクト

監視はしきい値やルールで既知の状態異常を見張る営みであり、ダッシュボードの可読性や通知の最適化が主戦場です。対してオブザーバビリティは、ログ・メトリクス・トレースという三種の信号を体系的に設計し、未知の障害にも探索的に仮説を立てられる状態を指します⁵。言い換えれば、データの取得方法、保存先、相関キー(リクエストやユーザを横断で結ぶID)、サンプリング戦略、問い合わせ言語、組織の運用作法にまで踏み込んだ設計原則です。経営面では、SLO(Service Level Objective)とエラーバジェット(許容できる失敗の余白)をプロダクト計画に織り込み、信頼性の投資判断を可視化します⁷。SLOを満たしている期間は機能開発に比重を置き、消費が進めばリライアビリティ改善にシフトするというポリシー運用は、機能と品質のゼロサムを回避します⁷。

監視から可観測性へ:未知の問いに答える力

分散システムでは、症状が一箇所で観測されても原因は別のサービスや下層のインフラに潜むことが多く、既知のアラートだけでは根治に至りません。そこで、リクエストのライフサイクルを横断する分散トレーシング、症状の全体量を俯瞰するサービス指標、そして局所の手がかりを蓄える構造化ログを、同一の相関ID(Correlation ID)で束ねます⁶。この設計により、ダッシュボードで異常を検知し、トレースでボトルネックを特定し、ログで具体的な失敗パスを再現する、という一直線の調査経路が生まれます。

KPIとROI:MTTR、MTBF、エラーバジェット

オブザーバビリティ投資の効果は、MTTRの短縮、インシデント件数の減少、ページャの時間外発火率の低下、そしてSLO違反時間の縮小として現れます⁹。ROIは、改善前後の年間停止時間の差分にビジネスの分あたり損失を掛け合わせるだけでも概算できます。たとえば、MTTRが90分から30分に短縮し、年20件のインシデントがあるなら、年間20時間の復旧時間が約6.7時間に圧縮されます。これにコンバージョン損失やSLAペナルティの逓減を加味すると、経営の意思決定に十分な説得力を持ちます。

三位一体の設計:ログ・メトリクス・トレース

三つの信号は相補的です。メトリクスは健康状態の輪郭と傾向を素早く示し、トレースは因果の経路を提示し、ログは局所の文脈を提供します⁵。重要なのは、これらをバラバラに導入せず、リクエストIDやユーザセッションIDなどの相関キーを共通化し、同一のデータ保持方針とスキーマ設計に落とし込むことです。

ログの設計原則と相関ID

フリーテキストのログは検索のノイズになります⁶。構造化し、レベルとコンテキストを明確にし、相関IDでトレースと結びます。次のような形が出発点になります。

{
  "timestamp": "2025-08-30T12:34:56Z",
  "level": "ERROR",
  "message": "payment failed",
  "service": "checkout",
  "trace_id": "4f3a...",
  "span_id": "9b1c...",
  "request_id": "req-7f2d...",
  "user_id": "u_12345",
  "http": {"method": "POST", "path": "/pay", "status": 502, "latency_ms": 812}
}

このログは検索容易性と相関を両立します。さらにPII(個人情報)の扱い、保持期間、マスキング方針を定めることで、コストとリスクの両輪を押さえます。

メトリクスとSLI/SLOの設計

ユーザ体験を近似するSLI(Service Level Indicator)を定義し、SLOで合意します⁸。典型的には、成功率、レイテンシ、スループット、フレッシュネスなどが候補です。たとえばAPIの成否を示す際、HTTP 200だけでなくアプリケーションレベルの成功判定を明示する必要があります。計測にはエクスポータやSDKを使い、可視化にはPrometheusとGrafana(監視とダッシュボードの定番)の組み合わせが堅実です。Pythonのメトリクス導入例は次の通りです。

from prometheus_client import Counter, Histogram, start_http_server
import time

REQUESTS = Counter('http_requests_total', 'Total HTTP requests', ['path', 'method', 'code'])
LATENCY = Histogram('http_request_latency_seconds', 'Request latency', ['path', 'method'])

def handle_request(path, method):
    start = time.time()
    code = '200'
    try:
        # do work
        time.sleep(0.05)
        return 'ok'
    except Exception:
        code = '500'
        raise
    finally:
        duration = time.time() - start
        REQUESTS.labels(path, method, code).inc()
        LATENCY.labels(path, method).observe(duration)

if __name__ == '__main__':
    start_http_server(9100)
    while True:
        handle_request('/pay', 'POST')

このメトリクスから、例えば直近30日で95パーセンタイルのレイテンシを抽出し、SLO合致率を算出するSQLをログ集約基盤で走らせることも可能です。

-- BigQueryの例:p95レイテンシSLI
WITH w AS (
  SELECT TIMESTAMP_TRUNC(timestamp, MINUTE) AS ts,
         APPROX_QUANTILES(latency_ms, 100)[OFFSET(95)] AS p95
  FROM `proj.logs.requests`
  WHERE path = '/pay' AND method = 'POST' AND timestamp > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 30 DAY)
  GROUP BY ts
)
SELECT AVG(CASE WHEN p95 <= 300 THEN 1 ELSE 0 END) AS slo_attainment
FROM w;

分散トレーシングの実装(OpenTelemetry)

トレーシングは遅延の集中箇所や外部依存の失敗を特定するための顕微鏡です。OpenTelemetryは多言語で統一のAPIを提供するオープンスタンダードで、導入コストを抑えられます³。Node.jsとExpressの例を示します。

import express from 'express';
import { context, trace } from '@opentelemetry/api';
import { NodeSDK } from '@opentelemetry/sdk-node';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';

const sdk = new NodeSDK({
  traceExporter: new OTLPTraceExporter({ url: 'http://otel-collector:4318/v1/traces' }),
  instrumentations: [getNodeAutoInstrumentations()],
});
await sdk.start();

const app = express();
app.post('/pay', async (req, res) => {
  const tracer = trace.getTracer('checkout');
  await tracer.startActiveSpan('charge', async (span) => {
    try {
      // call payment provider
      await new Promise(r => setTimeout(r, 80));
      res.json({ ok: true });
    } catch (e) {
      span.recordException(e);
      res.status(502).send('bad gateway');
    } finally {
      span.end();
    }
  });
});
app.listen(3000);

ここで生成されたtrace_idはログにも埋め込み、後述のCollector経由でバックエンドに集約します。

プラットフォーム実装:収集・集約・可視化・アラート

収集の入口はアプリのSDKとエージェントで、ネットワーク越しの送信はコレクタに集約します。OpenTelemetry Collectorは受信、変換、エクスポートのパイプラインをコードレスで宣言できます。Prometheusは/metricsエンドポイントをスクレイプし、連携するAlertmanagerとGrafanaで運用の背骨を構成します。

収集パイプライン(OTel Collector / Prometheus)

Collectorの最小構成は次のようになります。ここではOTLPで受けたトレースをTempoやJaegerへ、メトリクスをPrometheus互換で公開する想定です。

receivers:
  otlp:
    protocols:
      http:
exporters:
  otlphttp/tempo:
    endpoint: http://tempo:4318
  prometheus:
    endpoint: 0.0.0.0:9464
processors:
  batch: {}
service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [otlphttp/tempo]
    metrics:
      receivers: [otlp]
      processors: [batch]
      exporters: [prometheus]

スクレイプ対象はサービスディスカバリで自動化し、ラベルはチーム、サービス、リージョン、デプロイメントIDを含むよう標準化します。これにより、任意の断面でSLOやコストを切り出せます。

アラートは症状ベースへ(SLOアラート)

個別メトリクスの閾値ではなく、ユーザ影響を代表する症状に対してアラートします。エラーバジェットの消費率(バーンレート)に基づくアラートは、過検知を大きく抑制します⁸。Prometheusのアラート定義例を示します。

groups:
- name: slo-burn
  rules:
  - alert: SLOFastBurn
    expr: (
      rate(http_requests_total{code!~"2.."}[5m])
      /
      rate(http_requests_total[5m])
    ) > 0.02
    for: 10m
    labels:
      severity: page
    annotations:
      summary: "Fast burn: error budget depleting quickly"
  - alert: SLOSlowBurn
    expr: (
      rate(http_requests_total{code!~"2.."}[1h])
      /
      rate(http_requests_total[1h])
    ) > 0.005
    for: 2h
    labels:
      severity: ticket
    annotations:
      summary: "Slow burn: investigate during business hours"

短期と長期の窓を併用することで、ユーザ影響の強度と持続性に応じた対応を切り分けられます。夜間の人的負荷を抑え、日中の計画的改善に時間を回せるようになります。

ダッシュボードのアンチパターン

メトリクスの羅列や虹色メーターは意思決定に寄与しません。最初のスクリーンには「SLOの達成状況」「現在の症状(エラー率、レイテンシ、トラフィック)」「依存先の状態」「直近のデプロイ」の四つの問いに答えるカードを配置し、深掘りはトレースやログにドリルダウンできる導線に絞ります。ダッシュボードは運用レビューで毎週磨き込み、不要なパネルは削除します。

組織運用:インシデント対応と継続改善

可観測性はツール導入では完結しません。インシデントコマンド体系、オンコールの健康、変更管理、ポストモーテム(事後検証)の学習サイクルまでを含めた運用設計が必要です。特に、デプロイと障害の相関を迅速に検証できる仕組みは、復旧時間を大きく左右します。リクエストのコンテキストにGitのコミットやリリースIDを付与し、トレースのタイムラインにデプロイイベントを重ねて可視化すると、ロールバックの意思決定が速くなります。

エラーバジェット政策と開発の節度

SLO違反の頻度やバジェット消費に応じて、リリースフリーズ、ガードレールの強化、実験トラフィックの縮小といった方針を事前に合意します。定量的な基準はチーム間の交渉コストを下げ、信頼性への投資が後回しになることを防ぎます。可観測性の指標とロードマップをリンクさせることで、取り組みの成果が次の四半期のOKRに直結します⁷。

ポストモーテムの標準化とデータの再利用

無過失のポストモーテムを標準テンプレート化し、事実・影響・タイムライン・再発防止の四つを明確にします。タイムラインはトレース、ログ、アラート履歴から自動生成を試み、ヒューマンエラーの責任追及ではなく、検出・緩和・復旧の各フェーズのシグナル品質を評価します。次のようなタイムライン抽出スクリプトを用意しておくと、レビューの質が安定します。

import json
from datetime import datetime, timedelta

def incidents(stream):
    for line in stream:
        evt = json.loads(line)
        if evt.get('level') in ('ERROR','WARN') and evt.get('trace_id'):
            yield (evt['timestamp'], evt['service'], evt['message'], evt['trace_id'])

# 標準入力のログを時系列で整形

また、ログスキーマやメトリクス名はレジストリで管理し、命名のばらつきを防ぎます。これにより、横断的なクエリや全社SLOの集計が容易になります。

導入ロードマップと落とし穴

実務ではスモールスタートで十分ですが、相関キーとSLOだけは最初に決めることを強く勧めます。先にダッシュボードを作り込むと、肝心のデータが取れておらず再工事になりがちです。ランタイムオーバーヘッドへの懸念はもっともですが、ヘッドサンプリングとテールサンプリング(入口と出口の選別)を組み合わせ、ホットパスやエラーパスに重点配分すれば、p95レイテンシへの計測オーバーヘッドは低く抑えられます。依存の可視化では、外部SaaSや決済ゲートウェイのSLOも契約に織り込み、内製SLOとの合成を予算サイクルに組み込みます。最後に、可観測性はプロダクトの「要件」です。機能と同じように受け入れ基準を定め、コードレビューではトレース属性やログの粒度、メトリクスのラベル設計をチェック対象に含めます。

# 例:PRの受け入れ基準(観点)
observability:
  logs: structured, has trace_id, no PII
  metrics: has RED/USE, labels standardized
  traces: critical path instrumented, attributes include release_id

このようなゲートを設けると、後追いでの埋め込みコストを避け、インシデント発生時の探索速度が飛躍的に高まります。

Kubernetesとの統合の注意点

コンテナ環境では短命プロセスが常態で、ノードローカルのログやメトリクスに依存すると情報が欠落します。サイドカーなしでもDaemonSetのログエージェントやOpenTelemetry Operatorを使えば、Podのライフサイクルに追随した収集が可能です。Pod、Namespace、DeploymentなどのKubernetesメタデータを自動付与し、サービス名と環境(prod, staging)を正規化しておくと、影響範囲の推定が容易になります。

apiVersion: opentelemetry.io/v1alpha1
kind: OpenTelemetryCollector
metadata:
  name: otel-collector
spec:
  config: |
    receivers: { otlp: { protocols: { http: {} } } }
    processors: { k8sattributes: {}, batch: {} }
    exporters: { otlphttp/tempo: { endpoint: http://tempo:4318 } }
    service:
      pipelines:
        traces: { receivers: [otlp], processors: [k8sattributes, batch], exporters: [otlphttp/tempo] }

セキュリティとプライバシー

可観測性のデータはセンシティブです。トレース属性やログにPIIを含めない原則を徹底し、マスキングとトークナイズを入口で行います。データ保持は用途別に期間を分け、監査要件を満たしつつ保存コストの上振れを抑えます。アクセス制御は役割ベースで、インシデント対応者に一時的な昇格権限を付与できるよう運用で支えます。

まとめ:計測できるものだけが改善できる

不確実性の高いプロダクト開発では、障害そのものをゼロにすることはできません。しかし、影響の検知を速め、原因の特定を簡単にし、復旧までの道筋を定型化することはできます。オブザーバビリティ(可観測性)はそのための共通基盤であり、システムの内部状態を外から推論可能にする設計上の約束事です。今日できる第一歩は、相関IDの統一と、ユーザ中心のSLI・SLOの合意です。次いで、Collectorとダッシュボードを最小構成で立ち上げ、SLOアラートで過検知を抑えながら、トレースとログの粒度をスプリントごとに磨き込んでいくとよいでしょう。あなたの組織では、次の四半期にどのSLOを掲げ、どのエラーバジェット政策を運用に落とし込みますか。小さく始めて、学びを早く回すことが、障害を未然に防ぐいちばんの近道です。

参考文献

  1. Google SRE Book: Embracing Risk – availability “nines”と期待停止時間の関係
  2. IBM Newsroom (2024-07-30): Escalating data breach disruption pushes costs to new highs
  3. CNCF Blog (2022-09-13): APM vs. Application Observability
  4. CNCF Blog (2022-04-27): Are the three pillars of observability still relevant?
  5. TechTarget: The 3 pillars of observability: Logs, metrics and traces
  6. Better Stack Community: Structured logging guide
  7. Google SRE Workbook: Error budget policy(ポリシー運用と投資判断)
  8. Google SRE Workbook: Error budget policy(SLO達成時のリリース方針)
  9. Digitalisation World: Enterprises realise 2x ROI on observability (New Relic 2023 Observability Forecast)