ダッシュボードが丸ごと空になった日
一言でいうと
Envoyの統計名は、1つの文字列に複数の意味が入っている構造です(cluster.<이름>.upstream_rq_totalのプレースホルダーはクラスター名です)。Prometheusへエクスポートするとき、その文字列はメトリクス名とラベルに分割され、分割する規則は自分で追加できます。そして名前はそのままインターフェースなので、言葉を1つ変えただけで、それを使っていたダッシュボードが空になります。
なぜ必要なのか
プロキシで何が起きているかは、ログからもわかりますが、ログはリクエストごとに1行ずつたまるので、「いま全体がどうなっているか」を尋ねるには高くつきます。統計はその逆です。リクエスト数だけ増えることはなく、最初から集計された数字として持っています。
問題は、その数字が500個を超えるということです。デフォルトの設定でもそうで、クラスターが増えれば、クラスターごとに数十個ずつ追加されます。そのため、統計を扱う作業は「何があるか」ではなく、「必要なものをどう見つけ、不要なものをどう削るか」になります。
どう動くのか
名前の3つの系統。名前の最初の言葉が、何についての数字かを示します。
| プレフィックス | 何についての数字か | 代表例 |
|---|---|---|
cluster.<이름>.(プレースホルダーはクラスター名です) |
アップストリーム側 | upstream_rq_total、membership_healthy |
http.<stat_prefix>. |
そのHTTPコネクションマネージャーが処理したリクエスト側 | downstream_rq_5xx |
listener.<주소>.(プレースホルダーはアドレスです) |
ソケット側 | downstream_cx_total |
同じリクエスト1つが、3か所でそれぞれ数えられます。そのため、3つの数字がずれたとき、その差が手がかりになります。リスナーには接続が記録されているのにhttp.側のリクエスト数が少なければ、接続だけ確立してリクエストを送っていないということで、http.側の5xxは増えたのにcluster.側のリクエスト数がそのままなら、アップストリームまで届いていないということです。
種類が違います。カウンターは累積なので増えるだけで、ゲージは現在の状態であり、ヒストグラムは分布です。管理ポートの/reset_countersは、名前のとおりカウンターだけをリセットします。ゲージは「いま健全なサーバーが何台か」のような現在の状態なので、リセットするものがありません。
取り出す方法。/statsに?filter=<정규식>(プレースホルダーは正規表現です)と?format=jsonを付けられ、この2つは一緒に使えます。Prometheusでスクレイプするときは/stats/prometheusを使いますが、ここで名前が分割されます。cluster.good.upstream_rq_totalは、メトリクス名1つと、クラスター名を持つラベルになります。分割する規則はEnvoyがデフォルトで複数持っていて、stats_config.stats_tagsで追加できます。クラスター名にチーム名をプレフィックスとして付けてあるなら、その部分だけをラベルとして取り出し、ダッシュボードでチームごとにまとめて見られます。
削る方法。stats_config.stats_matcherで、包含リストか除外リストを置きます。ここで重要な性質が1つあります。除外された統計は、画面から隠されるのではなく、まったく記録されません。あとで必要になって復活させても、その間の値はありません。
現場での姿
ダッシュボードが丸ごと空になった日。誰かがstat_prefixをもっと読みやすい名前に変えました。トラフィックには何の影響もないので、デプロイは静かに過ぎ、数日後に誰かが「このグラフ、もともとこうでしたか」と尋ねます。値が0に落ちたのではなく、時系列そのものが消えたので、「データなし」を障害として扱わないアラートは、鳴りもしませんでした。名前はインターフェースです。変える前に、使う側を先に探します。
時系列が多くなりすぎた場合。クラスターが数百あるプロキシでは、統計の数が数万になります。Prometheus側の保存コストが先に悲鳴を上げますが、このとき手を付ける場所は、収集間隔ではなくエクスポートするリストです。
ヒストグラムをカウンターのように読む場合。リクエスト時間のような値は、平均1つで見ると、ほとんどいつも問題なさそうに見えます。遅いリクエストは数が少なく、平均をほとんど動かさないからです。ヒストグラムはそのためにあり、見るべきものは平均ではなく上側のパーセンタイルです。管理ポートのテキスト出力にはパーセンタイルも一緒に出ますが、記録された値がないとそのように表示されるので、値がないことと0であることを区別して読む必要があります。
「リセットしたのに値が変わりません」という場合です。ゲージをカウンターと勘違いしたのです。2種類を区別する習慣があれば、この疑問自体が生まれません。
公式ドキュメント: Statistics overview・Administration interface
次のラボですること
同じリクエストが3つの系統の名前でそれぞれ数えられることを確認し、filterとformat=jsonで必要なものだけを取り出し、Prometheusの出力で名前がどう分割されるかを見ます。その後、/reset_countersがカウンターにしか効かないこと、タグ規則を追加してラベルを作ること、除外リストで統計を削ることを順に行い、最後にstat_prefixの言葉を1つ変えて、古い名前の統計がまるごと消えるのを自分で確認します。