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

PCA — Prometheus認定アソシエイト

数字を作る側から見る — cAdvisor と node exporter

TT Labで続きを見る

一言でいうと

PromQLは、すでにある数字を選んで計算するだけです。その数字が何を数えたものかは、エクスポーターが決めます。cAdvisorのCPUはなぜ累積なのか、working setはなぜRSSと違うのか、上限(limit)はなぜcAdvisorにないのか。ここからがエクスポーター側の話です。

なぜ必要なのか

「CPU使用量が658855なのですが、これは多いのでしょうか」という質問を受けたことがあるなら、その人はクエリを間違えたのではなく、メトリクスの種類を知らなかったのです。 container_cpu_usage_seconds_totalは、コンテナが生まれてから使ったCPU時間を足し続けてきた値なので、長く生きているコンテナが常に勝ちます。大きさは何も教えてくれず、傾きが答えです。

累積カウンターは上がり続けるだけで、その傾きが、今何コア使っているかを表す。再起動すると値が0に落ちて、rateがその区間を補正する

同じ理由で、_totalで終わるものは常にrate・increaseを通して読み、メモリのようなゲージはそのまま読みます。接尾辞は慣例ではなく、読み方の宣言です。

どう動くのか

cAdvisorはkubeletの中にあります。ノードの/metrics/cadvisorをスクレイプすると、そのノードで動くすべてのコンテナのcgroup統計が出てきます。ラベルのidがcgroupのパスそのもので、そこにQoSクラスとPodのUID、コンテナIDが入っています。

cgroupパスの節がQoSクラス・PodのUID・コンテナIDで、kubeletがそれをnamespace・pod・containerラベルとして付けてくれる

メモリは、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とつなげて、上限に対する使用率を出します。