A Deep Dive into Kubernetes Metrics — Part 3

第三章 コンテナリソースメトリクス

gavin.zhou
Orangesys

--

TL;DR

今回はマルチパートシリーズの第三章です。ここではKuberneteクラスターから集めることのできる全てのメトリクスについてお話します。

第二章では、メモリー、CPU,ディスク、ネットワークなどのnode上の一番重要なリソースを選んだり、見極めたりするUSEメソッドについて説明をしたり、実践してみせたりしました。第三章では、コンテナレベルでのメトリクスを詳しく解説していきます。それらのメトリクスはcAdvisorによってリポートされたメトリクスです。

Container Metrics from cAdvisor

Googleが発足したcAdvisorプロジェクトは他に類を見ないプロジェクトとして始まりました。このプロジェクトはnode上の動作中のコンテナからパフォーマンスメトリクスやリソースを集めるためのプロジェクトです。Kuberneteの中ではcAdvisorはkubeletに埋め込まれています。このプロセスでは、クラスターの中のそれぞれのメトリクス上の全てのコンテナをコントロールするものです。コンテナメトリクスを集めるためにそれぞれのnodeで他のプロセスを動かす必要がないので、とても便利です。kubletはすべてのランタイムメトリクスをエクスポートします。そして、全てのcAdvisorメトリクス、Prometheus exposition フォーマットの中の/metricsのエンドポイント上のものも含めてエクスポートします。

cAdvisorからエクスポートされた“コンテナ”メトリクスは、ついに基本的なLinux cgroupの実装によってリポートされたメトリクスなのです。ちょうどnodeメトリクスと同じように“コンテナ”メトリクスはとてもたくさん存在し、より精密になっています。ですので、基本的なnodeによってプロバイドされたリソースを使ったコンテナに関心を持っています。特に私たちはCPU、メモリ、ネットワーク、ディスクにとても興味を持っています。リソースを取り扱う時、それらのメトリクス上のリポートを選ぶときはUSEメソッドが最適ですので、覚えておいてください。

The USE method and Container Metrics

ちょっと思い出してほしいのですが、USEメソッドはユーティライゼーション、サーチュレーション、エラーを表しているんでしたよね。USEメソッドについては第二章で詳しく書いていますので、そちらをご参照ください。それらのメトリクスのソースがnode(node_exporter)からコンテナ(cAdvisor)へ変化するため、そのメトリクスの名前も当然変わります。付け加えて言うと、クラスターの中の全てのコンテナのために、それぞれのメトリクスはリポートになります。アプリケーションの全体像を見るためにPrometheusの中のsumメソッドを使うことが必要なのです。

個々のリソースメトリクスについてお話をする前にKuberneteの特徴について触れておく必要があります。Kuberneteを使うとより簡単にサーチュレーションを算出することができるのです。これらの特徴はリソース“リクエスト”と“リミット”と言います。

Kubernetes Requests and Limits

Kuberneteシステムの核となるのはnode上にコンテナを設置するスケジューラーです。これは、サイズの異なるものをサイズの異なる箱に詰めていく作業に似ています。スケジューラーはnodeのキャパシティーとnode上におかれるコンテナのサイズを認識していないといけません。コンテナのサイズが分かってしないとクラスターの中のnodeにすぐに過剰にプロバイドされてしまいます。Nodeが集まりすぎてしまってパフォーマンスに問題が生じるのです。

リクエストリミットはデプロイの一部としてコンテナ指定にアプライされます。現在のKubernete1.10 2リソースタイプはリクエストとリミットのセットを持ち合わせています。CPUとメモリですね。cpu は cpu またはコアの分数として指定され (1000 分の1まで)、メモリはバイト単位で指定されます。

