Open15

入門Prometheus

daiskobadaiskoba

まずはおうちk8sにPrometheusを構築する

環境

  • k8s server: ubuntu 20.04
  • k8s version: 1.24

kube-prometheus

Prometheus, Grafana, node-exporter, kube-state-metrics全部入りのkube-prometheusを使う

https://github.com/prometheus-operator/kube-prometheus

インストールは QuickStart の通りCRDとmanifestをapplyする

❯❯❯  kubectl apply --server-side -f manifests/setup                                                  
❯❯❯ kubectl wait \(kubernetes-admin@kubernetes/default)
        --for condition=Established \
        --all CustomResourceDefinition \
        --namespace=monitoring
    kubectl apply -f manifests/

UIへのアクセス

Ingressがない場合はport-forwardでアクセスする

https://github.com/prometheus-operator/kube-prometheus/blob/main/docs/access-ui.md

  • prometheus
kubectl --namespace monitoring port-forward svc/prometheus-k8s 9090

http://localhost:9090

  • Grafana
kubectl --namespace monitoring port-forward svc/grafana 3000

http://localhost:3000

  • Alert Manager
kubectl --namespace monitoring port-forward svc/alertmanager-main 9093

http://localhost:9093

daiskobadaiskoba

Metrics

Prometheusは4種類をサポートする

  • Coutner
    • リクエスト数など単調増加するメトリクス向け
    • 気温やメモリ使用量など 減少する値 には使えない
  • Gauge
    • 任意に増減できるメトリクス
    • 気温やメモリ使用量などに使える
  • Histogram
    • 分位数のメトリクス
    • レイテンシなどエンドユーザの体感を推測する時に使う
    • イベントの内,上位x%が一定の数値に収まっていることを表現する
  • Summary
    • 任意の数をインクリメントできるメトリクス
    • レイテンシなど,時間の差分を知りたい時に使う
      • Counterはイベントなど1刻みで増える場合に使う
    • <summary>_countでイベントの回数,<summary>_sumである時点の合計値を表す

曖昧なメトリクスの意味

メトリクスは文脈によって別のものを意味することがある.
メトリクスの集合であるメトリクスファミリや,親子関係で言う子,時系列データのどれかを意味する.

daiskobadaiskoba

何を計測するべきか

オンライン配信システム

人間や他サービスがレスポンスを待つシステムで,Webサーバやデータベースが当てはまる.

  • REDメソッド
    • Request Rate
    • Errors
    • Duration
    • (Latency)

オフラインシステム

待っている人,他のサービスが無いシステムでログ処理システムなどがこれに当たる

  • USEメソッド
    • Utilsation: 使用率
    • Saturation: 飽和...キューの待ち行列の長さ
    • Error

バッチジョブ

バッチシステムがこれに当たる.継続的に実行するオフラインシステムとの違いは定期処理として実行される.

スクレイピングは役に立たないので,Pushgatewayなどのテクニックを使い,処理が終了した時にメトリクスを記録する.

daiskobadaiskoba

PushGateway

バッチジョブのような定期実行される処理は,一般にPrometheusの実行間隔よりライフサイクルが短い.
そこで,バッチ処理より長生きするPushGatewayというキャッシュにメトリクスを保存する.
PrometheusはPushGatewayをスクレイプすれば,ジョブのメトリクスを取得できる.

BacthJob -(push)-> Pushgateway <-(scrape)- Promtheus

daiskobadaiskoba

ラベル

メトリクスに付けられるK-V形式のメタ情報
アプリケーションやライブラリが自ら付ける インストルメーションラベル と,Prometheusが計測対象のまとまりに対して付ける ターゲットラベル の2つに分類される.

  • インストルメーションラベル
    • HTTPリクエストのタイプ,データベースの名前
  • ターゲットラベル
    • アプリケーション,リージョン,チーム
daiskobadaiskoba

Node Exporter

メトリクスを開示するコンポーネント

  • OSカーネルが開示するCPU,メモリ,ディスクスペース,ディスクI/O,ネットワーク帯域幅などを提供する
  • WindowsならWMI exporterを使う

取得できるメトリクス

コレクタ 内容
cpu CPUのメトリクス
filesystem マウントされているファイルシステムのメトリクス
diskstask Disk I/O のメトリクス
netdev ネットワークデバイスのメトリクス
meminfo メモリ関連のメトリクス
hwmon 温度やファンのハードウェア関連のメトリクス
stat /proc/stat のメトリクス
uname 実行環境のメトリクス
loadavg 負荷のメトリクス
textfile ユーザ自身が収集するメトリクス

