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

  • 93
    Like
  • 0
    Comment
More than 1 year has 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を参照してください。

参考文献