二度数えて、ラベルごとに払う
目標
Prometheusアドオンが載った本物のメッシュで、標準メトリクスをPromQLで尋ね、Telemetry APIでラベルを加え・外し、メトリクスを止め、アクセスログを絞って、その効果をサイドカーのメトリクスとログで確認します。
なぜ重要なのか
メッシュの観測は無料のように見えますが、ラベル1つ、ヒストグラム1つが、時系列の数を掛け算で増やします。逆に、業務に必要なディメンションは標準ラベルにありません。何を加え、何を捨てるかを決める仕組みがTelemetry APIで、その効果は、設定ではなく、実際に積み上がるメトリクスとログで確認する必要があります。
ステップ
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つのコードがどちらも見えるまで待ってから保存します。- Prometheusで、
destination_workload="web"かつresponse_code="503"のistio_requests_totalをreporter別に合計して、/root/istlab-telemetry/02-reporters.txtにsource_503=・destination_503=の2行で書いてください。 /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に保存してください。/root/istlab-telemetry/telemetry.yamlの同じoverrideのtagOverridesにrequest_protocol: {operation: REMOVE}を追加して、もう一度適用してください。適用後、x-tenant: betaで呼んだリクエストの新しい時系列には、request_protocolラベルがない必要があります。/root/istlab-telemetry/telemetry.yamlにoverrideをもう1つ置き、REQUEST_DURATION(CLIENT_AND_SERVER)をdisabled: trueで止めて、もう一度適用してください。適用後、リクエストを送っても、clientのサイドカーのistio_request_duration_milliseconds_countの合計が増えない必要があります(istio_requests_totalは増え続ける必要があります)。/root/istlab-telemetry/telemetry.yamlにaccessLoggingを追加して、もう一度適用してください。providerはenvoy、filter.expression: "response.code >= 400"です。適用後、clientの200のリクエストは、clientのサイドカーのアクセスログに残らず、/broken(503)のリクエストだけが残る必要があります。/root/istlab-telemetry/07-error-ratio.promqlに、webが受け取った(reporter destination)リクエストのうち、5xxの比率を返すPromQLを書き、そのクエリをPrometheusに尋ねて出た値を、/root/istlab-telemetry/07-ratio.txtにratio=の形で書いてください。/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)です。
参考
- このVMは準備に3–5分かかります。Prometheusは
istio-system/prometheus(9090)で動いています。kubectl -n istio-system get svc prometheus -o jsonpath='{.spec.clusterIP}'でアドレスを得ます。 - Prometheusは15秒ごとにスクレイプします。クエリの結果が空なら、少し待ってからもう一度尋ねてください。
- サイドカーが今持っているメトリクスは、
kubectl -n shop exec client -c istio-proxy -- pilot-agent request GET stats/prometheusですぐに見られます。 - Telemetryは、同じファイル(
telemetry.yaml)にステップごとに足していきながら、もう一度適用します。 - よくある失敗: カウンターが消えるのを待つケースです。すでに積み上がった時系列は残り、設定の変更は新しくできる時系列でしか見えません。
リクエストを流して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で絞らない合計がなぜ間違うか」を、自分の言葉で書いておいてください。