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

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

サブセットをクラスターとして立て、名前で追跡する

TT Labで続きを見る

目標

DestinationRuleからクラスター名を規則で導出し、その名前のままEnvoyを構成して、サブセットごとのルーティング・統計名・ないサブセットの503を、自分で確認します。

なぜ重要なのか

Istioの障害調査の半分は、proxy-config clustersと統計を読む作業です。クラスター名は規則で作られるので、規則を知っていれば、名前だけを見て、サービス・ポート・サブセットがわかり、「サブセットがない」「エンドポイントが空」「統計名が変わった」を、数秒で見分けられます。

ステップ

  1. /root/ist2-name/dr.yamlにDestinationRuleを書いてください。名前はreviews、ネームスペースはdefault、hostはreviews.default.svc.cluster.local、サブセットはv1(ラベルversion: v1)とv2(ラベルversion: v2)です。istioctl validate -f /root/ist2-name/dr.yamlの出力と終了コードを、/root/ist2-name/01-validate.txtに入れてください(最後の行はrc=0)。
  2. サービスreviewsは、ポート9080を使います。/root/ist2-name/dr.yamlから、サイドカーが持つことになるクラスター名を4つ、/root/ist2-name/02-names.txtに1行に1つずつ書いてください。ほかのPodがreviewsへ出ていくときに使うものが3つ(サブセットのないもの1つと、サブセットごとに1つ)、そしてreviewsのPod自身が、入ってくるリクエストをアプリへ渡すときに使うものが1つです。
  3. /root/ist2-name/name.yamlにEnvoyの設定を書いてください。管理ポートは9983、リスナーは127.0.0.1:10083(HTTP)、route_configの名前は9080で、すべてのパスをoutbound|9080|v1|reviews.default.svc.cluster.localへ送ります。クラスターは、出ていく側の3つです。outbound|9080||reviews.default.svc.cluster.local(エンドポイントは127.0.0.1:8103と127.0.0.1:8104の2つ)、outbound|9080|v1|reviews.default.svc.cluster.local(8103だけ)、outbound|9080|v2|reviews.default.svc.cluster.local(8104だけ)です。アップストリーム2つを8103・8104でokとして起動し、Envoyを起動してから、curl localhost:10083/の結果を、/root/ist2-name/03-v1.txtにstatus=とbody=の2行で書いてください。
  4. /root/ist2-name/name.yamlを/root/ist2-name/split.yamlにコピーして、ルートを重み付きルートに変えてください。outbound|9080|v1|reviews.default.svc.cluster.localが75、outbound|9080|v2|reviews.default.svc.cluster.localが25です。Envoyをsplit.yamlで、--concurrency 1を付けて起動し直してから、curl localhost:10083/をちょうど40回送り、応答本文でどのサブセットに行ったかを数えて、/root/ist2-name/04-split.txtにv1=、v2=、total=の3行で書いてください。
  5. /root/ist2-name/split.yamlのoutbound|9080|v2|reviews.default.svc.cluster.localクラスターに、alt_stat_name: outbound_9080_v2_reviewsを追加し(ほかはそのまま)、起動し直してから、リクエストを20回以上送ってください。管理ポートの/statsから2行をそのまま移して、/root/ist2-name/05-stats.txtに保存してください。v1クラスターのupstream_rq_200の行と、v2クラスターのupstream_rq_200の行(いまは変わった名前で出ます)です。
  6. /root/ist2-name/split.yamlを/root/ist2-name/missing.yamlにコピーして、2つのことを変えてください。route_configにvalidate_clusters: falseを追加し、既存のルートの前に、パスのプレフィックス/v3をoutbound|9080|v3|reviews.default.svc.cluster.localへ送るルートを追加します(このクラスターは作りません)。そして、missing.yamlからvalidate_clusters: falseの行だけを除いたコピー/root/ist2-name/missing-strict.yamlも作ってください。missing.yamlで起動してから、curl localhost:10083/v3を1回送り、/root/ist2-name/06-missing.txtに3行を書いてください。status=(HTTPコード)、no_cluster=(統計http.outbound_0.0.0.0_9080.no_clusterの値)、strict_rc=(missing-strict.yamlをenvoy --mode validateした終了コード)です。
  7. 起動しているEnvoyのlocalhost:9983/clustersから、reviewsの3つのクラスターが、それぞれエンドポイントをいくつ持っているかを数えて、/root/ist2-name/07-endpoints.txtに<클러스터 이름> <개수>の形で3行書いてください(プレースホルダーはクラスター名と個数です)。
  8. /root/ist2-name/08-report.mdに、default_cluster=、inbound_cluster=、v2_stat_name=、missing_subset_code=の4行を書き(それぞれ、サブセットのない出ていく側のクラスター名、入ってくる側のクラスター名、ステップ5で変えたv2の統計名、ステップ6で受け取ったHTTPコード)、その下に、- で始まる説明を4行以上書いてください。

