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

Envoyの内部構造

五百個の数字から必要なものだけ取り出す

TT Labで続きを見る

目標

統計名の構造を3つの系統に分けて見て、取り出し方・分割する規則・削り方を順に身につけた後、名前を変えたときに何が消えるかを自分で確認します。

なぜ重要なのか

統計は、ダッシュボードやアラートが立っている土台です。ところがその土台は名前でできているので、名前の規則を知らないと、/statsで見た値をダッシュボードのクエリに移せず、名前を気軽に変えて、グラフをまるごと空にしてしまいます。これに加えて、カウンターとゲージを区別できないと「リセットしたのに変わらない」で行き詰まり、除外リストの性質を知らないと「必要になったときに有効にしよう」と先送りして、いざ必要な瞬間に過去の値がないことに気づきます。

ステップ

  1. アップストリームを2つ起動してください。8093はok、8094はfailです。/root/envd-stats/stats.yamlに、stat_prefixがshopのリスナーとクラスターgood・badを置いて起動してください(管理9971、リスナー127.0.0.1:10071)。/helloを5回、/bad/xを1回リクエストしてから、/root/envd-stats/01-names.txtにcluster=・http=・listener=の3行を書いてください。それぞれcluster.good.upstream_rq_total、http.shop.downstream_rq_total、そのリスナーのdownstream_cx_totalの統計の名前全体です。
  2. /statsにfilterを付けてupstream_rq_totalを含む統計だけを取り出し、format=jsonも一緒に付けて、/root/envd-stats/02-filter.jsonに保存してください。そして/root/envd-stats/02-filter.txtにtotal_stats=(フィルターなしで受け取った行数)とmatched=(フィルターで絞った統計の個数)の2行を書いてください。
  3. /stats/prometheusを取得して、/root/envd-stats/03-prom.txtにcluster.good.upstream_rq_totalに該当する行だけを保存してください。そして/root/envd-stats/03-prom.mapにenvoy_name=(Prometheusでのメトリクス名)、label_key=(クラスター名を持つラベルのキー)、value=(その値)の3行を書いてください。
  4. /reset_countersをPOSTしてから、2つの統計を読み直して、/root/envd-stats/04-reset.txtにcounter_before=・counter_after=(cluster.good.upstream_rq_total)、gauge_before=・gauge_after=(cluster.good.membership_total)の4行を書いてください。
  5. /root/envd-stats/stats-tags.yamlを作ってください。stats.yamlと同じで、最上位にstats_config.stats_tagsを置き、タグ名envd_clusterを正規表現^cluster\.((.+?)\.)で取り出します。起動して/helloを2回リクエストしてから、/stats/prometheusからenvd_clusterラベルが付いた行を1つ、/root/envd-stats/05-tags.txtに保存してください。
  6. /root/envd-stats/stats-trim.yamlを作ってください。stats_config.stats_matcher.exclusion_listで、プレフィックスがcluster.bad.の統計を除外します。起動してから、/root/envd-stats/06-trim.txtにbefore=(ステップ5の設定での全統計の行数)、after=(この設定での行数)、bad_stats=(この設定でcluster.bad.で始まる行数)の3行を書いてください。
  7. /root/envd-stats/stats-rename.yamlを作ってください。ステップ1の設定から、stat_prefixだけをshopからcheckoutに変えたものです。起動して/helloを2回リクエストしてから、/root/envd-stats/07-rename.txtにold_prefix_stats=(http.shop.で始まる統計の行数)、new_prefix_stats=(http.checkout.で始まる行数)、new_rq_total=(http.checkout.downstream_rq_totalの値)の3行を書いてください。
  8. /root/envd-stats/08-report.mdに、counter_after_reset=・gauge_after_reset=(ステップ4)、prom_label=(ステップ5で追加したタグ名)、trim_removed=(ステップ6のbeforeからafterを引いた値)、renamed_lost=(ステップ7で古いプレフィックスの統計が消えたならyes)の5行を書き、その下に学んだことを4行以上書いてください。

参考

名前は3つの系統で始まる

アップストリームを2つ起動してください。8093はok、8094はfailです。/root/envd-stats/stats.yamlに、stat_prefixがshopのリスナーとクラスターgood・badを置いて起動してください(管理9971、リスナー127.0.0.1:10071)。/helloを5回、/bad/xを1回リクエストしてから、/root/envd-stats/01-names.txtにcluster=・http=・listener=の3行を書いてください。それぞれcluster.good.upstream_rq_total、http.shop.downstream_rq_total、そのリスナーのdownstream_cx_totalの統計の名前全体です。

Envoyの統計名は、何についての数字かをプレフィックスで示します。cluster.はアップストリーム側、http.はそのHTTPコネクションマネージャーが処理したリクエスト側、listener.はソケット側です。同じリクエスト1つが3か所でそれぞれ数えられるので、3つの数字がずれたとき、その差がそのまま手がかりになります。リスナー名にはアドレスとポートが入りますが、ドットの代わりにアンダースコアが使われる場所があるので、自分で確認してください。

500個の中から必要なものだけを取り出す

/statsにfilterを付けてupstream_rq_totalを含む統計だけを取り出し、format=jsonも一緒に付けて、/root/envd-stats/02-filter.jsonに保存してください。そして/root/envd-stats/02-filter.txtにtotal_stats=(フィルターなしで受け取った行数)とmatched=(フィルターで絞った統計の個数)の2行を書いてください。

