zaki work log

作業ログやら生活ログやらなんやら

PrometheusとNode ExporterをCentOS上でバイナリを直接動かして動きを確認してみる

先日のOperatorを使ったK8sクラスタへのデプロイは全自動だったので一体何が動いたのか!?という感じだったので、Prometheusのコンポーネントや動きを理解するために、素のPrometheusを使いGetting Startedの内容に沿って動かしてみる。

prometheus.io

環境はCentOS 7

$ cat /etc/redhat-release 
CentOS Linux release 7.8.2003 (Core)

Prometheus本体を動かす

Prometheusの入手

prometheus.io

2020.10.14時点で2.20のRC版がリリースされてるが、とりあえず2.21.0で。

$ curl -LO https://github.com/prometheus/prometheus/releases/download/v2.21.0/prometheus-2.21.0.linux-amd64.tar.gz
$ tar xf prometheus-2.21.0.linux-amd64.tar.gz

設定サンプル

アーカイブ内には、設定サンプル込みのpromehteus.ymlが含まれている。
サンプルの内容は、Prometheusサーバー自身のデータを収集する内容になっている。

# my global config
global:
  scrape_interval:     15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
  evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
  # scrape_timeout is set to the global default (10s).

# Alertmanager configuration
alerting:
  alertmanagers:
  - static_configs:
    - targets:
      # - alertmanager:9093

# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
  # - "first_rules.yml"
  # - "second_rules.yml"

# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
  # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
  - job_name: 'prometheus'

    # metrics_path defaults to '/metrics'
    # scheme defaults to 'http'.

    static_configs:
    - targets: ['localhost:9090']

実行

prometheus実行バイナリを直接実行する。
実行時には設定ファイルを引数で指定。

[zaki@cloud-dev prometheus-2.21.0.linux-amd64]$ ls -F
LICENSE  NOTICE  console_libraries/  consoles/  prometheus*  prometheus.yml  promtool*
[zaki@cloud-dev prometheus-2.21.0.linux-amd64]$ ./prometheus --config.file=prometheus.yml
level=info ts=2020-10-14T12:07:52.694Z caller=main.go:310 msg="No time or size retention was set so using the default time retention" duration=15d
level=info ts=2020-10-14T12:07:52.694Z caller=main.go:346 msg="Starting Prometheus" version="(version=2.21.0, branch=HEAD, revision=e83ef207b6c2398919b69cd87d2693cfc2fb4127)"
level=info ts=2020-10-14T12:07:52.694Z caller=main.go:347 build_context="(go=go1.15.2, user=root@a4d9bea8479e, date=20200911-11:35:02)"
level=info ts=2020-10-14T12:07:52.696Z caller=main.go:348 host_details="(Linux 3.10.0-1127.19.1.el7.x86_64 #1 SMP Tue Aug 25 17:23:54 UTC 2020 x86_64 cloud-dev (none))"
level=info ts=2020-10-14T12:07:52.696Z caller=main.go:349 fd_limits="(soft=4096, hard=4096)"
level=info ts=2020-10-14T12:07:52.696Z caller=main.go:350 vm_limits="(soft=unlimited, hard=unlimited)"
level=info ts=2020-10-14T12:07:52.707Z caller=main.go:701 msg="Starting TSDB ..."
level=info ts=2020-10-14T12:07:52.709Z caller=web.go:523 component=web msg="Start listening for connections" address=0.0.0.0:9090
level=info ts=2020-10-14T12:07:52.715Z caller=head.go:644 component=tsdb msg="Replaying on-disk memory mappable chunks if any"
level=info ts=2020-10-14T12:07:52.715Z caller=head.go:658 component=tsdb msg="On-disk memory mappable chunks replay completed" duration=7.065µs
level=info ts=2020-10-14T12:07:52.715Z caller=head.go:664 component=tsdb msg="Replaying WAL, this may take a while"
level=info ts=2020-10-14T12:07:52.715Z caller=head.go:716 component=tsdb msg="WAL segment loaded" segment=0 maxSegment=0
level=info ts=2020-10-14T12:07:52.715Z caller=head.go:719 component=tsdb msg="WAL replay completed" checkpoint_replay_duration=22.965µs wal_replay_duration=313.668µs total_replay_duration=378.051µs
level=info ts=2020-10-14T12:07:52.717Z caller=main.go:721 fs_type=XFS_SUPER_MAGIC
level=info ts=2020-10-14T12:07:52.717Z caller=main.go:724 msg="TSDB started"
level=info ts=2020-10-14T12:07:52.717Z caller=main.go:850 msg="Loading configuration file" filename=prometheus.yml
level=info ts=2020-10-14T12:07:52.720Z caller=main.go:881 msg="Completed loading of configuration file" filename=prometheus.yml totalDuration=3.638807ms remote_storage=4.799µs web_handler=447ns query_engine=939ns scrape=3.217009ms scrape_sd=67.157µs notify=25.552µs notify_sd=19.669µs rules=1.612µs
level=info ts=2020-10-14T12:07:52.720Z caller=main.go:673 msg="Server is ready to receive web requests."

