Elastic Stackでシステム監視 Heartbeatで収集した死活情報をKibanaで可視化

前回はHeartbeatで収集した情報をElasticsearchに送信し、そのデータを利用できるようにKibanaでインデックスパターンを登録するところまでやった。
今回はKibanaでその収集した情報をわかりやすく可視化したいと思う。

まず、Heartbeatが収集したデータを見てみる。

安物のなんちゃって管理機能付きL2SWをhttpで死活確認(稼働中)
{
  "@timestamp": "2018-04-19T00:52:11.281Z",
  "@metadata": {
    "beat": "heartbeat",
    "type": "doc",
    "version": "6.2.3"
  },
  "beat": {
    "hostname": "es.example.com",
    "version": "6.2.3",
    "name": "es.example.com"
  },
  "tcp": {
    "port": 80,
    "rtt": {
      "connect": {
        "us": 1718
      }
    }
  },
  "http": {
    "response": {
      "status": 200
    },
    "rtt": {
      "write_request": {
        "us": 67
      },
      "response_header": {
        "us": 3393
      },
      "validate": {
        "us": 3432
      },
      "content": {
        "us": 39
      },
      "total": {
        "us": 5236
      }
    },
    "url": "http://192.168.0.250"
  },
  "monitor": {
    "name": "http",
    "type": "http",
    "ip": "192.168.0.250",
    "duration": {
      "us": 5320
    },
    "status": "up",
    "scheme": "http",
    "id": "http@http://192.168.0.250"
  },
  "type": "monitor"
}

IP電話機をicmpで死活確認(稼働中)
{
  "@timestamp": "2018-04-19T00:52:11.281Z",
  "@metadata": {
    "beat": "heartbeat",
    "type": "doc",
    "version": "6.2.3"
  },
  "type": "monitor",
  "beat": {
    "hostname": "es.example.com",
    "version": "6.2.3",
    "name": "es.example.com"
  },
  "icmp": {
    "requests": 1,
    "rtt": {
      "us": 201992
    }
  },
  "monitor": {
    "name": "icmp",
    "type": "icmp",
    "ip": "192.168.6.145",
    "duration": {
      "us": 202085
    },
    "status": "up",
    "id": "icmp-icmp-ip@192.168.6.145"
  }
}

IP電話機をicmpで死活確認(電源OFF)
{
  "@timestamp": "2018-04-19T00:52:11.281Z",
  "@metadata": {
    "beat": "heartbeat",
    "type": "doc",
    "version": "6.2.3"
  },
  "beat": {
    "name": "es.example.com",
    "hostname": "es.example.com",
    "version": "6.2.3"
  },
  "icmp": {
    "requests": 0
  },
  "monitor": {
    "id": "icmp-icmp-ip@192.168.6.159",
    "name": "icmp",
    "type": "icmp",
    "ip": "192.168.6.159",
    "duration": {
      "us": 5001197
    },
    "status": "down"
  },
  "error": {
    "type": "io",
    "message": "write ip4 0.0.0.0-\u003e192.168.6.159: sendto: host is down"
  },
  "type": "monitor"
}

icmp(ping)で収集した場合とhttpで収集した場合でデータが別物な部分があるが、monitor.ip(監視対象ホストのIPアドレス)やmonitor.status(死活状態)は共通で、最低限の死活監視であればそれらとタイムスタンプだけ見れば良いのかも。

Heartbeatで収集したデータをKibanaを使って可視化 1
Kibana左列のVisualizeを開き、(新規作成)をクリック。

ヒートマップを使って時間別の死活状態を表示

Heartbeatで収集したデータをKibanaを使って可視化 2
ヒートマップを作成する場合は上の画像の赤枠の1つHeart Mapを使用する。

Heartbeatで収集したデータをKibanaを使って可視化 3
今回は前回作成したインデックスから作成するので heartbeat-* をクリック。

