概要
EnvoyやIstio では Outlier Detection という、連鎖障害を起こさないように不具合のあるホストを切り離す仕組みがあります。
- ヘルスチェックで外されるほど完全に落ちてはいない
- しかしサービスとしては無視できないほどエラーレートが高い
といった状況でうまく機能してくれます。
今回はこの機能の説明と、具体的な設定・挙動について紹介します。
環境
- Istio: 1.28.2
- Envoy: v1.37.0
前提知識
Envoy の Health Check と Outlier Detection の違い
まず混乱しやすい Health Check との違いを話します。
Envoy における Health Check と Outlier Detection の主な違いは、チェックの「能動性」にあります。
Health Check
能動的(Active)なチェックです。
自ら専用のリクエスト(L7のパス指定やL4の疎通確認)を定期的に送信し、エンドポイントが生存しているかを確認します。
実際のトラフィックが流れていなくても、強制的に状態を確認し続けます。



なのでホストが完全に落ちていれば必ず外されますが、中途半端に生きてるときは外してくれません。
Outlier Detection
受動的(Passive)なチェックです。
実際のユーザートラフィックの挙動を監視(観察)し、エラー率の上昇や連続した失敗などの異常を検知して、一時的にそのエンドポイントを負荷分散から外します。
実際にリクエストが流れて初めて異常に気づく仕組みです。



パラメーター次第ですが、何回エラーが出たら一時的に外す、といった設定が柔軟にできるため、中途半端に生きていてもハンドリングできます。
具体的な使い方
Envoy で OutlierDetection を使ってみる
Envoy では Cluster 設定の中で outlier_detection フィールドを定義します。
clusters: - name: my_service connect_timeout: 0.25s type: STRICT_DNS # ... (その他の設定) ... outlier_detection: consecutive_5xx: 5 # 5回連続で 5xx エラーが発生したら切り離す interval: 10s # 10秒ごとにスイープ(チェック)を行う base_ejection_time: 30s # 切り離す基本時間(30秒) max_ejection_percent: 10 # 切り離すホストの最大割合(デフォルト 10%)
パラメータ説明
| フィールド (Envoy) | 説明 | デフォルト値 |
|---|---|---|
consecutive_5xx |
ホストを切り離す判断基準となる、連続した 5xx エラー数。 | 5 |
interval |
異常検知のスイープを行う時間間隔。 | 10s |
base_ejection_time |
ホストが切り離される最短時間。切り離し回数が増えるとこの値の倍数で時間が延びる。 | 30s |
max_ejection_percent |
負荷分散プールから切り離すことができるホストの最大割合。 | 10% |
consecutive_gateway_failure |
ゲートウェイエラー(502, 503, 504)が連続した際の閾値。 | 5 (Envoy) 1 (Istioデフォルト動作時) |
split_external_local_origin_errors |
外部エラー(5xx)とローカル起因のエラー(接続タイムアウト等)を区別するか。 | false |
consecutive_local_origin_failure |
ローカル起因の失敗が連続した際の閾値。 | 5 |
max_ejection_time |
ホストが切り離される最大時間。 | 無制限 |
enforcing_consecutive_5xx |
consecutive_5xx による切り離しを実際に実行する確率(%)。 |
100% |
failure_percentage_threshold |
ホストの全トラフィックに対する失敗率の閾値(%)。 | 85% |
min_health_percent |
パニックモードに入らずに稼働し続けるべき最小の健康なホスト의 割合。 | 50% |
Istio で OutlierDetection を使ってみる
Istioだと DestinationRuleで次のように設定します。
apiVersion: networking.istio.io/v1 kind: DestinationRule metadata: name: reviews-cb-policy spec: host: reviews.prod.svc.cluster.local trafficPolicy: connectionPool: tcp: maxConnections: 100 http: http2MaxRequests: 1000 maxRequestsPerConnection: 10 outlierDetection: consecutive5xxErrors: 5 interval: 10s baseEjectionTime: 30s maxEjectionPercent: 10
Envoy とのデフォルト値の違い
Istio の DestinationRule では、いくつかのパラメータで Envoy の標準的なデフォルト値と異なる設定が採用されています。
| パラメータ | Envoy デフォルト | Istio デフォルト | 備考 |
|---|---|---|---|
minHealthPercent |
50% | 10% | Istio はより多くのホストが異常になっても切り離しを継続する傾向にあります。 |
consecutiveGatewayErrors |
5 | 1 (実質) | Istio では 5xx エラーの一部として扱われ、より敏感に反応する設定が一般的です。 |
動作確認
それでは実際の挙動を確認する構成を作成します。
アーキテクチャ
次のような構成で動作を確認します。