これで、ブラウザで9090/tcpにアクセスすればPrometheusのweb UIが表示される。

f:id:zaki-hmkc:20201014213145p:plain

※ firewalldが動いているなら9090/tcpを空ける

[zaki@cloud-dev ~]$ sudo firewall-cmd --add-port=9090/tcp
success

自身のメトリクス値

http://localhost:9090/metrics

ここにアクセスすると、実行したPrometheus自身の、Prometheusが値を取得しに来た時に返すメトリクス値の一覧が表示される。(ややこしいな) 言い換えると、Prometheusはターゲット(この場合は自分自身)の/metricsにアクセスしてメトリクスを収集する。

値のクエリー

例としてprometheus_target_interval_length_secondsの値を出してみる。

まずは「- insert metric at cursor」のドロップダウンメニューからprometheus_target_interval_length_secondsを選択する。

f:id:zaki-hmkc:20201014212241p:plain

選択したら"Execute"を押下。すると下部にqueryの結果が表示される。

f:id:zaki-hmkc:20201014212711p:plain

ちなみに http://localhost:9090/metrics にアクセスして表示されるメトリクス値は以下の通りなので、queryにヒットして表示された値と一致していることがわかる。

:
:

# HELP prometheus_target_interval_length_seconds Actual intervals between scrapes.
# TYPE prometheus_target_interval_length_seconds summary
prometheus_target_interval_length_seconds{interval="15s",quantile="0.01"} 14.99985301
prometheus_target_interval_length_seconds{interval="15s",quantile="0.05"} 14.999877942
prometheus_target_interval_length_seconds{interval="15s",quantile="0.5"} 15.000014192
prometheus_target_interval_length_seconds{interval="15s",quantile="0.9"} 15.00010798
prometheus_target_interval_length_seconds{interval="15s",quantile="0.99"} 15.000207268
prometheus_target_interval_length_seconds_sum{interval="15s"} 990.0011557570001
prometheus_target_interval_length_seconds_count{interval="15s"} 66

:
:

値の絞り込み

クエリーのprometheus_target_interval_length_secondsに条件を追加してみる。
具体的にはprometheus_target_interval_length_seconds{quantile="0.99"}と入力してExecute押下

すると、結果が絞り込まれる。

f:id:zaki-hmkc:20201014213929p:plain

※ ちょっとよくわかっていない。ざっくり検索した感じだと、PromQLを使って、ラベルの絞り込みをやってる…ぽい

グラフを表示

Graphタブを押下すれば、表示が切り替わる。

f:id:zaki-hmkc:20201014213657p:plain

Node Exporterでターゲットを追加する

ここまでは、Prometheus自身のメトリクスを表示していたが、Node Exporterを動かすことで、ターゲットを追加できる。

