サーキットブレーカーを設定したのに効かない理由
一言でいうと
DestinationRuleのtrafficPolicyは、Envoyクラスターのフィールドへ移されます。loadBalancerはlb_policy、connectionPoolはcircuit_breakers.thresholds、outlierDetectionはoutlier_detectionです。移された数字は、そのまま動作します。上限を超えたリクエストは、アップストリームへ行くこともなく503になり、連続して失敗したエンドポイントは、失敗を経験した後で外れます。
なぜ必要なのか
「サーキットブレーカーを設定したのに効果がない」「外れ値検出を有効にしたのにエラーが出続ける」という質問が、尽きることなく出てきます。ほとんどは、設定が間違っているのではなく、期待が間違っているのです。サーキットブレーカーの上限は、サービス全体ではなく、呼び出すサイドカー1つのクラスターごとに別々に数えます。外れ値検出は、予防ではなく事後の措置なので、決めておいた回数だけは、ユーザーが先に当たります。そして、サブセットにポリシーを別に与えると、親のポリシーが、フィールド1つ1つではなくブロックごとに上書きされ、書いていない上限が静かに消えます。
これらは、YAMLをいくら眺めても見えません。Envoyクラスターに変換された姿を見て、実際にあふれさせたり失敗させたりして初めて見えます。
どう動くのか
| DestinationRule | Envoyクラスター |
|---|---|
loadBalancer.simple |
lb_policy |
connectionPool.tcp.maxConnections |
circuit_breakers.thresholds[0].max_connections |
connectionPool.http.http1MaxPendingRequests |
…max_pending_requests |
connectionPool.http.http2MaxRequests |
…max_requests |
outlierDetection.consecutive5xxErrors |
outlier_detection.consecutive_5xx |
outlierDetection.baseEjectionTime・maxEjectionPercent |
base_ejection_time・max_ejection_percent |
書かなかった上限は、Envoyのデフォルト値1024ではなく、Istioが入れる4294967295(事実上無制限)になります。サブセットはクラスターを別に持つので、ポリシーも別に適用されますが、合成する規則は、公式ドキュメントの1行、つまりサブセットのポリシーが該当する設定を上書きする、がすべてです。上書きする単位は、connectionPool・loadBalancer・outlierDetection・tlsのようなブロックです。
サーキットブレーカーの計算は単純です。接続がmax_connections個ふさがっていれば、新しいリクエストは待ち行列に並び、待ち行列がmax_pending_requestsを超えれば、Envoyがすぐに503とx-envoy-overloaded: trueを返します。外れ値検出は、エンドポイントごとに連続した失敗を数え、consecutive_5xxに達したら、base_ejection_timeの間、負荷分散から外します。ただし、max_ejection_percentが、一度に外せる割合を制限します。全部外すと、送る先がなくなるからです。
現場での姿
負荷テストでサーキットブレーカーが開かない。上限は、呼び出す側のサイドカーごとに数えます。呼び出すPodが10個なら、サービスが実際に受ける同時接続は、上限の10倍です。逆に、呼び出すPodが1つだけのバッチジョブは、小さな上限にすぐ引っかかります。
カナリアのサブセットだけ、たまに503が降ってくる。サブセットにtcp.maxConnectionsを1つ書いたら、親のhttp1MaxPendingRequestsが消えた場合か、逆に、サブセットに小さなhttpの上限を書いて忘れた場合です。proxy-config clusterで、サブセットクラスターの上限を、親と並べて見ます。
外れ値検出を有効にしたのに、ダッシュボードの5xxが0にならない。外れる前の失敗と、外れた時間が終わって戻ってきた後の失敗が、見え続けます。外れ値検出は、エラーをなくす装置ではなく、長引かせない装置です。リトライと一緒に使って初めて、ユーザーが見るエラーが減ります。
公式ドキュメント: DestinationRule・Circuit breaking・Envoy cluster
次のラボですること
トラフィックポリシーの入ったDestinationRuleを書き、フィールドの対応表を作って、そのままクラスターを立てた後、config_dumpで読み直します。常に失敗するエンドポイントを混ぜて、外れ値検出が何回当たった後で外すかを数え、サブセットのポリシーが親をブロックごと上書きすることを/clustersで確認し、遅いアップストリームに同時にリクエストを集中させて、サーキットブレーカーの503を数えます。