Edited at

GrafanaとInfluxDBでネットワークリソースの視覚化

More than 3 years have passed since last update.

Rubyスクリプトで、CiscoルータにSSHでログインして、CLIから10秒間隔でshow interface叩いて、実行結果から必要な値を正規表現で抜き出して、InfluxDBに保存して、Grafanaで視覚化してみました。

GrafanaとInfluxDBでネットワークリソースを視覚化してみました。データベースはInfluxDB、ビジュアライゼーションツールはGrafana、データ取得はRubyスクリプト+expect4rライブラリで実装しています。

本記事はUsing InfluxDB + Grafana to Display Network Statisticsを参考にしています。データ取得にInfluxsnmpを利用していますが、今回は自作のRubyスクリプトで実行しています。

範囲を選択_032.png

カスタマイズすると上図のようにできます。今回はデータの視覚化までの基本的なところを説明します。


目的

ルータのトラフィックなどのネットワークリソースの視覚化することを目的としています。

従来はSNMPとRRDtoolなどを組み合わせて視覚化していました。ただし、トラフィック以外に取得項目が増えた場合や、取得間隔の変更など、手間がかかりました。そこで、代替ツールとしてInfluxDB+Grafanaを試用してみました。

今回は時系列データベースのInfluxDBを使います。メリットとして、スキーマレスのため、取得項目が増えた場合でも、データベース側で設定変更不要です。ガンガン監視項目の追加やルータの台数を増やすことができます。

視覚化ツールはGrafanaを使って、表示時間の変更や一度に複数の項目を表示などができます。ダイナミックに時間を変更でき、かっこ良く表示できます。

ネットワークリソースの値取得はSNMPが標準的ですが、今回はSSHからCLIでshowコマンドを実行し、その結果をデータベースに保存しています。想定はSNMPに対応していない値やSNMPできない環境を想定しています。


仕組み

データ取得・保存・表示は下記の仕組みで実現します。

スクリーンショット 2016-04-12 7.35.31.png


準備

今回はDockerHub上のイメージを使ってInfluxDBとGrafanaを使います。

※Grafana v3.0.0-beta1がInfluxDB v0.11以降に対応していないため、今回はInfluxDB v0.10を利用します。


ルータの準備

Cisco VIRLで下記の環境を構築します。擬似トラフィックを流すため、両端にlxc-iperf-1/2間でiperfでトラフィックを流しています。

VM Maestro _031.png


Ubuntu/Dockerの準備

InfluxDBとGrafanaをDocker上で動かすため、Dockerをインストールします。Ubuntu14.04で提供されているdocker.ioパッケージをインストールします。バージョンはv1.6.2です。

$ cat /etc/lsb-release 

DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=14.04
DISTRIB_CODENAME=trusty
DISTRIB_DESCRIPTION="Ubuntu 14.04.4 LTS"
$ sudo apt install docker.io


InfluxDBの準備

InfluxDBは時系列データベースです。スキーマレスのため、動的に保存する項目を増やしたりできます。

今回はInfluxDB v0.10をDockerHubのコンテナイメージを使って利用します。データベースは$HOME/influxdbに保存するよう設定します。InfluxDBはTCPポート8083,8086を使用します。TCP8083はWeb管理インタフェース、TCP8086はAPIのポートです。

# InfluxDB v0.10のコンテナイメージを取得

$ sudo docker pull tutum/influxdb:0.10

# InfluxDBのコンテナ起動
$ sudo docker run -d --name=influxdb --volume=$HOME/influxdb:/data -p 8083:8083 -p 8086:8086 tutum/influxdb:0.10

# InfluxDBが起動しているか確認
$ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
cb5e24f3065a tutum/influxdb:0.10 "/run.sh" 4 minutes ago Up 4 minutes 0.0.0.0:8083->8083/tcp, 0.0.0.0:8086->8086/tcp influxdb

# コンテナを停止したい場合に実行
$ sudo docker stop influxdb

# 停止したコンテナを起動
$ sudo docker start influxdb


InfluxDBでデータベースの作成

続いて、Web管理インタフェースからInfluxDB上でデータベースを作成します。今回はtraffic1というデータベース名で、4週間データを保存する設定をします。

DockerホストのTCPポート8083をWebブラウザで表示し、下記のクエリを発行し、データベースの作成とポリシーを設定します。

# データベースの作成

CREATE DATABASE traffic1
# 作成済みのデータベース確認
SHOW DATABASES

# デフォルトのポリシーtrafficを設定、4週間保存する
CREATE RETENTION POLICY traffic ON traffic1 DURATION 4w REPLICATION 1 DEFAULT
# ポリシーの確認
SHOW RETENTION POLICIES ON "traffic1"

