サブセットをクラスターとして立て、名前で追跡する
目標
DestinationRuleからクラスター名を規則で導出し、その名前のままEnvoyを構成して、サブセットごとのルーティング・統計名・ないサブセットの503を、自分で確認します。
なぜ重要なのか
Istioの障害調査の半分は、proxy-config clustersと統計を読む作業です。クラスター名は規則で作られるので、規則を知っていれば、名前だけを見て、サービス・ポート・サブセットがわかり、「サブセットがない」「エンドポイントが空」「統計名が変わった」を、数秒で見分けられます。
ステップ
/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)。- サービス
reviewsは、ポート9080を使います。/root/ist2-name/dr.yamlから、サイドカーが持つことになるクラスター名を4つ、/root/ist2-name/02-names.txtに1行に1つずつ書いてください。ほかのPodがreviewsへ出ていくときに使うものが3つ(サブセットのないもの1つと、サブセットごとに1つ)、そしてreviewsのPod自身が、入ってくるリクエストをアプリへ渡すときに使うものが1つです。 /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行で書いてください。/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行で書いてください。/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の行(いまは変わった名前で出ます)です。/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した終了コード)です。- 起動しているEnvoyの
localhost:9983/clustersから、reviewsの3つのクラスターが、それぞれエンドポイントをいくつ持っているかを数えて、/root/ist2-name/07-endpoints.txtに<클러스터 이름> <개수>の形で3行書いてください(プレースホルダーはクラスター名と個数です)。 /root/ist2-name/08-report.mdに、default_cluster=、inbound_cluster=、v2_stat_name=、missing_subset_code=の4行を書き(それぞれ、サブセットのない出ていく側のクラスター名、入ってくる側のクラスター名、ステップ5で変えたv2の統計名、ステップ6で受け取ったHTTPコード)、その下に、-で始まる説明を4行以上書いてください。
参考
- このPodには、本物のistiodも本物のサイドカーもありません。そのため、
istioctl proxy-configで実際の生成物を見られず、変換ルールを知って、手で同等のEnvoy設定を作って動作を確認します。同じルールが、本番クラスターのproxy-configの出力に、そのまま見えます。 - 分配を数えるステップ4からは、Envoyを
--concurrency 1で起動してください。ワーカーが複数あると、重みの実験がさらにぶれます。 /statsと/clustersの行には|が入っています。grepでは\|でエスケープするか、grep -Fを使ってください。- Envoyを起動するときは、
setsid --fork nohup envoy -c <파일> --log-level warn > <로그> 2>&1 </dev/null(プレースホルダーは設定ファイルとログファイルです)でシェルから完全に切り離してください。起動し直す前にはpkill -x envoyで片付けます(pkill -f 'envoy -c'は、その文字列を含むシェル自身まで終了させます)。 - アップストリームの代用サーバーがイメージにあります:
python3 /opt/lab/envoy/upstream.py <포트> ok|fail|slow(プレースホルダーはポート番号です)。応答本文は<모드>:<포트> <경로>(プレースホルダーはモード、ポート番号、パスです)です。 - 設定を直した後は、起動する前に
envoy --mode validate -c <파일>(プレースホルダーは設定ファイルです)で先に検査してください。クラスター名に|が入るので、YAMLでは必ず引用符で囲みます。
サブセットを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の出力を初めて見る同僚に、名前の読み方を教えるつもりで書くとよいです。