Heartbeatで収集したデータをKibanaを使って可視化 4
Metricsの側は1以上の値が入るなら何でも良いと思う。今回はAggregationにSum、フィールドにmonitor.duration.usを指定した。
バケツの指定
X軸(X-Axis)はAggregationに Date Histogram を指定。フィールドは @timestamp。
Y軸(Y-Axis)はSub Aggregationに Terms を指定。ファールドはmonitor.ip.keyword (監視対象ホストIP)、SizeにはHeartbeatでの監視対象台数以上を指定。
左上のAdd a filter でフィルターを追加する。条件は[monitor.status] [is] [up]でステータスがup(稼働中)のみ表示とする。
左上の黒塗りのheartbeat-*の下Optionsでオプション画面を開き、Numbers of colorsを2にするとヒートマップの表示色数が2色に減る。2色を指定しても実際には白・薄色・濃色の3色での表示だが。
(グラフ描画)をクリックしてヒートマップが表示されること。(上の画像では真っ白が「停止」、薄色・濃色が「稼働」を示す)
気に入ったら[Save]で保存する。

リスト表示で死活状態を表示

Heartbeatで収集したデータをKibanaを使って可視化 2
死活リストを作成する場合は上の画像の赤枠の1つData Tableを使用する。

Heartbeatで収集したデータをKibanaを使って可視化 3
今回は前回作成したインデックスから作成するので heartbeat-* をクリック。

Heartbeatで収集したデータをKibanaを使って可視化 5
Metricsの指定
AggregationにはTop Hitを指定する。フィールドは@timestampとする。 OrderはDescending(降順)、Sizeには1を指定し、最新の1つのレコードのみ表示とする。
バケツの指定
Split RowsでAggregationをTermにする。フィールドはmonitor.ip.keywordとする。SizeにはHeartbeatでの監視対象台数以上を指定。
これに死活状態を追加で表示するが、好みでSplit RowsにしてもSplit Tableにしても良いかと思う。今回はステータスがup(稼働)とdown(停止)を完全に分けて表示したかったのでSplit Tableを指定した。
Sub AggregationはTermを選択、フィールドはminitor.status.keyword、Sizeはupとdownの2種類なので2を指定。

Dashboardを作成

Heartbeatで収集したデータをKibanaを使って可視化 6
ダッシュボードに上で作成した2つのVisualizeを貼り付けて保存する。
ヒートマップは表示期間を広げても狭めても問題ないが、死活リストは表示期間を広げるとUpとDownの両方に同一の監視対象機材が表示されることがある。死活リストはUp, Downそれぞれで表示対象期間内のステータス変更最新日時を求めるものなので表示期間を例えば24時間とするとその期間にステータスが変わるとそれがそれぞれUp, Downに入るのは当然。どうしても現在のステータスだけを表示したいということなら表示する対象期間を直近の15分とか5分とかにすればその「直近」にステータスが変わっていなければ監視対象IPはUpまたはDownのどちらかだけに表示されることになる。ステータス変更日時を見れば理屈は解る筈。

関連記事:

Elastic Stackでシステム監視 Heartbeatを使う準備

Heartbeatは各ホストにインストールする必要がないタイプのbeatsの1つ。他所のホストにpingを飛ばしたりhttpでアクセスして反応を見るというもの。ホストがWindows, Linux, xBSDならMetricbeatやFilebeatを入れて、またはSNMP(または他の監視機能)付きのアプライアンスならそれで情報を取れば良いのだが、「管理機能付きアンマネージド(意味不明)」な安物L2SWや家庭用程度の無線LANのAPやIP電話機などはbeatsをインストールすることも出来ずSNMP等も無いことが多いので死活監視をするとしたらpingや管理画面のウェブにアクセスして反応を見る程度になる。その程度でも機器が多い環境では死活監視できるのはありがたい。

インストール

FreeBSDのportsではHeartbeatはMetricbeatやFilebeatなどと一緒にインストールされるのでそれらを既に使っているならインストール済みの筈。

beatsは初めてというなら一応、以下。
# cd /usr/ports/sysutils/beats
# make install
# sysrc heartbeat_enable=Yes
heartbeat_enable:  -> Yes