参考

node-exporterについてるラベル

kubectl get po -A -owide --show-labels | grep node-exporter                                                                                                                                                                                                                                                                                                       ⎈ (kubernetes-admin@kubernetes/default)
monitoring         node-exporter-676v5                        2/2     Running   0                 3d2h   192.168.5.110    k8s1   <none>           <none>            app.kubernetes.io/component=exporter,app.kubernetes.io/name=node-exporter,app.kubernetes.io/part-of=kube-prometheus,app.kubernetes.io/version=1.5.0,controller-revision-hash=5bddcf7c9d,pod-template-generation=1
monitoring         node-exporter-nnsq2                        2/2     Running   0                 3d2h   192.168.5.111    k8s2   <none>           <none>            app.kubernetes.io/component=exporter,app.kubernetes.io/name=node-exporter,app.kubernetes.io/part-of=kube-prometheus,app.kubernetes.io/version=1.5.0,controller-revision-hash=5bddcf7c9d,pod-template-generation=1
monitoring         node-exporter-z544q                        2/2     Running   0                 3d2h   192.168.5.112    k8s3   <none>           <none>            app.kubernetes.io/component=exporter,app.kubernetes.io/name=node-exporter,app.kubernetes.io/part-of=kube-prometheus,app.kubernetes.io/version=1.5.0,controller-revision-hash=5bddcf7c9d,pod-template-generation=1
daiskobadaiskoba

Service Discovery

閑話休題:Prometheus-Operatorの設定方法

今回利用した Prometheus-Operatorは,Operatorがprometheus.yamlを生成する.

ユーザはCRDに対して設定を行い,OperatorはCRDを参照してprometheus.yamlを生成する.

Design

prometheus CRDとservicemonitors CRDから監視するアプリケーションを決定する.

kubectl get prometheus k8s -oyaml | yq .spec                       ⎈ (kubernetes-admin@kubernetes/monitoring)
alerting:
  alertmanagers:
    - apiVersion: v2
      name: alertmanager-main
      namespace: monitoring
      port: web
enableFeatures: []
evaluationInterval: 30s
externalLabels: {}
image: quay.io/prometheus/prometheus:v2.41.0
nodeSelector:
  kubernetes.io/os: linux
podMetadata:
  labels:
    app.kubernetes.io/component: prometheus
    app.kubernetes.io/instance: k8s
    app.kubernetes.io/name: prometheus
    app.kubernetes.io/part-of: kube-prometheus
    app.kubernetes.io/version: 2.41.0
podMonitorNamespaceSelector: {}
podMonitorSelector: {}
probeNamespaceSelector: {}
probeSelector: {}
replicas: 2
resources:
  requests:
    memory: 400Mi
ruleNamespaceSelector: {}
ruleSelector: {}
scrapeInterval: 30s
securityContext:
  fsGroup: 2000
  runAsNonRoot: true
  runAsUser: 1000
serviceAccountName: prometheus-k8s
serviceMonitorNamespaceSelector: {}
serviceMonitorSelector: {}
version: 2.41.0

Prometheus-Operatorはendpointオブジェクトから監視対象を列挙する

kubectl get servicemonitors.monitoring.coreos.com                  ⎈ (kubernetes-admin@kubernetes/monitoring)
NAME                      AGE
alertmanager-main         3d3h
blackbox-exporter         3d3h
coredns                   3d3h
grafana                   3d3h
kube-apiserver            3d3h
kube-controller-manager   3d3h
kube-scheduler            3d3h
kube-state-metrics        3d3h
kubelet                   3d3h
node-exporter             3d3h
prometheus-adapter        3d3h
prometheus-k8s            3d3h
prometheus-operator       3d3h
kubectl get secret prometheus-k8s -ojsonpath='{.data.prometheus\.yaml\.gz}' | base64 -d | gunzip | yq .scrape_configs[].job_name
serviceMonitor/monitoring/alertmanager-main/0
serviceMonitor/monitoring/alertmanager-main/1
serviceMonitor/monitoring/blackbox-exporter/0
serviceMonitor/monitoring/coredns/0
serviceMonitor/monitoring/grafana/0
serviceMonitor/monitoring/kube-apiserver/0
serviceMonitor/monitoring/kube-controller-manager/0
serviceMonitor/monitoring/kube-scheduler/0
serviceMonitor/monitoring/kube-state-metrics/0
serviceMonitor/monitoring/kube-state-metrics/1
serviceMonitor/monitoring/kubelet/0
serviceMonitor/monitoring/kubelet/1
serviceMonitor/monitoring/kubelet/2
serviceMonitor/monitoring/node-exporter/0
serviceMonitor/monitoring/prometheus-adapter/0
serviceMonitor/monitoring/prometheus-k8s/0
serviceMonitor/monitoring/prometheus-k8s/1
serviceMonitor/monitoring/prometheus-operator/0

