概要 #
Metrics Explorerは、メトリクス名さえ分かれば絞り込めます。厄介なのはその先です。同じメトリクス・同じ期間でも、集計の選び方で値が変わります。
実際にこうなりました。
同じVM、同じ1時間の枠での CPU 使用率です。
| 集計 | 値 |
|---|---|
1時間平均(ALIGN_MEAN) |
0.14 |
1時間最大(ALIGN_MAX) |
0.4251 |
3倍です。 どちらも正しく、見たいものが違うだけです。ここを意識しないまま閾値を決めると、鳴らないアラートか鳴りっぱなしのアラートになり、あとから原因が追えなくて困ります。
自分が検証で実際に取ったメトリクスと値を、まとめて置いておきます。Cloud Loggingのクエリ集と対になるリファレンスです。貼り付けて使うクエリは、Cloud Monitoring メトリクス チートシートに目的別・サービス別でまとめています。
Google Cloudのコンソールを触ったことがある人向けです。監視の設計論やSLOの立て方は扱いません。
検証環境 #
- Google Cloud SDK 562.0.0
値は、このブログのTerraform/Google Cloudシリーズで作った検証環境から実際に取得したものです。
リソースは削除済みですが、メトリクスは残っています。 ログと同じで、片付けたあとでも調べ直せます。
ただし解像度は下がります。
| 種別 | 保持 | 解像度 |
|---|---|---|
| Compute Engine・GKE・Cloud SQL など、公式表が挙げる指標 | 24か月 | 6週間は元の頻度、以降は10分間隔 |
| カスタム・外部・エージェント | 24か月 | 同上 |
| Managed Service for Prometheus・OTLP | 24か月 | 1週間は元の頻度、次の5週間は1分間隔、以降は10分間隔 |
| それ以外 | 6週間 | — |
6週間を過ぎた分は10分間隔にダウンサンプリングされます。細かい変化を見たい検証結果は、その場で保存しておきます。
メトリクスの3属性 #
早見表を読む前に、これだけ押さえます。どの集計が使えるかが、ここで決まります。
| 属性 | 値 | 意味 |
|---|---|---|
metricKind |
GAUGE |
その瞬間の値。CPU使用率、メモリ使用量 |
DELTA |
期間内の増分。リクエスト数、送信バイト数 | |
CUMULATIVE |
起動時からの累計。再起動回数 | |
valueType |
DOUBLE / INT64 / DISTRIBUTION |
DISTRIBUTIONはパーセンタイルが取れる |
unit |
By / ms / 1 / 10^2.% |
10^2.%は「100倍すると%」 |
GAUGEに平均は使えても、CUMULATIVEには使えません。
$ ...&aggregation.perSeriesAligner=ALIGN_MEAN
ERR Field aggregation.perSeriesAligner had an invalid value of "ALIGN_MEAN":
The aligner cannot be applied to metrics with kind CUMULATIVE and value type INT64.
エラーが親切なので、迷ったら投げてみるのが早いです。CUMULATIVEならALIGN_DELTAかALIGN_RATEを使います。
早見表 #
kind・valueType・unitは、この環境の記述子から実際に読んだ値です。「時系列の本数」は2026年8月1日〜9月11日に実データがあった数で、どのメトリクスが実際に流れているかの目安です。
GCE #
| メトリクス | kind | valueType | unit | 時系列の本数 | ラベル |
|---|---|---|---|---|---|
compute.googleapis.com/instance/cpu/utilization |
GAUGE | DOUBLE | 10^2.% |
108 | instance_name |
compute.googleapis.com/instance/disk/read_bytes_count |
DELTA | INT64 | By |
108 | instance_name, device_name, device_type, storage_type |
compute.googleapis.com/instance/network/received_bytes_count |
DELTA | INT64 | By |
119 | instance_name, loadbalanced |
CPU使用率は10^2.%なので、0.14 が 14% です。コンソールは変換して表示しますが、APIで取ると生の値が返ります。
GKE #
| メトリクス | kind | valueType | unit | 時系列の本数 | ラベル |
|---|---|---|---|---|---|
kubernetes.io/node/cpu/allocatable_utilization |
GAUGE | DOUBLE | 1 |
57 | (なし) |
kubernetes.io/container/cpu/limit_utilization |
GAUGE | DOUBLE | 1 |
468 | (なし) |
kubernetes.io/container/memory/used_bytes |
GAUGE | INT64 | By |
3284 | memory_type |
kubernetes.io/container/restart_count |
CUMULATIVE | INT64 | 1 |
32 | (なし) |
kubernetes.io/pod/volume/utilization |
GAUGE | DOUBLE | 1 |
3116 | volume_name |
memory/used_bytesはmemory_type(evictable / non-evictable)で2本に割れます。同じ時刻の使用量を足すのは正しく、合計すれば両区分を合わせた使用量です。一方、本数を数えると1コンテナが2件になります。
{cluster_name: tf-adv-gke-ipmasq, container_name: curl, pod_name: curlpod} 0
{cluster_name: tf-adv-gke-ipmasq, container_name: curl, pod_name: curlpod} 311296
見た目が同じ2行に見えるのは、表示していないラベルで分かれているからです。「同じに見えるのに値が違う」ときは、隠れたラベルを疑います。
memory_type=evictableは「カーネルが容易に回収できるメモリ」で、Podの退避しやすさとは関係ありません。
Cloud Run #
| メトリクス | kind | valueType | unit | 時系列の本数 | ラベル |
|---|---|---|---|---|---|
run.googleapis.com/request_count |
DELTA | INT64 | 1 |
9 | response_code, response_code_class |
run.googleapis.com/request_latencies |
DELTA | DISTRIBUTION | ms |
9 | response_code, response_code_class |
run.googleapis.com/container/instance_count |
GAUGE | INT64 | 1 |
24 | state |
instance_countのstateはactive/idleに分かれます。合計は存在するインスタンス数です。課金の扱いは課金方式と最小インスタンス数で変わるので、合計がそのまま請求に対応するわけではありません。
request_countとrequest_latenciesにはrouteラベルもありますが、公式定義では常に空です。実測でも空でした。URLパス別の集計には使えません。
ロードバランサ #
| メトリクス | kind | valueType | unit | 時系列の本数 | ラベル |
|---|---|---|---|---|---|
loadbalancing.googleapis.com/https/request_count |
DELTA | INT64 | 1 |
8 | response_code, response_code_class, cache_result, client_country, ほか |
loadbalancing.googleapis.com/https/backend_latencies |
DELTA | DISTRIBUTION | ms |
8 | response_code_class, cache_result, protocol, ほか |
response_code_classに入っていたのは200/400/500で、2xxのような書き方ではありませんでした。
5xxだけを見るならresponse_code_class="500"です。response_codeまで下げると本数が増えます。
Cloud SQL / Memorystore #
| メトリクス | kind | valueType | unit | 時系列の本数 | ラベル |
|---|---|---|---|---|---|
cloudsql.googleapis.com/database/cpu/utilization |
GAUGE | DOUBLE | 10^2.% |
2 | (なし) |
redis.googleapis.com/stats/memory/usage_ratio |
GAUGE | DOUBLE | 1 |
1 | role |
この環境のroleはprimaryだけでした(レプリカを立てていないため)。レプリカがある構成では、値を混ぜないようにroleで分けます。
ラベルは2か所にある #
Cloud Loggingと同じ構造です。
| 場所 | 何が入るか | 例 |
|---|---|---|
resource.labels |
リソースを特定する軸 | cluster_name, pod_name, zone |
metric.labels |
そのメトリクス固有の軸 | memory_type, response_code_class |
早見表の「ラベル」列はmetric.labelsです。resource.labelsは種別ごとに決まっています。
| リソース種別 | resource.labels |
|---|---|
gce_instance |
project_id, instance_id, zone |
k8s_container |
project_id, location, cluster_name, namespace_name, pod_name, container_name |
k8s_node |
project_id, location, cluster_name, node_name |
k8s_pod |
project_id, location, cluster_name, namespace_name, pod_name |
cloud_run_revision |
project_id, service_name, revision_name, location, configuration_name |
cloudsql_database |
project_id, database_id, region |
redis_instance |
project_id, region, instance_id, node_id |
https_lb_rule |
project_id, region, url_map_name, backend_target_name, backend_name, ほか |
gce_instanceにinstance_nameはありません。 resource.labelsにあるのはinstance_idで、名前のほうはmetric.labels側に付いています。名前で絞りたいときにここで詰まります。
この検証ではmonitoredResourceDescriptorsから432件の種別が返りました。取得時点の件数で、製品全体の固定値ではありません。
集計で値が変わる #
冒頭の話です。同じメトリクス、同じ期間(2026-09-08 08:00〜09:30 UTC)で、集計だけ変えました。
まず alignment だけを変えます。instance_id を 1 台に固定し、同じポイント(終端 2026-09-08T09:30:00Z)を比べました。
instance_id = 2156948680906796148
ALIGN_MEAN 2026-09-08T09:30:00Z 0.14
ALIGN_MAX 2026-09-08T09:30:00Z 0.4251
次に、3台ぶんをまとめる reduction を足します。
| 集計 | 本数 | 値 |
|---|---|---|
3600s / ALIGN_MEAN + REDUCE_MEAN(zone) |
1 | 0.2343 |
3600s / ALIGN_MEAN + REDUCE_MAX(zone) |
1 | 0.5071 |
読み方は2段階です。
- **alignment(
perSeriesAligner)**は、1本の時系列を決めた間隔にそろえる - **reduction(
crossSeriesReducer)**は、整列済みの時系列をgroupByFieldsのグループごとにまとめる
reductionでどの軸を残すかはgroupByFieldsで決めます。
ALIGN_MEANからALIGN_MAXに変えただけで 0.14 → 0.4251 になりました。「1時間ならして14%」と「1時間のどこかで42%」は別の話です。
スパイクを見たいならMAX、傾向を見たいならMEANを選びます。
reductionも同じです。上の表はgroupByFieldsに zone を指定しているので、zone ごとに1本になります(この環境では1 zone なので1本)。
REDUCE_MEANは同じ zone の平均、REDUCE_MAXはその中で最も高い1台の値です。いずれも ALIGN_MEAN で1時間平均を取ったあとの比較です。
1台だけ張り付いている状況は、平均では見えません。
本数が 3 → 1 に減っているのが reduction です。まとめた結果、どのインスタンスだったかは分からなくなります。
3つの取り方 #
同じ値を3経路で取れます。どれを使うかは、何をしたいかで決まります。
同じメトリクス・同じ時刻・同じ集計で揃えたところ、3経路とも同じ値でした。
v3 REDUCE_MEAN 0.2343
MQL group_by [] 0.23426758725038063
PromQL avg() 0.23426758725038063
表示精度の範囲で一致しました。ただし掲載したコマンドだけでは、3つが同じ集計条件だったことを追試できません。
追試するなら、対象VMと評価時刻を固定し、alignment と reduction の両方をそろえます。
| 経路 | 向くこと |
|---|---|
v3 timeSeries API |
集計を細かく指定する。値を確かめる |
| PromQL | Prometheusの書き方をそのまま使う |
| MQL | パイプで読み書きする。2025年7月22日にサポート終了。コンソールからの新規作成には使えないが、既存の設定と API 経由は動く |
v3 timeSeries API #
curl -s -G -H "Authorization: Bearer $(gcloud auth print-access-token)" \
"https://monitoring.googleapis.com/v3/projects/YOUR_PROJECT_ID/timeSeries" \
--data-urlencode 'filter=metric.type="compute.googleapis.com/instance/cpu/utilization"' \
--data-urlencode "interval.startTime=2026-09-08T08:00:00Z" \
--data-urlencode "interval.endTime=2026-09-08T09:30:00Z" \
--data-urlencode "aggregation.alignmentPeriod=3600s" \
--data-urlencode "aggregation.perSeriesAligner=ALIGN_MEAN"
集計の指定がそのままパラメータになるので、上の表を作るのに使いました。
PromQL #
メトリクス名にスラッシュとドットが入るので、そのままでは識別子になりません。書き方は2つあります。
{"kubernetes.io/container/memory/used_bytes"}
kubernetes_io:container_memory_used_bytes
どちらも同じ62本の時系列が返りました。前者はUTF-8記法で名前を波括弧に入れる形です。後者は従来の記法で、最初の/を:に、.や残りの/を_に置き換えます(ドメイン自体はkubernetes_ioとして残ります)。
curl -s -H "Authorization: Bearer $(gcloud auth print-access-token)" \
"https://monitoring.googleapis.com/v1/projects/YOUR_PROJECT_ID/location/global/prometheus/api/v1/query" \
--data-urlencode 'query={"kubernetes.io/container/memory/used_bytes"}' \
--data-urlencode "time=2026-09-08T09:25:00Z"
MQL #
curl -s -X POST -H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H "Content-Type: application/json" \
"https://monitoring.googleapis.com/v3/projects/YOUR_PROJECT_ID/timeSeries:query" \
-d '{"query": "fetch gce_instance::compute.googleapis.com/instance/cpu/utilization | within 90m, d'\''2026/09/08 09:30'\'' | every 1h | group_by [], [v: mean(val())]"}'
2026-09-08T09:30:00Z 0.23426758725038063
v3のREDUCE_MEANと同じ 0.2343 になりました。 経路が違っても同じ数字が出ることは確かめておくと安心です。
gcloud では時系列を読めない #
gcloud monitoringにあるのは4つです。
dashboards Manage Cloud Monitoring dashboards.
policies Manage Cloud Monitoring alerting policies.
snoozes Manage Cloud Monitoring snoozes.
uptime Manage Cloud Monitoring uptime checks and synthetic monitors.
時系列を読むサブコマンドはありません。 gcloud monitoring time-series listのようなものを探しても見つからないので、値が要るならAPIを呼びます。アラートポリシーやダッシュボードの管理はgcloudでできます。
gcloud monitoring policies list --format="value(displayName)"
空が返ったときに疑う順 #
Cloud Loggingの「0件」と同じで、エラーにならないまま空が返ります。
1. その時刻にデータがあるか #
いちばん多い原因です。instantクエリは、指定時刻の近くにデータが無ければ空を返します。
$ ...query={"kubernetes.io/container/memory/used_bytes"}&time=2026-09-08T09:00:00Z
0 系列
$ ...query={"kubernetes.io/container/memory/used_bytes"}&time=2026-09-08T09:25:00Z
62 系列
25分ずらしただけです。 この環境ではクラスタが短命でした。
集計をかけずにv3で取ると、生の測定時刻が見えます。
2026-09-08T09:19:37Z 2026-09-08T09:20:37Z 2026-09-08T09:21:37Z
2026-09-08T09:22:37Z 2026-09-08T09:23:37Z
60秒間隔で5点だけでした。まずこうして、どこにデータがあるかを確かめます。集計をかけた結果と生の測定点は別物なので、時刻を詰めるなら集計を外します。
2. メトリクス名が合っているか #
metricDescriptorsにフィルタをかけて確かめます。
curl -s -G -H "Authorization: Bearer $(gcloud auth print-access-token)" \
"https://monitoring.googleapis.com/v3/projects/YOUR_PROJECT_ID/metricDescriptors" \
--data-urlencode 'filter=metric.type=starts_with("kubernetes.io/container")'
記述子があることと、データがあることは別です。記述子はプロジェクトで使っていなくても返ります。
3. 集計がそのメトリクスに使えるか #
これはエラーで落ちます。 メッセージにkindとvalueTypeが出るので、そのまま直せます。
4. リソース種別が合っているか #
k8s_containerとk8s_pod、k8s_nodeとk8s_clusterは別です。取りたい粒度の種別を選びます。
5. そもそも収集しているか #
GKEは有効化したコンポーネントの分しか出しません。GCEも、ゲストOS側の指標(メモリ使用量など)は収集の設定が要ります。Ops Agent が推奨ですが、旧来の Monitoring agent でも取れます。
アラートに持っていく #
Metrics Explorerで確かめた条件は、そのままアラートポリシーの条件になります。このとき集計を決め直さないこと。 見ていたグラフと違う集計で条件を書くと、グラフでは超えているのに鳴りません。
ポリシーはgcloudで一覧できます。
gcloud monitoring policies list --format="table(displayName,enabled,conditions[].displayName)"
アラートはTerraformで持つほうが、条件の変更をレビューで追えます。 閾値の根拠はコメントや設計資料に書きます。コンソールから作る場合も、ポリシーの Documentation 欄に根拠を残せます。どちらにしても、理由は書かないと残りません。
まとめ #
- 集計で値は変わる。 同じ期間でも
ALIGN_MEANは0.14、ALIGN_MAXは0.4251だった - alignmentは1本を時間方向にそろえる、reductionは複数本を1本にまとめる。 別の操作
kindが使える集計を決める。CUMULATIVEに平均は使えない- 単位を見る。
10^2.%の0.14は14% - 同じに見えて値が違うなら、隠れたラベルを疑う。
memory_typeで2本に割れていた gce_instanceのresource.labelsにinstance_nameは無い。 あるのはinstance_id- 空が返ったらまず時刻を疑う。 25分ずらしたら62本出た
- gcloudでは時系列を読めない。 値が要るならAPI