本文へスキップ
hdknr blog
戻る

Grafana OnCall は終わった、Grafana Cloud IRM が始まった — オンコール体制の現代的選択肢を整理する

前回の記事で「サーバー監視の王道スタック」として Prometheus + Loki + Grafana + Alloy を整理しました。アラート設計のセクションで触れた Grafana OnCall について、改めて単独で深掘りします。

ただし重要な注意点があります — Grafana OnCall OSS(grafana/oncall リポジトリ)は 2026 年 3 月 24 日にアーカイブされました。後継は **Grafana Cloud IRM(Incident Response Management)**で、OnCall と Incident の両アプリが 1 つに統合されています。

「Grafana OnCall を新規導入したい」「既存環境を移行すべきか」という人に向けて、何が終わって、何が始まったのかを整理します。

Grafana OnCall とは何だったのか

Grafana OnCall は 「アラートが鳴った後の対応フロー」を管理するツールでした。

PagerDuty や Opsgenie の OSS 互換ツールとして、Grafana エコシステムの中で重要なポジションを占めていました。

主な機能(当時)

  1. アラートの集約とルーティング — 複数の監視システムからのアラートを統合、内容に応じてチームへ振り分け
  2. オンコールシフト管理 — 担当者のカレンダー(シフト表)に従って当番者にだけ通知
  3. エスカレーションポリシー — 一定時間応答がなければ次の担当者へ自動エスカレーション
  4. ChatOps 連携 — Slack / Telegram 上でアラート確認・対応開始(Acknowledge)・解決(Resolve)が完結
  5. 柔軟な通知手段 — Slack / Microsoft Teams / SMS / 自動音声通話(電話)/ モバイルプッシュ
  6. IaC 対応 — Terraform プロバイダで設定をコード管理可能

連携先(インテグレーション)

カテゴリ代表的な連携先
監視・アラート検知Grafana, Prometheus (Alertmanager), Datadog, Zabbix, AWS CloudWatch, New Relic
通知・コミュニケーションSlack, Microsoft Teams, Telegram, SMS, 自動音声通話

OSS 版で自社サーバーに構築することも、Grafana Cloud のマネージドサービスとして利用することも可能でした。

「終わった」というのはどういうことか — タイムライン

日付できごと
2025-03-11Grafana OnCall OSS が read-only / メンテナンスモード に移行
2025-03 頃Grafana Cloud IRM(OnCall + Incident 統合)が全 Grafana Cloud 環境にロールアウト
2026-03-24grafana/oncall リポジトリがアーカイブ
2026-03-24OnCall OSS の Cloud Connection 機能(SMS / 電話 / プッシュ通知のクラウド経由配信)が停止
2026-05(現在)OSS 版を新規構築する選択肢は事実上なくなり、IRM か他社ツールへ

つまり現時点で Grafana OnCall OSS を新規導入する理由はほぼありません。既存の OSS 環境を運用している場合は、Cloud Connection 停止で SMS / 電話通知が動かなくなっているはずなので、移行が必須です。

後継: Grafana Cloud IRM とは

Grafana Cloud IRM (Incident Response Management) は、従来の Grafana OnCall と Grafana Incident(インシデントマネジメントアプリ)を1 つの統合アプリにまとめたもの。

従来の役割分担と統合

[従来]
 Grafana OnCall  : アラート → 通知 → エスカレーション
 Grafana Incident: インシデントが発生した後のドキュメント・タイムライン・ポストモーテム

[統合後]
 Grafana Cloud IRM: アラート → オンコール → インシデント対応 → 振り返りまで一気通貫

主な特徴

価格

Grafana Cloud IRM は Grafana Cloud 上の有料アドオン:

PagerDuty が $21〜/ユーザー/月 程度なので、1 ユーザーあたりの単価はほぼ同等。Grafana スタックを既に使っているなら統合の利便性で IRM が有利。Grafana を使っていない組織には PagerDuty / Opsgenie の方が中立的。

主要競合との比較

ツール価格特徴向いている組織
Grafana Cloud IRM$20〜/ユーザー/月 + Platform feeGrafana スタックと統合、ポストインシデントまでGrafana / Prometheus / Loki ユーザー
PagerDuty$21〜/ユーザー/月業界標準、エスカレーション・自動化が豊富、AIOps 充実エンタープライズ全般
Opsgenie(Atlassian)$9〜/ユーザー/月Jira / Confluence と統合、コスト優位Atlassian エコシステム
AWS Incident Manager従量課金AWS ネイティブ、CloudWatch 統合AWS 中心の組織
OnPage$13.99〜/ユーザー/月医療系で実績、確実な配信医療・ミッションクリティカル
Zenduty$7〜/ユーザー/月安価で機能十分、新興プレイヤーコスト重視

