Carpe Diem

備忘録

Prometheus の監視対象を ServiceDiscovery で動的に設定する

概要

PrometheusはPull型の監視ツールであるため、監視対象がどれかを教えてあげなければいけません。
これまでの記事は全てstatic_configsを用いた静的な値で、スケールした時やサービスが増減した時に柔軟性がありません。

PrometheusはServiceDiscoveryを指定することで動的にターゲットを検知できるのでそれを用います。

環境

利用できるService Discoveryは?

Configuration | Prometheus に対応しているSD一覧があります。
主に使いそうなのは以下でしょうか。

Kubernetesの例

今回はKubernetesを用いた方法を説明します。

ロール(ServiceDiscoveryの種類)

PrometheusはKubernetesAPIを用いてターゲットを探します。
Prometheusで使えるKubernetesのSDは以下の5種類があります。

ロール 何ができるか
node クラスタを構成するノードを検出
kubeletをスクレイプする
endpoints Serviceにぶら下がったPodを検出
service Serviceに見えるがロードバランシングされるのでPod検出には向かない
サービスが応答するかのブラックボックスモニタリングに向く
pod Serviceに繋がっていないPodも含め全てのPodを検出
ingress ingressクラスタ外にKubernetes Serviceを公開するのでserviceロールと同じように使う

よく使うであろうロールは

  • node
  • endpoints or pod

です。後者はメトリクス取得したい全てのPodはServiceに紐付けるというインフラ上のローカルルールがあればendpointsロールを使用しますし、そうでなければpodロールを使用することになります。

nodeロール

nodeロールを用いてクラスタのノードを検出してみます。
kubletをスクレイプするのでHTTPSである必要があり、TLSなどの設定も必要です。
ただ現在のDocker for Mac Kubernetesの場合TLS周りが上手く行かないのでinsecure_skip_verify: trueが必要です。

scrape_configs:
  - job_name: 'kubernetes-nodes'
    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

ServiceDiscovery結果

ちゃんと監視対象として認識されてTarget Labelが登録されてます。

f:id:quoll00:20190723200750p:plain

Targets結果

/metricsからメトリクスを取得できていることが分かります。

f:id:quoll00:20190723200838p:plain

cAdvisor

kubeletは/metrics/cadvisorにて各Podのコンテナ用メトリクスを公開しています。
なのでmetrics_path: /metrics/cadvisorを設定することでcAdvisorのメトリクスを取得できるようになります。

- 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
    bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
    metrics_path: /metrics/cadvisor

Targets結果

/metrics/cadvisorからメトリクスを取得できていることが分かります。

f:id:quoll00:20190723201258p:plain

Grafanaで見てみる

各PodのCPU使用率が出てますね。

f:id:quoll00:20190723201658p:plain

endpointsロール

kube-apiserverのServiceを除く全てのServiceを対象にします。

scrape_configs:
  - job_name: 'kubernetes-service-endpoints'
    kubernetes_sd_configs:
    - role: endpoints
    relabel_configs:
    - source_labels:
      - __meta_kubernetes_namespace
      - __meta_kubernetes_service_name
      regex: default;kubernetes
      action: drop
    - source_labels:
      - __meta_kubernetes_namespace
      - __meta_kubernetes_pod_container_port_number
      regex: default;9100
      action: keep
    - source_labels:
      - __meta_kubernetes_service_name
      target_label: job
    - source_labels:
      - __meta_kubernetes_pod_name
      target_label: pod

数行毎に説明していきます。

kube-apiserverのServiceを取り除く

    - source_labels:
      - __meta_kubernetes_namespace
      - __meta_kubernetes_service_name
      regex: default;kubernetes
      action: drop

ここではkube-apiserverのServiceを取り除くため、
__meta_kubernetes_namespacedefault
かつ
__meta_kubernetes_service_namekubernetes
のものをaction: dropしています。

特定のnamespace, portのものを残す

今回各サーバは9100に/metricsをexposeしているので、そのサーバのみ監視対象にします。

    - source_labels:
      - __meta_kubernetes_namespace
      - __meta_kubernetes_pod_container_port_number
      regex: default;9100
      action: keep

jobラベルを書き換える

jobラベルをService名に書き換えています。

    - source_labels:
      - __meta_kubernetes_service_name
      target_label: job

podラベルを追加する

podラベルを追加し、pod名をセットしています。

    - source_labels:
      - __meta_kubernetes_pod_name
      target_label: pod

ServiceDiscovery結果

ちゃんと監視対象として認識されてTarget Labelが登録されてます。

f:id:quoll00:20190723213305p:plain

action: dropにしたものはフィルタされてます。

f:id:quoll00:20190723212030p:plain

Targets結果

各Podが認識されてます。

f:id:quoll00:20190723213745p:plain

動作検証

Podの数を変えて動的にTargetsが変化するか見てみます。

スケールアウトさせてみる

replicas: 1->replicas: 3にしてみます。

f:id:quoll00:20190723214129p:plain

増えました。

スケールインさせてみる

replicas: 3->replicas: 1に戻します。

f:id:quoll00:20190723214246p:plain

減りました。

annotationベースでservice discovery

先ほどは元から存在するラベルでサービスを検知していました。
しかしmanifest側(Deployment, Service, Pod)でこれは検知してほしい、これは検知してほしくない、といった区別もしたいです。
そういった場合はannotationを使って判別します。

scrape_configs側

- job_name: 'kubernetes-service-endpoints'
  kubernetes_sd_configs:
  - role: endpoints
  relabel_configs:
  - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
    action: keep
    regex: true
  - source_labels: [__meta_kubernetes_endpoint_port_name]
    action: keep
    regex: metrics
  - 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
    regex: ([^:]+)(:\d+)?;(\d+)
    replacement: ${1}:${3}
    target_label: __address__

条件は上から

  • Serviceにannotation.prometheus.io/scrape: trueを持つ
  • Serviceのport名がmetrics

です。
加えてpathやportをService側で自由に設定しても良いようにreplace処理を入れています。

service側

gateway, backendと2つのdeploymentの内、backendの方のみannotationを付けるようにします。

apiVersion: v1
kind: Service
metadata:
  name: backend-svc
  annotations:
    prometheus.io/scrape: 'true'
    prometheus.io/port: '9101'
spec:
  type: ClusterIP
  ports:
  - name: app
    protocol: TCP
    port: 10000
    targetPort: 8080
  - name: metrics
    protocol: TCP
    port: 10001
    targetPort: 9101
  selector:
    app: backend-service

prometheus.io/porttargetPortにあわせることで、デフォルトの9100ポート以外も指定できるようになります。

動作確認

先ほどはgatewayのpodも検知されていたのに対し、今回はgatewayが含まれなくなりました。

f:id:quoll00:20200211052420p:plain

  • prometheus.io/scrape
  • prometheus.io/path
  • prometheus.io/port

のannotationは色んなアプリケーションで共通的に使われているので、今回corednsも含まれていました。

まとめ

Prometheusの監視対象をServiceDiscoveryにすることで動的に監視対象を検知することができました。

サンプルコード

今回のサンプルコードはこちらです。

通常↓

github.com

annotationベース↓

github.com

参考