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

Istio 実測ラボ

二度数えて、ラベルごとに払う

TT Labで続きを見る

目標

Prometheusアドオンが載った本物のメッシュで、標準メトリクスをPromQLで尋ね、Telemetry APIでラベルを加え・外し、メトリクスを止め、アクセスログを絞って、その効果をサイドカーのメトリクスとログで確認します。

なぜ重要なのか

メッシュの観測は無料のように見えますが、ラベル1つ、ヒストグラム1つが、時系列の数を掛け算で増やします。逆に、業務に必要なディメンションは標準ラベルにありません。何を加え、何を捨てるかを決める仕組みがTelemetry APIで、その効果は、設定ではなく、実際に積み上がるメトリクスとログで確認する必要があります。

ステップ

  1. kubectl apply -f /opt/fixtures/istlab/telemetry-app.yamlで材料を載せ、準備ができるまで待ってください。clientからhttp://web/を10回、http://web/brokenを3回呼んだ後、Prometheus(Serviceistio-system/prometheus、ポート9090)にsum by (response_code) (istio_requests_total{reporter="destination",destination_workload="web",destination_workload_namespace="shop"})を尋ね、そのレスポンスのJSONを、そのまま/root/istlab-telemetry/01-prom.jsonに保存してください。スクレイプの周期が15秒なので、2つのコードがどちらも見えるまで待ってから保存します。
  2. Prometheusで、destination_workload="web"かつresponse_code="503"のistio_requests_totalをreporter別に合計して、/root/istlab-telemetry/02-reporters.txtにsource_503=・destination_503=の2行で書いてください。
  3. /root/istlab-telemetry/telemetry.yamlに、ネームスペースshopのTelemetryであるshop-telemetryを書いて適用してください。metricsのproviderはprometheus、overrideはREQUEST_COUNT・CLIENT_AND_SERVERに、tagOverridesでtenantをrequest.headers['x-tenant']として加えます。適用後、x-tenant: acmeを付けて3回呼び、Prometheusにsum by (tenant) (istio_requests_total{tenant="acme"})を尋ねたレスポンスを/root/istlab-telemetry/03-tenant.jsonに保存してください。
  4. /root/istlab-telemetry/telemetry.yamlの同じoverrideのtagOverridesにrequest_protocol: {operation: REMOVE}を追加して、もう一度適用してください。適用後、x-tenant: betaで呼んだリクエストの新しい時系列には、request_protocolラベルがない必要があります。
  5. /root/istlab-telemetry/telemetry.yamlにoverrideをもう1つ置き、REQUEST_DURATION(CLIENT_AND_SERVER)をdisabled: trueで止めて、もう一度適用してください。適用後、リクエストを送っても、clientのサイドカーのistio_request_duration_milliseconds_countの合計が増えない必要があります(istio_requests_totalは増え続ける必要があります)。
  6. /root/istlab-telemetry/telemetry.yamlにaccessLoggingを追加して、もう一度適用してください。providerはenvoy、filter.expression: "response.code >= 400"です。適用後、clientの200のリクエストは、clientのサイドカーのアクセスログに残らず、/broken(503)のリクエストだけが残る必要があります。
  7. /root/istlab-telemetry/07-error-ratio.promqlに、webが受け取った(reporter destination)リクエストのうち、5xxの比率を返すPromQLを書き、そのクエリをPrometheusに尋ねて出た値を、/root/istlab-telemetry/07-ratio.txtにratio=の形で書いてください。
  8. /root/istlab-telemetry/08-report.mdに5行を書き、その下に学んだことを4行以上書いてください。5行は、requests_503_destination=(ステップ2のdestination_503)、counted_twice=(ステップ2でsourceとdestinationが同じ数を数えていればyes)、tenant_label=(ステップ3で付いたtenantの値)、duration_counting=(現在durationのメトリクスが増えていればyes)、logged_only_errors=(ステップ6の結果ならyes)です。

参考

リクエストを流してPrometheusに尋ねる

kubectl apply -f /opt/fixtures/istlab/telemetry-app.yamlで材料を載せ、準備ができるまで待ってください。clientからhttp://web/を10回、http://web/brokenを3回呼んだ後、Prometheus(Serviceistio-system/prometheus、ポート9090)にsum by (response_code) (istio_requests_total{reporter="destination",destination_workload="web",destination_workload_namespace="shop"})を尋ね、そのレスポンスのJSONを、そのまま/root/istlab-telemetry/01-prom.jsonに保存してください。スクレイプの周期が15秒なので、2つのコードがどちらも見えるまで待ってから保存します。

サイドカーは、リクエストごとにistio_requests_totalのような標準メトリクスを上げ、Prometheusは、Podのアノテーションを見て、サイドカーの15020ポートをスクレイプします。そのため、リクエストを送った直後には見えず、1周期ほど遅れて見えます。クエリはcurl -s http://<ClusterIP>:9090/api/v1/query --data-urlencode 'query=…'のように、HTTP APIで行います。

リクエスト1つを誰が数えるか: reporter

Prometheusで、destination_workload="web"かつresponse_code="503"のistio_requests_totalをreporter別に合計して、/root/istlab-telemetry/02-reporters.txtにsource_503=・destination_503=の2行で書いてください。