今回は192.168.122.103のDockerホスト上で動作しているため、Web管理インタフェースはhttp://192.168.122.103:8083/ となります。WebブラウザでWeb管理インタフェースを開き発行したいクエリを「Query」フォームに入力し、Enterで確定します。問題なければ、Success!が表示されます。

範囲を選択_001.png

上記のデータベース作成とポリシーを設定すると、下記のとおりとなります。

範囲を選択_002.png


データ取得用のRubyスクリプト

今回はSNMPを使わずに手製スクリプトでルータにSSHして、データを取得しています。想定としてはSNMPでは取得できないメトリクスや、そもそもSNMPできないルータに対して、CLIからコマンドを実行して、値を保存する方法を想定しています。

CiscoルータにSSHし、show interfaceコマンドの実行結果をデータベースに保存します。データベースはtraffic1で、SQLデータベースで言ういわゆるテーブル名はcisco_showintとしていします。InfluxDBでは、テーブル名はシリーズ名と言われます。

show interfaceから正規表現を利用して下記のパラメータを抽出します。抽出した結果は、ホスト名とインタフェース名を組み合わせたportというタグをつけてデータベースに保存します。


  • interface: インタフェース名

  • out_drops: 送信ドロップ数

  • in_packets: 受信パケット数

  • in_bytes: 受信バイト数

  • in_errors: 受信エラーパケット数

  • out_packets: 送信パケット数

  • out_bytes: 送信バイト数

  • out_errors: 送信エラーパケット数

  • lost_carriers: Ethernetキャリア信号の喪失回数

expect4rライブラリを使って、CiscoルータにSSHしています。詳細はRubyのexpect4rでCiscoルータにTelnet/SSHしてコマンド実行するを見てください。


collector.rb

require 'expect4r'

require 'awesome_print'
require 'clockwork'
require 'influxdb'
require 'parallel'
require 'yaml'

# InfluxDBの設定
influxdb = InfluxDB::Client.new 'traffic1', host: '192.168.122.103'

# 取得先ホスト
hosts = YAML.load(<<EOL)
- :hostname: iosv-1
:host: 172.16.1.205
- :hostname: iosv-2
:host: 172.16.1.206
EOL

# show interfaceの結果から値を抜き出す正規表現
REGEX_SHOWINT = %r{
^(?<interface>[
\w\.\/]+)\ is
.+?
Total
\ output\ drops:\ (?<out_drops>\d+)
.+?
(?<in_packets>
\d+)\ packets\ input,\ (?<in_bytes>\d+)\ bytes
.+?
(?<in_errors>
\d+)\ input\ errors,
.+?
(?<out_packets>
\d+)\ packets\ output,\ (?<out_bytes>\d+)\ bytes
.*?
(?<out_errors>
\d+)\ output\ errors,
.+?
(?<lost_carriers>
\d+)\ lost\ carrier
}xm

# 上記の正規表現は下記のshow interfaceの結果をインタフェース単位に抽出する
# GigabitEthernet0/2 is up, line protocol is up
# Hardware is iGbE, address is fa16.3e59.cd19 (bia fa16.3e59.cd19)
# Description: to iosv-2
# Internet address is 10.0.0.9/30
# MTU 1500 bytes, BW 1000000 Kbit/sec, DLY 10 usec,
# reliability 255/255, txload 1/255, rxload 1/255
# Encapsulation ARPA, loopback not set
# Keepalive set (10 sec)
# Full Duplex, Auto Speed, link type is auto, media type is RJ45
# output flow-control is unsupported, input flow-control is unsupported
# ARP type: ARPA, ARP Timeout 04:00:00
# Last input 00:00:07, output 00:00:00, output hang never
# Last clearing of "show interface" counters never
# Input queue: 0/75/0/0 (size/max/drops/flushes); Total output drops: 842948
# Queueing strategy: fifo
# Output queue: 0/40 (size/max)
# 5 minute input rate 115000 bits/sec, 171 packets/sec
# 5 minute output rate 2092000 bits/sec, 172 packets/sec
# 3475718 packets input, 286108879 bytes, 0 no buffer
# Received 0 broadcasts (0 IP multicasts)
# 0 runts, 0 giants, 0 throttles
# 0 input errors, 0 CRC, 0 frame, 0 overrun, 0 ignored
# 0 watchdog, 0 multicast, 0 pause input
# 3517278 packets output, 5302646275 bytes, 0 underruns
# 0 output errors, 0 collisions, 3 interface resets
# 0 unknown protocol drops
# 0 babbles, 0 late collision, 0 deferred
# 0 lost carrier, 0 no carrier, 0 pause output
# 0 output buffer failures, 0 output buffers swapped out

