> Source: https://www.ymotongpoo.com/works/otel-specs-ja/semconv/non-normative/groups/system/cpu-metrics-guidelines/


# 推奨とOpt-InのCPUメトリクス

[**計測器の命名**](/works/otel-specs-ja/semconv/general/naming/#計測器の命名)の節では`*.usage`、`*.limit`、`*.utilization`、`*.time`の各メトリクスを定義していますが、その[**要求レベル**](/works/otel-specs-ja/semconv/general/signal-requirement-level/)（`recommended`、`opt-in`）までは規定していません。これらのメトリクスは重複する情報を異なる形で伝えるため、明示的な指針がなければ実装がばらつく可能性があります。

この文書は、Semantic Conventionsの各領域にわたるCPUメトリクスの要求レベルについて指針を示すものです。

## 方針

- **recommended**: `*.cpu.time`
- **opt-in**（任意）: `*.cpu.utilization`、`*.cpu.usage`、`*.cpu.limit_utilization`、`*.cpu.request_utilization`

## 論拠

`*.cpu.time`系のメトリクスは、オペレーティングシステムやランタイムから直接測定されるため、あいまいさがありません。CPUやリソースをまたいで整合的に集計でき、空間的な集計に対応し、可能な場合はバックエンドや収集時点でusageやutilizationを導出するための一貫した基盤になります。

これに対して、`*.cpu.usage`と`*.cpu.utilization`は導出されたメトリクスであり、表示を目的としたものです。これらの定義は実装によって異なる場合があり、特にコンテナ環境やKubernetes環境ではCPU制限がコンテナやPodごとに定義されるため、その傾向が強くなります。そのため、これらのメトリクスをどう計算・報告すべきかについてあいまいさや不整合が生じます。ダッシュボードやアラートには便利ですが、任意項目のままとし、特定の環境が明示的にこれらを提供する場合に限って実装すべきです。例えば[Kubeletのstatsエンドポイント](https://github.com/kubernetes/kubernetes/blob/dbc7fe1b7fec4a76562d5e1565072a447fec5439/staging/src/k8s.io/kubelet/pkg/apis/stats/v1alpha1/types.go#L230-L233)は`*.cpu.usage`に対して意見の入ったメトリクスを提供しており、そのまま使うこともできますが、これは`.cpu.time`メトリクスから導出されたものであり、[Docker stats API](https://docs.docker.com/reference/api/engine/version/v1.52/#tag/Container/operation/ContainerStats)のような他システムで一意に実装されているわけではないため、任意項目にすべきです。

## 実装に関する指針

- システム、プロセスコンテナ、K8sリソースについては、デフォルトで`*.cpu.time`を出力すべきです（SHOULD）。
- `*.cpu.*utilization`と`*.cpu.usage`のメトリクスは、明示的な設定の背後にゲートすべきです（SHOULD）。

## バックエンドに関する指針

- 役立つ場合には、`*.cpu.time`からutilizationやusageを導出するための変換やビューを提供すべきです（SHOULD）。
- システム、コンテナ、K8sリソースをまたいで、`*.cpu.time`を正となる情報源として扱うべきです（SHOULD）。

## CPU時間の使い方

累積されたCPU時間の値は、utilizationやusageのメトリクスを導出するために使用できます。

**Usage**メトリクスは、指定したウィンドウで`rate()`関数を使い、ウィンドウサイズで割ることで計算できます。CPU usageは通常core-secondsで測定されます。

**Utilization**は、上記の結果を与えられたCPU制限で割ることで計算でき、通常は[0, 1]の範囲になります。

CPU時間からusageやutilizationのメトリクスを導出する方法の例を以下に示します。

### CPU時間からUsageへ

`rate(system.cpu.time[5m])/(5*60)`（core-secondsで測定）。

これはPromQLでの相当式です。`rate()`関数は現在の値と直前の値の差分に相当し、分母は経過秒数です。

### CPU時間からUtilizationへ

`rate(system.cpu.time[5m])/(5*60)`（コアあたり[0, 1]で測定、制限は1コアに相当）。

`rate(k8s.pod.cpu.time[5m])/(5*60)/k8s.pod.cpu.limit`

上記により`k8s.pod.cpu.limit_utilization`の派生メトリクスが得られます。

### `idle`以外の状態を除いたUtilization

システムが`idle`以外の状態にあった時間の割合としてutilizationを表現するには、次の式を使用できます。

`sum(rate(system.cpu.time{cpu.mode!="idle"}[5m]) without (cpu.mode))/(5*60))`（コアあたり[0, 1]で測定）。

### システム全体のUtilization

システム全体のutilizationを得るには、すべてのコアにわたる平均を使用できます。

`avg(sum(rate(system.cpu.time{cpu.mode!="idle"}[5m])) by (cpu.logical_number))/(5*60)`

上記の式はあいまいさを含む可能性があるため、Semantic Conventionsプロジェクトの一部として標準化されているわけではありません。あくまで例として示しているにとどまります。

[Prometheus Node Exporter](https://github.com/prometheus/node_exporter/blob/b959d48df950d5c446660eca3354c26eb997ca44/docs/node-mixin/lib/prom-mixin.libsonnet#L85-L87)のようなプロジェクトには、システムのutilizationを計算する独自の式が付属しています。

`k8s.*.cpu.usage`の標準化は例外です。これはKubeletのStats APIから直接収集され、K8s固有のものだからです。

## 参考文献

1. [System CPU Utilizationに関するgist](https://gist.github.com/braydonk/b2381da98dc3c4fd5ac064045d556634)（Braydon Kains（@braydonk）による）
2. [正規化された総CPU utilizationメトリクスを任意項目として導入する試み](https://github.com/open-telemetry/semantic-conventions/issues/1873)