リクエストはコンテナが必要とするリソースの一番小さい量を要求します。リクエストからは、どのくらいのリソースを使うようになるかを判断することはできませんが、どれくらのリソースが必要になるかということは分かります。そのジョブを実行するためにコンテナはいくつのリソースが必要かどうかをスケジューラーに教えます。リクエストは、Kuberneteスケジューラーによるスケジューリングのために使われます。CPUリクエストのために、リクエストはLinux kernelによってコンテナがスケジュールされるように設計するために使われます。

リミットはすごく大きく最大のリソース量です。自分のコンテナが絶対に使わないくらいの量です。リミットはリクエストと同じくらい、もしくはそれよりも優れています。もしあなたがリミットだけをセットしたら、リクエストはリミットと同じ役割をすることになります。

リミットは、リソースリクエストが越えて急増するためにコンテナにヘッドルームを与えます。リミットはKuberneteスケジューラーによって構成されていないので、リミットのおかげで一個のノブからnode上に存在する過剰にプロバイドされたコンテナまで表示されます。もし、コンテナがリミットを越えたら、アクションはリソースに依存するということも言われています。CPUリミットを越えたら、抑えられるでしょう。メモリーリミットをこえてもkillされます。

リソースリクエストとリミットと一緒に実行することは“セキュリティのベストプラクティス”です。

リソースアンバウンド コンテナを走らせるオプションは、自分のシステムをDoSという脅威にさらすことになるか、もしくは“うるさい 隣人”になるかという結果に導きます。そういったリスクを避けるか、もしくは、最小限にするためにリソースのクオータを規定すべきです。

ネームスペース上にクオータを設置すればすぐ、そのネームスペースの中のそれぞれのコンテナにリクエストとリミットをアプライしなければならなくなるでしょう。

Container CPU Utilization, Saturation, and Errors

CPUのユーティライゼーションのためにKuberneteはそれぞれのコンテナへの3つもメトリクスを示してくれます。

  1. container_cpu_user_seconds_total — “ユーザー”のトータル時間数(kernelの中で過ぎた時間ではない)
  2. container_cpu_user_seconds_total — “システム”のトータル時間数(kernelの中で過ぎた時間)
  3. container_cpu_usage_seconds_total — 上の二つの合計。Kubernete1.9に先立って、これは全てのnodeの中のそれぞれのCPUにリポートされています。これは1.1.0で変更された点です。

これらすべてのメトリクスはカウンター(計算装置)で、それらにアプライされるレイトを持つ必要があります。このクエリはそれぞれのコンテナによって使用中のコアの数を表示してくれます。システム全体のその名前のすべてのコンテナに対して:

sum(
rate(container_cpu_usage_seconds_total[5m]))
by (container_name)

CPUリミットを実装しているとき、サーチュレーションを求める計算はより簡単になります。というのもCPUの使用上限を決めるからです。コンテナがCPUのリミットを超えるとLinuxランタイムがコンテナを「throttle」し、container_cpu_cfs_throttled_seconds_totalの中でリミットされた時間を記録します。

sum(
rate(container_cpu_cfs_throttled_seconds_total[5m]))
by (container_name)

このメトリックはCPUがいつ実行されているかの状況を追っていくのに重要なメトリックです。node_exporterと同じようにcAdvisorはCPUエラーをエクスポートしません。

Memory Utilization, Saturation and Errors

cAdvisor で追跡されるメモリメトリックスは、node_exporter から公開される43メモリメトリックスのサブセットです。コンテナメモリメトリックは次のとおりです。

container_memory_cache -- ページキャッシュメモリのバイト数.
container_memory_rss -- バイト中のRSSサイズ.
container_memory_swap -- バイト中のコンテナスワップ使用.
container_memory_usage_bytes -- バイト中の現行のメモリ使用。それがいつアクセスされるかにかかわず、全てのメモリを含む.
container_memory_max_usage_bytes -- バイト中に記録された最大量の使用メモリー.
container_memory_working_set_bytes -- バイト中の進行中のセット.
container_memory_failcnt -- リミットに達したメモリ使用の数.
container_memory_failures_total -- メモリーアロケーション(メモリー配分)の数の累積.

