TT Lab
はじめる
学ぶ 学習パス コース

Istio深化 — なぜそう流れるのか

トラフィックポリシーをクラスターに移し、あふれさせてみる

TT Labで続きを見る

目標

DestinationRuleのトラフィックポリシーをEnvoyクラスターのフィールドに変換し、外れ値検出とサーキットブレーカーが実際にどう動くかを、リクエストを送って数えます。

なぜ重要なのか

トラフィックポリシーは、設定が間違っているからではなく、動作についての期待が間違っているために、事故になります。上限はサイドカーごとに数え、外れ値検出は事後の措置で、サブセットのポリシーは親をブロックごと上書きします。変換されたクラスターと実際の動作を一度見れば、設定レビューで、この3つをすぐに指摘できます。

ステップ

  1. /root/ist2-dr/dr.yamlにDestinationRuleを書いてください。名前はratings、ネームスペースはdefault、hostはratings.default.svc.cluster.localです。trafficPolicyは、loadBalancer.simple: LEAST_REQUEST、connectionPoolのtcp.maxConnections: 3・http.http1MaxPendingRequests: 2・http.http2MaxRequests: 5、outlierDetectionのconsecutive5xxErrors: 2・interval: 5s・baseEjectionTime: 30s・maxEjectionPercent: 50です。サブセットはv2(ラベルversion: v2)が1つで、そのサブセットにだけtrafficPolicy.connectionPool.tcp.maxConnections: 1を与えます。istioctl validateの出力と終了コードを、/root/ist2-dr/01-validate.txtに入れてください(最後の行はrc=0)。
  2. /root/ist2-dr/02-map.txtに、DestinationRuleのフィールドがEnvoyクラスターのどのフィールドになるかを、7行で書いてください。1行は<trafficPolicy 아래 경로>=<클러스터 아래 경로>の形で(プレースホルダーはtrafficPolicy配下のパスとクラスター配下のパスです)、ドットでつないだパスに、配列の添字は使いません。左側は、次の7つです。loadBalancer.simple、connectionPool.tcp.maxConnections、connectionPool.http.http1MaxPendingRequests、connectionPool.http.http2MaxRequests、outlierDetection.consecutive5xxErrors、outlierDetection.baseEjectionTime、outlierDetection.maxEjectionPercentです。
  3. /root/ist2-dr/pool.yamlにEnvoyの設定を書いてください。管理ポートは9985、リスナー127.0.0.1:10085のすべてのパスを、クラスターoutbound|9080||ratings.default.svc.cluster.localへ送ります。そのクラスターは、エンドポイント3つ(127.0.0.1:8107、127.0.0.1:8108、127.0.0.1:8115)と、ステップ1の親のトラフィックポリシーを、ステップ2の対応表どおりに移したフィールド(lb_policy、circuit_breakers.thresholds1つ、outlier_detection。intervalも一緒に)を持ちます。envoy --mode validateの出力と終了コードを、/root/ist2-dr/03-validate.txtに入れてください(最後の行はrc=0)。
  4. アップストリーム3つを起動し(8107・8115はok、8108はfail)、pool.yamlでEnvoyを起動してから、localhost:9985/config_dump?resource=static_clustersから、このクラスターの値を取り出して、/root/ist2-dr/04-dump.txtに1行で書いてください。lb=<lb_policy> maxconn=<max_connections> pend=<max_pending_requests> req=<max_requests> c5xx=<consecutive_5xx> base=<base_ejection_time> pct=<max_ejection_percent>です。
  5. pool.yamlでEnvoyを起動し直した直後に、リクエスト30回を順番に送り、/root/ist2-dr/05-eject.txtに4行を書いてください。failed_requests=(30回のうち503を受け取った数)、ejected=(/clustersでhealth_flagsが/failed_outlier_checkのエンドポイントの주소:포트)、ejections_enforced_total=(統計outlier_detection.ejections_enforced_totalの値)、ejections_active=(統計outlier_detection.ejections_activeの値)です。プレースホルダーは、アドレスとポートです。
  6. pool.yamlを/root/ist2-dr/pool-subset.yamlにコピーして、サブセットv2のクラスターoutbound|9080|v2|ratings.default.svc.cluster.local(エンドポイント127.0.0.1:8115)を追加してください。Istioがステップ1のDestinationRuleで作るものと同じにします。lb_policyとoutlier_detectionは親と同じで、circuit_breakers.thresholdsは、サブセットのconnectionPoolだけから作ります。max_connections: 1、そしてサブセットが決めていないmax_pending_requests・max_requests・max_retriesは、Istioのデフォルト値4294967295です。ルートは、プレフィックス/v2をこのクラスターへ、残りはそのままです。起動し直してから、/clustersのdefault_priorityの行から読み取って、/root/ist2-dr/06-subset.txtにv2_max_connections=、v2_max_pending_requests=、default_max_pending_requests=(親クラスターの値)の3行を書いてください。
  7. pool.yamlを/root/ist2-dr/pool-cb.yamlにコピーして、クラスターoutbound|9080|slow|ratings.default.svc.cluster.localを追加してください。エンドポイントは127.0.0.1:8116(3秒かけて応答するアップストリーム)、circuit_breakers.thresholdsはmax_connections: 1、max_pending_requests: 1(Istioではtcp.maxConnections: 1、http.http1MaxPendingRequests: 1)、外れ値検出はなしです。ルートは、プレフィックス/slowをこのクラスターへ送ります。アップストリームを8116でslowとして起動し、Envoyを--concurrency 1で起動し直してから、/slowのリクエスト5つを同時に送ってください(&で起動してwait)。/root/ist2-dr/07-overflow.txtに、ok=(200の数)、overflow_503=(503の数)、pending_overflow=(slowクラスターの統計upstream_rq_pending_overflowの値)の3行を書いてください。
  8. /root/ist2-dr/08-report.mdに、lb_policy=、failures_before_eject=(ステップ5で外れる前に受けた503の数)、v2_pending_limit=(ステップ6で見たv2の待ち行列の上限)、overflow_status=(ステップ7であふれたリクエストが受け取ったコード)の4行を書き、その下に、- で始まる説明を4行以上書いてください。

