TL; DR;
- cgroupでは時間を細分化した単位(period)内でプロセスが使っていい時間(quota)を管理している
- quotaを超えた分がスロットルと扱われる
- cAdvisorの各メトリクスは以下のような図に示される定義となっている
背景
kubernetesで監視を行おうとしたとき、意識せずともcAdvisor由来のメトリクスを見ることになると思います。 例えばCPU使用率はprometheusの場合、container_cpu_usage_seconds_totalといったメトリクスで取得することができます。 これには、kubeletがcAdvisorを内包しているからという理由があります(cf. Metrics For Kubernetes System Components)。
そのようなメトリクスの中にはスロットル関係のメトリクスとして、以下のような説明のCounterタイプメトリクスがあります。
# コンテナが実行可能だったperiodの個数
container_cpu_cfs_periods_total
# コンテナがスロットルされたperiodの個数
container_cpu_cfs_throttled_periods_total
# コンテナがスロットルされた時間(秒)
container_cpu_cfs_throttled_seconds_total
cf. cadvisor/docs/storage/prometheus.md at master · google/cadvisor · GitHub
私はこれを最初に見たとき、ナニコレ?と思ったのでこれらのメトリクスの定義を調べてみました。
メトリクスの取得元
cAdvisorのコードをみてみると、これらのメトリクスは、実際にはgolangのruncモジュールのlibcontainer/cgroupsパッケージのThrottlingData型のフィールドから値を取得しています。1 2 このフィールド自体はコンテナが属するcgroupsのcpu.statファイルから値を取得しているようです。3
結果的に、各メトリクスとcpu.statとの対応は以下のようになります。
- container_cpu_cfs_periods_total:nr_periods
- container_cpu_cfs_throttled_periods_total:nr_throttled
- container_cpu_cfs_throttled_seconds_total:throttled_time (v1) / throttled_usec (v2)
つまり、これらのメトリクスの意味を把握するには、cgroupsのperiodやthrottleについて理解すれば良いということになります。
cgroupsのperiodとthrottle
cgroupsでは、時間をperiodという短い区間単位に分割し、その単位内で制限を適用しています。 つまり、periodの中でプロセスが使うことのできる時間はどのくらいかというクオータ(quota)を指定し、クオータを使い切ってなお使おうとしている部分はスロットル(throttle)として扱われるということです。 なお、このクオータはperiodが変わると復活します。
例えば、Periodが100ms・Quotaが60msの場合のcgroupsによるプロセスの制限を図示すると下のようになります。
PeriodとQuotaの値はcgroup毎に変えることができ、
- cgroup v1(cf. CFS Bandwidth Control — The Linux Kernel documentation)
- period:cpu.cfs_period_usでマイクロ秒単位で指定。デフォルトは100ms
- quota:cpu.cfs_quota_usでマイクロ病で指定。デフォルトで-1(制限なし)
- cgroup v2(cf. Control Group v2 — The Linux Kernel documentation)
- period:cpu.maxの2番目の値でマイクロ秒単位で指定。デフォルトは100ms
- quota:cpu.maxの1番目の値でマイクロ秒単位で指定。デフォルトはmax(制限なし)
というパラメータ(cgroupファイル)で設定できます。 v1・v2のいずれにしてもデフォルトではperiodは100msで、quotaは設定されていません。
なお、クオータはマルチコア環境であってもグローバルなプールとなっているため、quotaがperiodと一致しているからといってCPUを全て利用できるわけではありません。
また、上の説明では実際に使用した分を積算してクオータを超えていないか判断数という仕組みに見えまるが、実際にそれぞれのプロセスに対して使用量を厳密にトラッキングするのはオーバーヘッドが大きいので、periodをさらに細かく分けたsliceという単位を配分しその使用量で管理されます。
まとめ
以上の説明からに、スロットル関連のメトリクスの取得元・意味は以下のようになります。
- container_cpu_cfs_periods_total:コンテナが実行可能だったperiodの個数(nr_periods)
- container_cpu_cfs_throttled_periods_total:コンテナがスロットルされたperiodの個数(nr_throttled)
- container_cpu_cfs_throttled_seconds_total:コンテナがスロットルされた時間(秒)(throttled_time (v1)・throttled_usec (v2))
また、これらを図示すると以下のようになります。
参考
- 第53回 Linuxカーネルのコンテナ機能 - cgroup v2から使うCPUの帯域幅制限(1) | gihyo.jp
- 第54回 Linuxカーネルのコンテナ機能 ―cgroup v2から使うCPUの帯域幅制限(2) | gihyo.jp
- KubernetesのResource Requests & Resource Limitsの内部処理をソースコードレベルで読み解く - inductor’s blog
- Site Unreachable
- CPU Throttling - Unthrottled: How a Valid Fix Becomes a Regression
- CPU Throttling - Unthrottled: Fixing CPU Limits in the Cloud
- KubernetesのOOMとCPUスロットリング – Sysdig
- Using Prometheus to Avoid Disasters with Kubernetes CPU Limits | Containers
- CFS Bandwidth Control — The Linux Kernel documentation
- Understanding CPU throttling in Kubernetes to improve application performance #k8sjp - Speaker Deck