概要 #
第4回では、kubernetes_sd_configsとrelabel_configsを手で書き、Prometheus用のRBACも自分で用意した。対象がNode Exporterとkube-state-metricsの2種類だけでもこれだけの記述が必要だった。監視対象が増えるほどkubernetes_sd_configsやrelabel_configsの管理対象も増え、実運用では保守負荷が高くなる。
Prometheus Operatorは、この繰り返しをCRD(Custom Resource Definition)で定義された型のリソースに置き換える。ServiceMonitor・PodMonitor・PrometheusRuleといったCustom Resourceを作るだけで、Operatorがそれらを読み取り、対応するPrometheus設定を生成する。この記事では、kube-prometheus-stack(Prometheus OperatorとPrometheus本体・Alertmanager・kube-state-metricsなどをまとめたHelm chart)を導入し、第1回〜第4回で手で書いた設定がこれらのCustom Resourceでどう表現されるかを、実際に生成された設定と見比べながら確認する。
対象読者は、第1回〜第4回でPrometheusのkubernetes_sd_configs・alerting rule・RBACを手で書いた経験がある人。Prometheus Operator・kube-prometheus-stackに初めて触れる人を想定している。GKEやGoogle Cloud Managed Service for Prometheusは次回(第6回)で扱う。Alertmanagerの高可用構成、long-term storage、Grafana Operatorなど追加コンポーネントは扱わない。
今回使う環境 #
第1回〜第4回で使っているminikube(--driver=docker、container runtime は containerd)をそのまま使う。minikube・Kubernetes・kubectlのバージョンは変えていない。開始時点は第4回の「元に戻す」を終えた状態、つまりsingle-nodeで、monitoring NamespaceにNode Exporter・Prometheus(static_configs構成)・Grafanaだけが動いている状態を前提とする。第4回で追加したkube-state-metrics・Prometheus用RBAC・kubernetes_sd_configsは、比較のために参照はするが、今回の開始時点では存在しない。
minikube version: v1.39.0
Kubernetes (Server Version): v1.37.0
kubectl (Client Version): v1.34.5-dispatcher
kubectlのクライアント版(1.34)とサーバー版(1.37)は、Kubernetesのversion skew policyが定める前後1マイナーバージョンの範囲を超えている。第1回から変えていない既知の状態であり、この記事内のコマンドはいずれも動作したが、再現する場合はサーバーと同じ1.37系のkubectlを使うことを勧める。
Prometheus Operatorの導入にはHelmを使う。
version.BuildInfo{Version:"v3.14.3", ...}
kube-prometheus-stackはprometheus-community/helm-chartsのHelm chartで、chart version 91.8.2(Prometheus Operator v0.94.1)を使う。
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm search repo prometheus-community/kube-prometheus-stack --versions | head -1
prometheus-community/kube-prometheus-stack 91.8.2 v0.94.1 kube-prometheus-stack collects Kubernetes manif...
第1回〜第4回の手動構成(monitoring Namespace)はそのまま残し、kube-prometheus-stackは別Namespace(monitoring-operator)へ導入する。chartが作るリソース名にはrelease名の接頭辞が付くため名前自体は衝突しないが、同じNamespaceに手動構成とOperator管理の両方のPrometheus・Alertmanagerが混在すると、どちらの構成を見ているか分かりにくくなる。そのため今回はNamespaceを分けて区別しやすくした(後述する通り、hostNetworkを使うNode ExporterのDaemonSetはNamespaceを分けてもポートの競合を避けられない)。
kubectl create namespace monitoring-operator
すべてローカルのminikube上で完結し、クラウドの課金は発生しない。
flowchart LR
subgraph manual["monitoring Namespace(第1回〜第4回・手動構成)"]
NESVC["node-exporter Service"]
NE["node-exporter Pod"]
GF["grafana Pod"]
PR1["prometheus
(手動構成)"]
end
subgraph op["monitoring-operator Namespace(本回)"]
OP["kube-prometheus-stack
-operator"]
PR2["prometheus
(Operator管理)"]
AM["alertmanager"]
KSM2["kube-state-metrics"]
end
SM["ServiceMonitor"]
PM["PodMonitor"]
RULE["PrometheusRule"]
OP -- "watch" --> SM
OP -- "watch" --> PM
OP -- "watch" --> RULE
OP -- "設定を生成" --> PR2
SM -- "selector" --> NESVC
NESVC -.->|"Endpoints/EndpointSlice"| NE
PM -- "selector" --> GF
PR2 -- "scrape" --> NE
PR2 -- "scrape" --> GF
Prometheus Operatorとは #
Prometheus Operatorは、PrometheusやAlertmanagerの構成・運用をKubernetesのCustom Resourceで宣言的に管理する仕組みである。kube-prometheus-stackをインストールすると、次のCRDが追加される。
kubectl get crd | grep monitoring.coreos.com
alertmanagerconfigs.monitoring.coreos.com
alertmanagers.monitoring.coreos.com
podmonitors.monitoring.coreos.com
probes.monitoring.coreos.com
prometheusagents.monitoring.coreos.com
prometheuses.monitoring.coreos.com
prometheusrules.monitoring.coreos.com
scrapeconfigs.monitoring.coreos.com
servicemonitors.monitoring.coreos.com
thanosrulers.monitoring.coreos.com
このうち今回扱うのは3つである。
ServiceMonitor: Serviceをラベルで選び、そのServiceのEndpoints / EndpointSliceを通じて監視対象を発見する。第4回でkube-state-metrics向けに使ったrole: endpointsliceのkubernetes_sd_configsに近いPodMonitor: Serviceを介さずPodを直接選ぶ。第4回でNode Exporter向けに使ったrole: podのkubernetes_sd_configsに近いPrometheusRule: 第3回のalerting ruleに相当する。alert/expr/for/labels/annotationsはそのまま使える
Prometheus・Alertmanagerも同様にCRDで定義された型で、そのCustom Resourceは、Prometheus・Alertmanagerそのものの構成(レプリカ数やバージョンなど)を宣言するためのものである。今回はkube-prometheus-stackが作成したものをそのまま使う。
kube-prometheus-stackを導入する #
helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack \
--version 91.8.2 \
--namespace monitoring-operator \
--set grafana.enabled=false
Grafanaは第1回で導入済みのものをそのまま使うため、chartに含まれるGrafanaは無効にした(grafana.enabled=false)。
minikubeは1 Nodeのため、chartが標準で導入するNode ExporterのDaemonSetは、第1回から動いている手動のNode Exporterと同じhostNetwork・ポート9100を使おうとして衝突する。
Warning FailedScheduling 0/1 nodes are available: 1 node(s) didn't have free ports for the requested pod ports.
今回はNode Exporterの比較が目的ではないため、chart側のNode Exporterを無効にする。最初にprometheus-node-exporter.enabled=falseを試したが、PodはPendingのままで効果がなかった。values.yamlを確認すると、prometheus-node-exporter:はサブchartへ渡す設定のまとまりで、有効・無効の切り替えには使えない。サブchart自体の有効・無効は、Chart.yamlのcondition: nodeExporter.enabledが指すnodeExporter.enabledで決まる。
helm upgrade kube-prometheus-stack prometheus-community/kube-prometheus-stack \
--version 91.8.2 \
--namespace monitoring-operator \
--set grafana.enabled=false \
--set nodeExporter.enabled=false
導入後のPodを確認する。
kubectl get pods -n monitoring-operator
NAME READY STATUS RESTARTS AGE
alertmanager-kube-prometheus-stack-alertmanager-0 2/2 Running 0 89s
kube-prometheus-stack-kube-state-metrics-78dc75b868-glhlb 1/1 Running 0 96s
kube-prometheus-stack-operator-8494f9cfd4-66lwj 1/1 Running 0 96s
prometheus-kube-prometheus-stack-prometheus-0 2/2 Running 0 89s
導入しただけで、ServiceMonitorとPrometheusRuleがすでに多数作られている。件数は、このHelm releaseが実際に作成したマニフェストを数える(クラスタ全体の一覧では、他の要因で増減した数と区別できない)。
helm get manifest kube-prometheus-stack -n monitoring-operator | grep -c "^kind: ServiceMonitor$"
helm get manifest kube-prometheus-stack -n monitoring-operator | grep -c "^kind: PrometheusRule$"
11
35
apiserver・coredns・kube-state-metrics・kubeletなど、Kubernetesのコンポーネントを監視するためのServiceMonitorと、ディスク逼迫やメモリ枯渇などを検知するPrometheusRuleが、Helm chartの一部として最初から入っている。この件数は、今回grafana.enabled=false・nodeExporter.enabled=falseにした構成でのものであり、既定値のまま導入した場合はGrafana用・Node Exporter用のServiceMonitorがさらに増える。第1回〜第4回でNode Exporter 1種類のscrape設定とalerting rule 1つを手で書いたのに対し、ここではインストール1回でこれだけの数が揃う。
ServiceMonitorで手動構成を置き換える #
第4回で、Node Exporterを検出するために次の設定を書いた。
scrape_configs:
- job_name: kubernetes-pods
kubernetes_sd_configs:
- role: pod
namespaces:
names:
- monitoring
relabel_configs:
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
action: keep
regex: "true"
第4回のこの設定はrole: podでNode ExporterのPodを直接発見していた。ServiceMonitorはServiceを介して発見する仕組みのため、これは同じ設定をOperator形式へそのまま書き換えるのではなく、発見方法自体をPod直接検出からServiceベースの検出へ変える、という違いがある(role: podに近い書き方は後述のPodMonitorで扱う)。ServiceMonitorでは、Node ExporterのServiceを指すラベルセレクタを書くだけでよい。ただし、既存のNode ExporterのServiceには、ServiceMonitorで選択できるラベルが付いていないため、まずラベルを1つ追加する(既存のSelector・Portは変更しない、追加のみ)。
kubectl label service node-exporter -n monitoring app=node-exporter
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: node-exporter
namespace: monitoring
labels:
release: kube-prometheus-stack
spec:
selector:
matchLabels:
app: node-exporter
endpoints:
- targetPort: 9100
endpoints[].portはServiceの名前付きport(.spec.ports[].name)を指す。今回のNode ExporterのServiceには名前付きportが無いため、Pod側のcontainer port番号を直接指すtargetPortを使った。Serviceに名前付きportがある場合はportを使う方が、Service定義の変更に追従しやすい。
labels.release: kube-prometheus-stackが無いと、このServiceMonitorはPrometheusから認識されない。最初はこのラベルを付けずに試し、targetが増えないことを確認した。
kubectl get prometheus kube-prometheus-stack-prometheus -n monitoring-operator \
-o jsonpath='{.spec.serviceMonitorSelector}{"\n"}'
{"matchLabels":{"release":"kube-prometheus-stack"}}
kube-prometheus-stackのchartにはserviceMonitorSelectorNilUsesHelmValues: trueという設定があり、serviceMonitorSelectorを明示的に指定しない(=値がnilのまま)場合、chartのテンプレートがrelease: <リリース名>というラベル条件を自動で生成する。values.yamlでserviceMonitorSelector: {}(全選択)という例も示されているが、これは明示的に{}を指定した場合の話であり、何も指定しない既定の状態とは異なる。podMonitorSelector・ruleSelectorにも同様の*SelectorNilUsesHelmValuesがあり、Prometheus Operatorの仕様として必須なのではなく、kube-prometheus-stackのこの既定値によって、自作するServiceMonitor・PodMonitor・PrometheusRuleそれぞれにこのリリースのラベルを付ける必要がある。
ここから先のManifestは、この記事のsampleディレクトリに収録している。リポジトリルートからcd content/blog/2026_10_02_prometheus_operator_kube_prometheus_stack/sampleを実行し、以降の相対パスのコマンドはこのディレクトリで実行する。
ラベルを付けたうえでkubectl applyし、targetを確認する。Operator管理のPrometheus(monitoring-operator Namespace)へ、第1回〜第4回の手動Prometheusとは別のポートでport-forwardする。
kubectl port-forward -n monitoring-operator svc/kube-prometheus-stack-prometheus 9091:9090
kubectl apply -f servicemonitor/node-exporter.yaml
curl -s http://localhost:9091/api/v1/targets | jq '.data.activeTargets[] | select(.labels.job=="node-exporter") | {health, scrapeUrl, labels}'
{
"health": "up",
"scrapeUrl": "http://192.168.157.2:9100/metrics",
"labels": {
"container": "node-exporter",
"endpoint": "9100",
"instance": "192.168.157.2:9100",
"job": "node-exporter",
"namespace": "monitoring",
"pod": "node-exporter-5nqhp",
"service": "node-exporter"
}
}
namespace ・pod・service・containerのラベルが自動的に付いている。第4回では、このうちnode・podの2つだけをrelabel_configsで手で書いており(namespace・service・containerは付与していなかった)、今回のServiceMonitorではこれらが既定で付く。
ここで押さえておきたいのは、ServiceMonitor(monitoring Namespace)を見つけて設定へ反映しているのはPrometheus自身ではなく、Prometheus Operatorだという点である。ServiceMonitor・PodMonitor・PrometheusRuleをwatchするのはOperatorの役割で、Operator用のClusterRoleにその権限がある。
kubectl get clusterrole kube-prometheus-stack-operator -o json | jq '.rules[] | select(.apiGroups[]? == "monitoring.coreos.com") | select(.verbs | index("watch"))'
{
"apiGroups": ["monitoring.coreos.com"],
"resources": [
"alertmanagers", "alertmanagerconfigs", "podmonitors", "probes",
"prometheusagents", "prometheuses", "prometheusrules", "servicemonitors",
"scrapeconfigs", "thanosrulers"
],
"verbs": ["get", "list", "watch"]
}
Operatorは、Prometheus Custom Resourceの2つのフィールドで対象のServiceMonitorを決める。serviceMonitorNamespaceSelectorはどのNamespaceを見るか、serviceMonitorSelectorはその中のどのServiceMonitorを選ぶかで、役割が異なる。今回の値はそれぞれ次の通りである。
kubectl get prometheus kube-prometheus-stack-prometheus -n monitoring-operator \
-o jsonpath='{.spec.serviceMonitorNamespaceSelector}{"\n"}{.spec.serviceMonitorSelector}{"\n"}'
{}
{"matchLabels":{"release":"kube-prometheus-stack"}}
serviceMonitorNamespaceSelector: {}は「全Namespaceが対象」を意味するため、monitoring Namespaceも対象に含まれる。その中からserviceMonitorSelectorのrelease: kube-prometheus-stackに一致するServiceMonitorだけが選ばれる。Namespaceをまたいで検出できるのはNamespaceの絞り込みが無いためであり、ラベルの条件とは別の設定である。Operatorがこれらを読み取れるのは、Operator用ClusterRoleがクラスタ全体を対象にしているためである。
Prometheus自身にも別のClusterRoleがあるが、これはOperatorが生成した設定に従って実際にscrapeの対象(Service・Pod・Endpoints等)を検出するためのものであり、ServiceMonitor自体を検出する権限ではない。
kubectl get clusterrole kube-prometheus-stack-prometheus -o jsonpath='{.rules}' | jq .
[
{
"apiGroups": [""],
"resources": ["nodes", "nodes/metrics", "services", "endpoints", "pods"],
"verbs": ["get", "list", "watch"]
},
{
"apiGroups": ["discovery.k8s.io"],
"resources": ["endpointslices"],
"verbs": ["get", "list", "watch"]
},
{
"apiGroups": ["networking.k8s.io"],
"resources": ["ingresses"],
"verbs": ["get", "list", "watch"]
},
{
"nonResourceURLs": ["/metrics", "/metrics/cadvisor"],
"verbs": ["get"]
}
]
第4回のRole(pods・services・endpointslicesの3種類をmonitoring Namespaceのみ)と比べ、Prometheus自身の対象もクラスタ全体に広がっている。nodes/metricsは、kubeletの/metrics/*に対するresource/subresourceの認可である(kubeletの認証・認可)。一方nonResourceURLsは、Kubernetes APIのリソースとして扱われないHTTPパスへのアクセス権を表す仕組みで(RBACの認可)、このClusterRoleでは/metrics・/metrics/cadvisorへのgetをこの形で許可している。両者はRBAC上の別種の権限である。いずれも第4回のRoleには無かった権限で、「RBACを自分で書かなくて済む」ことと「権限が最小限である」ことは別である。
ここまでの「誰が何をwatchし、何を生成し、何をscrapeするか」を時系列で整理する。
sequenceDiagram
participant User as 利用者
participant SM as ServiceMonitor(CR)
participant OP as Prometheus Operator
participant SEC as Secret(生成設定)
participant RC as config-reloader
participant PR as Prometheus
participant NE as node-exporter
User->>SM: kubectl apply
OP->>SM: watch(get/list/watch)
OP->>SEC: relabel_configs等を生成して書き込み
SEC-->>RC: マウントされた設定ファイルが更新
RC->>PR: 設定リロード(POST /-/reload)
Note over PR: Podは再作成されない
PR->>NE: scrape(生成された設定に従う)
ServiceMonitorを検出して設定を書くのはOperator、できあがった設定でscrapeするのはPrometheus、設定ファイルの変更を検知してリロードを指示するのはconfig-reloaderと、3つの別々の主体が関わっている。config-reloaderがwatchしているのはSecretというKubernetesオブジェクト自体ではなく、そのSecretがVolumeとしてマウントされたファイルである(本文で後述する通り、実際のログも「ファイルとディレクトリの変更を監視」としている)。この構成では、設定変更の経路にPrometheus Podの再作成を必要としない。後の節では、実際にPod UID・再起動回数が変化しないことも確認する。
生成された設定を確認する #
ServiceMonitorから実際にどのようなPrometheus設定が生成されたかを確認する。設定はSecretとしてPrometheusにマウントされている。
kubectl get secret prometheus-kube-prometheus-stack-prometheus -n monitoring-operator \
-o jsonpath='{.data.prometheus\.yaml\.gz}' | base64 -d | gunzip > generated-prometheus.yaml
grep -A 49 "job_name: serviceMonitor/monitoring/node-exporter/0" generated-prometheus.yaml
- job_name: serviceMonitor/monitoring/node-exporter/0
honor_labels: false
kubernetes_sd_configs:
- role: endpoints
namespaces:
names:
- monitoring
relabel_configs:
- source_labels:
- job
target_label: __tmp_prometheus_job_name
- action: keep
source_labels:
- __meta_kubernetes_service_label_app
- __meta_kubernetes_service_labelpresent_app
regex: (node-exporter);true
- action: keep
source_labels:
- __meta_kubernetes_pod_container_port_number
regex: 9100
- source_labels:
- __meta_kubernetes_endpoint_address_target_kind
- __meta_kubernetes_endpoint_address_target_name
separator: ;
regex: Node;(.*)
replacement: ${1}
target_label: node
- source_labels:
- __meta_kubernetes_endpoint_address_target_kind
- __meta_kubernetes_endpoint_address_target_name
separator: ;
regex: Pod;(.*)
replacement: ${1}
target_label: pod
- source_labels:
- __meta_kubernetes_namespace
target_label: namespace
- source_labels:
- __meta_kubernetes_service_name
target_label: service
- source_labels:
- __meta_kubernetes_pod_name
target_label: pod
- source_labels:
- __meta_kubernetes_pod_container_name
target_label: container
- action: drop
source_labels:
- __meta_kubernetes_pod_phase
regex: (Failed|Succeeded)
このjob全体は80行(1491〜1570行目)で、relabel_configsだけで17個のルールがある。掲載したのは先頭から10個で、監視対象の絞り込みとラベル付けを行う部分である。残り7個は__tmp_hash等を使ったシャーディング(Prometheusを複数台に分ける際の分散)用の汎用ルールで、各scrape jobに共通して付く。13行のServiceMonitor(spec以下は6行)から、意味のある部分だけでも10個のルールが生成されている。第4回で手で書いたrelabel_configsはnode・podの2ルールのみで、Failed・Succeeded状態のPodを除外するルールのように、手動構成では書いていなかったものも含まれる。
一方で、kubernetes_sd_configsのroleはendpointsである。Kubernetesの発表にある通りEndpoints APIはv1.33以降非推奨で、第4回ではrole: endpointsliceを使った。これはバージョン固有の制約ではなく、Prometheus Operator v0.94.1の時点でも、ServiceMonitorのspec.serviceDiscoveryRole(またはPrometheus Custom Resourceのspec.serviceDiscoveryRole)にEndpointSliceを指定すれば、今すぐ切り替えられる。既定がEndpointsなだけである。
kubectl patch prometheus kube-prometheus-stack-prometheus -n monitoring-operator \
--type=merge -p '{"spec":{"serviceDiscoveryRole":"EndpointSlice"}}'
prometheus.monitoring.coreos.com/kube-prometheus-stack-prometheus patched
適用後、生成設定を確認するとrole: endpointsliceに変わっている(対象はこのPrometheus配下の全ServiceMonitor)。
kubectl get secret prometheus-kube-prometheus-stack-prometheus -n monitoring-operator \
-o jsonpath='{.data.prometheus\.yaml\.gz}' | base64 -d | gunzip | grep -A 3 "job_name: serviceMonitor/monitoring-operator/kube-prometheus-stack-kube-state-metrics"
- job_name: serviceMonitor/monitoring-operator/kube-prometheus-stack-kube-state-metrics/0
honor_labels: true
kubernetes_sd_configs:
- role: endpointslice
今回は以降の確認を既定のEndpointsのまま進めるため、設定を元に戻す。
kubectl patch prometheus kube-prometheus-stack-prometheus -n monitoring-operator \
--type=json -p '[{"op":"remove","path":"/spec/serviceDiscoveryRole"}]'
PodMonitorを使う #
PodMonitorはServiceを介さず、Podを直接選ぶ。GrafanaはServiceを持つが、ここではPodMonitorの例として、Pod自体を対象にする。Grafanaは/metricsでPrometheus形式のメトリクスを公開している。
kubectl exec -n monitoring deployment/grafana -- wget -qO- http://localhost:3000/metrics | head -2
# HELP deprecated_flags_inuse_total The number of deprecated flags currently set.
# TYPE deprecated_flags_inuse_total counter
GrafanaのPodにはすでにapp: grafanaラベルが付いている(第1回のDeploymentで設定済み)ため、Service側のようにラベルを追加する必要はない。
apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
name: grafana
namespace: monitoring
labels:
release: kube-prometheus-stack
spec:
selector:
matchLabels:
app: grafana
podMetricsEndpoints:
- portNumber: 3000
kubectl apply -f podmonitor/grafana.yaml
curl -s http://localhost:9091/api/v1/targets | jq '.data.activeTargets[] | select(.scrapePool=="podMonitor/monitoring/grafana/0") | {health, scrapeUrl, labels}'
{
"health": "up",
"scrapeUrl": "http://10.244.0.3:3000/metrics",
"labels": {
"container": "grafana",
"instance": "10.244.0.3:3000",
"job": "monitoring/grafana",
"namespace": "monitoring",
"pod": "grafana-7d9d4457b8-lrjhj"
}
}
jobラベルの既定値が、ServiceMonitorでは対象Serviceの名前(node-exporter)だったのに対し、PodMonitorでは<Namespace>/<PodMonitor名>(monitoring/grafana)になる。PodMonitorはServiceを参照しないため、jobLabelを指定しない場合のjobラベルはPodMonitor自身のNamespace・名前から決まる。
PrometheusRuleで手動のalerting ruleを置き換える #
第3回では、Node Exporterへのscrape失敗を検知するalerting ruleを、PrometheusのConfigMapに直接書いた。
groups:
- name: node-exporter
rules:
- alert: NodeExporterDown
expr: up{job="node-exporter"} == 0
for: 45s
PrometheusRuleでは、alert / expr / for / labels / annotationsの書き方はそのままで、Custom Resourceの形に包むだけでよい。
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: node-exporter-rules
namespace: monitoring
labels:
release: kube-prometheus-stack
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'
kubectl apply -f prometheusrule/node-exporter.yaml
curl -s http://localhost:9091/api/v1/rules?type=alert | jq '.data.groups[] | select(.file | contains("monitoring-node-exporter-rules")) | .rules[0] | {name, query, health}'
{
"name": "NodeExporterDown",
"query": "up{job=\"node-exporter\"} == 0",
"health": "ok"
}
health: okで、ルール自体は正しく読み込まれ評価されている(pending・firingへの状態遷移そのものは第3回で確認済みのため、ここでは再現しない)。
第4回では、ConfigMapを変更した後にPrometheusのPodを手動で再起動(rollout restart)しないと新しい設定が反映されなかった。kube-prometheus-stackのPrometheusにはconfig-reloaderというsidecarが付いており、マウントされたファイルの変更を検知して自動で設定をリロードする。これを、Podが再作成されていないことと合わせて確認する。まずPodの名前・UID・コンテナの再起動回数を記録する。
kubectl get pod -n monitoring-operator -l app.kubernetes.io/name=prometheus \
-o jsonpath='{.items[0].metadata.name}{"\t"}{.items[0].metadata.uid}{"\t"}{.items[0].status.containerStatuses[0].restartCount}{"\n"}'
prometheus-kube-prometheus-stack-prometheus-0 68a5b083-da5f-493c-9c63-3d735e5af921 0
PrometheusRuleのannotations.descriptionだけを書き換えてkubectl applyする。
kubectl patch prometheusrule node-exporter-rules -n monitoring --type=json \
-p='[{"op":"replace","path":"/spec/groups/0/rules/0/annotations/description","value":"CHANGED: up{job=\"node-exporter\"} has been 0"}]'
反映されたかをrules APIで確認し、Podの名前・UID・再起動回数が変わっていないことも合わせて確認する。
curl -s http://localhost:9091/api/v1/rules?type=alert | jq '.data.groups[] | select(.file | contains("monitoring-node-exporter-rules")) | .rules[0].annotations'
kubectl get pod -n monitoring-operator -l app.kubernetes.io/name=prometheus \
-o jsonpath='{.items[0].metadata.name}{"\t"}{.items[0].metadata.uid}{"\t"}{.items[0].status.containerStatuses[0].restartCount}{"\n"}'
{
"description": "CHANGED: up{job=\"node-exporter\"} has been 0"
}
prometheus-kube-prometheus-stack-prometheus-0 68a5b083-da5f-493c-9c63-3d735e5af921 0
変更内容はAPIに反映されているが、Pod名・UID・再起動回数はすべて変更前と一致している。Podは置き換わっておらず、コンテナも再起動していない。config-reloaderのログにも、このタイミングでリロードが走ったことが残っている。
kubectl logs -n monitoring-operator prometheus-kube-prometheus-stack-prometheus-0 -c config-reloader --tail=3
level=info ts=2026-10-02T12:36:33Z caller=reloader.go:546 msg="Reload triggered" cfg_in=/etc/prometheus/config/prometheus.yaml.gz cfg_out=/etc/prometheus/config_out/prometheus.env.yaml cfg_dirs= watched_dirs="/etc/prometheus/rules/..."
PrometheusRuleをkubectl applyしただけで、Podを再作成せずに設定がリロードされている。
手動構成とOperatorベース構成の対応関係 #
| 項目 | 第1回〜第4回(手動) | 今回(Operator) |
|---|---|---|
| 監視対象の検出 | kubernetes_sd_configs + relabel_configsを手で書く |
ServiceMonitor / PodMonitorのラベルセレクタ |
| alerting rule | PrometheusのConfigMapに直接記述 |
PrometheusRule |
| Prometheus用RBAC | monitoring Namespaceに限定したRoleを自分で作る |
Helm chartがClusterRoleとClusterRoleBindingを作成する(Prometheus自身・Operator自身とも既定でクラスタ全体) |
| ServiceMonitor等の検出 | (該当なし) | Prometheus OperatorがClusterRoleでwatchし、Prometheus用の設定を生成する |
| 設定変更の反映 | rollout restart等でPodを再作成 |
config-reloaderが自動でリロード(Pod UID・再起動回数は変化しないことを実測) |
| 変更の単位 | ConfigMap全体を書き換える |
対象ごとに独立したServiceMonitor等を増減する |
初期状態の監視対象(grafana.enabled=false・nodeExporter.enabled=false時) |
自分で追加した分だけ(今回までで2種類) | このreleaseが作成する11個のServiceMonitor・35個のPrometheusRule |
Operatorが自動化するもの、見えにくくするもの #
自動化される点は、relabel_configsの大部分(Namespace・Pod・Service・Containerのラベル付け、Failed/Succeeded podの除外)、設定変更時のリロード、Kubernetesの標準的なコンポーネント(apiserver、kubelet等)の監視設定が最初から揃うことである。
一方で見えにくくなる点もある。
- RBACの範囲が広がる。 第4回のRoleは
monitoringNamespace・3リソースに限定していたが、Prometheus自身・Operator自身とも、Helm chartが作成するClusterRoleでクラスタ全体のnodes・services・endpoints・pods・ingressesやCRD自体を見られる - 設定を生成する主体が、設定を使う主体と別になる。 ServiceMonitor等をwatchして設定を書くのはPrometheus Operator、実際にscrapeするのはPrometheusで、どちらも別々の
ClusterRoleを持つ。13行のServiceMonitorから、シャーディング用のルールを含めて17個のrelabel_configsが生成される。手元のPrometheus設定ファイルを直接見ても、どのルールがどのCustom Resourceから来たのか追いにくい releaseラベルのように、chartの既定動作を知らないと動かない設定がある。*SelectorNilUsesHelmValues: trueという既定値がrelease: <リリース名>という条件を生成することは、values.yamlの表示だけでは気づけなかった- 既定の内部実装が、必ずしも最新の推奨事項とは限らない。 今回のバージョンでは
role: endpoints(非推奨)が既定だったが、serviceDiscoveryRole: EndpointSliceで切り替えられる helm uninstallで消えないリソースもある。 Prometheus Operatorが実行時に作成するkube-systemのkubelet監視用Service・Endpoints(Helmのrelease管理外)は、個別に削除する必要がある(後述)
今回は扱わなかったこと #
AlertmanagerConfigによるAlertmanagerのルーティング設定、ProbeによるBlackbox Exporter連携、Prometheus・Alertmanagerの高可用構成、long-term storage(Thanos等)は扱わなかった。次回(第6回)は、GKEとGoogle Cloud Managed Service for Prometheusを扱う。Managed Service for PrometheusもCRDに似た宣言的な設定(PodMonitoring / ClusterPodMonitoring)を使うため、今回のServiceMonitor・PodMonitorの経験はそのまま活きる。
元に戻す #
前提として、今回の構成は学習用の非永続構成であり、PersistentVolumeは使っていない。
まず、今回作成したCustom Resource(ServiceMonitor / PodMonitor / PrometheusRule)と、手動で追加したServiceのラベルを削除する。
# 対象はmonitoring Namespace内の今回作成分のみ。削除しても第1回〜第4回の構成には影響しない
kubectl delete servicemonitor node-exporter -n monitoring
kubectl delete podmonitor grafana -n monitoring
kubectl delete prometheusrule node-exporter-rules -n monitoring
kubectl label service node-exporter -n monitoring app-
次に、kube-prometheus-stack一式を削除する。monitoring Namespaceの第1回〜第4回の構成には影響しないが、helm uninstallが削除するのはHelmがテンプレートとして管理しているリソースであり、対象はmonitoring-operator Namespace内に限らない。Prometheus・Operator用のClusterRole・ClusterRoleBindingのようなクラスタスコープのリソースも、このreleaseが作成したものであれば削除される。CRD自体はHelmの管理対象外として扱われており削除されない。
helm uninstall kube-prometheus-stack --namespace monitoring-operator
# monitoring-operator Namespace自体も削除する。影響は今回作成した分のみで、monitoring Namespaceには及ばない
kubectl delete namespace monitoring-operator
release "kube-prometheus-stack" uninstalled
namespace "monitoring-operator" deleted
これでmonitoring-operator Namespace内と、Helmがテンプレートとして管理していたクラスタスコープのリソースは消える。ただし、Prometheus Operatorはkubelet監視用にkube-system NamespaceへService・Endpointsを実行時に作成しており、これはHelmのテンプレートではなくOperator自身が作る(app.kubernetes.io/managed-by: prometheus-operatorというラベルを持ち、Helmのrelease管理外)ため、helm uninstallでは消えない。
kubectl get svc,endpoints -n kube-system | grep kube-prometheus-stack
service/kube-prometheus-stack-kubelet ClusterIP None <none> 10250/TCP,4194/TCP,10255/TCP 40m
endpoints/kube-prometheus-stack-kubelet 192.168.157.2:10250,192.168.157.2:4194,192.168.157.2:10255 40m
個別に削除する。Endpointsは対応するServiceを削除すると一緒に消える。
kubectl delete svc kube-prometheus-stack-kubelet -n kube-system
CRD自体も削除する場合は、他にそのCRDを使っているリソースが無いことを確認してから削除する。CRDを削除すると、そのCRDを使う全NamespaceのCustom Resourceが削除される点に注意する(今回は他に使っていないため影響はない)。
kubectl get crd -o name | grep monitoring.coreos.com | xargs kubectl delete
まとめ #
Prometheus Operatorは、kubernetes_sd_configsやrelabel_configsを手で組み立てる作業を、ServiceMonitor / PodMonitorという宣言的なCustom Resourceへ抽象化する。alerting ruleも同様にPrometheusRuleへ抽象化される。書く内容自体(selector、alert/expr/for)は手動構成の延長線上にあり、第1回〜第4回の知識がそのまま使える。
ただし、これらのCustom Resourceを作るだけで済む裏側では、ServiceMonitor等をwatchして設定を生成するPrometheus Operatorと、生成された設定でscrapeを行うPrometheus自身が、それぞれ別の広いRBACを持っている。13行のServiceMonitorから17個のルールを含むrelabel_configsが自動生成され、helm uninstallでは消えないkube-system内のリソースも作られる。「手で書かなくてよい」ことと「何が起きているか把握しやすい」ことは別で、生成された設定や実際のリソースを見ないと、こうした内部実装の詳細には気づけない。
次回は、この構成をGKEへ展開し、Google Cloud Managed Service for Prometheusとの違いを確認する。
参考資料 #
- Prometheus Operator: https://prometheus-operator.dev/
- Prometheus Operator API reference (v0.94.1): https://github.com/prometheus-operator/prometheus-operator/blob/v0.94.1/Documentation/api-reference/api.md
- kube-prometheus-stack (Helm chart): https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack
- Kubernetes Endpoints API非推奨のお知らせ: https://kubernetes.io/blog/2025/04/24/endpoints-deprecation/
- Kubernetes Version Skew Policy: https://kubernetes.io/releases/version-skew-policy/#kubectl
- Kubernetes kubeletの認証・認可: https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/