参考

トラフィックポリシーの入ったDestinationRuleを書く

/root/ist2-dr/dr.yamlにDestinationRuleを書いてください。名前はratings、ネームスペースはdefault、hostはratings.default.svc.cluster.localです。trafficPolicyは、loadBalancer.simple: LEAST_REQUEST、connectionPoolのtcp.maxConnections: 3・http.http1MaxPendingRequests: 2・http.http2MaxRequests: 5、outlierDetectionのconsecutive5xxErrors: 2・interval: 5s・baseEjectionTime: 30s・maxEjectionPercent: 50です。サブセットはv2(ラベルversion: v2)が1つで、そのサブセットにだけtrafficPolicy.connectionPool.tcp.maxConnections: 1を与えます。istioctl validateの出力と終了コードを、/root/ist2-dr/01-validate.txtに入れてください(最後の行はrc=0)。

DestinationRuleは「送った後でどう扱うか」です。トラフィックポリシーは3つのブロックです。誰に送るか(loadBalancer)、一度にどれだけ受け入れるか(connectionPool)、不調なエンドポイントをいつ外すか(outlierDetection)です。サブセットの下にも、同じ形のtrafficPolicyを置けますが、それが親とどう合成されるかが、ステップ6の主題です。

7つのフィールドの対応表を作る