サイドカーモードでは、リクエスト1つを、送信側(source)のサイドカーと受信側(destination)のサイドカーが別々に数えます。2つの値が同じになるのが正常で、ダッシュボードでreporterで絞らずに足すと、リクエスト数が2倍に見えます。受信側がメッシュの外ならsource側だけ、送信側がメッシュの外ならdestination側だけが残ることも覚えておいてください。

メトリクスに自分たちのラベルを付ける: tagOverrides

/root/istlab-telemetry/telemetry.yamlに、ネームスペースshopのTelemetryであるshop-telemetryを書いて適用してください。metricsのproviderはprometheus、overrideはREQUEST_COUNT・CLIENT_AND_SERVERに、tagOverridesでtenantをrequest.headers['x-tenant']として加えます。適用後、x-tenant: acmeを付けて3回呼び、Prometheusにsum by (tenant) (istio_requests_total{tenant="acme"})を尋ねたレスポンスを/root/istlab-telemetry/03-tenant.jsonに保存してください。

Telemetry APIは、サイドカーのメトリクス設定を、ネームスペース(またはワークロード)単位で変更します。tagOverridesの値はリクエスト属性の式なので、ヘッダー・パスのようなリクエスト情報をラベルにできます。ただし、ラベルの値が際限なく増えるもの(ユーザーIDのようなもの)をラベルにすると、時系列が爆発するので注意してください。サイドカーが付けたものは、kubectl -n shop exec client -c istio-proxy -- pilot-agent request GET stats/prometheusですぐに見られ、Prometheusには1周期ほど後に見えます。

使わないラベルを外す

/root/istlab-telemetry/telemetry.yamlの同じoverrideのtagOverridesにrequest_protocol: {operation: REMOVE}を追加して、もう一度適用してください。適用後、x-tenant: betaで呼んだリクエストの新しい時系列には、request_protocolラベルがない必要があります。

ラベル1つが常に同じ値(http)でも、すべての時系列に付いて保存領域を食います。使わない標準ラベルを外すのが、カーディナリティを減らす最も安い方法です。すでにできた時系列はカウンターなので消えずに残るため、新しくできる時系列(新しいtenantの値)で確認してください。

使わないメトリクスは止める

/root/istlab-telemetry/telemetry.yamlにoverrideをもう1つ置き、REQUEST_DURATION(CLIENT_AND_SERVER)をdisabled: trueで止めて、もう一度適用してください。適用後、リクエストを送っても、clientのサイドカーのistio_request_duration_milliseconds_countの合計が増えない必要があります(istio_requests_totalは増え続ける必要があります)。

レイテンシのヒストグラムは、バケットごとに時系列ができて、標準メトリクスの中で最も重くなります。ゲートウェイだけで見て、サイドカーでは止めるという選択を、Telemetry APIでできます。止めても古い値が消えるわけではないので、「増えない」ことで確認します。リクエストの前後で合計を2回測って比べてください。

エラーだけをアクセスログに残す

/root/istlab-telemetry/telemetry.yamlにaccessLoggingを追加して、もう一度適用してください。providerはenvoy、filter.expression: "response.code >= 400"です。適用後、clientの200のリクエストは、clientのサイドカーのアクセスログに残らず、/broken(503)のリクエストだけが残る必要があります。

このメッシュは、インストール時にmeshConfigですべてのアクセスログを有効にしてあります。TelemetryのaccessLoggingは、それをネームスペース単位で上書きして、条件式(CEL)に合うリクエストだけを残せます。成功したリクエストまで全部残すと、ログのコストがリクエスト数に比例して増えるので、運用では、エラーと遅いリクエストだけを残すことが多いです。リクエストIDを付けて送り、kubectl -n shop logs client -c istio-proxyで探してください。

エラー率をPromQL 1行で

/root/istlab-telemetry/07-error-ratio.promqlに、webが受け取った(reporter destination)リクエストのうち、5xxの比率を返すPromQLを書き、そのクエリをPrometheusに尋ねて出た値を、/root/istlab-telemetry/07-ratio.txtにratio=の形で書いてください。

比率はsum(5xx 카운터) / sum(전체 카운터)です(プレースホルダーは5xxのカウンターと全体のカウンターです)。reporterを1つに固定しないと、1つのリクエストが2回数えられたもの同士を割ることになり、メッシュの外から来たリクエストがあれば、分母と分子が互いに違うものを数えることになります。運用のダッシュボードならrate(…[5m])で直近の比率を見ますが、このラボのリクエストは数回だけなので、カウンターそのもので割ります。クエリファイルの改行は、--data-urlencode "query=$(cat 파일)"で送れば、そのまま通ります(プレースホルダーはファイル名です)。

何を数えて何を捨てたかを書く

/root/istlab-telemetry/08-report.mdに5行を書き、その下に学んだことを4行以上書いてください。5行は、requests_503_destination=(ステップ2のdestination_503)、counted_twice=(ステップ2でsourceとdestinationが同じ数を数えていればyes)、tenant_label=(ステップ3で付いたtenantの値)、duration_counting=(現在durationのメトリクスが増えていればyes)、logged_only_errors=(ステップ6の結果ならyes)です。

説明の行には、「ラベルを加えるときのコスト(カーディナリティ)」と「reporterで絞らない合計がなぜ間違うか」を、自分の言葉で書いておいてください。