メトリクス・アクセスログ・トレース — プロキシがタダでくれるものとそうでないもの
一言でいうと
メッシュは、リクエストがプロキシを通るついでに、メトリクス・アクセスログ・スパンを作ってくれます。ただし、トレースだけは、アプリケーションがヘッダーを引き渡さないと途切れます。
なぜ必要なのか
オブザーバビリティをアプリケーション側で用意すると、2つの点でずれが生じます。第一に、定義がサービスごとに違います。あるチームは5xxだけをエラーとして数え、別のチームは4xxも数えます。あるチームはクライアント基準で、別のチームはサーバー基準でレイテンシを測ります。ダッシュボードを並べても比較になりません。第二に、計装されないサービスが残ります。急いで作った社内ツール、引き継いだチームのレガシー、外注で受け取ったコンポーネントには計装がなく、それがいつも障害の死角になります。
メッシュはこの問題を、「リクエストはどうせプロキシを通るのだから、そこで測ろう」という発想で解きます。標準メトリクスの名前とラベルが、すべてのサービスで同じになります。この均一性は、計装を新しく付けて回るよりもはるかに大きな価値です。
どう動くのか
メトリクス: 各サイドカーは、ポート15090でPrometheus形式の統計を公開します。核心は次の3つです。
| メトリクス | 種類 | 使う場所 |
|---|---|---|
istio_requests_total |
カウンター | リクエスト数、エラー率 |
istio_request_duration_milliseconds |
ヒストグラム | p50/p99のレイテンシ |
istio_request_bytes / istio_response_bytes |
ヒストグラム | ペイロードのサイズ |
ラベルの中で、特に知っておくべきものが3つあります。reporterは、source(送る側のプロキシ)とdestination(受け取る側のプロキシ)に分かれますが、同じリクエストが両側でそれぞれ記録されるため、2つを合算すると2倍に数えてしまいます。response_flagsは、アクセスログのフラグと同じ値なので、503の性質をメトリクスだけでも区別できます。connection_security_policyはmutual_tlsかnoneで、STRICT切り替えの完了基準は、このラベルがnoneであるリクエストが0件になることです。
落とし穴の1つはカーディナリティです。ラベルにリクエストのパスやユーザーIDのような値を入れると、時系列が爆発します。カスタムタグには、値の種類が有限なものだけを入れる必要があります。
アーキテクチャが変わった歴史も知っておく価値があります。以前は、Mixerという別のサービスがリクエストごとに呼び出されてメトリクスを集めており、それがレイテンシと障害の原因でした。今は、プロキシ内部のフィルターが直接作ります(Telemetry API v2)。そのため、オブザーバビリティのためにリクエスト経路にホップが追加されることは、もうありません。
アクセスログ: プロキシが、リクエストごとに1行を残します。人が読むべき部分は、レスポンスコードと、その隣のフラグです。
| フラグ | 意味 | 最初に見るもの |
|---|---|---|
UH |
正常なアップストリームなし | subsetのラベルとPodのラベル |
UF |
アップストリームへの接続失敗 | 片方だけSTRICTのmTLSの不一致 |
UO |
接続プールの超過 | サーキットブレーカーの上限 |
NR |
ルートなし | catch-allルートがあるか |
URX |
リトライの使い切り | 根本原因は他のフラグと一緒に |
UAEX |
外部認可の拒否 | CUSTOMポリシーと認可サーバーの状態 |
全件のログ記録は高くつきます。Telemetryリソースのフィルターで、response.code >= 400のような条件を付けて、失敗だけを残す構成がよく使われます。
分散トレーシング: ここが「タダではない」部分です。プロキシは、スパンを作り、タイミングを測り、コレクターへ送るところまでやってくれます。ところが、インバウンドリクエストのトレースヘッダー(B3系、またはW3Cのtraceparent)を、アウトバウンドリクエストにコピーする作業は、アプリケーションが行わなければなりません。これをしないと、サービスごとに別々のトレースが作られて、呼び出しの連鎖が途切れます。「メッシュを入れたのに、トレースが1ホップ分しか見えない」の答えは、ほとんどいつもこれです。
サンプリングの既定値は1%です。最初のプロキシがサンプリングするかどうかを決めてヘッダーで伝播するので、後ろのプロキシはその決定に従います。デバッグのときだけ上げて、常時100%にはしません。
可視化: Kialiは、Prometheusのメトリクスでサービスグラフを描き、Kubernetes APIとIstioの設定を読んで、参照整合性まで検査します。つまり、Kialiの設定検証は、istioctl analyzeと同じ種類の仕事を画面で見せてくれるものです。
現場での姿
第一に、sourceとdestinationを混ぜて数えるダッシュボードです。リクエスト数がちょうど2倍になっていたら、十中八九、reporterのフィルターを入れ忘れています。クライアントの観点ならsource、サーバーの観点ならdestinationのどちらか1つに固定する必要があります。
第二に、メッシュのメトリクスはアプリケーションのメトリクスの代わりにはなりません。プロキシが知っているのは「リクエストが200を返した」ところまでです。レスポンスの本文が間違っていたか、注文が実際に保存されたかはわかりません。メッシュのオブザーバビリティはインフラ層の均一な土台であり、ドメインの指標は、やはりアプリケーションが出す必要があります。
第三に、サイドカーのない経路は見えません。メッシュの外のcronや、注入されていないネームスペースのトラフィックは、グラフにまったく現れません。グラフがきれいだからといって、トラフィックがないという意味ではありません。
次のラボですること
最初に正直にお伝えしておきます。このラボ環境には、実際にトラフィックを運ぶEnvoyがありません。サイドカーが付いたPodがRunningになっても、リクエストは流れず、15090をスクレイプすることも、15000の管理APIを叩くこともできません。そのため、メトリクスの値やトレースの図を作り出すようなラボは作っていません。
その代わり、すぐあとのラボでは、シグナルを設計する側を扱います。メッシュ全体に既定のアクセスログを敷き、レスポンスコードで失敗だけを残すフィルターをかけ、ワークロード1つにサンプリング率とリテラルのタグを付け、標準メトリクスを1つオフにします。そのあと、カーディナリティを爆発させるタグを、デプロイ前に弾く検査ツールを作り、セレクターのないTelemetryが1つのネームスペースに2つあると、istioctl analyzeがエラーとして検出するところまで見ます。値が流れなくても、設定が有効か、範囲が正しいかは、そのまま検証されます。現場で人がつまずく場所も、たいていその2つです。