FreeBSDのportsでは各beatsの実行ファイルと簡易版の設定ファイルの雛形だけがインストールされる。
できれば下のファイルも/usr/local/etc/あたりにコピーしておくと便利。Elasticのウェブサイトのドキュメントでは設定の情報を探すのが大変なので。
/usr/ports/sysutils/beats/work/src/github.com/elastic/beats/heartbeat/heartbeat.reference.yml

存在しない場合は
# cd /usr/ports/sysutils/beats
# make extract
# cp ./work/src/github.com/elastic/beats/heartbeat/heartbeat.reference.yml /usr/local/etc/
# cp ./work/src/github.com/elastic/beats/libbeat/_meta/
# make clean

Heartbeatの設定

/usr/local/etc/heartbeat.yml
#== Configure monitors ==
heartbeat.monitors:
# 監視対象1 IP-Phone #155
- type: icmp
  schedule: '@every 600s'
  hosts: ["192.168.6.155"]

# 監視対象2 LAN#4 Wi-Fi AP#2
- type: http
  urls: ["http://192.168.2.248/admin"]
  schedule: '@every 600s'

# 監視対象3 L2SW-LAN#5
- type: http
  urls: ["http://192.168.0.250"]
  schedule: '@every 300s'

#== Elasticsearch template setting ==
#setup.template.enabled: true
setup.template.name: "heartbeat"        #インデックス名を変更する場合は必要な設定(下の方)
setup.template.pattern: "heartbeat-*"   #インデックス名を変更する場合は必要な設定(下の方)

setup.template.settings:
  index.number_of_shards: 1
  index.codec: best_compression
  #_source.enabled: false

#== Dashboards ==
setup.dashboards.enabled: false
#setup.dashboards.url:

#== Kibana ==
#setup.kibana:
#  host: "127.0.0.1:5601"

#== Outputs ==
#output.logstash:
#  hosts: ["localhost:5044"]

output.elasticsearch:
  enabled: true                         #←この行が無いとelasticsearchに出力されないが簡易版の設定ファイルには書かれて無い
  hosts: ["localhost:9200"]
  ssl.enabled: false                    #SSL無効
  index: "heartbeat-%{+yyyy.MM.dd}"     #インデックス名を変更 初期値はheartbeat-バージョン-日付

  #username: "elastic"
  #password: "changeme"

#== Logging ==
#logging.level: debug
#logging.selectors: ["*"]
#logging.to_syslog: false
#logging.to_files: true
#logging.files:
#  path: /var/log
#  name: heartbeat.log

今回はHeartbeatなので出力するデータにそれほど無駄な内容はない筈。なのでLogstashでデータ加工をする必要はない。そこで、Heartbeatから直接elasticsearchに出力させることにした。
その場合、41行目が大事。

Heartbeat用テンプレートの登録

少なくとも「FreeBSDのportsでインストールしたHeartbeat 6.2.3」では上の設定でHeartbeatを起動するとタイムスタンプが「日時」ではなく「文字列」として認識されるためテンプレートを指定して@timestampの値を強制的に「日時」にする。今回はテンプレートで指定するのはタイムスタンプだけとする。

Elastic Stackでシステム監視 Heartbeat 1
Kibanaで左列のでDev Toolを開き、以下を入力してで実行する。

PUT /_template/heartbeat
{
"template" : "heartbeat-*",
  "mappings": {
    "doc": {
      "properties": {
        "@timestamp": {
          "type": "date"
        }
      }
    }
  }
}

右列の結果欄に  "acknowledged": true   が表示されること。

同様に  GET heartbeat-2018.04.18/_mapping   などのように"heartbeat-今日の日付"でマッピングを取得し、右列の結果で@timestampのtypeがdateという表示であればOK.
ただし、これはheartbeat起動後。

ここまでやったらHeartbeatを起動する。

# service heartbeat start

Kibanaでインデックスパターンを登録

Elastic Stackでシステム監視 Heartbeat 2
左列のManagementを開き、Index Patternsをクリック。