OSS 版を諦めない場合の代替選択肢

「OSS 縛り」「自前運用したい」「コストを最小化したい」という要件なら、以下が現実的な選択肢:

1. Karma(Alertmanager UI)+ 自前運用

2. Apprise + cron でシフト管理を自前

3. Signoz / Uptrace 系の OSS 監視に内蔵

4. OnPage 等のリーズナブルな商用 SaaS

代替選択肢は Grafana プラグインとして追加できるのか

「Karma や Apprise を Grafana サーバーにプラグインとして組み込めばいいのでは」という疑問が出てきます。正確な答え: 大半は Grafana プラグインではなく独立サービスです。プラグイン化できるかどうかは、種類によってまったく違います。

Grafana のプラグインアーキテクチャ

Grafana のプラグインは 3 種類:

  1. Panel plugin — ダッシュボード内のカスタムパネル
  2. Data source plugin — 新しいデータソース接続
  3. App plugin — Grafana 内に独自ページを持つフルアプリ

OnCall 系の機能を提供できるのは App plugin ですが、現実にはほとんどの代替が「プラグインではなく外部サービス」です。

各代替選択肢のプラグイン化可否

選択肢プラグイン化形態
Grafana OnCall(過去)◯(App plugin)grafana-oncall-app + 別エンジンの 2 層、アーカイブ済
Grafana Cloud IRM✕(Cloud 限定)Grafana Cloud 上でのみ動作、OSS サーバーには入らない
KarmaAlertmanager 専用 Web UI、別プロセス・別ポート
ApprisePython ライブラリ / CLI、Webhook 中継として配置
SignozGrafana 競合の独立 OSS プラットフォーム
PagerDuty / Opsgenie / ZendutySaaS、Webhook 連携で繋ぐ

つまり 「Grafana サーバーに何かを追加するだけで OnCall 相当が手に入る」道は、OnCall OSS のアーカイブで完全に閉じたのが現状です。

Grafana 標準機能だけでどこまでできるか(プラグイン不要)

Grafana 8 以降の Unified Alerting には、実はかなりの機能が標準装備されています。

標準で使える機能

これだけで「チーム別 Slack 通知 + critical は PagerDuty + 業務時間外は別ルーティング」までは プラグインなしで完結します。

標準では足りない機能

これらはプラグインでは実装できない領域で、専用サービス(IRM / PagerDuty 等)が必要になる根本理由です。シフト管理だけでも、カレンダー・タイムゾーン・休暇管理・ユーザー在不在の追跡が必要で、これを Grafana のアプリプラグインだけで完結させるのは設計上難しい。

プラグイン形式の限界と現実解

「プラグインで済ませたい」という発想自体が、OnCall OSS が成立していたからこそ可能だったものです。アーカイブ後の現実解は 「Grafana 標準アラート + 外部サービスの Webhook 連携」のハイブリッド:

[Grafana OSS サーバー]          ← プラグインで賄う部分
   ├─ アラートルール定義
   ├─ Contact Points
   └─ Notification Policies
        ↓ webhook
[外部サービス]                   ← プラグインでは賄えない部分
   ├─ Karma(Alertmanager UI 補完、別プロセス)
   ├─ Apprise(通知ハブ、別プロセス)
   ├─ シフト管理(自作 or Zenduty 等の安価 SaaS)
   └─ 音声通話・モバイルプッシュ(Twilio / SaaS)

判断基準:

中規模以上のチームで本格的なオンコール運用を組むなら、**「シフト管理は SaaS に任せ、Grafana 側はアラート発火に専念」**が最もコスト効率が良い構成です。

Apprise + 自作 Web サービスで「ack URL + エスカレーション」を組む

メール通知に承認 URL を埋め、一定時間踏まれなかったら次の担当へ自動エスカレーション」というシンプルなフローなら、Apprise + 軽量な Web サービスで素直に実装できます。OSS 諦めない派にとって、自作の現実的な落としどころです。

全体フロー

[Grafana Alerting]
      ↓ webhook
[自作 Web サービス(Ack & Escalation)]
   ├─ アラート受信 → DB に登録
   ├─ ユニークな ack URL 発行
   └─ Apprise で通知送信

[1次担当者にメール]
   "障害発生。承認するには ↓ をクリック
    https://oncall.example.com/ack/abc123"

   ┌─ N 分以内にクリック → 解決済みに更新、終了
   └─ N 分タイムアウト  → 2次担当者にメール(新 URL)
                            → さらに N 分待つ → 3次担当 → ...