hosts.each do |host|
ios = Expect4r::Ios.new_ssh(
host: host[:host], user: 'cisco',
pwd: 'cisco', enable_password: 'cisco'
)
# 10秒間隔で定期的に実行する
Clockwork.every(10.seconds, "#{host[:hostname]} collector") do
ios.login

# Ciscoルータでshow interfaceを実行し、
# 正規表現でメトリクスを抜き出す
response = ios.exec('show interface').join.delete("\r")
response.scan(REGEX_SHOWINT) do
m = Regexp.last_match
data = {
values: {
out_drops: m[:out_drops].to_i,
in_packets: m[:in_packets].to_i,
in_bytes: m[:in_bytes].to_i,
in_errors: m[:in_errors].to_i,
out_packets: m[:out_packets].to_i,
out_bytes: m[:out_bytes].to_i,
out_errors: m[:out_errors].to_i,
lost_carriers: m[:lost_carriers].to_i
},
tags: {
port: "#{host[:hostname]} #{m[:interface]}"
}
}

# InfluxDBにメトリクスを保存
influxdb.write_point('cisco_showint', data)
ap data
end
end
end



データ取得用のRubyスクリプトの実行

上記のRubyスクリプトは定期実行するためにclockworkを利用して、10秒間隔で実行します。


実行結果

$ clockwork collector.rb 