参考

サブセットを2つ持つDestinationRuleを書く

/root/ist2-name/dr.yamlにDestinationRuleを書いてください。名前はreviews、ネームスペースはdefault、hostはreviews.default.svc.cluster.local、サブセットはv1(ラベルversion: v1)とv2(ラベルversion: v2)です。istioctl validate -f /root/ist2-name/dr.yamlの出力と終了コードを、/root/ist2-name/01-validate.txtに入れてください(最後の行はrc=0)。

サブセットは、「同じサービスのPodのうち、このラベルが付いたもの」という名前付きのセレクターです。これ自体はトラフィックを変えず、VirtualServiceがsubset: v1で指したときに、初めて使われます。hostは短い名前でもかまいませんが、Istioは結局FQDNに解決して使います。ここでは最初からFQDNで書いておくと、次のステップの名前の導出が楽です。istioctl validateは、クラスターなしで、ファイルだけでスキーマを検査します。

規則でクラスター名を4つ導出する

サービスreviewsは、ポート9080を使います。/root/ist2-name/dr.yamlから、サイドカーが持つことになるクラスター名を4つ、/root/ist2-name/02-names.txtに1行に1つずつ書いてください。ほかのPodがreviewsへ出ていくときに使うものが3つ(サブセットのないもの1つと、サブセットごとに1つ)、そしてreviewsのPod自身が、入ってくるリクエストをアプリへ渡すときに使うものが1つです。

名前は、방향|포트|서브셋|호스트(プレースホルダーは方向、ポート、サブセット、ホストです)の4つの欄です。方向はoutboundかinbound、ポートはサービスポートで、サブセットがなければ、その欄を空にして||になります。入ってくる側は、自分のPodのアプリへ送るものなので、サブセットもホストも必要なく、後ろの2つの欄が両方空になります。ホストの欄は、DestinationRuleのhostをFQDNで書いたものです。yqで.spec.subsets[].nameを取り出して、ループで出力すれば、タイプミスがありません。

その名前のままEnvoyを立てて、v1へ送る

/root/ist2-name/name.yamlにEnvoyの設定を書いてください。管理ポートは9983、リスナーは127.0.0.1:10083(HTTP)、route_configの名前は9080で、すべてのパスをoutbound|9080|v1|reviews.default.svc.cluster.localへ送ります。クラスターは、出ていく側の3つです。outbound|9080||reviews.default.svc.cluster.local(エンドポイントは127.0.0.1:8103と127.0.0.1:8104の2つ)、outbound|9080|v1|reviews.default.svc.cluster.local(8103だけ)、outbound|9080|v2|reviews.default.svc.cluster.local(8104だけ)です。アップストリーム2つを8103・8104でokとして起動し、Envoyを起動してから、curl localhost:10083/の結果を、/root/ist2-name/03-v1.txtにstatus=とbody=の2行で書いてください。

istiodは、サービス1つに「サブセットのないクラスター」を常に作り、DestinationRuleのサブセットごとに、クラスターをもう1つずつ作ります。サブセットクラスターのエンドポイントは、サービス全体のエンドポイントのうち、ラベルが合うものだけを絞り込んだものです。ここでは、ポートで疑似的に表します(8103 = version v1のPod、8104 = version v2のPod)。クラスター名に|があるので、引用符で囲み、route_configの名前も、数字に見えても文字列なので、引用符を付けます。

サブセットの重みを40回数えてみる

/root/ist2-name/name.yamlを/root/ist2-name/split.yamlにコピーして、ルートを重み付きルートに変えてください。outbound|9080|v1|reviews.default.svc.cluster.localが75、outbound|9080|v2|reviews.default.svc.cluster.localが25です。Envoyをsplit.yamlで、--concurrency 1を付けて起動し直してから、curl localhost:10083/をちょうど40回送り、応答本文でどのサブセットに行ったかを数えて、/root/ist2-name/04-split.txtにv1=、v2=、total=の3行で書いてください。

