数字を作る側から見る — cAdvisor と node exporter
一言でいうと
PromQLは、すでにある数字を選んで計算するだけです。その数字が何を数えたものかは、エクスポーターが決めます。cAdvisorのCPUはなぜ累積なのか、working setはなぜRSSと違うのか、上限(limit)はなぜcAdvisorにないのか。ここからがエクスポーター側の話です。
なぜ必要なのか
「CPU使用量が658855なのですが、これは多いのでしょうか」という質問を受けたことがあるなら、その人はクエリを間違えたのではなく、メトリクスの種類を知らなかったのです。
container_cpu_usage_seconds_totalは、コンテナが生まれてから使ったCPU時間を足し続けてきた値なので、長く生きているコンテナが常に勝ちます。大きさは何も教えてくれず、傾きが答えです。
同じ理由で、_totalで終わるものは常にrate・increaseを通して読み、メモリのようなゲージはそのまま読みます。接尾辞は慣例ではなく、読み方の宣言です。
どう動くのか
cAdvisorはkubeletの中にあります。ノードの/metrics/cadvisorをスクレイプすると、そのノードで動くすべてのコンテナのcgroup統計が出てきます。ラベルのidがcgroupのパスそのもので、そこにQoSクラスとPodのUID、コンテナIDが入っています。
メモリは、3つとも違うものを数えています。
| メトリクス | 意味 |
|---|---|
container_memory_usage_bytes |
このcgroupが使っているとカーネルが数えるすべて |
container_memory_working_set_bytes |
usageから、回収可能な非アクティブのファイルキャッシュを引いた値 |
container_memory_rss |
匿名メモリ(ファイルのないメモリ) |
cAdvisorのソースの計算は、usage - total_inactive_file(cgroup v1)またはusage - inactive_file(cgroup v2)で、負の値なら0に切り上げます。Kubernetesがエビクションを判定するときに使う値も、このworking setです。公式ドキュメントは、「kubeletはinactive_fileを計算から除外する。圧迫が来れば回収できると見なすからだ」と述べています。OOMを心配するなら、usageではなくworking setを見るべき理由がそれです。
上限は、cAdvisorにないことがあります。cAdvisorの原文にはcontainer_spec_cpu_quotaが確かにありますが、kube-prometheus-stackは、デフォルトのmetric_relabel_configsで、その系列のほとんどを捨てます。そのため、「使用量に対する上限」を見るには、kube-state-metricsのkube_pod_container_resource_limitsと、namespace・pod・containerでつなげる必要があります。これはこのクラスターだけの事情ではなく、ほとんどのスタックがそうです。
node exporterのCPUは、モードごとの累積時間です。node_cpu_seconds_totalは、コア1つ×モード1つごとに時系列が1つです。使用率は、「休んでいない割合」で求めます。1 - avg(rate(...{mode="idle"}[5m]))です。sumを使うと、コア数の分だけ大きくなります。
MemFreeとMemAvailableは、別の質問への答えです。freeは誰も使っていないメモリで、availableは「新しい作業に渡せる量」です。ページキャッシュのほとんどは回収できるので、availableのほうがはるかに大きくなります。このクラスターのcubi01は、freeが1.5 GiBでavailableが9.0 GiBでした。freeだけを見てアラートを設定すると、正常なノードが毎日鳴ります。
現場での姿
CPU throttlingを「0より大きければ事故」と見るアラートがよくありますが、実際に測ってみると、上限が設定されたコンテナのほとんどが、0.0x%程度のthrottleを常に経験しています。このクラスターの実測でも、最も高いコンテナが0.19%で、残りは0でした。そのため、判定は比率で行います。rate(throttled_periods) / rate(periods)です。そして、上限のないコンテナには、CFSのメトリクスがまったく存在しません。存在しないことと0であることは、違います。
次のラボですること
このサイトを動かしている7ノードのクラスターから取得した、3時間分の実測データが、ラボのPodのPrometheusに入っています。時系列は674個で、本物のノード、本物のコンテナ、本物のGPUです。そのデータで、コンテナのCPUの傾きを測り、throttleの比率で上限の超過を判定し、usageとworking setの差がどのメトリクスと同じかを確認し、cgroupのパスからQoSクラスを読み、kube-state-metricsとつなげて、上限に対する使用率を出します。