統計は、デフォルトの設定でも500個を超えます。そのため、管理ポートにはクエリパラメーターがあります。?filter=は正規表現で、?format=jsonは機械が読める形に変えます。2つは&で一緒に使えます。シェルでは?と&が特殊文字なので、アドレス全体を引用符で囲んでください。JSON側の個数はjq '.stats | length'で数えます。

同じ値が別の名前で出力される

/stats/prometheusを取得して、/root/envd-stats/03-prom.txtにcluster.good.upstream_rq_totalに該当する行だけを保存してください。そして/root/envd-stats/03-prom.mapにenvoy_name=(Prometheusでのメトリクス名)、label_key=(クラスター名を持つラベルのキー)、value=(その値)の3行を書いてください。

Prometheusには「ドットでつないだ長い名前」という概念がありません。メトリクス名とラベルの集合で表現します。そのためEnvoyは、エクスポートするときに名前を分割します。cluster.good.upstream_rq_totalは、メトリクス名1つと、クラスター名を持つラベルに分かれます。この規則を知っていてはじめて、/statsで見た名前をダッシュボードのクエリに移せます。ラベルは波括弧の中に열쇠="값"(プレースホルダーはキーと値です)の形で入っています。

リセットするボタンは半分にしか効かない

/reset_countersをPOSTしてから、2つの統計を読み直して、/root/envd-stats/04-reset.txtにcounter_before=・counter_after=(cluster.good.upstream_rq_total)、gauge_before=・gauge_after=(cluster.good.membership_total)の4行を書いてください。

統計には種類があります。カウンターは増え続けるだけの累積値で、ゲージはこの瞬間の状態であり、ヒストグラムは値の分布です。/reset_countersは名前のとおり、カウンターだけを0に戻します。ゲージは「いま健全なサーバーが何台か」のような現在の状態なので、戻すものがありません。この違いを知らないと、「リセットしたのに値が変わらない」で行き詰まります。POSTはcurl -X POSTで送ります。

名前を分割する規則を自分で決める

/root/envd-stats/stats-tags.yamlを作ってください。stats.yamlと同じで、最上位にstats_config.stats_tagsを置き、タグ名envd_clusterを正規表現^cluster\.((.+?)\.)で取り出します。起動して/helloを2回リクエストしてから、/stats/prometheusからenvd_clusterラベルが付いた行を1つ、/root/envd-stats/05-tags.txtに保存してください。

デフォルトのタグ規則は、Envoyがすでに複数持っています(クラスター名・リスナーアドレスなど)。そこに規則を追加すれば、自分の組織の命名規則をラベルとして取り出せます。たとえば、クラスター名にチーム名をプレフィックスとして付けてあるなら、その部分だけを別にラベルにでき、そうすればダッシュボードでチームごとにまとめて見られます。正規表現の1つ目の括弧が名前から切り取る部分で、2つ目の括弧がラベルの値です。フィールド名はstats_tagsです(stat_tagsではありません)。

エクスポートしないものを選ぶ

/root/envd-stats/stats-trim.yamlを作ってください。stats_config.stats_matcher.exclusion_listで、プレフィックスがcluster.bad.の統計を除外します。起動してから、/root/envd-stats/06-trim.txtにbefore=(ステップ5の設定での全統計の行数)、after=(この設定での行数)、bad_stats=(この設定でcluster.bad.で始まる行数)の3行を書いてください。

統計はメモリを消費し、Prometheusへエクスポートすると、時系列の数だけ保存コストになります。クラスターが数百あるプロキシでは、この数字がすぐに手に負えなくなります。そのため、エクスポートするものを選ぶ仕組みがあります。除外リストや包含リストで、プレフィックス・正確な名前・正規表現を指定します。注意すべき点は、除外された統計は画面から消えるのではなく、まったく記録されないことです。必要になった後で復活させても、その間の値はありません。

言葉を1つ変えると、ダッシュボードが空になる

/root/envd-stats/stats-rename.yamlを作ってください。ステップ1の設定から、stat_prefixだけをshopからcheckoutに変えたものです。起動して/helloを2回リクエストしてから、/root/envd-stats/07-rename.txtにold_prefix_stats=(http.shop.で始まる統計の行数)、new_prefix_stats=(http.checkout.で始まる行数)、new_rq_total=(http.checkout.downstream_rq_totalの値)の3行を書いてください。

stat_prefixは名前のための値なので、トラフィックには何の影響もありません。そのため、リファクタリングの途中で気軽に変えやすいのですが、その瞬間にそのプレフィックスを使っていたすべてのダッシュボードとアラートが、空のグラフになります。しかも、値が0に落ちるのではなく時系列そのものが消えるので、「データなし」を障害として扱わないアラートなら、誰も気づきません。名前はインターフェースです。変えるときは、使う側を先に探す必要があります。

統計運用のメモを残す

/root/envd-stats/08-report.mdに、counter_after_reset=・gauge_after_reset=(ステップ4)、prom_label=(ステップ5で追加したタグ名)、trim_removed=(ステップ6のbeforeからafterを引いた値)、renamed_lost=(ステップ7で古いプレフィックスの統計が消えたならyes)の5行を書き、その下に学んだことを4行以上書いてください。

このメモは、次にダッシュボードを作るときやプロキシの設定をリファクタリングするときに、自分が読む文章です。特に最後の行は、「名前はインターフェースである」という規則として書いておくとよいです。値は、前のステップで作ったファイルから取ってください。