ELK Stackでシステム監視 MeticbeatでRaspberry Pi Zero WのVolumioを監視

$ ssh volumio@192.168.2.117
volumio@192.168.2.117's password: 
                       ___                                      
                      /\_ \                        __           
         __  __    ___\//\ \    __  __    ___ ___ /\_\    ___   
        /\ \/\ \  / __`\\ \ \  /\ \/\ \ /' __` __`\/\ \  / __`\ 
        \ \ \_/ |/\ \L\ \\_\ \_\ \ \_\ \/\ \/\ \/\ \ \ \/\ \L\ \
         \ \___/ \ \____//\____\\ \____/\ \_\ \_\ \_\ \_\ \____/
          \/__/   \/___/ \/____/ \/___/  \/_/\/_/\/_/\/_/\/___/ 


        
             Free Audiophile Linux Music Player - Version 2.0

          C 2015 Michelangelo Guarise - Volumio Team - Volumio.org
                               

以前にNanoPi NEO2用にFilebeatをビルドする記事を書いたが、同じやり方でMetricbeatをビルドできるかと思ったらNaniPi NEO2 (armbian)ではエラーが出てビルドできなかった。
Goに詳しくなくてエラーメッセージからは何が悪いのか推測できなかったのでNaniPi NEOではなくデスクトップPCとして常用している Gekko Linux (openSUSE亜種)でビルドすることにした。ビルドにあたりGo (golang)とgitをインストールした。

なお、記事の題名にはVolumioと書いているが実はMetricbeatがVolumioと関係する部分は全く無い。armまたはarm64のLinuxなら殆ど動くかと。(次のFilebeatの記事でVolumioが関係する予定)

$ printenv | grep GOPATH        #環境変数を表示
$ export GOPATH=$HOME/go        #GOPATHが未設定の場合に実行
                                #またはGOPATHが2つ以上のPathならホームディレクトリ下のgoを再指定

# arm用(RPi0Wなど)の作業ディレクトリの準備例 (ビルド環境が64bitのLinuxでgo1.9の場合)
$ sudo mkdir /usr/lib64/go/1.9/pkg/linux_arm
$ sudo chmod 777 /usr/lib64/go/1.9/pkg/linux_arm

# arm64用 (NanoPi NEO2など)の作業ディレクトリの準備例 (ビルド環境が64bitのLinuxでgo1.9の場合)
$ sudo mkdir /usr/lib64/go/1.9/pkg/linux_arm64
$ sudo chmod 777 /usr/lib64/go/1.9/pkg/linux_arm64

$ mkdir -p ${GOPATH}/src/github.com/elastic
$ cd ${GOPATH}/src/github.com/elastic
$ git clone https://github.com/elastic/beats.git
$ cd ./beats/metricbeat

#ビルド
$ GOOS=linux GOARCH=arm make            #arm用 (RPi0Wなど)
# or
$ GOOS=linux GOARCH=arm64 make          #arm64用 (NanoPi NEO2)など

#完成したmetricbeatをとりあえずホームディレクトリにコピー
$ cp -p metricbeat ~/
$ cp metricbeat.yml ~/

ホームディレクトリに置いた2つのファイル(metricbeat.ymlは正直どうでもいい)をRPi0WやNanoPi NEO2などに何らかの方法でコピーする。
もしもvolumioからftpを使うなら先に apt-get install ftp しておく。

Volumioのホスト側の設定

ここからはMetricbeatをインストールするホスト、Volumio(Raspbian?)が動くRPi0Wやarmbianの動くNanoPi NEO2の側の設定。

metricbeatを /usr/local/bin/ に置く。この記事では /usr/local/bin/としたが、任意の場所で可。ただし、下のサービス設定ファイル内のpathもそれに合わせること。