メモリーユーティライゼーションはcontainer_memory_usage_bytesを使えば簡単に追えると思っているかもしれませんが、このメトリックは、キャッシュされた(ファイルシステムキャッシュと考えてください)アイテムも含んでいます。そのアイテムとは、メモリープレッシャーのせいで排除されてしまうものです。さらに優れているメトリックはcontainer_memory_working_set_bytesです。このメトリックはOOMキラーが注視しているメトリックです。

コンテナメモリーユーティライゼーションを算出するにはこれを使います。

sum(container_memory_working_set_bytes{name!~"POD"})
by (name)

上のクエリでは、誰かの名前を含む「POD」のコンテナを除外する必要があります。これはこのコンテナのための元のcgroupです。ポッドの中の全てのコンテナのスタットをトラックします。

コンテナメモリーサーチュレーションは、メモリーリミットが実行中のときに簡単になります。サーチュレーションは、リミットからの利用可能なメモリー量だと定義されます。

sum(container_memory_working_set_bytes) by (container_name) / sum(label_join(kube_pod_container_resource_limits_memory_bytes,
"container_name", "", "container")) by (container_name)

ここでは、2つのシリーズを使用しなければいけません。一つはcAdvisorからで、もう一つはkube-state-metricsからです。残念なことにコンテナの名前のラベルは関連していませんが、PromQLがここでlabel_joinの役割を担ってくれています。

メモリーエラーはkubeletにエクスポートされていません。

Disk Utilization, Saturation, and Errors

ディスクI/Oを扱うとき、書き込みと読み込みの両方の面をみてすべてのディスクのユーティライゼーションをトラックすることから始めます。cAdvisorにはcontainer_fs_io_time_seconds_totalcontainer_fs_io_time_weighted_seconds_totalのためのシリーズがあります。これらはノードレベルにおいて似たようなメトリックをトラックする必要があります。しかし私のインストールの場合には、それらはすべてzeroになります。

一番ベーシックなディスクI/Oユーティライゼーションは書き込まれた/読み込まれたバイトです。

sum(rate(container_fs_writes_bytes_total[5m])) by (container_name,device)sum(rate(container_fs_reads_bytes_total[5m])) by (container_name,device)

コンテナごとの全体的なユーティライゼーション値を求めるには、これを合計します。

Kubeletはコンテナ ディスク サーチュレーションやエラーのためにの意味のあるクエリを得るために十分に詳しくエクスポートしません。

Network Utilization, Saturation and Errors

コンテナレベルでのネットワーク ユーティライゼーション。送受信両方においてバイトで計測するかパケットで計測するか選択することができます。このクエリは、すべてのネットワークアカウンティングがコンテナではなく、Podレベルで行われる点で少し異なります。

このクエリでは、それぞれのポッドのためのポッド名によるネットワーク ユーティライゼーションが表示されます。

sum(rate(container_network_receive_bytes_total[5m])) by (name)sum(rate(container_network_transmit_bytes_total[5m])) by (name)

もう一度言いますが、ネットワークのためのサーチュレーションは最大のネットワークのバンド幅がどれくらいになるのかわかっておらず、とても不明確なものです。ドロップされたパケットをプロキシとして使用することも可能です。cAdvisorはcontainer_network_receive_packets_dropped_totalcontainer_network_transmit_packets_dropped_total の両方を表示してくれます。cAdvisorはまたcontainer_network_receive_errors_totalcontainer_network_transmit_errors_totalの両方のシリーズのエラーの数も表示してくれます。

まとめ

cAdvisorを利用したkubeletは、Kubernetクラスターの中の全てのコンテナのためのリソースについての豊富な情報をエクスポートしてくれます。ユーティライゼーション、サーチュレーション、エラーなどの観点からそれらのリソースを見ることで、リソースリミットやキャパシティープランニングにおける新たな発見があると思います。

Orangesys.ioでは、kuberneteクラスターの内部動作を明らかにするお手伝いをぜひ私たちにおまかせください。

--

--