概要 #
第1回から第5回までは、minikube上にPrometheus・Grafana・Prometheus Operator・kube-prometheus-stackを自分で構築し、仕組みを1つずつ確認してきた。ここまでの構成はすべて、自分でPrometheusを運用する前提(self-managed)だった。
GKE Autopilot 1.25以降、GKE Standard 1.27以降では、Google Cloud Managed Service for Prometheus(以下GMP)のManaged Collectionが既定で有効になっている。Standardはクラスタ作成時に無効化できるが、Autopilot 1.25以降では無効化できない。GMPが管理するのはCollectorのスケール・保存先(Monarch)・PromQLクエリの処理基盤であり、Exporterのインストールや、ServiceMonitor / PodMonitor / PrometheusRuleに代わるPodMonitoring / ClusterPodMonitoring / Rulesの適用は引き続き利用者側の責任である。
この記事では、GKE Standardクラスタを作り、第1回〜第5回の手動構成がGMPのCustom Resourceでどう表現されるかを実測する。可視化については、収集・保存がマネージドであることと、Grafanaを使い続けるかどうかは別の軸であることを示すため、次の2パターンを確認する。パターンAはCloud Monitoring Dashboardのウィジェットが使うクエリと同じPromQLで実際に時系列を取得できることまで、パターンBはPromQL APIでの取得とdata sourceの設定までを確認する。いずれもブラウザ上での画面描画そのものは確認していない。
- パターンA(可視化までGoogle Cloudで完結): Cloud Monitoring(Metrics Explorer・Dashboard)のPromQL APIで同じメトリクスを取得できるかを確認する。これを本線とする
- パターンB(Grafana継続利用): GrafanaをGKE上に再デプロイし、第1回で作ったDashboard資産のPromQL定義をそのまま使ったクエリがAPI経由で同じ値を返すかを確認する
対象読者は、第1回〜第5回でPrometheusのkubernetes_sd_configs・Prometheus Operator・kube-prometheus-stackを手で構築した経験がある人。GKE・GMPに初めて触れる人を想定している。マルチクラスタ・マルチプロジェクトでのメトリクス集約、remote_write・federationなどの高度な構成は扱わない。
今回使う環境 #
第1回〜第5回のminikube環境とは別に、GKE Standardクラスタを新規に作る。第1回から使っているnode-exporterのDaemonSetはhostNetwork: true / hostPID: trueを設定し、hostPath: path: /をreadOnly: trueでマウントしている。GKE AutopilotはhostNetworkとhost namespaceを許可せず、hostPathについても書き込みモードは全面禁止、read-onlyモードでも/var/log/配下のパスのみを許可するため、/をマウントする同じManifestを再利用できない。同じExporter・同じPromQLクエリで第5回までと比較するという本記事の目的上、GKE Standardを選ぶ。
GCPリソースの作成はTerraformで管理する。コードはterraform-gcp-examples/Advanced-Examples/31-gke-managed-prometheusにある。
$ terraform version
Terraform v1.14.3
on linux_amd64
+ provider registry.terraform.io/hashicorp/google v7.46.1
$ kubectl version
Client Version: v1.35.2
Server Version: v1.35.8-gke.1225000
$ gcloud container clusters describe tf-adv-managed-prometheus --zone asia-northeast1-a \
--format="value(currentMasterVersion,currentNodeVersion,releaseChannel.channel)"
1.35.8-gke.1225000 1.35.8-gke.1225000 REGULAR
ノード1台のゾーンクラスタ(asia-northeast1-a)、マシンタイプはe2-standard-2でSpot VMを使う。ノードは外部IPを持たず、コントロールプレーンは公開エンドポイントのまま許可CIDRで絞っている。踏み台を立てないのは、主題がGMPの構成比較だからである。
cd terraform-gcp-examples/Advanced-Examples/31-gke-managed-prometheus
cp terraform.tfvars.example terraform.tfvars # project_id・authorized_ipv4_cidrを書く
terraform init && terraform apply
eval "$(terraform output -raw get_credentials)"
全体の構成は次の通り。収集・保存はどちらのパターンでも共通で、可視化だけがパターンAとBで分かれる。
flowchart LR
NE["node-exporter"] --> MC["Managed Collector"]
MC --> MN["Monarch"]
subgraph A["パターンA"]
CM["Cloud Monitoring"]
end
subgraph B["パターンB"]
GF["Grafana"]
DS["data source syncer"]
end
MN --> CM
GF -->|"PromQLクエリ"| MN
DS -.->|"URL・トークンを設定"| GF
data source syncerはクエリの経路には入らず、Grafana側の設定を定期的に書き換えるだけであることを点線で示している。
Managed Collectionの有効状態を確認する #
クラスタ作成直後、PodMonitoringやRulesといったCustom Resourceを何も作っていない時点でgmp-systemにcollector・gmp-operatorが起動している。
$ kubectl get pods -n gmp-system
NAME READY STATUS RESTARTS AGE
collector-r7hz2 2/2 Running 0 57s
gmp-operator-6b4f84fd77-xwcgd 1/1 Running 0 2m44s
ただし、Terraform側のgoogle_container_clusterではmonitoring_config.managed_prometheus.enabled = trueを明示的に指定している。GKE Standard 1.27以降・Autopilot 1.25以降ではこの指定が無くても既定で有効になるというのは公式仕様として確認した内容であり、「指定を省略しても有効になること」自体は本記事では実測していない(Standardは作成時に無効化できるが、Autopilotは無効化できない)。「導入する」ではなく「有効状態を確認し、必要なら調整する」という順序になる。
GKEのmanaged kube-state-metricsパッケージは、Terraformのmonitoring_config.enable_componentsにPODを明示したことで動いている(Standardは1.29.2-gke.2000以降で既定有効だが、本記事ではこの既定動作そのものは検証していない)。
$ kubectl get pods --all-namespaces | grep state-metrics
gke-managed-cim kube-state-metrics-0 2/2 Running
Node ExporterをPodMonitoringでscrapeする #
第1回のDaemonSetをそのまま適用する。
kubectl create namespace monitoring
kubectl apply -f k8s/01-node-exporter.yaml
第5回のServiceMonitorに対応するのがPodMonitoringである。最初に次のように書いた。
spec:
selector:
matchLabels:
app: node-exporter
endpoints:
- port: "9100"
interval: 30s
ターゲットが1つも見つからなかった。生成されたscrape設定を確認すると、次のrelabel_configsが入っていた。
- source_labels: [__meta_kubernetes_pod_container_port_name]
regex: "9100"
action: keep
portフィールドはx-kubernetes-int-or-stringで、YAMLでクォートすると文字列(ポート名として検索)、**クォートしないと数値(ポート番号として検索)**になる。このDaemonSetのcontainerPortにはnameを付けていないため、__meta_kubernetes_pod_container_port_nameは空文字列になり、"9100"という名前には一致しなかった。port: 9100(数値)に直すと、__address__を$1:9100に書き換える設定が生成され、scrapeできるようになった。
apiVersion: monitoring.googleapis.com/v1
kind: PodMonitoring
metadata:
name: node-exporter
namespace: monitoring
spec:
selector:
matchLabels:
app: node-exporter
endpoints:
- port: 9100
interval: 30s
target statusでActive Targetsを確認する #
GMPには、Prometheus本体の/targetsに相当する機能として、PodMonitoringのstatus.endpointStatusesでActive Targetsを確認できる仕組みがある。既定では無効で、OperatorConfigで有効にする。
最初にgmp-system名前空間へOperatorConfig/configを新規作成しようとしたところ、次のエラーで拒否された。実機で確認すると、OperatorConfig/configは**gmp-public名前空間**に既定で存在していた。
Error from server (BadRequest): operatorconfigs "config" is invalid: :
ValidatingAdmissionPolicy 'operatorconfigs.monitoring.googleapis.com' with binding
'operatorconfigs.monitoring.googleapis.com' denied request: failed expression:
object.metadata.namespace == 'gmp-public'
既存のgmp-public/configをpatchする。
kubectl patch operatorconfig config -n gmp-public --type merge \
-p '{"features":{"targetStatus":{"enabled":true}}}'
設定変更がPodへ反映されるまで、patch実行から数分かかった。この間に、ConfigMapの配布・設定の再読み込み・target statusの更新のどこで時間を要したかは切り分けていない。反映後、PodMonitoringにActive Targetsが載った。
status:
conditions:
- status: "True"
type: ConfigurationCreateSuccess
endpointStatuses:
- activeTargets: 1
collectorsFraction: "1"
name: PodMonitoring/monitoring/node-exporter/9100
sampleGroups:
- sampleTargets:
- health: up
labels:
cluster: tf-adv-managed-prometheus
instance: gke-xxxxx-xxxxx:9100
job: node-exporter
location: asia-northeast1-a
namespace: monitoring
pod: node-exporter-xxxxx
project_id: YOUR_PROJECT_ID
top_level_controller_name: node-exporter
top_level_controller_type: DaemonSet
project_id・location・clusterはOperatorConfigのexternalLabelsとして全ジョブに付与される。namespace・pod・top_level_controller_name・top_level_controller_typeはPodMonitoringの既定のtargetLabels.metadataから付く。minikube時代のkubernetes_sd_configsが付けていたラベルと似ているが、付与元が「自分で書いたrelabel_configs」から「GMPの既定動作」に変わっている。
Cloud Monitoringへの取り込みをPromQLで確認する #
target statusの確認とは別に、実際にCloud Monitoringへ取り込まれているかをPromQL APIで確認する。
$ curl -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=up{job="node-exporter"}'
{"status":"success","data":{"resultType":"vector","result":[{"metric":{
"__name__":"up","cluster":"tf-adv-managed-prometheus","instance":"...:9100",
"job":"node-exporter","location":"asia-northeast1-a","namespace":"monitoring",
"pod":"node-exporter-...","project_id":"YOUR_PROJECT_ID",
"top_level_controller_name":"node-exporter","top_level_controller_type":"DaemonSet"
},"value":[...,"1"]}]}}
第5回のCRとManaged CollectionのCRの対応関係 #
第5回ではServiceMonitor / PodMonitor / PrometheusRuleを使った。GMPのCRDはこれらと1対1ではない。
| 第5回 | GMP | 対応のしかた |
|---|---|---|
ServiceMonitor |
PodMonitoring |
Serviceのselectorではなく、対応するPodのselectorへ変換する |
PodMonitor |
PodMonitoring |
両方Pod selectorを使うため、比較的直接移植できる |
| (Namespace横断が必要な場合) | ClusterPodMonitoring |
PodMonitoringのcluster-scoped版。managed kube-state-metrics自身もこちらを使っている |
PrometheusRule |
Rules / ClusterRules / GlobalRules |
スコープが違う |
RulesはNamespace単位、ClusterRulesはクラスタ単位でメトリクスを評価する。GlobalRulesはmetrics scope全体(Cloud Monitoring独自のメトリクスやクラスタをまたいだ集計が必要な場合)を対象にする。Google自身はRules・ClusterRulesを優先し、GlobalRulesは必要な場合に限って使うことを推奨している。本記事のNodeExporterDownは単一Namespaceのメトリクスを対象にするためRulesを使う。
managed kube-state-metrics用に実際に生成されているClusterPodMonitoringを見ると、portは名前(k8s-objects)で指定され、metricRelabelingでnamespace=~"gke-managed-.*"をdropしている。このdrop設定はこのClusterPodMonitoring自身のscrape対象だけに効くため、自前で別のkube-state-metricsを追加しても、この設定によって自動的にdropされることはない。両方を有効にする場合の重複リスクは、drop設定の有無ではなく、同じメトリクス名を別々のCRが別々にscrapeして送る点にある。
apiVersion: monitoring.googleapis.com/v1
kind: ClusterPodMonitoring
metadata:
name: kube-state-metrics
spec:
endpoints:
- interval: 30s
metricRelabeling:
- action: drop
regex: gke-managed-.*
sourceLabels: [namespace]
port: k8s-objects
selector:
matchLabels:
app.kubernetes.io/name: gke-managed-kube-state-metrics
有効にしたPODコンポーネントは4メトリクスだけ公開する #
gke-managed-cimNamespaceのkube-state-metrics-0(PODコンポーネントを有効にして動いているインスタンス)へport-forwardして/metricsを直接確認すると、公開されているのは次の4つだけだった。
$ kubectl port-forward -n gke-managed-cim kube-state-metrics-0 18080:8080
$ curl -s http://localhost:18080/metrics | grep "^# HELP"
# HELP kube_pod_container_status_ready [STABLE] Describes whether the containers readiness check succeeded.
# HELP kube_pod_container_status_waiting_reason [STABLE] Describes the reason the container is currently in waiting state.
# HELP kube_pod_status_phase [STABLE] The pods current phase.
# HELP kube_pod_status_unschedulable [STABLE] Describes the unschedulable status for the pod.
kube_pod_info・kube_node_info・kube_deployment_status_replicasなどは無い。これらが要る場合は自前でkube-state-metricsを入れ、PodMonitoringで拾う必要がある。両方を有効にすると重複・不正確なメトリクスが出るので、どちらを使うか決める。
Rulesで第3回のalerting ruleを再現する
#
第3回(#465)のNodeExporterDownをRulesで再現する。
apiVersion: monitoring.googleapis.com/v1
kind: Rules
metadata:
name: node-exporter-rules
namespace: monitoring
spec:
groups:
- name: node-exporter
rules:
- alert: NodeExporterDown
expr: up{job="node-exporter"} == 0
for: 45s
labels:
severity: warning
annotations:
description: 'up{job="node-exporter", instance="{{ $labels.instance }}"} has been 0 for more than 45s'
適用した直後、クラスタ作成時点では存在しなかった2つのコンポーネントが新たに起動した。
$ kubectl apply -f k8s/03-rules.yaml
$ kubectl get pods -n gmp-system
alertmanager-0 2/2 Running
rule-evaluator-... 2/2 Running
ルールの評価はrule-evaluator、通知の配送はalertmanagerという別々のコンポーネントが担う。collector・gmp-operatorはクラスタ作成と同時に動いていたが、rule-evaluator・alertmanagerはクラスタ作成直後には存在せず、Rulesを適用した後に新たに起動した。Rulesを削除した後にこれらが停止するかどうかは今回確認していない。
Managed Alertmanagerは執筆時点でPreview機能であり、GA機能と同等のサポートは保証されない。本記事で確認しているのは、検証環境で自動配置されたこの時点の挙動である。
rule-evaluatorに渡された式を見ると、up{job="node-exporter"}だけでなくcluster・location・namespace・project_idも自動で追加されていた。OperatorConfigにはcollection.externalLabelsとは別にrules.externalLabelsという項目があり、ルール評価のクエリにはこちらが使われている。収集側と同じ値が入っているが、別々に設定される項目である。
up{cluster="tf-adv-managed-prometheus",job="node-exporter",location="asia-northeast1-a",namespace="monitoring",project_id="YOUR_PROJECT_ID"} == 0
alertingの一連の流れ #
Node ExporterのDaemonSetへ一時的に存在しないnodeSelectorを付けてPodを消し、up==0を45秒超続けた。
sequenceDiagram
participant NE as node-exporter
participant RE as rule-evaluator
participant AM as Alertmanager
participant User as 利用者
User->>NE: Podを削除(nodeSelector変更)
Note over RE: up==0が45秒続く
RE->>RE: inactive → firing
RE->>AM: アラートを送信
Note over AM: receiver: noop(通知先未設定)
User->>NE: nodeSelectorを戻す
Note over RE: 新Podのupは1に戻る(旧系列はしばらく残る)
RE->>RE: firing → resolved
実測した時刻は次の通り。
07:27:01頃 up==0 開始(Pod削除)
07:27:46 rule-evaluatorがfiringへ遷移(activeAt、45秒後)
07:28:46 Alertmanagerがアラートを保持(startsAt。評価間隔60秒の通知タイミングによるずれ)
07:29頃 Pod復旧(nodeSelectorを戻す)。新Podのupはただちに1になった
07:32:46 rule-evaluatorがinactiveへ遷移(lastEvaluation)
今回の検証環境では、Alertmanagerのルートはreceiver: noopになっており、通知先を設定しない限りどこにも送られなかった(通知経路の設定は本記事の対象外)。firingしたアラートにはgeneratorURLが付き、該当PromQLを埋め込んだCloud Monitoring Metrics Explorerへ直接リンクしていた。
{
"labels": {"alertname": "NodeExporterDown", "severity": "warning"},
"status": {"state": "active"},
"receivers": [{"name": "noop"}],
"generatorURL": "https://console.cloud.google.com/monitoring/metrics-explorer;...?project=YOUR_PROJECT_ID"
}
Pod復旧直後は、新しいPodのupが1になっても、up{job="node-exporter"}==0というルールの式自体は、削除された旧Podのラベルを持つ系列(値は0のまま更新が止まっている)にも一致し続けていた。この系列が更新されなくなってからしばらく経ってクエリの結果から外れ、ルールが評価する対象が新Podの系列だけになった時点でresolvedへ遷移したと考えられる。この遷移の正確なタイミングは厳密には切り分けていない。図の「upが1に戻る」は新Podの状態を指しており、resolvedへの遷移条件そのものではない。
パターンA:Cloud Monitoringで可視化する #
第1回のPromQL(CPU・メモリ・ディスク使用率)の算出式を、Cloud MonitoringのPromQL APIでも使えるか確認する。
$ curl ... --data-urlencode 'query=100 - (avg(rate(node_cpu_seconds_total{mode="idle",job="node-exporter"}[5m])) * 100)'
{"status":"success","data":{"resultType":"vector","result":[{"metric":{},"value":[...,"54.34"]}]}}
算出式自体は第1回と同じだが、対象を絞るためjob="node-exporter"を追加している(第1回はDashboard全体が単一クラスタ向けだったため不要だった)。
メモリ使用率・ディスク使用率(node_filesystem_avail_bytes{mountpoint="/var"})も同じPromQLで取得できた。
// query=(1 - (node_memory_MemAvailable_bytes{job="node-exporter"} / node_memory_MemTotal_bytes{job="node-exporter"})) * 100
{"value": [..., "15.676640435433553"]}
// query=(1 - (node_filesystem_avail_bytes{job="node-exporter",mountpoint="/var"} / node_filesystem_size_bytes{job="node-exporter",mountpoint="/var"})) * 100
{"metric": {"device": "/dev/sda1", "fstype": "ext4", "mountpoint": "/var", ...}, "value": [..., "36.79711694038231"]}
ディスク使用率のクエリはmountpoint="/var"を指定しているが、GKEのContainer-Optimized OSにも/varという独立したマウントポイントが存在する(上記のdevice・fstypeラベルで確認)。minikubeとGKE Nodeでfilesystem構造が違う可能性を事前に挙げていたが、今回のクエリは変更なしで動いた。
Cloud MonitoringのMetrics Explorer、またはDashboard作成時の「Code」タブでPromQLを直接入力できる。GKEなど他のCloud Monitoringメトリクスと同じ画面・同じPromQLインターフェースで扱える。
実際にgcloud monitoring dashboards createで、このCPU使用率のPromQLを埋め込んだxyChartを1つ作成した。Dashboardそのものの画面描画は確認していないが、timeSeriesQuery.prometheusQueryに設定した式を、クラスタが稼働していた時間帯(query_range、13分間・1分刻み)に対して実行すると、13点の時系列が返った(87.9 → 54.3 → 10.5といった変動を含む)。Dashboardのウィジェットが使うのと同じクエリ・同じバックエンドから、1点ではなく連続した時系列が取得できることを確認した。
$ curl ... .../api/v1/query_range?query=...&start=2026-10-03T07:20:00Z&end=2026-10-03T07:37:00Z&step=60s
[1791012300, "87.9648175"]
[1791012360, "69.70670655555556"]
[1791012420, "54.341666666666654"]
...
[1791013020, "10.529623111111093"]
(13点)
Cloud Monitoring Alerting Policy(Alertmanagerとは別の仕組み)も存在するが、設定・通知は本記事の対象外とする(Rulesによる評価・firingまでを扱う)。
パターンB:Grafanaを継続利用する #
GrafanaをGKE上に再デプロイする。
kubectl apply -f k8s/04-grafana-deployment.yaml
kubectl apply -f k8s/05-grafana-service.yaml
実際のクエリ経路は、Grafanaからhttps://monitoring.googleapis.com/.../prometheus/へ直接である。data source syncerはこの経路には入らず、GrafanaのPrometheus data sourceにこのURLとOAuth2トークンを定期的に設定・更新する補助ツール(CronJob)として別に動く。
クエリ経路: Grafana → Cloud Monitoring Prometheus API → Monarch
設定経路: data source syncer → Grafana data source API(URL・トークンの更新のみ)
このクラスタではWorkload Identity Federationを有効にしていない。それでもsyncer Podは、Terraformで作成しノードプールに割り当てた専用のサービスアカウント(roles/monitoring.viewerを付与済み。GCPが自動生成する既定のCompute Engineサービスアカウントとは別物)を使って問題なく動いた。Workload Identityは「GMPに必須」ではなく、認証方法の選択肢の1つである。
Grafana service accountのtokenを発行し、data source(httpMethod: GET、prometheusVersion: 2.40.0)を作成したあと、syncerを一度実行すると、data sourceのJSON設定が書き換わった。
{
"jsonData": {
"httpHeaderName1": "Authorization",
"httpMethod": "GET",
"prometheusType": "Prometheus",
"prometheusVersion": "2.40.0",
"queryTimeout": "2m",
"timeout": "120"
},
"secureJsonFields": {"httpHeaderValue1": true}
}
この設定越しに同じPromQLを実行すると、Cloud Monitoring APIを直接呼び出したときと同じ結果が返った。
第1回のDashboard資産を再利用する #
第1回のDashboard JSONをこのdata sourceへ切り替えて取り込む。エクスポート時のテンプレート変数${DS_PROMETHEUS}を、GMPのdata source UIDに置き換えるだけで、Dashboard自体はimportできた。3つのPanel(CPU・メモリ・ディスク使用率)が参照しているPromQLは、Grafanaのdata source経由で問い合わせても同じ値が返ることを確認した。実際のブラウザ上でのPanel描画は確認していない。
GMPのPrometheus API互換性には制約があり、Grafana variableのlabel_values($label)は失敗しlabel_values($metric, $label)が必要になる。第1回のDashboardにはvariableが無いため、今回の実測には影響しない。すべてのGrafana Dashboardが無変更で動くとは限らない。
補足として、Grafana Dashboard JSONをCloud Monitoringへ持ち込む方法も2通りある。Cloud MonitoringのDashboardsページから「Import Dashboard」で直接JSONを選択してimportする方法と、Googleが提供するdashboard importer toolで変換結果を検査・編集してからuploadする方法である。どちらも内部ではGrafana JSONをCloud Monitoring形式へ変換しており、完全な1対1変換ではない。importer toolを使うと、対応する機能がない場合に生成されるreport.jsonで非対応箇所を確認できる。「Grafanaを捨ててもDashboard資産をそのまま移せる」というほど単純ではない。
metrics scope:クエリ方法によらずデータ範囲は1つ #
第5回までは、Grafanaが特定の1つのPrometheus serverへ問い合わせていた。GMPでは、Cloud MonitoringとGrafanaのどちらを使っても、取得できるデータはCloud Monitoringのmetrics scopeという概念で決まる。今回は単一Projectなので既定のmetrics scopeをそのまま使っており、パターンAとパターンBで同じメトリクスが取得できたのはこのためでもある(マルチプロジェクトでのmetrics scope設定は本記事の対象外)。
IAM:誰が・何のために・どのRoleを使うか #
Kubernetes RBACとは別に、Google Cloud側のIAMが必要になる。
| 主体 | 用途 | 認証方法 | Role |
|---|---|---|---|
| Managed collector | Cloud Monitoringへのメトリクス書き込み | Terraformで作成したノード用サービスアカウント | roles/monitoring.metricWriter |
| rule-evaluator | ルール評価のための読み取り・評価結果の書き込み | 同上 | roles/monitoring.viewer + roles/monitoring.metricWriter |
| data source syncer(パターンB、今回) | 自分のサービスアカウントでOAuth2アクセストークンを取得し、Grafanaのdata sourceに設定する。実際にそのトークンでCloud Monitoringを読むのはGrafana自身 | 同上(Workload Identityなし) | roles/monitoring.viewer |
| data source syncer(パターンB、Workload Identity Federation + 専用SA) | 同上 | 専用のGoogle Cloudサービスアカウントを、GKEのKubernetesサービスアカウントにバインド | roles/monitoring.viewer + roles/iam.serviceAccountTokenCreator |
今回の構成ではWorkload Identity Federationを使っていないため、roles/iam.serviceAccountTokenCreatorは不要だった。公式ドキュメントのWorkload Identity Federation利用時の手順ではこのRoleも付与しており、「専用SAならViewerだけでよい」とは限らない。
第5回までのKubernetes RBAC(Prometheus用のRole / RoleBinding、Prometheus OperatorのClusterRole)は、クラスタ内でどのオブジェクトを見てよいかを決めるものだった。GMPのIAMは、クラスタの外、Cloud Monitoringに対して何をしてよいかを決める。両方が必要という点は変わらないが、見ている境界が違う。
コスト確認:metricRelabelingでメトリクスを止める
#
Billingの金額変化は反映に遅延があるため、metricRelabelingで特定メトリクスをdropし、そのメトリクスのsample timestampが進まなくなることで確認する。
spec:
endpoints:
- port: 9100
interval: 30s
metricRelabeling:
- action: drop
sourceLabels: ["__name__"]
regex: "node_textfile_scrape_error"
適用時刻(07:33:26 UTC)を記録し、設定反映後に確認した。
timestamp(node_textfile_scrape_error{job="node-exporter"}) = 07:33:24(drop直前で停止)
timestamp(up{job="node-exporter"}) = 07:34:54(更新継続中)
同じ名前のメトリクスを送る他のtargetが無いことを確認したうえでの判定である(他targetがあると、limitなしのクエリでは別seriesのsampleが更新され続けて見える)。GMPは既存データを保持期間中(24か月。先頭1週間は元粒度、以降1分粒度、最終的に10分粒度へdownsample)残すため、単に検索して値が見えるかどうかでは判定できない。今回は反映直後に1回だけ確認しており、その後も継続してsample timestampが進まないままであることは追跡していない。
3つの構成の比較 #
| 項目 | 第5回(kube-prometheus-stack) | パターンA(Cloud Monitoring) | パターンB(Grafana+GMP) |
|---|---|---|---|
| Collection | Prometheus(自分で運用) | Managed Collector | Managed Collector |
| Storage | Prometheus TSDB | Monarch | Monarch |
| Query scope | 特定のPrometheus instance | Cloud Monitoringのmetrics scope | 同上 |
| Query | Prometheus自身 | Cloud Monitoring PromQL API | Grafana → 同じAPIへ直接 |
| Dashboard | Grafana | Cloud Monitoring | Grafana |
| Grafana運用 | 必要 | 不要 | 必要 |
| Dashboard資産 | (起点) | 再構築、またはimporter経由で変換 | 既存資産をdata source切り替えのみで再利用しやすい |
今回は扱わなかったこと #
- マルチクラスタ・マルチプロジェクトでのmetrics scope設定、federation、remote_write
- Alertmanager・Cloud Monitoring Alerting Policyの通知経路の設定
- self-deployed collection(第5回のkube-prometheus-stackをGKE上でそのまま継続する選択肢)
元に戻す #
kubectl delete -f k8s/ # CRと通常のk8sリソースを削除
terraform destroy
Terraformで作ったGKEクラスタ・VPC・サービスアカウントが削除されたことはgcloud container clusters list / gcloud compute networks listで確認した。パターンAの確認用に作成したCloud Monitoring Dashboardはgcloud monitoring dashboards deleteで個別に削除した(Terraformの管理対象外のため)。
新規サンプルの取り込みが止まったことも、destroy後にCloud Monitoringへ問い合わせて確認した。up{job="node-exporter"}を単純に検索するのではなく、過去の時刻を指定したtimeパラメータで最後のサンプルを特定し、それより先にサンプルが無いことを確認した。
time=2026-10-03T07:40:00Z 以降は 0 series(destroy開始から数分後)
time=2026-10-03T07:45:00Z・08:00:00Z・09:00:00Z も 0 series
最後に確認できたサンプル: 07:37:54Z(node poolの削除完了が3m23s後だった時間帯と一致)
Cloud Monitoringへ既に取り込まれた時系列データは、収集を止めても即座には消えず、保持期間(24か月)中は残る。実際、destroyから数時間後でもtimeパラメータで過去を指定すれば07:37:54Zのサンプルは検索できた。
まとめ #
GMPのManaged Collectionを使っても、Exporterの導入やPodMonitoring・Rulesの記述は引き続き自分で行う。マネージドになるのはCollectorの運用・保存・クエリ処理の基盤であり、この境界線は第5回のPrometheus Operatorが隠していた範囲とは違う。また、収集・保存のマネージド化と、可視化にGrafanaを使い続けるかどうかは別の軸であり、パターンA・パターンBの両方を試すことでその境界が具体的に見えた。
参考資料 #
- Google Cloud Managed Service for Prometheus
- Get started with managed collection
- GKE Autopilotのセキュリティ制約
- Managed rule evaluation and alerting
- Troubleshooting Managed Service for Prometheus
- Query using Grafana
- Collect and view kube state metrics
- Cost controls and attribution
- PromQL for Cloud Monitoring metrics
- Import Grafana dashboards into Cloud Monitoring
- Ingestion and querying with managed and self-deployed collection
- terraform-gcp-examples/Advanced-Examples/31-gke-managed-prometheus