# mv metricbeat /usr/local/bin/
# chmod +x /usr/local/bin/metricbeat        #実行権限を付けるのを忘れずに
# mv metricbeat.yml /etc/                   #これはどうでもいい
# mkdir /etc/beats
# mkdir /var/lib/metricbeat
# metricbeat version                        #バージョン確認
metricbeat version 7.0.0-alpha1 (arm), libbeat 7.0.0-alpha1 [5426852a0971bb148b6ca60590ee47f68e6eaf24 built 2018-03-16 02:37:25 +0000 UTC]

Metricbeatのサービス設定

/lib/systemd/system/metricbeat.service (新規作成)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
[Unit]
Description=metricbeat
Documentation=https://www.elastic.co/guide/en/beats/metricbeat/6.2/index.html
Wants=userwork-online.target
After=network-online.target

[Service]
ExecStart=/usr/local/bin/metricbeat -path.home /var/db/beats/metricbeat -path.config /etc
Restart=always

[Install]
WantedBy=multi-user.target
/etc/metricbeat.yml (変更または新規作成)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
metricbeat.config.modules:
  path: ${path.config}/beats/metric_*.yml   #標準的な構成と違うので注意        
  reload.enabled: false

processors:
  - drop_fields:
      fields: ["metricset.rtt", "beat.name", "beat.version"]
                                     #全般の出力不要なフィールド名を指定
output.logstash:
  hosts: ["192.168.2.24:5043"]       #テスト用Logstash 192.168.2.24  ポート5043
#  hosts: ["192.168.2.24:5044"]      #本番用Logstash 192.168.2.24  ポート5044

logging.level: debug
logging.selectors: ["*"]
logging.to_syslog: false
logging.to_files: false              #動作確認段階ではtrueにしておく
logging.files:
  path: /var/log
  name: #↑trueにすると/var/log/metricbeat.logにログ出力

ログの出力は動作確認時だけ。ファイルが肥大化するので通常稼働時には出力させない。

/etc/beats/metric_system.yml (新規作成)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
- module: system
  metricsets: ["cpu"]
  cpu.metrics: [percentages, normalized_percentages, ticks]
  enabled: true
  period: 60s

- module: system
  metricsets: ["filesystem"]
  enabled: true
  period: 60s

- module: system
  metricsets: ["fsstat"]
  enabled: false            #←無効にした
  period: 60s

- module: system
  metricsets: ["load"]
  enabled: true
  period: 60s

- module: system
  metricsets: ["memory"]
  enabled: true
  period: 60s

- module: system
  metricsets: ["network"]
  enabled: true
  period: 60s

- module: system
  metricsets: ["process"]
  enabled: true
  period: 60s
  processors:
  - drop_fields:            #↓(MetricSet別) processで出力しないフィールド名を指定
      fields: ["system.process.cmdline", "system.process.cwd","system.process.cpu", "system.process.pid", "system.process.ppid", "system.process.pgid", "system.process.username", "system.process.fd"]

- module: system
  metricsets: ["uptime"]
  enabled: true
  period: 60s
# systemctl enable metricbeat   #サービス有効化
# service metricbeat start      #サービス開始

Kibanaでグラフ化

Vilumioに仕掛けたMetricbeatから送信される情報をLogstashで受け取るが、Metricbeatのデータは加工無しでいける筈。
で、Kibanaの側だが、すでに作成済みの推移データを見るためのVisualizeはTimeLionあたりが使われているかと思われるので、既存の他のホストのVisualizeを開いてクエリーのホスト名だけあるいは単位周りを少し修正して新しいVisualizeとして保存すれば1つのVisualizeあたり1分もかからずに作成できる筈。まさか何でもかんでもSearchオブジェクトを保存してそれを使ってArea Chart等のVisualizeを作成してないよね?

Kibanaの画面でVolumioを監視
ダッシュボードに貼り付けるとこんな感じ。

ということで、Volumioが動いているRaspberry Pi Zero WをELK Stackで監視できるようになった。
Metricbeatで1分に1度の頻度で情報を取得するように設定したが、Volumioで音楽再生中に1分ごとに音が途切れるとかそういうのは如何にRaspberry Pi Zero Wが非力とはいえどさすがに無いみたい。もしかしたら世の中にたくさんいらっしゃるらしい人の能力を遥かに超えた耳を持つオーディオマニアさんなら ゆらいでない音のゆらぎ、途切れてない音の途切れ、ありもしない音の違いをはっきり聞き分けられるのかもしれないけど。