最小実装サンプル(FastAPI + Apprise + SQLite + APScheduler)

# main.py(抜粋、全体で 150 行程度で動く)
import secrets
from datetime import datetime, timedelta
from fastapi import FastAPI, HTTPException
from sqlmodel import SQLModel, Field, Session, create_engine
from apscheduler.schedulers.background import BackgroundScheduler
import apprise

ESCALATION_POLICY = [
    "mailto://primary@example.com",
    "mailto://secondary@example.com",
    "mailto://manager@example.com",
]
ESCALATION_TIMEOUT_MIN = 5
BASE_URL = "https://oncall.example.com"

class Alert(SQLModel, table=True):
    id: str = Field(primary_key=True)
    title: str
    message: str
    status: str = "pending"   # pending / acknowledged / closed
    level: int = 0
    created_at: datetime = Field(default_factory=datetime.utcnow)

engine = create_engine("sqlite:///alerts.db")
SQLModel.metadata.create_all(engine)
scheduler = BackgroundScheduler()
scheduler.start()
app = FastAPI()

def send_notification(alert_id: str):
    with Session(engine) as session:
        alert = session.get(Alert, alert_id)
        if not alert or alert.status != "pending":
            return
        if alert.level >= len(ESCALATION_POLICY):
            alert.status = "closed"
            session.add(alert); session.commit()
            return

        target = ESCALATION_POLICY[alert.level]
        ack_url = f"{BASE_URL}/ack/{alert.id}"
        apobj = apprise.Apprise()
        apobj.add(target)
        apobj.notify(
            title=f"[L{alert.level + 1}] {alert.title}",
            body=(
                f"{alert.message}\n\n"
                f"承認 URL({ESCALATION_TIMEOUT_MIN} 分以内にクリック):\n{ack_url}\n\n"
                f"応答がなければ次の担当者へエスカレーションします。"
            ),
        )
        scheduler.add_job(
            escalate, "date",
            run_date=datetime.utcnow() + timedelta(minutes=ESCALATION_TIMEOUT_MIN),
            args=[alert_id],
            id=f"escalate-{alert_id}-{alert.level}",
            replace_existing=True,
        )

def escalate(alert_id: str):
    with Session(engine) as session:
        alert = session.get(Alert, alert_id)
        if not alert or alert.status != "pending":
            return
        alert.level += 1
        session.add(alert); session.commit()
    send_notification(alert_id)

@app.post("/webhook")
async def receive(payload: dict):
    """Grafana Alerting / Alertmanager の webhook を受ける"""
    alert_id = secrets.token_urlsafe(16)
    alert = Alert(
        id=alert_id,
        title=payload.get("title", "Alert"),
        message=payload.get("message", ""),
    )
    with Session(engine) as session:
        session.add(alert); session.commit()
    send_notification(alert_id)
    return {"alert_id": alert_id}

@app.get("/ack/{alert_id}")
async def acknowledge(alert_id: str):
    with Session(engine) as session:
        alert = session.get(Alert, alert_id)
        if not alert:
            raise HTTPException(404, "Alert not found")
        if alert.status != "pending":
            return {"message": f"Already {alert.status}"}
        alert.status = "acknowledged"
        session.add(alert); session.commit()
    for job in scheduler.get_jobs():
        if job.id.startswith(f"escalate-{alert_id}-"):
            job.remove()
    return {"message": "Acknowledged. ありがとうございます。"}

これだけで「webhook 受信 → メール送信 → ack URL → 5 分タイムアウト → 次担当者」が動きます。

Grafana 側の設定

Grafana Alerting の Contact Point を Webhook にして、自作サービスの /webhook を指定:

# Grafana Alerting Contact Point
type: webhook
settings:
  url: https://oncall.example.com/webhook
  httpMethod: POST
  username: oncall-webhook   # オプション: Basic 認証
  password: <secret>

通知ペイロードのテンプレートを Grafana 側で整形して、titlemessage を送れば自作サービス側で扱いやすくなります。

設計上のポイントと落とし穴

1. ack URL は「推測不能 + 単発 + 期限付き」に

2. SPOF(単一障害点)対策

「監視通知を司るサービス自身が落ちたら気づけない」という致命的な問題:

3. メール配信遅延

メールは平均 10〜30 秒、最大数分の遅延があります。

4. シフト管理を組み込むなら

ESCALATION_POLICY を時間帯で動的に切り替える:

def get_policy_for_now():
    now = datetime.now()
    if now.weekday() < 5 and 9 <= now.hour < 18:
        return ["mailto://team-a@example.com", "mailto://manager@example.com"]
    else:
        return ["mailto://oncall-tonight@example.com", "mailto://manager@example.com"]