Elastic Stackでシステム監視 Heartbeat 3
[Create Index Pattern]をクリック。

Elastic Stackでシステム監視 Heartbeat 4
Index patternのテキストボックスに heartbeart-* を入力する。このとき下部にhearbeat-yyyy.MM.ddのインデックスが表示されていること。(無ければ登録できない)
[ Next step] をクリック。

Elastic Stackでシステム監視 Heartbeat 5
Time Filter field nameのプルダウンメニューが表示されていること。ここでプルダウンメニューが表示されていなければタイムスタンプとなるフィールドが存在しないと認識されているのでテンプレートの登録が正しく行われていない筈。
[]をクリックしてリストから @timestamp を選択する。
[Create Index pattern]をクリック。

Elastic Stackでシステム監視 Heartbeat 6
heartbeat-*のインデックスパターンが表示されていることを確認。違うインデックスパターンが表示されているなら上部でheartbeat-*を選択する。
フィールドリストの@timestampの右側に(時計アイコン)が表示されていること。

これでHeartbeatのデータをKibanaで使える状態になった。
Kibanaでの死活状態の可視化は次回。

関連記事:

NginxでBrotliによるコンテンツのデータ圧縮を行う

Brötli
© kochtopf.

いまさらだけどBrotliの圧縮機能ををNginxで有効にすることにした。

環境は例によってFreeBSDでNginxやBrotliはportsでインストールすることにする。

Brotliモジュール付きNginx portsの更新/インストール

# cd /usr/ports/www/nginx-devel  #使用中orインストールしたい方のports
または
# cd /usr/ports/www/nginx        #使用中orインストールしたい方のports

Nginxを新規でインストールする場合
# make install

既にportsのNginxを利用している場合
# make config
# portupgrade -f www/nginx-devel  #以下2行のどちらか
# portupgrade -f www/nginx

Nginx portsのmake config
make install (新規)または make config (更新)を実行するとビルドオプションが表示されるので BROTLI にチェックする。(カーソルキーで行を合わせて[Space]など)

更新の場合はmake configの代わりに /var/db/ports/www_nginx-devel/options か /var/db/ports/www_nginx/optionsをエディタで開いて OPTIONS_FILE_UNSET+=BROTLI の行をOPTIONS_FILE_SET+=BROTLI に変更して保存するでも可。

BROTLIを有効にしてビルドすると自動的にarchivers/brotliのportsがインストールされる。

なお、portsでインストールされるNginx用のBrotliモジュールはngx_brotliのようなので設定の詳細はそちらから。

Nginxの設定

/usr/local/etc/nginx/nginx.conf (設定追加)
load_module /usr/local/libexec/nginx/ngx_http_brotli_filter_module.so;
load_module /usr/local/libexec/nginx/ngx_http_brotli_static_module.so;

中略

http {

   中略

   gzip                 on;
   gzip_http_version    1.0;
   gzip_static          on;
   gzip_min_length      100;
   gzip_buffers         16 8k;
   gzip_vary            on;
   gzip_comp_level      1;
   gzip_types           application/atom+xml
                        application/javascript
                        application/rss+xml
                        application/x-javascript
                        application/xml
                        application/xml+rss
                        image/svg+xml
                        text/css
                        text/javascript
                        text/plain
                        text/xml;

    brotli              on;
    brotli_comp_level   6;
    brotli_static       on;
    brotli_types        application/atom+xml
                        application/javascript
                        application/rss+xml
                        application/x-javascript
                        application/xml
                        application/xml+rss
                        image/svg+xml
                        text/css
                        text/javascript
                        text/plain
                        text/xml;

    中略

    }

中略

}

Brotliモジュールは自動では使用されない筈なので設定ファイルの最初でロードさせる。

brotliモジュールの設定はgzip圧縮の設定と似た感じ。brotli_staticをオンにした場合は静的なbrotli圧縮ファイルを用意する。これはgzip_staticと同様。
全てのブラウザでbrotli圧縮が使えるわけではないのでgzip圧縮も(これまで有効にしていたなら)有効のままににしておく。