関連記事:

ELK Stackでシステム監視 FilebeatでFreeBSDのCPU温度取得+Kibanaグラフ化

この記事ではFreeBSDでCPUコアの温度を取得してログ化し、Filebeatで取得、Logstashで加工してKibanaでグラフ表示できるようにする。

FreeBSDでCPU温度取得

CPUの温度確認の記事と変わらないが以下。

# kldstat
Id Refs Address            Size     Name
 1   27 0xffffffff80200000 1f67a88  kernel
 2    1 0xffffffff82169000 12c80    linprocfs.ko
 3    4 0xffffffff8217c000 df88     linux_common.ko
 4    1 0xffffffff8218a000 2678     accf_http.ko
 5    1 0xffffffff8218d000 4d18     coretemp.ko     ←coretemp.koがある
 6    1 0xffffffff82192000 39d8     cc_htcp.ko
 7    1 0xffffffff82221000 34d5c    pf.ko
 8    1 0xffffffff82256000 42864    linux.ko
 9    1 0xffffffff82299000 3c93f    linux64.ko

上のようにcoretempが読み込まれていない場合は以下。

# kldload coretemp                        #←coretempモジュール読み込み         
# sysctl dev.cpu | grep temperature       #←温度表示
dev.cpu.7.temperature: 26.0C
dev.cpu.6.temperature: 26.0C
dev.cpu.5.temperature: 26.0C
dev.cpu.4.temperature: 26.0C
dev.cpu.3.temperature: 25.0C
dev.cpu.2.temperature: 25.0C
dev.cpu.1.temperature: 26.0C
dev.cpu.0.temperature: 26.0C

このように表示されるなら次回以降のOS起動時に自動的にcoretempモジュールが読み込まれるように設定する。

/boot/loader.conf (1行追加)
coretemp_load="YES"

これで温度が取れるようになったが、先のような結果出力ではログとしては使いにくい。そこでスクリプトにした。

/usr/local/etc/sbin/get_temperature.sh (新規作成)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#!/bin/sh
ncpu=$( sysctl hw.ncpu | awk '{ print $2 }' )
out=$( /bin/date '+%Y-%m-%d %H:%M:%S' )
i=0

while [ ${i} -lt ${ncpu} ]
do
 out="${out} `sysctl dev.cpu.${i}.temperature | sed -e 's|.*: \([0-9.]*\)C|\1|'`"
 i=`expr ${i} + 1`
done

echo ${out} > /tmp/temperature.tmp
sleep 15
rm /tmp/temperature.tmp

スクリプトには実行権限を付けておく。
CPUのコア数を確認してコア数だけ順番に温度を取る。以下のようなログが出力される。

2018-03-14 11:24:00 26.0 27.0 28.0 28.0 27.0 28.0 30.0 28.0
日付 時刻 0 1 2 3 4 5 6 7 ←CPUコアの温度

日付と時刻は出力する必要はないのだが、一応付けた。各CPUコアの温度の間に半角スペースを挟んで並べた。
で、仮にこのスクリプトを1分ごとに実行するとして、出力を追記にするとファイルが肥大するのが嫌いなので毎回上書きにしたかったのだが、それをするとFilebeatがファイルの更新に気付いてくれないようなので、出力後15秒で一旦ファイルを削除することにした。ということは、温度記録を蓄積するログではないので/var/logに置くのではなく/tmpに一時ファイルとして置くようにした。

このスクリプトをcronで1分毎など好みの間隔で定期実行させる。

Filebeatの設定