Starting up some sample targets

Node Exporterの入手

Prometheus本体には含まれていないので、別途ダウンロードする。

https://prometheus.io/download/#node_exporter

$ curl -LO https://github.com/prometheus/node_exporter/releases/download/v1.0.1/node_exporter-1.0.1.linux-amd64.tar.gz
$ tar xf node_exporter-1.0.1.linux-amd64.tar.gz 

Node Exporterを実行する

[zaki@cloud-dev node_exporter-1.0.1.linux-amd64]$ ls -F
LICENSE  NOTICE  node_exporter*

foregroundで動くので、ターミナル3つ起動して、3つのコマンドそれぞれ実行する。 (あと、リモートから見たいので、Listenのアドレスを0.0.0.0に変更してる)

[zaki@cloud-dev node_exporter-1.0.1.linux-amd64]$ ./node_exporter --web.listen-address 0.0.0.0:8080
level=info ts=2020-10-14T13:01:02.719Z caller=node_exporter.go:177 msg="Starting node_exporter" version="(version=1.0.1, branch=HEAD, revision=3715be6ae899f2a9b9dbfd9c39f3e09a7bd4559f)"
level=info ts=2020-10-14T13:01:02.719Z caller=node_exporter.go:178 msg="Build context" build_context="(go=go1.14.4, user=root@1f76dbbcfa55, date=20200616-12:44:12)"
level=info ts=2020-10-14T13:01:02.720Z caller=node_exporter.go:105 msg="Enabled collectors"
level=info ts=2020-10-14T13:01:02.720Z caller=node_exporter.go:112 collector=arp
level=info ts=2020-10-14T13:01:02.720Z caller=node_exporter.go:112 collector=bcache
level=info ts=2020-10-14T13:01:02.720Z caller=node_exporter.go:112 collector=bonding
level=info ts=2020-10-14T13:01:02.720Z caller=node_exporter.go:112 collector=btrfs
level=info ts=2020-10-14T13:01:02.720Z caller=node_exporter.go:112 collector=conntrack
level=info ts=2020-10-14T13:01:02.720Z caller=node_exporter.go:112 collector=cpu
level=info ts=2020-10-14T13:01:02.720Z caller=node_exporter.go:112 collector=cpufreq
level=info ts=2020-10-14T13:01:02.720Z caller=node_exporter.go:112 collector=diskstats
level=info ts=2020-10-14T13:01:02.720Z caller=node_exporter.go:112 collector=edac
level=info ts=2020-10-14T13:01:02.720Z caller=node_exporter.go:112 collector=entropy
level=info ts=2020-10-14T13:01:02.720Z caller=node_exporter.go:112 collector=filefd
level=info ts=2020-10-14T13:01:02.720Z caller=node_exporter.go:112 collector=filesystem
level=info ts=2020-10-14T13:01:02.720Z caller=node_exporter.go:112 collector=hwmon
level=info ts=2020-10-14T13:01:02.720Z caller=node_exporter.go:112 collector=infiniband
level=info ts=2020-10-14T13:01:02.720Z caller=node_exporter.go:112 collector=ipvs
level=info ts=2020-10-14T13:01:02.720Z caller=node_exporter.go:112 collector=loadavg
level=info ts=2020-10-14T13:01:02.720Z caller=node_exporter.go:112 collector=mdadm
level=info ts=2020-10-14T13:01:02.720Z caller=node_exporter.go:112 collector=meminfo
level=info ts=2020-10-14T13:01:02.720Z caller=node_exporter.go:112 collector=netclass
level=info ts=2020-10-14T13:01:02.720Z caller=node_exporter.go:112 collector=netdev
level=info ts=2020-10-14T13:01:02.720Z caller=node_exporter.go:112 collector=netstat
level=info ts=2020-10-14T13:01:02.720Z caller=node_exporter.go:112 collector=nfs
level=info ts=2020-10-14T13:01:02.720Z caller=node_exporter.go:112 collector=nfsd
level=info ts=2020-10-14T13:01:02.720Z caller=node_exporter.go:112 collector=powersupplyclass
level=info ts=2020-10-14T13:01:02.720Z caller=node_exporter.go:112 collector=pressure
level=info ts=2020-10-14T13:01:02.720Z caller=node_exporter.go:112 collector=rapl
level=info ts=2020-10-14T13:01:02.720Z caller=node_exporter.go:112 collector=schedstat
level=info ts=2020-10-14T13:01:02.720Z caller=node_exporter.go:112 collector=sockstat
level=info ts=2020-10-14T13:01:02.720Z caller=node_exporter.go:112 collector=softnet
level=info ts=2020-10-14T13:01:02.720Z caller=node_exporter.go:112 collector=stat
level=info ts=2020-10-14T13:01:02.720Z caller=node_exporter.go:112 collector=textfile
level=info ts=2020-10-14T13:01:02.720Z caller=node_exporter.go:112 collector=thermal_zone
level=info ts=2020-10-14T13:01:02.720Z caller=node_exporter.go:112 collector=time
level=info ts=2020-10-14T13:01:02.720Z caller=node_exporter.go:112 collector=timex
level=info ts=2020-10-14T13:01:02.720Z caller=node_exporter.go:112 collector=udp_queues
level=info ts=2020-10-14T13:01:02.720Z caller=node_exporter.go:112 collector=uname
level=info ts=2020-10-14T13:01:02.720Z caller=node_exporter.go:112 collector=vmstat
level=info ts=2020-10-14T13:01:02.720Z caller=node_exporter.go:112 collector=xfs
level=info ts=2020-10-14T13:01:02.720Z caller=node_exporter.go:112 collector=zfs
level=info ts=2020-10-14T13:01:02.720Z caller=node_exporter.go:191 msg="Listening on" address=0.0.0.0:8080
level=info ts=2020-10-14T13:01:02.720Z caller=tls_config.go:170 msg="TLS is disabled and it cannot be enabled on the fly." http2=false