backend 1はリクエストでエラーレートを変更できるようにしています。
設定
まずはEnvoyの設定です。
envoy.yaml
outlier_detection を設定したクラスタを定義します。
static_resources: listeners: - address: socket_address: { address: 0.0.0.0, port_value: 10000 } filter_chains: - filters: - name: envoy.filters.network.http_connection_manager typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager stat_prefix: ingress_http route_config: name: local_route virtual_hosts: - name: service domains: ["*"] routes: - match: { prefix: "/" } route: { cluster: backend_cluster } http_filters: - name: envoy.filters.http.router typed_config: {} clusters: - name: backend_cluster type: STRICT_DNS lb_policy: ROUND_ROBIN load_assignment: cluster_name: backend_cluster endpoints: - lb_endpoints: - endpoint: { address: { socket_address: { address: backend1, port_value: 8080 }}} - endpoint: { address: { socket_address: { address: backend2, port_value: 8080 }}} outlier_detection: consecutive_5xx: 3 consecutive_gateway_failure: 2 interval: 5s base_ejection_time: 15s max_ejection_percent: 50 split_external_local_origin_errors: true enforcing_consecutive_5xx: 100 enforcing_consecutive_gateway_failure: 100 success_rate_minimum_hosts: 5 success_rate_request_volume: 100 success_rate_stdev_factor: 1900
この設定では、単純な連続エラー(consecutive_5xx)だけでなく、ゲートウェイエラー(consecutive_gateway_failure)や、クラスタ全体の平均成功率と比較して異常なホストを特定する成功率ベースの検知(success_rate_...)も有効にしています。
また、split_external_local_origin_errors: true を設定することで、ネットワーク層のタイムアウト等の「ローカル起因の失敗」と、アプリケーションが返す 5xx 等の「外部起因の失敗」を切り分けてカウントしています。
実行
期待する挙動は以下です
- k6でリクエストを流す
- 正常にリクエストが流れていることを確認
backend 1のエラーレートを上昇させる- envoyが
backend 1を切り離す backend 1のエラーレートを戻し、復旧する- エラーレートが大きく上がらない状態が担保される
1. k6でリクエストを流す
k6を実行します。
$ k6 run k6/script.js
2. 正常にリクエストが流れていることを確認
エラーも特に出ず、2台とも正常にリクエストが流れています。

3. backend 1 のエラーレートを上昇させる
backend 1のエラーレートを50%にします。
curl -X POST "http://localhost:8081/error-rate?rate=50"

4. envoyがbackend 1を切り離す
envoyがbackend 1を切り離し、アクティブなホストが1つだけになります。

リクエストもbackend 2に偏ります。

トータルとしては若干エラーレートが出るようになります。

5. backend 1のエラーレートを戻し、復旧する
backend 1のエラーレートを0%にします。
curl -X POST "http://localhost:8081/error-rate?rate=50"
backend 1が復旧し、envoyにルーティングされるようになります。

リクエストも再び分散されます。

6. エラーレートが大きく上がらない状態が担保される
特定のサービスが50%ものエラーレートになりましたが、全体でのエラーレートは1%にも満たない状態に抑えられました。

█ TOTAL RESULTS
checks_total.......: 113724 189.491626/s
checks_succeeded...: 50.00% 56862 out of 113724
checks_failed......: 50.00% 56862 out of 113724
✗ is status 200
↳ 99% — ✓ 56796 / ✗ 66
✗ is status 5xx
↳ 0% — ✓ 66 / ✗ 56796
その他
サンプルコード
今回のサンプルコードはこちら
なぜ外してからもエラーレートが微少に出る?
外してからもEnvoyは一定時間後に復旧できるかどうかのチェックを行うため、トラフィックを backend 1 にも流します。
まだ backend 1 のエラーレートが高いままであればまたしばらくリクエストを流しませんが、その期間が過ぎると再びチェックのためリクエストを流します。
それによってトータルで微少にエラーが生まれています。
まとめ
Outlier Detection を使うことで特定のサービスでエラーレートが大きく上昇しても、障害影響を局所かすることが可能になります。