/usr/local/etc/filebeat.yml (途中追加・変更)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
filebeat.prospectors:
#前回の記事のFail2ban用の設定ここから
- type: log
  enabled: true
  paths:
    - /var/log/fail2ban.log
  fields:
    type: fail2ban
  include_lines: [' Ban ']
  exclude_lines: ['Restore']
  processors:
    - drop_fields:
         fields: ['offset', 'source']
#前回の記事のFail2ban用の設定ここまで
#今回の記事用の設定はここから
- type: log
  enabled: true
  paths:
    - /tmp/temperature.tmp              #←ログファイル
  fields:
    type: temperature                   #fields.typeを temperature とした
  processors:
    - drop_fields:                      #出力したくないフィールドの指定
         fields: ['offset', 'source']   #今回はoffsetとsourceを出力しない
#←今回の記事用の設定はここまで

filebeat.config.modules:
  path: ${path.config}/beats/file_*.yml #今回も使わない
  reload.enabled: false

output.logstash:
  hosts: ["192.168.2.24:5043"]          #Logstash稼働ホストのテスト用ポートに出力
#  hosts: ["192.168.2.24:5044"]         #Logstash稼働ホストの本番用ポート

#logging.level: debug
logging.selectors: ["*"]
logging.to_syslog: false                #Filebeatのログをsyslogに出力しない
logging.to_files: false                 #Filebeatのログファイルは出力しない
logging.files:
  path: /var/log
  name: filebeat.log

Filebeatを再起動する。
# service filebeat restart

Filebeat(のホスト)側はこれだけ。

Logstashの設定

今回もFilebeatから送られてきたデータを加工するのでfilter部に追記する。

/usr/local/etc/logstash/test.conf
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
input {
    beats {
        port => 5043    #テスト用ポート
    }
}

filter {
  #前回の記事のFail2ban用ここから
  if [fields][type] == "fail2ban" {
    grok {
      patterns_dir => ["/usr/local/etc/logstash/patterns"]
      match => {"message" => "%{YEAR}-%{MONTHNUM}-%{MONTHDAY} %{TIME},%{NUMBER} fail2ban\.actions%{SPACE}\[%{NUMBER}\]\: NOTICE  \[%{USERNAME:jail}\] Ban %{IP:ip}"}
      remove_field => ["message", "beat", "tags"]
    }
    geoip {
      source => "ip"
    }
  }
  #前回の記事のFail2ban用ここまで

  #今回の記事用の追加設定ここから
  if [fields][type] == "temperature" {
    grok {
      patterns_dir => ["/usr/local/etc/logstash/patterns"]
      match => { "message" => "%{YEAR}-%{MONTHNUM}-%{MONTHDAY} %{TIME} %{GREEDYDATA:message}" }
      "overwrite" => "message"
    }
    grok {
      "match" => { "message" => [
        "%{NUMBER:[temperature]core0} %{NUMBER:[temperature]core1} %{NUMBER:[temperature]core2} %{NUMBER:[temperature]core3} %{NUMBER:[temperature]core4} %{NUMBER:[temperature]core5} %{NUMBER:[temperature]core6} %{NUMBER:[temperature]core7}",
        "%{NUMBER:[temperature]core0} %{NUMBER:[temperature]core1} %{NUMBER:[temperature]core2} %{NUMBER:[temperature]core3}",
        "%{NUMBER:[temperature]core0} %{NUMBER:[temperature]core1}",
        "%{NUMBER:[temperature]core0}"
        ]
      }
      remove_field => ["message", "beat", "tags"]
    }
    mutate {
      convert => {
        "[temperature]core0" => "float"
        "[temperature]core1" => "float"
        "[temperature]core2" => "float"
        "[temperature]core3" => "float"
        "[temperature]core4" => "float"
        "[temperature]core5" => "float"
        "[temperature]core6" => "float"
        "[temperature]core7" => "float"
      }
    }
  }
  #今回の記事用の追加設定ここまで
}

output {
    stdout { codec => rubydebug }           #テスト用  コンソールに出力
    #elasticsearch { hosts => [ "localhost:9200" ] }
}