/root/ist2-dr/02-map.txtに、DestinationRuleのフィールドがEnvoyクラスターのどのフィールドになるかを、7行で書いてください。1行は<trafficPolicy 아래 경로>=<클러스터 아래 경로>の形で(プレースホルダーはtrafficPolicy配下のパスとクラスター配下のパスです)、ドットでつないだパスに、配列の添字は使いません。左側は、次の7つです。loadBalancer.simple、connectionPool.tcp.maxConnections、connectionPool.http.http1MaxPendingRequests、connectionPool.http.http2MaxRequests、outlierDetection.consecutive5xxErrors、outlierDetection.baseEjectionTime、outlierDetection.maxEjectionPercentです。

Envoyのクラスター設定のドキュメント(cluster.proto)で、名前が似たフィールドを探せば済みます。接続プールの上限は、Envoyでは、circuit_breakers.thresholdsの下に、優先度ごとのリストとして入ります。Istioが「接続プール」と呼ぶものと、Envoyが「サーキットブレーカー」と呼ぶものは、同じ数字です。外れ値検出のフィールド名は、consecutive5xxErrorsのように、キャメルケースからスネークケースに変わるときに、少しずつ短くなります。

対応表どおりにクラスターを立てる

/root/ist2-dr/pool.yamlにEnvoyの設定を書いてください。管理ポートは9985、リスナー127.0.0.1:10085のすべてのパスを、クラスターoutbound|9080||ratings.default.svc.cluster.localへ送ります。そのクラスターは、エンドポイント3つ(127.0.0.1:8107、127.0.0.1:8108、127.0.0.1:8115)と、ステップ1の親のトラフィックポリシーを、ステップ2の対応表どおりに移したフィールド(lb_policy、circuit_breakers.thresholds1つ、outlier_detection。intervalも一緒に)を持ちます。envoy --mode validateの出力と終了コードを、/root/ist2-dr/03-validate.txtに入れてください(最後の行はrc=0)。

circuit_breakers.thresholdsはリストです。優先度(DEFAULT・HIGH)ごとに上限を別に与えられるからです。IstioはDEFAULT1つだけを使います。時間の値は、30sのように、単位を付けた文字列で書きます。8108は、常に503を返すエンドポイントとして使う場所です(ステップ5)。

config_dumpから6つの値を読み直す

アップストリーム3つを起動し(8107・8115はok、8108はfail)、pool.yamlでEnvoyを起動してから、localhost:9985/config_dump?resource=static_clustersから、このクラスターの値を取り出して、/root/ist2-dr/04-dump.txtに1行で書いてください。lb=<lb_policy> maxconn=<max_connections> pend=<max_pending_requests> req=<max_requests> c5xx=<consecutive_5xx> base=<base_ejection_time> pct=<max_ejection_percent>です。

ファイルに書いたものと、Envoyが読み込んだものを突き合わせるステップです。本番でistioctl proxy-config cluster <파드> --fqdn … -o json(プレースホルダーはPod名です)が見せるものが、まさにこのダンプです。.configs[].cluster | select(.name==…)で選んでから、jqの文字列補間で1行を作ってください。ダンプはデフォルト値のフィールドを省略するので、値が欠けていれば、設定に入っていないということです。

外れ値検出は、失敗を経験した後で外す

pool.yamlでEnvoyを起動し直した直後に、リクエスト30回を順番に送り、/root/ist2-dr/05-eject.txtに4行を書いてください。failed_requests=(30回のうち503を受け取った数)、ejected=(/clustersでhealth_flagsが/failed_outlier_checkのエンドポイントの주소:포트)、ejections_enforced_total=(統計outlier_detection.ejections_enforced_totalの値)、ejections_active=(統計outlier_detection.ejections_activeの値)です。プレースホルダーは、アドレスとポートです。

外れ値検出は、やられてから外す装置です。1つのエンドポイントが連続してconsecutive5xxErrors回5xxを出したときに、baseEjectionTimeの間、負荷分散から外します。そのため、その回数だけは、ユーザーが先に503を受けます。外れた後は、残りの2つだけが受けるので、503が止まります。maxEjectionPercent: 50は、3つのうち1つまでしか外せないという意味です。/clustersの行は、클러스터::주소::health_flags::값(プレースホルダーはクラスター、アドレス、値です)の形です。