列挙したServiceMonitorオブジェクトがsecretの prometheus-<cluster name> の中で prometheus.yaml が生成される.

kubectl get secret prometheus-k8s -ojsonpath='{.data.prometheus\.yaml\.gz}' | base64 -d | gunzip | head
global:
  evaluation_interval: 30s
  scrape_interval: 30s
  external_labels:
    prometheus: monitoring/k8s
    prometheus_replica: $(POD_NAME)
rule_files:
- /etc/prometheus/rules/prometheus-k8s-rulefiles-0/*.yaml
scrape_configs:
- job_name: serviceMonitor/monitoring/alertmanager-main/0
- ```
daiskobadaiskoba

cAdvisor

cgroupsについてのメトリクスを提供するexporter
cAdvisorをdockerで起動して, localhost:8080/metrics で見れる

docker run \
  --volume=/:/rootfs:ro \
  --volume=/var/run:/var/run:rw \
  --volume=/sys:/sys:ro \
  --volume=/var/lib/docker/:/var/lib/docker:ro \
  --volume=/dev/disk/:/dev/disk:ro \
  --publish=8080:8080 \
  --detach=true \
  --name=cadvisor \
  google/cadvisor:v0.28.3

取得できるメトリクスはcgourpに属するCPUとメモリのメトリクスが取れる

cpu

  • ユーザモード,カーネルモード,両者の合計の3つが取れる

memory

container_memory_cache ページキャッシュ
container_memory_rss 常駐セットサイズだが memory-mapped file は含まないため,プロセスのRSSとは一致しない

  • memor-mapped fileの解説
    container_memory_usage_bytes ①+②
    container_memory_working_set_bytes は ③ - inactive_file()

(補足)④がOOMで監視されるが,これはカーネルが保持するスラブ・ページキャッシュも保持されているため不正確

プロセスが生成する process_resident_memory_bytes の方が良いが,公開されていないければ使えない

daiskobadaiskoba

Sercie Discovery

Prometheusがk8sでサポートするのは次の5種類

  • node
    • kubernetesのnodeを探す
  • endpoints
    • Serviceを構成する個々のPodが見える
  • service
    • ロードバランスが働くので,個々のPodを見るには不向き
  • pod
    • serviceの一部になっていないPodを見つける
  • ingress
    • ロードバランサであるため,ブラックボックスモニタリングだけに使うべきである

kube-state-metrics

kubeletなどのアプリケーションが開示するのは自分自身のパフォーマンスの情報で,kubeletが知り得るPod,Deploymentなどのリソースの情報は吐き出さない.

これらのメトリクスは他のエンドポイントから入手するか,適切なexporterを使って入手する.
Kubernetesでは kube-state-metrics がその役割に該当する.

取れるメトリクス

daiskobadaiskoba

よく使われるexporter

Blackbox exporter

E2Eで監視するときに使える
サポートするプロトコルは多岐に渡る

  • ICMP
  • TCP
  • HTTP
  • DNS

他のシステムとの連携

  • influxDB
  • StatsD
daiskobadaiskoba

PromQL

Aggergation query

  • ゲージ
    • 状態のスナップショット
    • 用途は合計,平均,最小,最大値を計算するなど
    • 増減する
  • カウンタ
    • イベントの数やサイズ
    • 開始からの合計を示す
    • 用途はカウンタの時間経過に伴う増分(rate関数を使う)など
  • サマリ
    • _sum, _count の2つのデータを含み,ともにカウンタである
  • ヒストグラム
    • 分布を表す
      -用途はパーセンタイルの計算など

Selctor

metrics_name{key="value"} で絞り込む
key="value" の箇所はマッチャと呼ばれる.

メトリクス名は __name__ というラベルで格納されており,process_resident_memory_bytes{job="node"}{__name__="process_resident_memory_bytes",job="node"} として表せる.

インスタントベクトル

最新のサンプルの時系列データのリスト.

範囲ベクトル

一つのサンプルを返すインスタントベクトルに対して,一つの時系列データのためにサンプルを返すことができる.

例えば, container_cpu_user_seconds_total[1m] は1分未満のすべてのサンプルを返す.

オフセット

container_cpu_user_seconds_total offset 1h とすると,1時間前のメモリ使用量を返す.

API

PrometheusはAPIからもクエリが引ける

  • /api/v1/query
    指定された時刻でPromQLを実行して結果を返す
% curl -s http://localhost:9090/api/v1/query\?query=process_resident_memory_bytes              ⎈ (kubernetes-admin@kubernetes/monitoring)
{
  "status": "success",
  "data": {
    "resultType": "vector",
    "result": [
      {
        "metric": {
          "__name__": "process_resident_memory_bytes",
          "container": "alertmanager",
          "endpoint": "web",
          "instance": "172.16.109.72:9093",
          "job": "alertmanager-main",
          "namespace": "monitoring",
          "pod": "alertmanager-main-1",
          "service": "alertmanager-main"
        },
        "value": [
          1680081858.524,
          "44109824"
        ]
      },
      {
        "metric": {
          "__name__": "process_resident_memory_bytes",
          "container": "alertmanager",
          "endpoint": "web",
          "instance": "172.16.166.230:9093",
          "job": "alertmanager-main",
          "namespace": "monitoring",
          "pod": "alertmanager-main-2",
          "service": "alertmanager-main"
        },
        "value": [
          1680081858.524,
          "43188224"
        ]
      },
      ...
    ]
  }
}
  • query_range

範囲ベクトルの指定

集計演算子

PromQL doc 参照

便利そうな関数

  • stddevとstdvar
    標準偏差,標準分散

  • topk, bottomk
    上位/下位N個の時系列データを返す

  • quantile
    グループ内の分位数を返す

便利そうな演算子

  • bool

process_open_fds > bool 10のように bool をつけると,フィルタリングをせずに個々の比較結果を真偽で返す

daiskobadaiskoba

カウンタ

単調増加するデータ
カウンタで使う関数は引数として範囲ベクトルを取り,インスタントベクトルを返す

  • rate()
    引数の範囲ベクトルに含まれる個々の時系列データについて,1秒あたりどのくらいのペースで増えているかを返す

カウンタに減少が見られた場合は,リセットと判断する.
[5,10,4,6] というデータは [5,10,14,16] と解釈される.

範囲ベクトルの範囲を指定する場合は,スクレイプインターバルの4倍が望ましい.
スクレイプインターバルに対して十分なサンプルが取れなければ,ログデータでデバッグするのが良い.

  • irate()

rate() より単純化され,渡された範囲ベクトルの最後の2個のサンプルしかみていない.
応答が早くなり,スクレイプインターバルの関係をあまり気にしなくて良いが,解像度は落ちて不正確なデータとなる.
また,グラフの変動が激しく,読みづらくなる.

daiskobadaiskoba

レコーディングルール

PromQL式を定期的に評価させて,その結果をインジェストする.
用途はダッシュボードの高速化など

レコーディングルールのメリット

  • 範囲ベクトルの定期評価によるカーディナリティの削減
  • 範囲ベクトル関数の入力を中間生成する
    • rate() 関数の出力はインスタントベクトルとなるので, max_over_time() で扱えないが,レコーディングルールで rate() を評価しておけば,範囲ベクトルで扱える
    • sumしてからrateするような処理ではカウンタのリセットや欠損が発生するたびに大きなスパイクが起きる点に注意
    • irate, increaseも同じこと

ルールの禁じ手

  • ラベルの代わりにレコーディングでフィルタすること
    • 例えばdeviceラベルが増えるたびにレコーディングルールが作られてしまう
  • インターバルを長くする
    • 15m -> 1hとすると,Prometheusは1hの中で任意時点で実行することを保証するため直感のタイミングとずれてしまう(1hの最初に実行してほしいが,任意時点となってしまう)
daiskobadaiskoba

アラート

PrometheusとAlertmanagerのアーキテクチャ

Prometheusで発火したアラートをAlertmanagerが送信先に通知する

このアーキテクチャにより,複数の異なるPrometheusサーバからアラートに基づいて一つの通知を送ることができる.
グルーピングを使えば,複数のPrometheusからのアラートを一つに集約できる

ルールの要素

  • for
    一定期間状態が継続したら発火させる

  • label
    severity をつけて優先度をつける

  • annocation
    アラートのサマリなど付加情報を提供する

  • external label
    PrometheusがAlertmanager,フェデレーション,リモート呼び出し/書き込みで外部システムとやり取りするときに適用されるラベル

通知パイプライン

Alertmanagerが持つ機能は次の通り.
複数のチームで運用するための機能も提供する.

  • 抑止
    • 他のアラートが発火しているときに,一部のアラートを発火していないものとして扱える機能
    • データセンタの障害などの大規模な問題だけで使うようにすべき
  • サイレンス
    • メンテナンスで発生するアラートは事前にサイレンスを設定しておく
  • ルーティング
    • `route フィールドで木構造を定義して複数の通知先を設定する
    • severity などラベルに応じた条件分岐を設定する
  • グルーピング
    • 一つのルートに送られたすべてのアラートを一つのグループに集約する
    • group_by でラベルをまとめて集約する
  • スロットリングと再送
    • group_wait でAlertmanagerは最初の通知までにデフォルトで30秒待つ
    • 30秒が重要な意味を持つなら最初の30秒で自動化できないか を考える契機になる
    • group_interval はグループで別のアラートが出たときに通知するまでの待ち時間
  • 通知
    • receiver で通知先を定義する
    • テンプレートを使うとカスタマイズが楽になる
      • GroupLabels, CommonLabelsなど ref
daiskobadaiskoba

デプロイ

大規模な導入の前に小さく導入するのを推奨する.
推奨はNode exporterとPromtheusの組み合わせから.

以降の導入順序の目安

Node exporter -> Dashboard, Alert -> App/Middle exporter(SNMP, Kafka, Cassandra) -> E2E monitoring -> 組織構造に合わせたアプリケーションのモニタリング,アラートルールの設定

Prometheusのスケーリング

Promtheusはモニタリングの対象と同じネットワークで実行することを想定して作られている.
これはエラーが発生する経路が絞られ,障害ドメインが揃えられ,レイテンシが低く帯域幅広い環境だからである.

複数の組織が使うためには

  • ラベルやスクレイプインターバルをカスタマイズを設定してメトリクスやアラートのオーナーを自明にする
  • 垂直シャーディングでネットワーク,インフラ,アプリケーションでPrometheusを分割する
  • 複数のデータセンタを跨ぐならフェデレーションで集約する
  • 水平シャーディング
    • hashmod で分散させることもできる

など組織に合わせた設計が必要になる

長期記憶ストレージ

数年分のメトリクスを取るための設定

  • /api/v1/admin/tsdb/snapshot エンドポイントでスナップショットを取る
  • 一部のメトリクスを残し,/api/v1/admin/tsdb/delete で定期削除する
    • start, endパラメータが使える
  • 長期保存用ストレージに remote_write で書き込む
    • Prometheusを一次キャッシュ,リモートストレージを永続化ストレージと割り切ることができる

デプロイ先のスペック見積もり

  • rate(prometheus_tsdb_head_samples_append_total[5m]) で1秒にインジェストされるサンプル数を確認できる
  • 1サンプルで1.3バイト,大きめに2倍としてに圧縮されるため,次の計算で求まる
    • 1秒間のサンプル数 x 2 x (606024(1day)) x 保存期間
  • RAMは12時間のサンプルを保持できるのが出発点
    • クエリの実行中に使われるRAMも考慮が必要
    • 1秒あたり10万サンプルなら8GB
  • 他にも消費する要素
    • ネットワーク
    • ファイルディスクリプタ

障害対策

  • クラスタリング
    AlertManger間でクラスタリング機能を利用すると,gossip プロトコルで通知をシステムとして集約できる

  • メタモニタリング
    Prometheus自身が動作しているかをモニタリングする

パフォーマンス

  • コストが高いメトリクスを見つける
    カーディナリティが高いメトリクスは topk などで見つける.
    ただし,すべての時系列データを参照するため非常に高コストになる.

/tsdb-statusTop 10 series count by metric names は目安になる

action: hasmoddrop を使うと全体のN%にまとめることができる.