オンコール体制 — On-Call SSoT
このドキュメントが Parky における on-call 運用の SSoT (Single Source of Truth)。 Severity 区分・Channel 振り分け・Format 標準は notification-strategy.md に従う。本 doc は 「誰が、どの device で、何分以内に、何をする」を扱う。
想定読者: Parky 開発担当本人 (1 人運用フェーズ)、将来チーム拡大時の新 joiner。
1. 設計原則 — 1 行で
1 人運用 + Discord push 二重化 で「P0 を 15 分以内に必ず ack できる」状態を作る。 PagerDuty / Opsgenie 等の専用 incident management SaaS は MAU 1 万到達まで導入しない (コスト判断)。 代わりに Discord mobile push + GH Actions failure email + Cloudflare email alert を物理経路を分けて二重化し、SPOF を作らない。
参照: project_parky_finance_state_2026_04 (残予算 2,000 万 / Burn 120 / Runway 16.7 ヶ月 → 月額 SaaS 追加は最低限)
2. ロール定義
2.1 Primary on-call
| 項目 | 内容 |
|---|---|
| 氏名 | Parky 開発担当 (当面 1 人体制) |
| 連絡先 | [email protected] (email backup 配信先と一致) |
| 受信 device 1 | iPhone Discord アプリ (#p0-alerts の push 通知 ON、機内モード時は失敗扱い) |
| 受信 device 2 | デスクトップ Discord (作業時間中、音声通知 ON) |
| 受信 device 3 (backup) | Gmail ([email protected]) — Resend 経由の P0 backup email + GH Actions failure email |
| 勤務時間外 | 当面 24/7 自分。睡眠中の P0 は「起きてから対応」を許容 (§3 参照) |
2.2 Secondary on-call
- 現状: 不在 (1 人運用)
- 将来 (Phase 3, チーム拡大時): rotation を組む。週次担当を
#p1-opsの channel topic に明示。
2.3 担当範囲
| 領域 | Primary | Secondary |
|---|---|---|
| API (Workers / Hyperdrive / R2) | o | - |
| Web (Pages / public / portal) | o | - |
| Supabase DB / migration | o | - |
| Cron (PLACES_WEEKLY 等) | o | - |
| Mobile (Flutter リリース後) | o | - |
| Security (secret leak / CVE) | o | - |
3. 応答 SLA (severity 別)
| Severity | 応答 SLA | 解決目標 | 備考 |
|---|---|---|---|
| P0 | 15 分以内 ack | 4 時間以内 復旧 | Discord で「対応開始」reply or :eyes: reaction。睡眠中も起きたら最優先 |
| P1 | 4 時間以内 | 翌営業日 中 | 営業時間内のみ、夜間は朝対応 OK |
| P2 | 翌営業日 | 1 週間以内 | 朝の Discord チェックでまとめて triage |
| P3 | 週次 review | 月次 review | 月曜 18:00 JST の #p3-insights digest をまとめて確認 |
Ack の定義 (P0)
- Ack = 「自分が認識して対応に入った」を Discord で表明すること
- 具体的には次のいずれか (どれか 1 つで OK):
- Alert message に
:eyes:reaction を付ける - Alert message に reply で「対応開始」「調査中」等を投稿
#p0-alertsで新規 message を投稿し、statusを共有
- Alert message に
Ack ≠ 解決。Ack は「見えたよ」のシグナル。解決時は別途 thread で
:white_check_mark:reaction or 「resolved」reply。
4. P0 受信の二重化 (重要)
監査指摘: 「アラートが Discord のみ → P0 を 7 日後に発見した PLACES_WEEKLY 失敗の前例あり」 → 物理経路を分けた 3 経路冗長化 で対策する。
経路 A: Discord #p0-alerts への @here mention (一次)
- 配線元:
synthetic-healthcheck.yml/ Sentry alert rule / GHA failure / DLQ monitor - 形式: notification-strategy.md §5 の Discord embed format に従う
- mention 形式: P0 のみ
@here\n:rotating_light: **[P0] ...**(mention は別行) 参照: feedback_discord_p0_mention_newline
経路 B: iPhone Discord アプリの push 通知 (一次の届け先)
これが「夜中に音で気付く」物理経路。最重要。
iPhone 設定手順 (初回のみ、5 分)
- iOS 設定 → 通知 → Discord
- 通知許可: ON
- サウンド: ON (集中モード時も鳴らすために重要)
- バッジ: ON
- ロック画面 / 通知センター / バナー: 全 ON
- Discord アプリ → ユーザー設定 (歯車) → 通知
- Push Notifications: Enabled
- In-App Sounds: Enabled
- Discord → Parky サーバー →
#p0-alertschannel- channel 名長押し → Notification Settings
- 設定: All Messages
- Mute: Off (絶対 mute しない)
@everyoneand@here: Allow (デフォルト ON、これを切ると意味なし)
- iOS 集中モード (Focus / Sleep) の例外設定
- 設定 → 集中モード → 睡眠 → 通知を許可するアプリ → Discord を追加
- これがないと睡眠モード中の P0 通知が無音化される (致命的)
- 動作確認
- 月次 smoke workflow (
.github/workflows/oncall-smoke.yml参照、§7 参照) で iPhone に通知が届くことを確認
- 月次 smoke workflow (
#p1-ops (P1) の push 設定
- channel 名長押し → Notification Settings → Only @mentions
- P1 は mention なしなので push は飛ばないが、Discord アプリを開けば未読が見える状態を維持
経路 C: Email backup (二次、Discord 障害時の保険)
C-1. Resend 経由 P0 backup email
- 配線: notification-strategy.md §7 Layer 3
- 条件: severity = P0 かつ Discord webhook 全 retry 失敗時のみ送信
- 送信先:
[email protected](固定) - Subject:
[P0 BACKUP] <title> - iOS Mail app の通知を ON にしておけば push で気付ける
C-2. GitHub Actions failure email (built-in)
GitHub の標準機能で workflow failure を email 通知。Discord webhook が落ちている場合の最終ラインとして稼働。
設定手順 (GitHub.com):
- github.com → 右上アバター → Settings → Notifications (左 sidebar)
- Actions セクション
- Email: ON
- Notify me for: Only failed workflows にチェック
- Send to:
[email protected](primary email)
- iOS Gmail / Mail アプリの通知 ON
注意: GH の email 通知は per-user 設定。複数人運用時は全員が個別に設定する。
C-3. Cloudflare email alert (任意、Phase 2 で導入検討)
- CF dashboard → Notifications → Add → 各 alert (Workers outage / Pages deploy fail / R2 quota / WAF surge)
- Destination: email (
[email protected]) - 既に notification-strategy.md §4 に webhook 配線済みだが、CF 側 webhook 障害時の保険として email を multi-destination で並走 させる
- 工数: CF dashboard で 10 分
経路の優先度と SLA
| 経路 | 想定遅延 | 障害時の挙動 |
|---|---|---|
| A: Discord webhook | < 5 sec | Layer 2 retry → 失敗時 Layer 3 へ |
| B: iPhone Discord push | < 30 sec | 経路 A 成功時のみ。Discord アプリ次第 |
| C-1: Resend backup email | 1-3 min | 経路 A 全失敗時のみ起動 |
| C-2: GHA failure email | 1-5 min | GHA workflow 失敗時に常時並走 |
| C-3: CF email (任意) | 1-2 min | CF 由来の P0 のみ、CF Discord webhook と並走 |
5. エスカレーション手順
5.1 1 人運用フェーズ (現状) のフロー
P0 alert 発火
↓
├─ 起きていて気付いた? → 即 ack → 対応 → resolve
├─ 寝ていて気付かない? → iPhone 経路 B push で起こす
├─ iPhone も気付かなかった (集中モード等) → 経路 C-1 / C-2 email backup で起こす
└─ 全経路失敗 (確率限りなく 0) → 翌朝の DLQ digest (#p3-insights, 月曜 18:00 JST) で発見
→ postmortem 必須 (経路の見直し)
5.2 即時必要な P0 vs 翌日対応 OK な P1/P2 の判別フロー
「夜中の Discord 通知音で起きた」状況での 30 秒判断:
通知タイトルの先頭を見る
↓
├─ [P0] → 起きて即対応 (ack 15 分以内)
├─ [P1] → アプリで内容だけ確認、朝対応で OK (mention なしで起こされる事はない想定)
└─ [P2] [P3] → 翌朝
重要: P0 の判定は alert rule 作成時に確定済み (notification-strategy.md §2)。 通知音で起きてから「これ P0?」と悩むのは時間の無駄。先頭の
[P0]表記だけを信じる。
5.3 Primary が応答できない場合
1 人運用フェーズ (現状)
不在パターン 1: 短期不在 (旅行 / 休暇 < 1 週間)
- 事前:
#p1-opsの channel topic に「YYYY-MM-DD ~ YYYY-MM-DD 不在、緊急時は status page で告知」と明記 - 期間中: 自動 maintenance window にはせず、P0 のみは可能な限り対応 (海外旅行中も iPhone は持つ)
- 期間中の P1 以下: 帰宅後に triage、ユーザー影響継続中なら status page 告知 (sentry 流ステータスページ を将来構築)
- 事前:
不在パターン 2: 緊急不能 (病気 / 事故)
- 第三者 (家族 / 共同創業者候補) が
#p0-alertsを見られる状態を作る (Discord ID 共有) - 第三者の役割: 「alert が来ている」事実だけ気付く役。技術対応はできなくて OK
- 第三者経由でユーザー告知 → 復旧は復帰後
- 第三者 (家族 / 共同創業者候補) が
Phase 3 (チーム拡大後)
- Primary が 30 分以内に ack しない場合、Secondary を escalate
- Discord webhook で
@<secondary-user>mention を 30 分後に自動 fire (Phase 3 で実装、現状未配線) - 週次 rotation を
#p1-opschannel topic に固定明記
6. 月次レビュー
6.1 レビュー時刻
- 毎月 1 日 09:00 JST (土日祝なら次の平日)
- Calendar に reminder 登録 (Google Calendar 等)
- 所要時間: 30-60 分
6.2 レビュー内容
過去 30 日を対象に、以下を .work/parky/oncall-review/YYYY-MM-DD_oncall_review.md に記録:
| 項目 | 確認内容 |
|---|---|
| 発火した P0 件数 | admin.notification_failures + Sentry alerts dashboard + GHA run history を集計 |
| 応答時間の中央値 | 各 P0 の「発火 → ack」までの時間 (Discord reaction timestamp で測定) |
| SLA 達成率 | 15 分以内 ack の割合 (目標 100%) |
| 見逃し / 遅延 alert | 30 分以上 ack 遅延した P0 を全件列挙 → 原因分析 (デバイス / 設定 / 経路 / 集中モード) |
| 抜けた監視 | 「あの障害が alert で気付けなかった」事象を列挙 → 該当 alert rule を追加 |
| 誤検知 (false alarm) | P0 で発火したが実害なかった事象 → 閾値見直し |
| 抑制ミス | Cascade 抑制が効かず重複通知になった事象 |
| DLQ 蓄積件数 | admin.notification_failures で status='failed' の件数 (目標 < 5/月) |
| smoke test 結果 | oncall-smoke.yml (§7) の月次実行結果 (push が iPhone に届いたか) |
6.3 SLO Error Budget との連動
- slo-error-budget.md の burn rate と本レビューの SLA 達成率を併せて見る
- どちらかが目標未達 → 次月の優先タスク (例: alert rule 追加 / 閾値再調整 / 経路強化)
6.4 Action Item 起票
- 改善項目は GitHub Issue で起票 (label:
oncall-review, milestone: 翌月) - SMART 形式: Owner / 期限 / 検証方法を明記 (incident-response.md 同準拠)
7. 受信確認用 smoke workflow (任意)
P0 push が本当に iPhone に届くかを月 1 回検証する。
7.1 配線
- Workflow: .github/workflows/oncall-smoke.yml
- Cron: 毎月 1 日 09:00 JST (
0 0 1 * *UTC) - 配信先:
#p0-alerts(P0 channel) に@here付き smoke message - 形式:
[SMOKE] On-call 受信確認 YYYY-MM-DD+ 「iPhone push が届いた? Discord で:eyes:reaction を付けてください」
7.2 確認手順
- 月初にこの smoke message が iPhone に push される
- push 通知音で気付いたら
:eyes:reaction を付ける - 24 時間以内に reaction が付かない → 翌月の月次レビューで「設定見直し」を action item 化
7.3 手動 trigger
緊急時 (デバイス変更後等) は workflow_dispatch で即時実行可能:
gh workflow run oncall-smoke.yml
8. 移行プラン
Phase 1 — 現状 (~MAU 1 万)
- コスト: $0/mo
- 構成: Discord webhook + iPhone push + Resend backup email + GHA email + (任意) CF email
- 想定 SLA: P0 15 分 ack 95% 以上
Phase 2 — MAU 1 万到達後 (Parky phase_strategy Phase 1 中盤)
- PagerDuty Pro plan 検討 ($21/user/mo)
- 機能: SMS / 電話 escalation / on-call rotation / 自動 escalation
- 採用条件: Discord push の miss 率 > 5% (月次レビュー集計) または P0 件数 > 月 5 件
- 不採用条件: 上記未達なら継続して Discord メイン
- Status page 構築 (Better Uptime Free or Atlassian Statuspage Free)
- customer-facing 透明性の向上 (status.parky.co.jp)
- on-call 不在時のユーザー告知導線
Phase 3 — チーム拡大後 (2-3 人体制)
- Rotation 開始: 週次 primary / secondary を
#p1-opstopic で明記 - 自動 escalation 配線: Primary が 30 分以内 ack しないと Secondary に mention
- PagerDuty 本格運用: rotation を PagerDuty 側で管理、Discord は配信先の 1 つに格下げ
- 月次レビュー: チーム全員参加、postmortem は必ず blameless
9. 関連 doc
このドキュメントが on-call 運用 SSoT。以下は関連 runbook / 設計:
- notification-strategy.md — 通知配線 SSoT (severity / channel / format / DLQ)
- incident-response.md — P0/P1 発火後の対応コマンド集 + エスカレーションパス
- postmortem-template.md — P0 解決後の postmortem 雛形
- slo-error-budget.md — SLO 定義 + burn rate alert + 月次レビュー連動
- synthetic-healthcheck.md — 5 分粒度の外部監視 (P0 経路 A の主要発火源)
- secret-rotation.md — Webhook / email provider key の rotation 手順
監査根拠
- 2026-04-29 監査指摘: 「Discord 単一経路で P0 見逃しリスク (PLACES_WEEKLY 7 日後発見の前例)」 → 本 doc 作成で iPhone push + email backup の二重化を文書化
変更履歴
- 2026-04-30: 初版作成。M-4 監査対応 (オンコール体制 doc 整備)。 Discord + iPhone push + email 三重化、月次レビュー、PagerDuty 移行プランを定義。