Grafana + InfluxDB による時系列データの表示

2018年2月3日

はじめに

物理シミュレーションの計算状況を可視化できないかと思い、Grafana + InfluxDB で計算データの表示を試みた。

環境

  • Windows 7 64 bit
  • Grafana 4.6.3
  • InfluxDB 1.4.2
  • Python 3.6 (Anaconda)

InfluxDB

InfluxDB は時系列データを扱うデータベース管理サーバーである。Grafana は InfluxDB のデータの表示機能を持つ。

こちら から Windows 用パッケージ (influxdb-1.4.2_windows_amd64.zip) をダウンロードする。パッケージを展開して適当なところに置いて、パスを通す。

ユーザーのディレクトリパス HOME を手動で設定したほうがよいらしい。環境変数 HOMEPATH と同様の設定にする (参考: CLI History Error on ARM (GitHub))。

コマンドラインでサーバーの起動。

>influxd

クエリは http://localhost:8086 で受け付ける。

InfluxDB のデータベースは以下の項目からなる。

  • time : タイムスタンプ (必ず含まれる)
  • tag : キーの項目 (文字列)
  • field : 測定値の項目 (数値)

別のコマンドラインで influx (クライアント) を実行。コマンドは以下の通りである。

データベースの一覧の表示

> show databases

データベースの作成

> create database <name>

データベースの削除

> drop database <name>

使用するデータベースの設定

> use <name>

データの追加

> insert <measurement>,<tag>=<label>,... <field>=<value>,...

データの一覧の表示

> show measurements

データの削除

> drop measurement <measurement>

データの中身の表示

> select * from <measurement>

終了

> quit

たとえば、データベース "case1" を作って温度データを追加するには、次のようにする。

> create database case1
> use case1
> insert temperature value=1000

Python からの操作

InfluxDB-Python というもので Python から InfluxDB サーバーにアクセスできる。

pip でインストール。

>pip install influxdb

JSON 形式でデータを登録する。

from influxdb import InfluxDBClient

c = InfluxDBClient(host="localhost", port=8086)

c.create_database("case1")
c.switch_database("case1")

for i in range(10):
    json_body = [
        {
            "measurement": "temperature",
            "fields" : {
                "iteration" : i,
                "value" : 1000. + i
            }
        }
    ]

    c.write_points(json_body)

result = c.query("select iteration,value from temperature")
print("Result: {0}".format(result))

データベースがすでに作成されているかどうかを確認するには、次のような関数を作ればよい。

def created_database(c, name):
    db_list = c.get_list_database()

    found = False
    for db_name in db_list:
        if db_name['name'] == name:
            found = True
            break

    return found

Grafana

Grafana は時系列データベース表示用のダッシュボードを作るための仕組みである。

こちら から Windows 用パッケージ (grafana-4.6.3.windows-x64.zip) をダウンロードして展開し、適当な場所に置く。

conf/sample.ini をコピーして conf/custom.ini を作る。custom.ini を編集する。

# The http port  to use
;http_port = 3000
http_port = 8080

コマンドラインで bin フォルダに移動して、サーバー grafana-server.exe を実行する。

ブラウザで http://localhost:8080/login にアクセスする。ログイン画面が出てきたら、User: admin, Password: admin でログインできる。

左上のアイコン [Admin]-[Global Users] ユーザー名、パスワードを設定できる。ユーザーの作成もできる。

データソースの追加

左上のアイコン [Data Sources] の "Add data source" でデータソースを追加する。

  • Name: 適当な名前
  • Type: InfluxDB
  • URL: http://localhost:8086
  • Access: direct
  • Database: データベース名

ダッシュボードの追加

左上のアイコン [Dashboards]-[New] を選択、"Graph" を選ぶ。"Panel Title" をクリックして "Edit" を選ぶ。下のクエリの設定の右にメニューのアイコンがあるので、"Toggle Edit Mode" を選ぶ。InfluxDB のコマンドを直接編集できるので、たとえば、次のようにする。

SELECT "value" FROM "temperature" WHERE $timeFilter

グラフの自動更新

右上の時刻をクリック、"Time range" のところの "Refreshing every" から更新間隔を選択する。

シミュレーション計算状況の可視化

シミュレーション計算状況を Grafana で可視化することを試みる。

熱流体計算の状況表示を考える。計算は定常計算である。データは平均流速と平均温度で、繰り返し計算回数 (iteration) ごとに出力される。ファイルのタイムスタンプを監視して、更新があったら最後のデータを InfluxDB に送るようにしてみた。

watch-timestamp.py

import os
from stat import *
import time
from influxdb import InfluxDBClient


def created_database(c, name):
    db_list = c.get_list_database()

    found = False
    for db_name in db_list:
        if db_name['name'] == name:
            found = True
            break

    return found


db = "case1"

files = {
    "plot-vel.txt" : "velocity",
    "plot-temp.txt" : "temperature",
}

def main():
    c = InfluxDBClient(host="localhost", port=8086)

    if not created_database(c, db):
        c.create_database(db)

    c.switch_database(db)

    timestamp0 = {}
    for file in files.keys():
        if not os.path.exists(file):
            with open(file, "w"):
                pass
        timestamp0[file] = os.stat(file)[ST_MTIME]

    print("Waiting...")

    try:
        while True:
            for file in files.keys():
                timestamp = os.stat(file)[ST_MTIME]
                if timestamp > timestamp0[file]:
                    print("update: %s" % file)

                    item = []
                    with open(file, "r") as f:
                        line = f.readlines()
                        item = line[-1].split()

                    json_body = [
                        {
                            "measurement": files[file],
                            "fields" : {
                                "iteration" : item[0],
                                "value" : item[1]
                            }
                        }
                    ]

                    c.write_points(json_body)

                    timestamp0[file] = timestamp

            time.sleep(1)
    except KeyboardInterrupt:
        pass


if __name__ == "__main__":
    main()

これではたまにしかデータを送れなかった。Windows の問題かもしれない。そこで、ある時間間隔でデータを送るようにしてみた。

watch-interval.py

import os
from stat import *
import time
from influxdb import InfluxDBClient


def created_database(c, name):
    db_list = c.get_list_database()

    found = False
    for db_name in db_list:
        if db_name['name'] == name:
            found = True
            break

    return found


db = "case1"

files = {
    "plot-vel.txt" : "velocity",
    "plot-temp.txt" : "temperature",
}

interval = 10

def main():
    c = InfluxDBClient(host="localhost", port=8086)

    if not created_database(c, db):
        c.create_database(db)

    c.switch_database(db)

    print("Waiting...")

    try:
        while True:
            for file in files.keys():
                print("update: %s" % file)

                item = []
                with open(file, "r") as f:
                    line = f.readlines()
                    item = line[-1].split()

                json_body = [
                    {
                        "measurement": files[file],
                        "fields" : {
                            "iteration" : item[0],
                            "value" : item[1]
                        }
                    }
                ]

                c.write_points(json_body)

            time.sleep(interval)
    except KeyboardInterrupt:
        pass


if __name__ == "__main__":
    main()

頻繁にデータが更新されるなら、時間間隔で送った方がよいかもしれない。

感想

Grafana は (今のところ) 実時間に対するグラフしか表示できないので、時間が実時間とは異なるシミュレーションでは少し使いにくい。長時間の計算に対する状況の監視には使えないこともないが。