今回の追加部分の処理は1回めのgrok{}で日時の後の部分のメッセージをmessageとして上書き。これでmessageはCPUのコア数分の温度だけが記載されたものになる。

上の設定の30〜33行ではコア数が8,4,2,1のCPUの温度出力用として4種類(4行)書いた。もっと違うコア数のCPUのホストがあるなら %{NUMBER:[temperature]core番号} をコア数だけ並べた行を追加する。(半角スペースを挟んで並べること)
マッチする行は全体で一致である必要があるようなので、データの要素数(この記事ではCPUのコア数分の温度の数)が不足or過剰だとマッチしてくれないので監視対象ホストのCPUのコア数の全パターンを用意してやらないといけないっぽい。つまりCPUコア数が2のホスト,4のホスト,16のホストがあるなら2,4,16の3パターンで作成する。
近似一致が使えたら設定が楽なんだけどなぁと思ったりもするけど、予想してなかった動作をして後でアレ?ってなるよりは融通が効かない方が良いのかしら?
matchで抽出すると数値に見えるものも文字列型になっているので40〜47行で温度の数値をfloat型に変換する。(最大8コアと想定、足りなければ増やす)

Logstashでデータ受信テスト

テスト用Logstashを起動してFilebeatからログデータを渡してみる。
本番環境に影響させずに試行錯誤を行うためのテスト用Logstashの起動については以前の記事を参照

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
{
         "fields" => {
        "type" => "temperature"
    },
       "@version" => "1",
     "@timestamp" => 2018-03-14T07:34:00.963Z,
           "host" => "hoge.localnet",
     "prospector" => {
        "type" => "log"
    },
    "temperature" => {
        "core2" => "32.0",
        "core0" => "32.0",
        "core3" => "32.0",
        "core1" => "32.0"
    }
}

CPUコア別の温度が意図したとおり出力できている。(出力の並び順は定まっていない)

Kibanaで温度をグラフ出力

KibanaでCPUの温度をグラフ表示 1
まず、elasticsearchに溜まっている筈のCPU温度情報データを確認する。(データの形が頭に浮かぶなら必須ではない)
今回は監視対象ホスト(hosts)とfields.typeがtemperatureで絞ると温度のデータを見ることができそう。
ただし、取得するデータとしてはfields.typeで絞る必要はなくて直接temperature.core?を取得すれば良さそうなのがわかる。
今回はTimelionでグラフ化するので、ここで絞った条件はSearchオブジェクトとして保存する必要はない。

KibanaでCPUの温度をグラフ表示 2
左列のメニューからVisualizeをクリックし、 (新規作成)を押すと、上の画面が表示される。
今回はTimelionを使う。

KibanaでCPUの温度をグラフ表示 3
とりあえず、単純にCPU1コア分ずつグラフを作成して並べる。hostに対象ホスト名、metric(値)はtemperature.core?の最大値(max)でも指定しておく。
線グラフの線の太さが初期値では太すぎると思うならwidthで好みに調整。最初のグラフ(core0)1本だけは見栄えの為に線グラフの下側を塗りつぶすことにした。fill=?がそれ。数値が小さいと薄い色、10に近いほど濃い色となる。最後のグラフに.yaxis()を付けてグラフで表示する温度の範囲を指定した。今回は0〜60℃ということにした。
希望どおりに表示できるようになったら保存する。

1
2
3
4
.es(interval='1m', q='host:hoge.localnet', metric='max:temperature.core0').lines(width=1, fill=1).label('core0'), 
.es(interval='1m', q='host:hoge.localnet', metric='max:temperature.core1').lines(width=1).label('core1'), 
.es(interval='1m', q='host:hoge.localnet', metric='max:temperature.core2').lines(width=1).label('core2'), 
.es(interval='1m', q='host:hoge.localnet', metric='max:temperature.core3').lines(width=1).label('core3').yaxis(min=0, max=60)

KibanaでCPUの温度をグラフ表示 4
ダッシュボードに貼り付けるとこんな感じ。(赤い四角枠のグラフ)

関連記事:
Up