VirtualServiceのroute[].weightは、Envoyのweighted_clustersになります。重みは、リクエストごとにサイコロを振るものなので、40回でちょうど30対10になるとは限りません。数字が少しずれても正常です。--concurrency 1は、ワーカーを1つにして、実験のぶれをなくします。本文のポート(ok:8103かok:8104か)で、サブセットを分ければ済みます。

統計名はクラスター名である — alt_stat_nameで変える

/root/ist2-name/split.yamlのoutbound|9080|v2|reviews.default.svc.cluster.localクラスターに、alt_stat_name: outbound_9080_v2_reviewsを追加し(ほかはそのまま)、起動し直してから、リクエストを20回以上送ってください。管理ポートの/statsから2行をそのまま移して、/root/ist2-name/05-stats.txtに保存してください。v1クラスターのupstream_rq_200の行と、v2クラスターのupstream_rq_200の行(いまは変わった名前で出ます)です。

Envoyは、クラスターの統計をcluster.<클러스터 이름>.<지표>(プレースホルダーはクラスター名とメトリクスです)として積みます。Istioのクラスター名には|が入っていて、Prometheusへ移すときに扱いにくくなります。そのため、メッシュ設定のoutboundClusterStatNameにパターンを与えると、istiodが各クラスターにalt_stat_nameを付けて、統計名だけを変えます。ルーティングは、依然として元の名前で行います。/stats?filter=upstream_rq_200のように絞ると、2行を見つけやすくなります。

ないサブセットを指すと503になる

/root/ist2-name/split.yamlを/root/ist2-name/missing.yamlにコピーして、2つのことを変えてください。route_configにvalidate_clusters: falseを追加し、既存のルートの前に、パスのプレフィックス/v3をoutbound|9080|v3|reviews.default.svc.cluster.localへ送るルートを追加します(このクラスターは作りません)。そして、missing.yamlからvalidate_clusters: falseの行だけを除いたコピー/root/ist2-name/missing-strict.yamlも作ってください。missing.yamlで起動してから、curl localhost:10083/v3を1回送り、/root/ist2-name/06-missing.txtに3行を書いてください。status=(HTTPコード)、no_cluster=(統計http.outbound_0.0.0.0_9080.no_clusterの値)、strict_rc=(missing-strict.yamlをenvoy --mode validateした終了コード)です。

VirtualServiceが、DestinationRuleにないサブセットを指すのは、Istioで非常によくあるミスです。istiodはルートをRDSで送りますが、動的に受け取ったルートは、指すクラスターがなくても受け入れられます(validate_clustersのデフォルトは、動的のときfalseです)。すると、リクエストのときになって初めて503が出て、アクセスログにはレスポンスフラグNC(no cluster)が出力されます。静的設定はデフォルトがtrueなので、同じ設定をそもそも拒否します。2つの姿を並べて見るステップです。

サブセットは、同じサービスのエンドポイントの部分集合である

起動しているEnvoyのlocalhost:9983/clustersから、reviewsの3つのクラスターが、それぞれエンドポイントをいくつ持っているかを数えて、/root/ist2-name/07-endpoints.txtに<클러스터 이름> <개수>の形で3行書いてください(プレースホルダーはクラスター名と個数です)。

/clustersは、エンドポイントごとに이름::주소::지표::값(プレースホルダーは名前、アドレス、メトリクス、値です)の行を、複数出力します。エンドポイント1つにつき1回しか出ないメトリクス(例: cx_active)で行を選んで、クラスター名で数えれば済みます。サブセットのないクラスターが、2つのサブセットのエンドポイントを、両方持つことが核心です。サブセットは、新しいサービスではなく、同じエンドポイントのリストを、ラベルで分けたものです。そのため、ラベルのないPodは、どのサブセットにも入らず、サブセットのないクラスターにだけ行きます。

名前の読み方としてまとめる

/root/ist2-name/08-report.mdに、default_cluster=、inbound_cluster=、v2_stat_name=、missing_subset_code=の4行を書き(それぞれ、サブセットのない出ていく側のクラスター名、入ってくる側のクラスター名、ステップ5で変えたv2の統計名、ステップ6で受け取ったHTTPコード)、その下に、- で始まる説明を4行以上書いてください。

前のステップのファイルから移してください。説明の行には、proxy-config clustersの出力を初めて見る同僚に、名前の読み方を教えるつもりで書くとよいです。