Prometheus Adapterの設定にPrometheusサーバー本体のアドレスを設定する箇所があったのは覚えてたので、ここにリモートで動作するサーバーを指定すれば、クラスター内にPrometheus本体や溜めたデータのストレージを持つ必要がなくなるはずと見込んで、ラズパイKubernetesクラスターみたいな軽量さが欲しいところにちょうどいいと思い試してみた。
構成としては以下の通りで、Prometheus本体はK8sクラスターの外部、K8sクラスター内にはPrometheusサーバー本体無しでPrometheus AdapterとNode Exporterをデプロイする。
(矢印の向きはデータの流れ)
ちなみに、原理はあまりわかってなくて「動いてる環境からどの設定をコピーしたら動くかをトライアルアンドエラーした」結果がこうなった、というものの調査結果。
デフォルトのメトリクスサーバーのオフ
事前準備として/etc/systemd/system/k3s.service
にあるsystemdユニットファイル末尾にある起動引数に以下を追記してサービスを再起動。
--disable metrics-server
systemctl daemon-reload systemctl restart k3s.service
ちなみにメトリクスサーバーが動いたままだとPrometheus Adapterのインストールに失敗する。
Error: INSTALLATION FAILED: Unable to continue with install: APIService "v1beta1.metrics.k8s.io" in namespace "" exists and cannot be imported into the current release: invalid ownership metadata; label validation error: missing key "app.kubernetes.io/managed-by": must be set to "Helm"; annotation validation error: missing key "meta.helm.sh/release-name": must be set to "prometheus-adapter"; annotation validation error: missing key "meta.helm.sh/release-namespace": must be set to "monitoring"
始めからメトリクスサーバーをオフでK3sをデプロイしてれば不要。
Prometheus
作業の順序は最終的に不問だが、「データを取りに行ける順」で記述。
(「データを取られるための設定」をまず入れてから、「データを取りに行く設定」を入れている、ということ。逆順でも作業中にデータが取れないだけで最終的にはあるべき状態になるのでそんな気にしなくてよい)
K3sクラスターにおいてリソースのデプロイ先は、以下すべてmonitoring
ネームスペースとする。
Node Exporter (on K3s)
Prometheusサーバーがノード情報を参照するためのもの。kubectl top node
で使用するメトリクス情報で必要。
Node Exporterはデフォルトの設定で特に問題は無く余計なものも入らない(多分)ためパラメタの指定は無し。
helm upgrade --install prometheus-node-exporter -n monitoring --create-namespace prometheus-community/prometheus-node-exporter
Prometheusアクセス用トークン (on K3s)
外部にあるPrometheusサーバーがKubernetesのAPIエンドポイントにアクセスするためのServiceAccountとトークンを作成する。
また、作成したServiceAccountにはメトリクス情報を参照するための権限を付与する。
権限の内容についてはこれといった決め手となる情報がなかったため、「Prometheus本体をKubernetesにデプロイした際に設定されるClusterRole」をHelmチャートから拝借して、同じ権限にしている。
まとめると、作成するリソースは以下の通り。
- serviceaccount/monitoring-user
- clusterrole/monitoring-metrics-clusterrole
- clusterrolebinding/monitoring-metrics-clusterrole
- secret/monitoring-token
--- apiVersion: v1 kind: ServiceAccount metadata: name: monitoring-user namespace: monitoring --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: monitoring-metrics-clusterrole rules: - apiGroups: - "" resources: - nodes - nodes/proxy - nodes/metrics - services - endpoints - pods - ingresses - configmaps verbs: - get - list - watch - apiGroups: - "extensions" - "networking.k8s.io" resources: - ingresses/status - ingresses verbs: - get - list - watch - apiGroups: - "discovery.k8s.io" resources: - endpointslices verbs: - get - list - watch - nonResourceURLs: - "/metrics" verbs: - get --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: monitoring-metrics-clusterrole roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: monitoring-metrics-clusterrole subjects: - kind: ServiceAccount name: monitoring-user namespace: monitoring --- apiVersion: v1 kind: Secret metadata: name: monitoring-token namespace: monitoring annotations: kubernetes.io/service-account.name: "monitoring-user" type: kubernetes.io/service-account-token
リソースを作成したらSecretからトークンを取得し、トークンファイルをPrometheusサーバーにコピーする。
kubectl get secret -n monitoring monitoring-token -o jsonpath='{.data.token}' | base64 -d > token scp token prometheus-server:/path/to/token
Prometheusサーバー本体 (on リモート)
設定内容のベースは以下(「リモートで動いているPrometheus」なので本エントリにおいてはこれがK8sで動いてるのかDockerなのかVMに直接入れてるのかは特に影響しない)
既存のPrometheusサーバーに、Service Discoverを使ったKubernetes情報取得の設定を追加する。
接続の際に前述の定義で作成したトークンファイルを指定する。
kubectl top
を最低限動作させるために必要なprometheus.ymlの設定ポイントは以下の通り。
job_name
は設定名のため重複しなければなんでもよい。以下はデフォルト設定に-k3s
を付与。複数のクラスターを管理するなら、クラスター名とかをsuffixとかにするのが良さそう。
トークンファイルはbearer_token_file
で指定し、監視対象のK3sクラスターはkubernetes_sd_configs[*].api_server
にAPIサーバーのURLを指定する。
cadvisorの定義はbearer_token_file
やtls_config
が同じ内容で複数個所あるが、どちらにも書かないと動作しなかった(詳細は不明)。また、relabel_configs
のtarget_label: __address__
の次の行のreplacement
にも監視対象のIPアドレスとポートを指定する。
設定自体はPrometheusのHelmチャートの内容から拝借。
kubectl top pod
に必要なkubernetes-nodes-cadvisor
の設定はここからkubectl top node
に必要なkubernetes-service-endpoints
の設定はここから
- job_name: 'kubernetes-nodes-cadvisor-k3s' # kubectl top podが有効になる scheme: https tls_config: insecure_skip_verify: true bearer_token_file: /var/run/token kubernetes_sd_configs: - role: node api_server: https://192.168.0.77:6443 tls_config: insecure_skip_verify: true bearer_token_file: /var/run/token relabel_configs: - action: labelmap regex: __meta_kubernetes_node_label_(.+) - target_label: __address__ replacement: 192.168.0.77:6443 - source_labels: [__meta_kubernetes_node_name] regex: (.+) target_label: __metrics_path__ replacement: /api/v1/nodes/$1/proxy/metrics/cadvisor - job_name: 'kubernetes-service-endpoints-k3s' # kubectl top nodeが有効になる(要Node Exporter) honor_labels: true kubernetes_sd_configs: - role: endpoints api_server: https://192.168.0.77:6443 tls_config: insecure_skip_verify: true bearer_token_file: /var/run/token relabel_configs: - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape] action: keep regex: true - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape_slow] action: drop regex: true - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme] action: replace target_label: __scheme__ regex: (https?) - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path] action: replace target_label: __metrics_path__ regex: (.+) - source_labels: [__address__, __meta_kubernetes_service_annotation_prometheus_io_port] action: replace target_label: __address__ regex: (.+?)(?::\d+)?;(\d+) replacement: $1:$2 - action: labelmap regex: __meta_kubernetes_service_annotation_prometheus_io_param_(.+) replacement: __param_$1 - action: labelmap regex: __meta_kubernetes_service_label_(.+) - source_labels: [__meta_kubernetes_namespace] action: replace target_label: namespace - source_labels: [__meta_kubernetes_service_name] action: replace target_label: service - source_labels: [__meta_kubernetes_pod_node_name] action: replace target_label: node
Prometheus Adapter (on K3s)
メトリクスサーバーとして動作。
以前はクラスター内のPrometheusサーバーを参照するように設定してデプロイしたが、今回はここにリモートのPrometheusサーバーを指定する。
values.yamlの内容は以下の通り、prometheus.url
にリモートのPrometheusサーバーのアドレスをセットする。
また、rules.resource
にkubectl top
を使ってCPU/Memoryのメトリクスを取得するのに必要な定義があるため、コメントアウトされてる箇所を有効にする。
# Url to access prometheus prometheus: # Value is templated url: http://192.168.0.22 port: 9090 path: "" rules: resource: cpu: containerQuery: | sum by (<<.GroupBy>>) ( rate(container_cpu_usage_seconds_total{container!="",<<.LabelMatchers>>}[3m]) ) nodeQuery: | sum by (<<.GroupBy>>) ( rate(node_cpu_seconds_total{mode!="idle",mode!="iowait",mode!="steal",<<.LabelMatchers>>}[3m]) ) resources: overrides: node: resource: node namespace: resource: namespace pod: resource: pod containerLabel: container memory: containerQuery: | sum by (<<.GroupBy>>) ( avg_over_time(container_memory_working_set_bytes{container!="",<<.LabelMatchers>>}[3m]) ) nodeQuery: | sum by (<<.GroupBy>>) ( avg_over_time(node_memory_MemTotal_bytes{<<.LabelMatchers>>}[3m]) - avg_over_time(node_memory_MemAvailable_bytes{<<.LabelMatchers>>}[3m]) ) resources: overrides: node: resource: node namespace: resource: namespace pod: resource: pod containerLabel: container window: 3m
これを指定してデプロイすればOK
helm upgrade --install prometheus-adapter -n monitoring --create-namespace prometheus-community/prometheus-adapter -f values.yaml
これで、ノードのメトリクス情報を集めるために必要なNode ExporterとリモートのPrometheusサーバーからデータを引っ張ってきて処理するメトリクスサーバーがK3sノードに、対象K3sへメトリクス値を取りに行くPrometheusサーバー設定が投入されたので、クラスター内にPrometheusを必要とせずにkubectl top
やHPAが使用可能になる。
K3s用カスタムリソースHelmChartにまとめると
ここまで手動の構築手順を説明してきたけど、K3sには標準でHelmによるデプロイを宣言的に行うカスタムリソースHelmChartが用意されている。
helm
CLIツールを使ったリポジトリ管理やパラメタ指定をすべてマニフェストへ記述できるので、手動あるいはHelmで生成される前述の全リソースは以下のリソース1発でデプロイできる。
(デプロイしたあとのトークン取得とリモートのPrometheusサーバーへの設定投入は別途必要だけど)
ここまでの全マニフェストを繋げただけだけど、以下の通り。
(マニフェストファイル利用時はリモートのPrometheusサーバーのアドレスは読み替えること)
注意点
PrometheusのService DiscoverでデフォルトではCoreDNSのメトリクスも取得する設定が投入されるが、クラスターの外からのIPリーチが無いためメトリクス情報が取れない。
外部Prometheusサーバーありきの構成でCoreDNSのメトリクスも必要であれば、従来通りクラスター内にPrometheusサーバーも立ててメトリクスを収集し、フェデレーション機能で外部へ集約する方法がある。あるいは、本エントリの構成にしつつ外部からのメトリクス情報を参照するためにLoadBalancer ServiceとかでCoreDNSのメトリクスを参照するURLを外部公開し、外部Prometheusからそれを参照するようにするとか、かな。(前者は検索すれば情報たくさんあると思う)
環境とバージョン
- K3s (v1.31.6+k3s1)
- Prometheus Adapter (helm chart 4.13.0)
- Prometheus Node Exporter (helm chart 4.45.0)
- Prometheus (3.2.1 / Ubuntu 24.04上にDockerで動作)