カレンダー連携が必要なら Google Calendar API でシフト表を読むのが現実的。

5. 永続化と再起動耐性

APScheduler のデフォルトはメモリベース。プロセス再起動でジョブが消えるので、本番では DB に永続化:

from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
scheduler = BackgroundScheduler(jobstores={
    "default": SQLAlchemyJobStore(url="sqlite:///jobs.db")
})

または Celery + Redis で別プロセス化。

6. 監査ログ

「誰がいつ ack したか」を残しておくと、ポストインシデント分析や運用改善で役立ちます。Alert テーブルに acknowledged_by_ip, acknowledged_at, user_agent を追加。

メリット・デメリット

メリット

デメリット・限界

規模別の判断

規模推奨
1〜3 人体制、週数件のアラートこの自作サービスで十分
5〜10 人、エスカレーション + 簡易シフト自作 + シフト管理を YAML / Calendar 連携で拡張
10 人超、24/7 シフト + 電話通知Zenduty($7〜)など安価 SaaS の方がトータルコスト低
エンタープライズ、コンプライアンス必須PagerDuty / Grafana Cloud IRM

既存 OSS で似たアプローチ

ゼロから作るのが面倒なら、近い思想の OSS:

これらは「自作」と「フル SaaS」の中間に位置します。

Apprise + FastAPI 150 行で OnCall の核を再現」できることが分かると、OnCall OSS のアーカイブ後の心理的インパクトはかなり下がります。中身をブラックボックスにせず、自分の手で組める範囲で済ませる選択肢が現実的に存在します。

アーキテクチャ的な位置付け

前回の記事のスタックに IRM(または代替)を組み込んだ全体像:

[アプリ・サーバー]

   Alloy(収集)

[Prometheus / Loki / Tempo]

   アラートルール(PromQL / LogQL)
      ↓ 発火
[Alertmanager または Grafana Alerting]
      ↓ webhook
   ┌─────────────────────────┐
   │   Grafana Cloud IRM     │ ← ここを担当
   │  ・シフト管理            │
   │  ・エスカレーション      │
   │  ・ChatOps 連携          │
   │  ・ポストインシデント    │
   └─────────────────────────┘

[担当者のスマホ / Slack / 電話]

「アラートが鳴る」までと「鳴ってから人が動く」のは別のレイヤーであり、IRM はその「人が動く」部分の OS に相当します。

移行ガイド: OnCall OSS → Grafana Cloud IRM

既存の Grafana OnCall OSS 環境がある場合、以下が公式の移行手順:

  1. Grafana Cloud アカウント作成 — Free tier から始められる
  2. IRM アプリを有効化 — Cloud Stack のインストール画面から
  3. OnCall OSS の設定をエクスポート — Terraform で管理していた場合は最小の変更で移行可能
  4. Webhook 連携の付け替え — Alertmanager の receiver URL を IRM のエンドポイントへ
  5. ユーザー・スケジュール・統合の再作成 — 公式ドキュメントの移行ガイド に従う
  6. 動作確認後、OSS 環境を停止

OSS の Cloud Connection(SMS / 電話通知)は既に止まっているので、音声通話やモバイル通知が動かなくなった時点で移行は実質必須です。

当該プロジェクトでの導入判断

「Grafana OnCall を導入したい」と思った時点で、選択肢は以下のいずれかです:

状況推奨
Grafana Cloud を既に使っている / 使う予定Grafana Cloud IRM — エコシステム統合最強
AWS 中心、コスト最小化したいAWS Incident Manager — 従量課金、CloudWatch 統合
Atlassian エコシステムOpsgenie — Jira 連携が深い
エンタープライズ標準準拠が必要PagerDuty — 業界標準、監査・コンプライアンス対応強い
コスト最優先 + OSS 寄りZenduty or OnPage + Alertmanager + Karma
完全自前 OSS 運用Alertmanager + Apprise + 自作シフト管理(フル機能は諦める)

純粋な OSS で OnCall を完全代替する選択肢は弱く、「重要なオンコール機能はマネージド SaaS に任せる」のが現実解になっています。

まとめ

OnCall OSS の終了は寂しいニュースですが、Grafana Cloud IRM は OSS 版より機能的に明確に進化しています。Grafana オブザーバビリティスタックを既に使っているチームには、自然な拡張として導入を検討する価値があります。

参考リンク



前の記事
現代的サーバー監視の王道スタック — Prometheus + Loki + Grafana + Alloy で始めるオブザーバビリティ基盤
次の記事
Apprise + シフト管理ツールで OnCall 自作スタックを組む — PyShift・OR-Tools・GoAlert の役割と選び方