同様に、8081/tcp, 8082/tcpでも別ターミナルで起動する。

これで、Prometheus本体のときと同じように、http://localhost:8080/metrics にアクセスすると、今起動したNode Exporterが提供するメトリクス値を確認できる。

Node Exporter追加分の設定を変更しPrometheus再起動

3つのメトリクスのエンドポイントを用意したので、prometheus.ymlに項目追加する。
既存のlocalhost:9090一つのリストに単純に追加してもいいが、Getting Startedの設定例にならって、グループ名も設定してみる。

  - job_name:       'node'
    scrape_interval: 5s
    static_configs:
    - targets: ['localhost:8080', 'localhost:8081']
      labels:
        group: 'production'

    - targets: ['localhost:8082']
      labels:
        group: 'canary'

PrometheusはCtrl-cで停止し、上記の内容に変更してPrometheusを再度起動。

メトリクス取得対象となっているターゲットは、web画面の「Targets」ページから確認できる。

f:id:zaki-hmkc:20201014222342p:plain

現在、デフォルトのPrometheus本体と、Node Exporterを使った3つのターゲットになっており、job_nameで設定した名前単位でリストされる。

f:id:zaki-hmkc:20201014231212p:plain

値のクエリー

この状態でnode_cpu_seconds_totalだけの値を出してみると、3つのNode Exporter全ての値(さらに環境によるがCPUコアごと…手元の環境だと4コアなのでcpu="0"からcpu="3"まで)が出力される。

prometheus.ymlで定義したグループの「production」だけに絞る場合はnode_cpu_seconds_total{group="production"}でクエリーすれば項目を絞れる。

ルールの作成

Configure rules for aggregating scraped data into new time series