静的コンテンツを圧縮

静的ファイルをBrotliで圧縮する。

/PATH/index.htmlをbrotliで圧縮する実行例
$ /usr/local/bin/brotli /PATH/index.html --force --output=/PATH/index.html.br
または
$ /usr/local/bin/brotli -f /PATH/index.html

圧縮レベル ( -q 値 ) は0〜11で11が最も高い圧縮率。未指定で6。おそらく未指定で良いかと。
-f または --forceオプションで出力ファイルは上書きされる。コンテンツを更新して再圧縮する場合などに有用。
-s または --suffixオプションは初期値が.br なので上の実行例の1つめと2つめは同じ結果となる。(出力ファイル名は「元のファイル名.br」で既に存在する場合は上書き)

ウェブサイトのファイルは殆どの場合に大量に存在する筈なので1つ1つ手作業で圧縮するのは非常に手間。更新したときの再圧縮も面倒でイヤになる筈。指定したディレクトリ内のファイルを一気に(下層も再帰的に)圧縮するスクリプトを用意する。

brotli.sh
#!/usr/bin/env bash
if [ $# -ne 1 ]; then
    echo "usage: brotli.sh /Path"
    exit 1
fi

for FILE in $(find $1 -type f  -iname "*.html" -o -iname "*.xml" -iname "*.css" -o -iname "*.js" -o -iname "*.svg" ); do
    echo -n "${FILE}..."
    /usr/local/bin/brotli ${FILE} --force --output=${FILE}.br;
    echo "done."
done

指定されたディレクトリ内のファイルを順番に処理。拡張子が html, xml, css, js, svg のファイルならbrotliで圧縮するというもの。


使い方:
$ brotli.sh /usr/local/www/example
 

圧縮したいディレクトリのPathを引数に指定して実行

Nginxの設定確認と再起動

# service nginx configtest    #設定確認 これは稼働中のNginxに影響しない
Performing sanity check on nginx configuration:
nginx: the configuration file /usr/local/etc/nginx/nginx.conf syntax is ok
nginx: configuration file /usr/local/etc/nginx/nginx.conf test is successful

# service nginx restart
または
# service nginx gracefulstop && service nginx start
または
# service nginx reload

設定確認でtest is successfulになるまではNginxを再起動させない。設定が不完全で下手に再起動させようとするとサービス停止になる。

Brotli圧縮の動作確認

# 圧縮無し
% curl -I https://example.com/index.html
HTTP/2 200
server: nginx
date: Fri, 13 Apr 2018 04:22:00 GMT
content-type: text/html; charset=utf-8
content-length: 12270                             #←コンテンツサイズ
last-modified: Sun, 11 Feb 2018 14:04:55 GMT
vary: Accept-Encoding
etag: "5a804d87-2fee"
expires: Fri, 13 Apr 2018 04:22:00 GMT
cache-control: max-age=0
strict-transport-security: max-age=31536000;
accept-ranges: bytes

# Brotli圧縮
% curl -H "Accept-Encoding: br" -I https://example.com/index.html
HTTP/2 200
server: nginx
date: Fri, 13 Apr 2018 04:19:51 GMT
content-type: text/html; charset=utf-8
content-length: 2673                             #←コンテンツサイズ
last-modified: Sun, 11 Feb 2018 14:04:55 GMT
vary: Accept-Encoding
etag: "5a804d87-a71"
content-encoding: br                             #←Brotli圧縮
expires: Fri, 13 Apr 2018 04:19:51 GMT
cache-control: max-age=0
strict-transport-security: max-age=31536000;

正しく機能していれば圧縮されたコンテンツが取得されるのでコンテンツサイズが小さい筈。

ウェブツールでも確認してみる。

Brotli Test
https://tools.keycdn.com/brotli-testでBrotli Testを実行できる。URL (静的コンテンツ)を入力して[Test]をクリックして薄緑の四角の部分に「Yeah! [入力したURL] suppors Brotli compression.」が表示されればOK.