Prometheusはコンテナやクラウドなどの大量コンポーネントを監視する事に特化したソフトウェアです。監視対象は大量に存在しますので、その監視対象を1つ1つ手作業で定義するのは非現実的です。このような問題に対応するため、Prometheusには監視対象を動的に検出するサービスディスカバリという機能が備わっています。このページではKubernetesをサービスディスカバリする方法を説明します。
この記事ではラベル操作の説明は省略します。ラベル操作は「Prometheus リラベル(ラベル書き換え)」を参照ください。
前提
参照資料
- Install Docker Engine on CentOS
- kind Quick Start
- kubectlのインストールおよびセットアップ
- Prometheus公式 / CONFIGURATION / kubernetes_sd_config
- 入門 Prometheus
- GitHub prometheus-deployment.yml
- Katastros blog / prometheus monitors Kubernetes cluster nodes (node-exporter)
動作確認済環境
Redhat推奨コンテナのpodmanを使用する場合は、kindのissue2706に該当するため、issue回避のための手間が必要になります。不要なトラブルに遭遇したくない方は、podmanではなくDockerをご利用ください。
- Rocky Linux 8.6
- Docker 20.10.17
- kind v0.14.0
- kindest/node:v1.23.6
- prom/prometheus:v2.36.1
- prom/node-exporter:v1.3.1
Kubernetes環境の構築
概要
小リソース・小学習コストを優先し、kind(Kubernetes in Docker)による環境準備例を示します。もし、使い慣れたオンプレミスKubernetes環境やマネージドサービスKubernetes環境やMinikube環境があるならば、その環境を利用しても差し支えございません。
Docker インストール
Dockerをインストールします。
dnf config-manager \ --add-repo \ https://download.docker.com/linux/centos/docker-ce.repo dnf install docker-ce
Dockerを起動します。
systemctl enable docker.service --now
kind インストール
Kubernetesは複数台の物理マシンor仮想マシンを必要とする大掛かりなシステムですが、kind(Kubernetes in Docker)は文字通りDocker上でKubernetesを動作させる仕組みです。Kubernetesに比べれば非常に簡単に構築できますので、ここではkind(Kubernetes in Docker)のインストール操作を説明します。
kindをインストールします。現在(2022年6月)時点のインストールコマンドは以下の通りです。インストール方法は時々刻々と変わる可能性がありますので、必要に応じて一次情報の「kind quick start」を参照ください。
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.14.0/kind-linux-amd64 chmod +x ./kind mv ./kind /usr/local/bin/kind
kubectl インストール
kubernetesクラスタを操作するためのコマンドラインツールkubectlをインストールします。現在(2022年6月)時点のインストールコマンドは以下の通りです。インストール方法は時々刻々と変わる可能性がありますので、必要に応じて一次情報の「kubectlのインストールおよびセットアップ」を参照ください。
cat <<EOF > /etc/yum.repos.d/kubernetes.repo [kubernetes] name=Kubernetes baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64 enabled=1 gpgcheck=1 repo_gpgcheck=1 gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg EOF yum install kubectl
Kubernetes クラスタの作成
Kunernetesは、workerとcontrol-planeの2種類の役割のマシンで構成されます。オンプレミス環境では、規模に応じて複数台のworkerとcontrol-planeを構築しますが、kindの場合はyamlファイルに定義したworkerとcontrol-planeをDockerの上に構築する事ができます。
control-plane 1台、worker 3台の構成ならば、以下のように定義します。
cat << EOF > kind-config.yaml kind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 nodes: - role: control-plane - role: worker - role: worker - role: worker EOF
もし、最新バージョンではなくバージョンを固定したい場合は、以下のようにバージョンを明示指定します。
動作確認時点の最新バージョンv1.24.1ではdaemonsetがcontol-planeにデプロイされない不具合があるように見えたので(今のところバグ報告なし)、v1.23.6を使用しています。
cat << EOF > kind-config.yaml kind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 nodes: - role: control-plane image: kindest/node:v1.23.6 - role: worker image: kindest/node:v1.23.6 - role: worker image: kindest/node:v1.23.6 - role: worker image: kindest/node:v1.23.6 EOF
kubernetesクラスタを作成します。kind create clusterコマンドに前述の操作で作成したyamlファイルを指定する事で、クラスタを作成できます。
kind create cluster --config kind-config.yaml
クラスタ作成に成功した場合は、以下のようなログが表示されます。
[root@rocky040 ~]# kind create cluster --config kind-config.yaml Creating cluster "kind" ... ✓ Ensuring node image (kindest/node:v1.23.6) 🖼 ✓ Preparing nodes 📦 📦 📦 📦 ✓ Writing configuration 📜 ✓ Starting control-plane 🕹️ ✓ Installing CNI 🔌 ✓ Installing StorageClass 💾 ✓ Joining worker nodes 🚜 Set kubectl context to "kind-kind" You can now use your cluster with: kubectl cluster-info --context kind-kind Have a nice day! 👋 [root@rocky040 ~]#
kubectl get nodesコマンドで、control-plane 1台、worker 3台が作成された事を確認します。
[root@rocky040 ~]# kubectl get nodes NAME STATUS ROLES AGE VERSION kind-control-plane Ready control-plane 13m v1.23.6 kind-worker Ready <none> 12m v1.23.6 kind-worker2 Ready <none> 12m v1.23.6 kind-worker3 Ready <none> 12m v1.23.6 [root@rocky040 ~]#
Kubernetes環境の監視
サンプル設定のダウンロード
「入門 Prometheus」で紹介されているサンプルのdeployment fileをダウンロードします。
wget https://raw.githubusercontent.com/prometheus-up-and-running/examples/master/9/prometheus-deployment.yml
サンプル設定の修正
Kubernetesは仕様変更の頻度が早いため、2019年当時のサンプルは若干の修正が必要になります。何も修正しないでymlファイルを適用しようとすると、以下のようにエラーメッセージが出力されます。
[root@rocky040 ~]# kubectl apply -f prometheus-deployment.yml serviceaccount/prometheus created configmap/prometheus-config created service/prometheus created resource mapping not found for name: "prometheus" namespace: "" from "prometheus-deployment.yml.bak": no matches for kind "ClusterRole" in version "rbac.authorization.k8s.io/v1beta1" ensure CRDs are installed first resource mapping not found for name: "prometheus" namespace: "" from "prometheus-deployment.yml.bak": no matches for kind "ClusterRoleBinding" in version "rbac.authorization.k8s.io/v1beta1" ensure CRDs are installed first resource mapping not found for name: "prometheus" namespace: "" from "prometheus-deployment.yml.bak": no matches for kind "Deployment" in version "apps/v1beta2" ensure CRDs are installed first
エラーメッセージを見つつ、prometheus-deployment.ymlを修正します。修正箇所をハイライトして記します。
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: prometheus rules: - apiGroups: [""] resources: - nodes - services - endpoints - pods - metrics - nodes/metrics verbs: ["get", "list", "watch"] - apiGroups: - extensions resources: - ingresses verbs: ["get", "list", "watch"] --- apiVersion: v1 kind: ServiceAccount metadata: name: prometheus namespace: default --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: prometheus roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: prometheus subjects: - kind: ServiceAccount name: prometheus namespace: default --- apiVersion: v1 data: prometheus.yml: | global: scrape_interval: 10s scrape_configs: - job_name: 'kubelet' kubernetes_sd_configs: - role: node scheme: https tls_config: ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt insecure_skip_verify: true # Unfortunately required with Minikube. - job_name: 'cadvisor' kubernetes_sd_configs: - role: node scheme: https tls_config: ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt insecure_skip_verify: true # Unfortunately required with Minikube. metrics_path: /metrics/cadvisor - job_name: 'k8apiserver' kubernetes_sd_configs: - role: endpoints scheme: https tls_config: ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt insecure_skip_verify: true # Unfortunately required with Minikube. bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token relabel_configs: - source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name] action: keep regex: default;kubernetes;https - job_name: 'k8services' kubernetes_sd_configs: - role: endpoints relabel_configs: - source_labels: - __meta_kubernetes_namespace - __meta_kubernetes_service_name action: drop regex: default;kubernetes - source_labels: - __meta_kubernetes_namespace regex: default action: keep - source_labels: [__meta_kubernetes_service_name] target_label: job - job_name: 'k8pods' kubernetes_sd_configs: - role: pod relabel_configs: - source_labels: [__meta_kubernetes_pod_container_port_name] regex: metrics action: keep - source_labels: [__meta_kubernetes_pod_container_name] target_label: job kind: ConfigMap metadata: name: prometheus-config --- apiVersion: apps/v1 kind: Deployment metadata: name: prometheus spec: selector: matchLabels: app: prometheus replicas: 1 template: metadata: labels: app: prometheus spec: serviceAccountName: prometheus containers: - name: prometheus image: prom/prometheus:v2.1.0 ports: - containerPort: 9090 name: default volumeMounts: - name: config-volume mountPath: /etc/prometheus volumes: - name: config-volume configMap: name: prometheus-config --- kind: Service apiVersion: v1 metadata: name: prometheus spec: selector: app: prometheus type: LoadBalancer ports: - protocol: TCP port: 9090 targetPort: 9090
prometheusをデプロイします。
kubectl apply -f prometheus-deployment.yml
操作ログは以下の通りです。すべてのコンポーネントが「created」と表示される事を確認します。
[root@rocky040 ~]# kubectl apply -f prometheus-deployment.yml clusterrole.rbac.authorization.k8s.io/prometheus created serviceaccount/prometheus created clusterrolebinding.rbac.authorization.k8s.io/prometheus created configmap/prometheus-config created deployment.apps/prometheus created service/prometheus created [root@rocky040 ~]#
podが起動するまで待機します。以下のコマンドでSTATUSがRunningになるまで待ちます。
[root@rocky040 ~]# kubectl get pods NAME READY STATUS RESTARTS AGE prometheus-76c59df85c-tmppj 1/1 Running 0 3s [root@rocky040 ~]#
Prometheusにアクセスするため、ポートフォワードを一時的に実行します。以下のようにkubectl port-forwardコマンドにpod名を指定して実行します。
kubectl port-forward prometheus-76c59df85c-tmppj --address 0.0.0.0 9090:9090
もし、pod名を調査するのが手間でしたら、以下のようなワンライナーで操作する事もできます。
kubectl port-forward $(kubectl get pods -o=json | jq -r .items[].metadata.name) --address 0.0.0.0 9090:9090
以下スクリーンショットのように、kubectl port-forwardコマンド実行中のみポートフォワードされます。後続のブラウザ確認が完了するまで、ターミナルはそのままの状態にします。ブラウザ確認が終わりましたら、Ctrl+Cなどの操作で停止します。
ブラウザでTargetページ(http://<IPアドレス>:9090/targets)を閲覧します。いくつかは失敗しているものもありますが、いくつかはKubernetesのリソースを動的に取得しているものもある事が分かります。
kubelet
前述のprometheus-deployment.ymlで「data:」以降で記述されている部分がprometheus.ymlの設定です。この部分を最新仕様に合わせて修正し、監視がなされるようにしましょう。
--- apiVersion: v1 data: prometheus.yml: | global: scrape_interval: 10s scrape_configs: - job_name: 'kubelet' kubernetes_sd_configs: - role: node scheme: https tls_config: ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt insecure_skip_verify: true # Unfortunately required with Minikube.
Kubernetesのコンポーネントを監視するにはkubernetes_sd_configsのroleにnodeを指定します。必要に応じて、公式ドキュメント「CONFIGURATION / kubernetes_sd_config」も併せて参照ください。
まずは、kubeletを監視してみましょう。kubeletは各ノードでpodが正常に動作する事を保証するコンポーネントです。kubeletを監視するには、prometheus-deployment.ymlのdata部分を以下のように修正します。「入門 Prometheus」が出版された頃と比較すると、認証の仕様変更が発生しています。
--- apiVersion: v1 data: prometheus.yml: | global: scrape_interval: 10s scrape_configs: - job_name: 'kubelet' kubernetes_sd_configs: - role: node scheme: https tls_config: ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt insecure_skip_verify: true bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token kind: ConfigMap metadata: name: prometheus-config
設定を反映させます。
kubectl delete -f prometheus-deployment.yml kubectl apply -f prometheus-deployment.yml
ポートフォワードします。
kubectl port-forward $(kubectl get pods -o=json | jq -r .items[].metadata.name) --address 0.0.0.0 9090:9090
ブラウザでTargetページ(http://<IPアドレス>:9090/targets)を閲覧します。kubeletをスクレイピングに成功している事を確認します。
ブラウザでGraphページ(http://<IPアドレス>:9090/graph)を閲覧します。kubeletに関するメトリックを収集できている事を確認します。
pods
podを監視してみましょう。podはコンテナ群やストレージ等の周辺リソースなどを指すkubernetesの抽象概念です。podを監視する最低限の設定は以下のようになります。
- job_name: 'k8pods' kubernetes_sd_configs:
kubernetesには、アプリケーション動作目的のpodだけなく、システム管理目的のpodも存在します。以下のようにcoredns, etcdなどの多数のpodが動作している事が分かります。
[root@rocky040 ~]# kubectl get pods -n kube-system NAME READY STATUS RESTARTS AGE coredns-6d4b75cb6d-g8j7z 1/1 Running 0 25m coredns-6d4b75cb6d-nqz92 1/1 Running 0 25m etcd-kind-control-plane 1/1 Running 0 25m kindnet-f45cz 1/1 Running 0 24m kindnet-vkd52 1/1 Running 0 25m kindnet-x9kh4 1/1 Running 0 24m kindnet-znxz9 1/1 Running 0 24m kube-apiserver-kind-control-plane 1/1 Running 0 25m kube-controller-manager-kind-control-plane 1/1 Running 0 25m kube-proxy-5f7t8 1/1 Running 0 25m kube-proxy-8j6ww 1/1 Running 0 24m kube-proxy-g2n47 1/1 Running 0 24m kube-proxy-q786k 1/1 Running 0 24m kube-scheduler-kind-control-plane 1/1 Running 0 25m
これらpodは全てがtcpで接続できるわけではありません。tcpで接続できないpod含めて監視を試みると、prometheusに不要なエラーが表示されますので、監視対象のpodを絞り込みます。
- job_name: 'k8pods' kubernetes_sd_configs:
tcpで接続できない(「__meta_kubernetes_pod_container_port_name」のラベルを持たない)podを監視対象外とします。また、コンテナ名(__meta_kubernetes_pod_container_name)をジョブ名(job)としてリラベルします。prometheus-deployment.ymlを以下のように書き換えます。
- job_name: 'k8pods' kubernetes_sd_configs: - role: pod relabel_configs: - source_labels: [__meta_kubernetes_pod_container_port_name] regex: metrics action: keep - source_labels: [__meta_kubernetes_pod_container_name] target_label: job
設定を反映させます。
kubectl delete -f prometheus-deployment.yml kubectl apply -f prometheus-deployment.yml
ポートフォワードします。
kubectl port-forward $(kubectl get pods -o=json | jq -r .items[].metadata.name) --address 0.0.0.0 9090:9090
ブラウザでTargetページ(http://<IPアドレス>:9090/targets)を閲覧します。corednsの監視ができている事を確認します。
ブラウザでGraphページ(http://<IPアドレス>:9090/graph)を閲覧します。corednsに関するメトリックを収集できている事を確認します。
node
パブリッククラウドを使用する場合はクラウドベンダーの責務になりますが、オンプレミス環境ではcontrol-planeおよびworkerとして動作するノードも監視が必要です。このような要件を求められた場合は、node-exporterを利用すると良いでしょう。まずはDaemonSetで全てのノードにnode-exporterを配置します。
cat << EOF > node-exporter.yml apiVersion: apps/v1 kind: DaemonSet metadata: labels: name: node-exporter name: node-exporter spec: selector: matchLabels: name: node-exporter template: metadata: labels: name: node-exporter spec: tolerations: - key: node-role.kubernetes.io/master effect: NoSchedule containers: - name: node-exporter image: 'prom/node-exporter:latest' ports: - name: http containerPort: 9100 protocol: TCP hostNetwork: true EOF kubectl apply -f node-exporter.yml
workerノードだけでなくcontrol-planeノードにもnode-exporterが配置されている事を確認します。kubectl get podsの実行結果でnode-exporterが4つ表示される事を確認します。
[root@rocky040 ~]# kubectl get pods NAME READY STATUS RESTARTS AGE node-exporter-7jsms 1/1 Running 0 3m11s node-exporter-922ds 1/1 Running 0 3m11s node-exporter-dgl9j 1/1 Running 0 3m11s node-exporter-gjvcw 1/1 Running 0 3m11s prometheus-84fd95f467-j7jb8 1/1 Running 0 2m54s [root@rocky040 ~]#
Node Exporterを使用した監視はややトリッキーです。「Katastros blog / prometheus monitors Kubernetes cluster nodes (node-exporter)」で紹介された手法の概要を説明します。
前述のkubeletの監視方法を思い出してください。kubeletを監視するにはscrape_configsでroleにnodeを指定しました。この指定によって、kubeletが待ち受けるtcp10250に対して監視を試みます。前述のDaemonSetによって全てのノードにNode Exporterがデプロイされtcp9100で待ち受けてていますので、kubeletの監視方法をそのまま流用し、監視先のポート番号を10250から9100にリラベルする事で監視を実現できます。実装例は以下のようになります。
- job_name: 'node-exporter' scheme: http kubernetes_sd_configs: - role: node relabel_configs: - source_labels: [__address__] action: replace regex: (.*):10250 replacement: ${1}:9100 target_label: __address__
設定を反映させます。
kubectl delete -f prometheus-deployment.yml kubectl apply -f prometheus-deployment.yml
ポートフォワードします。
kubectl port-forward $(kubectl get pods -o=json | jq -r .items[].metadata.name | grep prometheus) --address 0.0.0.0 9090:9090
ブラウザでTargetページ(http://<IPアドレス>:9090/targets)を閲覧します。Node Exporterによる監視ができている事を確認します。
ブラウザでGraphページ(http://<IPアドレス>:9090/graph)を閲覧します。Node Exporterのメトリックを収集できている事を確認します。