複雑なルールや時系列の集約を都度実行すると負荷が高いため(←解釈間違ってるかもしれないけど)、Prometheusでは予め作成した式を用いて値を記録しておくことができる。(と書いてあるように読み取った)

5分間の平均CPU時間(でいい?)であれば

avg by (job, instance, mode) (rate(node_cpu_seconds_total[5m]))

というクエリーになる。

※ 実際にExecuteしてグラフ表示するとこうなった

f:id:zaki-hmkc:20201014223911p:plain

この結果の値をクエリーでなくメトリクス値として記録するには、ルールファイルを別途作成する。
作成するルールファイルprometheus.rules.ymlは以下の通り

groups:
- name: cpu-node
  rules:
  - record: job_instance_mode:node_cpu_seconds:avg_rate5m
    expr: avg by (job, instance, mode) (rate(node_cpu_seconds_total[5m]))

そして、メインの設定ファイルprometheus.ymlから、このルールファイルをincludeするように記述を追加する。
初期のサンプル設定ファイルだと、以下のコメントアウトされている部分。

# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
  # - "first_rules.yml"
  # - "second_rules.yml"

これをこうじゃ

# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
  - "prometheus-rule.yml"

この設定変更を行い、Prometheusを再起動する。
すると、ドロップダウンメニューで選べるメトリクス種別の中にjob_instance_mode:node_cpu_seconds:avg_rate5mが追加され、これを選ぶだけで(式を評価した結果の)メトリクスを表示できる。

収集したメトリクス値

ちなみにPrometheusのバイナリのあるディレクトリはこの通り。

[zaki@cloud-dev prometheus-2.21.0.linux-amd64]$ ls -lF
合計 161132
-rw-r--r--. 1 zaki zaki    11357  9月 11 22:29 LICENSE
-rw-r--r--. 1 zaki zaki     3420  9月 11 22:29 NOTICE
drwxr-xr-x. 2 zaki zaki       38  9月 11 22:29 console_libraries/
drwxr-xr-x. 2 zaki zaki      173  9月 11 22:29 consoles/
drwxrwxr-x. 4 zaki zaki       70 10月 14 21:07 data/
-rwxr-xr-x. 1 zaki zaki 88471209  9月 11 20:37 prometheus*
-rw-rw-r--. 1 zaki zaki      166 10月 14 22:41 prometheus-rule.yml
-rw-r--r--. 1 zaki zaki     1184 10月 14 22:43 prometheus.yml
-rw-r--r--. 1 zaki zaki      926 10月 14 22:13 prometheus.yml.org
-rwxr-xr-x. 1 zaki zaki 76493104  9月 11 20:39 promtool*

data/以下にデータが出力される。

前述のように、収集済みメトリクス値からクエリーで出した値でなく、ルールを作成して新しいメトリクス値を作った場合は、作成した時点からの収集となるので、それ以前のデータは(メトリクス値としては)見えない (同じ式でクエリーを実行すればクエリーの結果としては見える)


参考: Dockerイメージ

ちなみにDockerイメージもあるので、簡単に試すこともできた。
prometheus.ymlを用意するようになってるけど、とりあえず空ファイルでも動作する。

$ touch /tmp/prometheus.yml
$ docker run \
    -p 9090:9090 \
    -v /tmp/prometheus.yml:/etc/prometheus/prometheus.yml \
    --rm \
    --name prometheus \
    prom/prometheus

実行例

