信頼性
信頼性とは、周囲が失敗しているときでもシステムを稼働させ続けるための規律です。部分的な障害を先に設計し、正常系は自然とうまくいきます。
ルール
- すべての外部呼び出しにタイムアウトを設定する。 例外はありません。タイムアウトがないと、スレッドプール枯渇事故が発生します。
- べき等な操作のみをリトライする。 指数バックオフとジッターを併用すること。非べき等なPOSTをリトライすると、重複が発生し、クレジットカードに二重請求が行われ、2通のメールが送信されます。
- 変更を伴うパブリックAPIにはべき等性キーを使用する。 クライアントがネットワークの一時的な問題を乗り越えて安全にリトライできるようになり、サーバー側で推測する必要がなくなります。
- 外部依存関係にはサーキットブレーカーを適用する。 ダウンストリームが異常なとき、早期に失敗することで、自分自身とその依存先の両方を保護します。盲目的なリトライは1つの障害を2つに増やします。
- クリティカルパスはバルクヘッドする。 ダウンストリームごとにスレッドプール/接続プールを分離し、1つの遅いサービスが全体を圧迫しないようにします。
- グレースフルデグラデーションはハード障害に勝る。 500エラーを返すよりも、古いキャッシュ、部分的なレスポンス、または機能を制限した状態を提供します。
- 単一障害点を作らない。 すべてのステートフルコンポーネントにはマルチAZ、すべてのステートレスコンポーネントには複数レプリカ、LBのヘルスチェックは実際に異常を検出できるようにします。
- シャットダウンはグレースフルに行う。 処理中のリクエストをドレインし、プールをクローズし、バッファをフラッシュします。SIGTERM → 新規受け入れ停止 → 完了またはキャンセル → 終了。
- いつでも安全に再起動できるようにする。 任意の単一Podがいつでも、ユーザーから見える影響をゼロで強制終了可能でなければなりません。そうでないなら、それが修正すべきバグです。
- バックアップは復元して初めてテストされたと言える。 定期的に実際の復元訓練を実施してください。
危険信号
- タイムアウトが設定されていない
restTemplate.exchange(...) - 上限もバックオフもないリトライループ
- 「再デプロイすれば動く」(メモリ内の隠れた状態)
- 実際にはゼロダウンタイムではないゼロダウンタイムデプロイ(SIGTERMでリクエストがドロップする)
- 一度も実行されたことのないディザスタリカバリのランブック