Prometheus サービスディスカバリ (Kubernetes編)

スポンサーリンク

Prometheusはコンテナやクラウドなどの大量コンポーネントを監視する事に特化したソフトウェアです。監視対象は大量に存在しますので、その監視対象を1つ1つ手作業で定義するのは非現実的です。このような問題に対応するため、Prometheusには監視対象を動的に検出するサービスディスカバリという機能が備わっています。このページではKubernetesをサービスディスカバリする方法を説明します。

この記事ではラベル操作の説明は省略します。ラベル操作は「Prometheus リラベル(ラベル書き換え)」を参照ください。

前提

参照資料

動作確認済環境

Redhat推奨コンテナのpodmanを使用する場合は、kindのissue2706に該当するため、issue回避のための手間が必要になります。不要なトラブルに遭遇したくない方は、podmanではなくDockerをご利用ください。

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などの操作で停止します。

kubectl port-forward の実行

ブラウザでTargetページ(http://<IPアドレス>:9090/targets)を閲覧します。いくつかは失敗しているものもありますが、いくつかはKubernetesのリソースを動的に取得しているものもある事が分かります。

oreilly本サンプル 修正前の状態

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をスクレイピングに成功している事を確認します。

kubeletの監視

ブラウザでGraphページ(http://<IPアドレス>:9090/graph)を閲覧します。kubeletに関するメトリックを収集できている事を確認します。

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の監視ができている事を確認します。

corednsの監視

ブラウザでGraphページ(http://<IPアドレス>:9090/graph)を閲覧します。corednsに関するメトリックを収集できている事を確認します。

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による監視ができている事を確認します。

NodeExporterによる監視

ブラウザでGraphページ(http://<IPアドレス>:9090/graph)を閲覧します。Node Exporterのメトリックを収集できている事を確認します。

NodeExporterによるメトリック収集

タイトルとURLをコピーしました