zaki@cloud-dev ~]$ docker run \
>     -p 9090:9090 \
>     -v /tmp/prometheus.yml:/etc/prometheus/prometheus.yml \
>     --rm \
>     --name prometheus \
>     prom/prometheus
level=info ts=2020-10-14T11:44:44.529Z caller=main.go:310 msg="No time or size retention was set so using the default time retention" duration=15d
level=info ts=2020-10-14T11:44:44.529Z caller=main.go:346 msg="Starting Prometheus" version="(version=2.21.0, branch=HEAD, revision=e83ef207b6c2398919b69cd87d2693cfc2fb4127)"
level=info ts=2020-10-14T11:44:44.529Z caller=main.go:347 build_context="(go=go1.15.2, user=root@a4d9bea8479e, date=20200911-11:35:02)"
level=info ts=2020-10-14T11:44:44.529Z caller=main.go:348 host_details="(Linux 3.10.0-1127.19.1.el7.x86_64 #1 SMP Tue Aug 25 17:23:54 UTC 2020 x86_64 1951c31b3707 (none))"
level=info ts=2020-10-14T11:44:44.529Z caller=main.go:349 fd_limits="(soft=1048576, hard=1048576)"
level=info ts=2020-10-14T11:44:44.529Z caller=main.go:350 vm_limits="(soft=unlimited, hard=unlimited)"
level=info ts=2020-10-14T11:44:44.530Z caller=web.go:523 component=web msg="Start listening for connections" address=0.0.0.0:9090
level=info ts=2020-10-14T11:44:44.530Z caller=main.go:701 msg="Starting TSDB ..."
level=info ts=2020-10-14T11:44:44.535Z caller=head.go:644 component=tsdb msg="Replaying on-disk memory mappable chunks if any"
level=info ts=2020-10-14T11:44:44.535Z caller=head.go:658 component=tsdb msg="On-disk memory mappable chunks replay completed" duration=5.044µs
level=info ts=2020-10-14T11:44:44.535Z caller=head.go:664 component=tsdb msg="Replaying WAL, this may take a while"
level=info ts=2020-10-14T11:44:44.535Z caller=head.go:716 component=tsdb msg="WAL segment loaded" segment=0 maxSegment=0
level=info ts=2020-10-14T11:44:44.535Z caller=head.go:719 component=tsdb msg="WAL replay completed" checkpoint_replay_duration=31.707µs wal_replay_duration=184.249µs total_replay_duration=234.576µs
level=info ts=2020-10-14T11:44:44.536Z caller=main.go:721 fs_type=XFS_SUPER_MAGIC
level=info ts=2020-10-14T11:44:44.536Z caller=main.go:724 msg="TSDB started"
level=info ts=2020-10-14T11:44:44.536Z caller=main.go:850 msg="Loading configuration file" filename=/etc/prometheus/prometheus.yml
level=info ts=2020-10-14T11:44:44.536Z caller=main.go:881 msg="Completed loading of configuration file" filename=/etc/prometheus/prometheus.yml totalDuration=557.508µs remote_storage=3.834µs web_handler=392ns query_engine=939ns scrape=503.038µs scrape_sd=5.5µs notify=442ns notify_sd=953ns rules=741ns
level=info ts=2020-10-14T11:44:44.537Z caller=main.go:673 msg="Server is ready to receive web requests."

空設定だと動かないと思ってたのでちょっとビックリ笑

[zaki@cloud-dev ~]$ docker ps | head -2
CONTAINER ID        IMAGE                  COMMAND                  CREATED              STATUS               PORTS                       NAMES
1951c31b3707        prom/prometheus        "/bin/prometheus --c…"   About a minute ago   Up About a minute    0.0.0.0:9090->9090/tcp      prometheus

今回はNode Exporterを使う時にlocalhostでやろうとするとコンテナ間通信とか出てきて、コンテナネットワークの設定の要素も出てくるので、解説視点的にややこしくなるので一旦保留した。

まとめ

ここまでで確認した内容は以下の通り。
(ここで書いたのが全て、というわけじゃないです)

  • Prometheus本体
    • 対象のメトリクスの収集
    • メトリクスの保存
    • クエリーによるメトリクスの抽出
  • Node Exporter
    • 代表的なExporterの一つ (Node以外にいろいろある)
    • ノードのCPUやメモリ、ネットワークやストレージなどのメトリクスを収集
  • Exporter
    • Prometheusからのリクエストに対して収集したメトリクスを返す