サブセットのポリシーは、ブロックごと上書きする

pool.yamlを/root/ist2-dr/pool-subset.yamlにコピーして、サブセットv2のクラスターoutbound|9080|v2|ratings.default.svc.cluster.local(エンドポイント127.0.0.1:8115)を追加してください。Istioがステップ1のDestinationRuleで作るものと同じにします。lb_policyとoutlier_detectionは親と同じで、circuit_breakers.thresholdsは、サブセットのconnectionPoolだけから作ります。max_connections: 1、そしてサブセットが決めていないmax_pending_requests・max_requests・max_retriesは、Istioのデフォルト値4294967295です。ルートは、プレフィックス/v2をこのクラスターへ、残りはそのままです。起動し直してから、/clustersのdefault_priorityの行から読み取って、/root/ist2-dr/06-subset.txtにv2_max_connections=、v2_max_pending_requests=、default_max_pending_requests=(親クラスターの値)の3行を書いてください。

公式ドキュメントは、「サブセットレベルのトラフィックポリシーが、DestinationRuleレベルの該当する設定を上書きする」とだけ書いています。上書きする単位は、connectionPool・loadBalancer・outlierDetection・tlsのようなブロックです。そのため、サブセットにtcp.maxConnectionsだけを書くと、connectionPoolのブロックがまるごと変わって、親のhttpの上限(2・5)は、v2に引き継がれません。空いた部分は、Istioが入れるデフォルト値(4294967295、事実上無制限)です。Envoy自体のデフォルト値1024よりもゆるいです。/clustersから、<이름>::default_priority::max_pending_requests::<값>(プレースホルダーは名前と値です)の行を探してください。

サーキットブレーカーが実際に開くのを数える

pool.yamlを/root/ist2-dr/pool-cb.yamlにコピーして、クラスターoutbound|9080|slow|ratings.default.svc.cluster.localを追加してください。エンドポイントは127.0.0.1:8116(3秒かけて応答するアップストリーム)、circuit_breakers.thresholdsはmax_connections: 1、max_pending_requests: 1(Istioではtcp.maxConnections: 1、http.http1MaxPendingRequests: 1)、外れ値検出はなしです。ルートは、プレフィックス/slowをこのクラスターへ送ります。アップストリームを8116でslowとして起動し、Envoyを--concurrency 1で起動し直してから、/slowのリクエスト5つを同時に送ってください(&で起動してwait)。/root/ist2-dr/07-overflow.txtに、ok=(200の数)、overflow_503=(503の数)、pending_overflow=(slowクラスターの統計upstream_rq_pending_overflowの値)の3行を書いてください。

アップストリームが遅いと、接続1つが3秒間ふさがります。その間に来たリクエストは、待ち行列に並び、待ち行列まで埋まると、Envoyはアップストリームへ行ってみることもなく、すぐに503とx-envoy-overloaded: trueヘッダーを返します。そのため、受け入れられる数は「接続数+待ち行列の長さ」です。順番に送ると、1つずつ終わって何もあふれないので、必ず同時に送ってください。統計は累積なので、起動し直した直後に1回だけ数えます。

DestinationRuleの変換表にまとめる

/root/ist2-dr/08-report.mdに、lb_policy=、failures_before_eject=(ステップ5で外れる前に受けた503の数)、v2_pending_limit=(ステップ6で見たv2の待ち行列の上限)、overflow_status=(ステップ7であふれたリクエストが受け取ったコード)の4行を書き、その下に、- で始まる説明を4行以上書いてください。

値は前のステップのファイルから移してください。説明の行には、「DestinationRuleを直すとき、何に気をつけるか」を書くとよいです。特に、サブセットのポリシーが親を上書きする方式は、レビューのときに見落としやすいです。