I, [2016-04-10T11:30:36.846900 #1318] INFO -- : Starting clock for 2 events: [ iosv-1 collector iosv-2 collector ]
I, [2016-04-10T11:30:36.847066 #1318] INFO -- : Triggering 'iosv-1 collector'
{
:values => {
:out_drops => 0,
:in_packets => 744202,
:in_bytes => 45856886,
:in_errors => 0,
:out_packets => 780556,
:out_bytes => 126581158,
:out_errors => 0,
:lost_carriers => 1
},
:tags => {
:port => "iosv-1 GigabitEthernet0/0"
}
}
{
:values => {
:out_drops => 0,
:in_packets => 5049224,
:in_bytes => 7628494232,
:in_errors => 0,
:out_packets => 4034600,
:out_bytes => 332766793,
:out_errors => 0,
:lost_carriers => 1
},
:tags => {
:port => "iosv-1 GigabitEthernet0/1"
}
}
{
:values => {
:out_drops => 988441,
:in_packets => 4029369,
:in_bytes => 332510281,
:in_errors => 0,
:out_packets => 4080969,
:out_bytes => 6136203204,
:out_errors => 0,
:lost_carriers => 0
},
:tags => {
:port => "iosv-1 GigabitEthernet0/2"
}
}


InfluxDBでデータ保存結果の確認

データが保存されているか、InfluxDBのWeb管理インタフェースで確認します。

右上から対象のデータベースtraffic1を選択し、下記のクエリを実行します。

select * from cisco_showint where port = 'iosv-1 GigabitEthernet0/1' order by desc

InfluxDB - Admin Interface - Mozilla Firefox_004.png

cURLコマンドでデータ一覧を取得することができます。APIのエンドポイントはhttp://192.168.122.103:8086/query となります。パラメータにデータベースとクエリを指定します。結果はJSONとなります。詳細はInfluxDB Querying Dataを参照してください。

$ curl -G "http://192.168.122.103:8086/query?pretty=true" --data-urlencode "db=trffic1" --data-urlencode "q=SELECT * FROM cisco_showint WHERE port = 'iosv-1 GigabitEthernet0/1' order by desc limit 2"

{
"results": [
{
"series": [
{
"name": "cisco_showint",
"columns": [
"time",
"in_bytes",
"in_errors",
"in_packets",
"lost_carriers",
"out_bytes",
"out_drops",
"out_errors",
"out_packets",
"port"
],
"values": [
[
"2016-04-10T02:51:33Z",
7.628494232e+09,
0,
5.049224e+06,
1,
3.32786237e+08,
0,
0,
4.034858e+06,
"iosv-1 GigabitEthernet0/1"
],
[
"2016-04-10T02:51:23Z",
7.628494232e+09,
0,
5.049224e+06,
1,
3.32786087e+08,
0,
0,
4.034856e+06,
"iosv-1 GigabitEthernet0/1"
]
]
}
]
}
]
}


Grafanaの準備

ビジュアライゼーションツールのGrafanaを設定します。今回はv3.0.0の正式リリース前だったため、v3.0.0-beta1を使用します。

$HOME/grafanaに設定データを保存するよう設定します。TCP3000ポートで稼働します。

# Grafana v3.0.0-beta1のコンテナイメージを取得

$ sudo docker pull grafana/grafana:3.0.0-beta1

# Grafanaのコンテナ起動
sudo docker run -d --name=grafana --volume=$HOME/grafana/:/var/lib/grafana -p 3000:3000 grafana/grafana:3.0.0-beta1

# InfluxDBとGrafanaが起動しているか確認
$ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
813046bdf50e grafana/grafana:3.0.0-beta1 "/run.sh" 6 seconds ago Up 6 seconds 0.0.0.0:3000->3000/tcp grafana
cb5e24f3065a tutum/influxdb:0.10 "/run.sh" 58 minutes ago Up 58 minutes 0.0.0.0:8083->8083/tcp, 0.0.0.0:8086->8086/tcp influxdb

# コンテナを停止したい場合に実行
$ sudo docker stop grafana

# 停止したコンテナを起動
$ sudo docker start grafana


Grafanaにログイン

Grafanaの設定をするために、WebブラウザからGrafanaを表示します。

Webブラウザでhttp://192.168.122.103:3000 を開くと、ログイン画面が表示されます。デフォルトは、ユーザ名:admin、パスワード:adminでログインできます。

範囲を選択_006.png


Grafanaでデータソースの指定

ログイン後、最初にデータソースの設定をします。データソースにInfluxDBを使用するよう指定します。

左上のGrafanaアイコンをクリックし、メニューが表示されたら、Data Sourcesリンクをクリックし、Data Sources画面に移動します。

範囲を選択_007.png

Data Sources画面でAdd data sourceボタンをクリックします。

範囲を選択_008.png

Add data source画面で下記のパラメータを入力します。


  • Name: traffic1 ※データベース名と同じ名前を設定します。

  • Default: ON

  • Type: InfluxDB ※データベースにInfluxDBを指定。指定すると入力項目がInfluxDBに合わせて変化します

  • Http Settings


    • Url: http://192.168.122.130:8086 ※InfluxDBのTCP8086を指定します。

    • Access: direct ※ブラウザから直接アクセスするように指定します。



  • InfluxDB Details


    • Database: traffic1 ※InfluxDBで作成したデータベースを指定

    • User: admin ※入力必須項目のためダミーで入力

    • Password: admin ※入力必須項目のためダミーで入力



Addボタンをクリックし、データソースを追加します。

範囲を選択_009.png

追加後にテスト接続し、問題ないことを確認するため、Test Connectionボタンをクリックし、Successと表示がでることを確認します。問題なければ、Saveボタンで保存します。

範囲を選択_010.png


Grafanaダッシュボードの作成

Grafanaアイコン => Dashboards => Newからダッシュボード作成画面に移動します。

Screenshot from 2016-04-10 13:06:32.png

新しいダッシュボードを作成したら、グラフパネルを追加します。ROWアイコン => Add Panel => Graphからグラフパネル作成画面に移動します。

ワークスペース 1_020.png

メトリクスの追加は下記のYouTubeの動画のとおりに設定します。

Grafanaの設定

※追記:動画ではGraph Metricsでfill(null)となっていますが、fill(none)に変更が必要です。fill(null)で表示時間を変更した場合、うまく表示されない場合があります。

Graph Generalの設定。グラフのタイトルを設定します。

範囲を選択_023.png

Graph Metricsの設定。データベースを選択し、表示対象のメトリクスを設定します。

トラフィックの場合、累積カウンタとなるため、直前の値と差分を取る必要があります。non_negative_derivative(1s)関数を使用し、直前との差分の1秒間隔の平均をグラフにプロットします。ただし、ラップアラウンドは考慮されていません。値がバイトのため、ビットに変換するためにmath(*8)関数を使用して、8倍しています。GROUP BYでfill(none)を選択します。

範囲を選択_029.png

Graph Axesの設定。X-Y軸の設定をします。Unit(単位)をbits/sec、Y-Minを0、Right YをOFFに設定します。

範囲を選択_026.png

Graph Legendの設定。判例の設定をします。判例に最大と現在の値を表示するよう設定します。

範囲を選択_027.png

最後にダッシュボードを保存します。

Screenshot from 2016-04-10 13:44:35.png

今回は必要最小限の設定のみ説明しました。今回説明していないTemplatingを使うことで、動的にポートを選択できるようになります。詳しくはUsing InfluxDB + Grafana to Display Network Statisticsを参照してください。


参考文献