Norikraでそこそこ手軽にNetFlow解析

去年のJANOGで別の方が発表された内容とダダ被りではあるものの,折角なのでメモ程度に書いておく.

概要

  • データセンタネットワークやバックボーンネットワークの運用をやっていると,インターネットに出入りするトラフィックの内訳 (どのISP向けのトラフィックが多いのか,どの回線にトラフィックが乗っているか,等) を見たいことがよくある
  • NetFlowをNorikraで解析し,その結果をGrowthForecastに流し込むと,トラフィックの内訳をそこそこ手軽に見ることができる
  • 定常的なモニタリング用途以外に,突発的なトラブルシューティングにも使えていろいろ便利

背景

自前でAS (Autonomous System) を運用している事業者では,トラフィックのコントロールの観点から,「どのAS向けのトラフィックがどのくらいあるのか」「このASとはどの回線を使って通信しているか」といった情報を知りたいケースがよくある (と思う). このような場面では,NetFlowに代表されるトラフィック統計情報を収集する機能が用いられる.NetFlowを使うと,ある機器を通過した通信フローに関する情報 (src/dst IP, src/dst ASなど) が取得できるので,これらの情報を解析すればトラフィックの内訳を知ることができる.

NetFlow解析を行う場合,一般的には次のような選択肢がある.

回線ごとのトラフィック集計を行いたいケースや複数ASを運用しているようなケースでは,柔軟性の観点から自前での構築を選びたくなる.が,一から全てやるのは結構大変なので,解析はNorikraに,結果のグラフ化はGlowthForecastにやってもらうことにする.

実現方法

構成はNorikra+Fluentd+GrowthForecast.NetFlowコレクタはfluent-plugin-netflowを使用する.

f:id:yunazuno:20150331024123p:plain

Fluentdの設定はこんな感じ.fluent-plugin-forest使ってるので、AS数や回線数によってはメモリの消費に注意.

## <- NetFlow
<source>
  type netflow
  tag  netflow.flow

  bind 0.0.0.0
  port 2055
</source>

## -> Norikra
<match netflow.*>
  type     norikra
  norikra  127.0.0.1:26571

  remove_tag_prefix  netflow
  target_map_tag     true
</match>

## <- Norikra
<source>
  type    norikra
  norikra 127.0.0.1:26571
  <fetch>
    method     sweep
    tag_prefix norikra.query
    tag field  _gf_key
    interval   1m
  </fetch>
</source>

## -> GrowthForecast
<match norikra.query.**>
  type forest
  subtype growthforecast
  remove_prefix norikra.query

  <template>
    gfapi_url http://127.0.0.1:5125/api/
    graph_path netflow/as${tag_parts[0]}/${tag_parts[1]}_bps_${tag_parts[2]}
    name_keys traffic_bps
  </template>
</match>

この構成でNorikraに下のようなクエリを登録した後NetFlowを流し込めば,ひとまずAS毎の集計結果がNorikraで出力されるはず (ただしこの時点では全ての回線の合計値が出力される).

select
    src_as, dst_as,
    (SUM(in_bytes * sampling_interval) * 8) / 60 as traffic_bps,
from
    flow.win:time_batch(60 sec, 0L)
group by
    src_as, dst_as

次は回線毎の集計.SNMP ifIndexと回線の対応関係やフローの向きのチェックを全てEPLで書くのは辛いので,簡単なUDFを作った.

github.com

norikra-udf-netflowでは,NetFlowのデータを扱い易い形式に変換するためのいくつかの関数を定義している.内容は下の表のような感じ.変換に必要な情報はインストール前にあらかじめdefinition.yamlに書いておく必要がある.

Function Description
NFDirection(src_as, dst_as) フローの向き ("in" or "out")
NFOppositeASN(src_as, dst_as, ipv4_src_addr, ipv4_dst_addr) 対向のAS番号
NFRouter(host_ipaddr) NetFlowの生成元のルータの名称
NFCareer(host_ipaddr, ifindex_in, ifindex_out, flow_direction) フローが通過した回線の名称

これらの関数を使って回線毎の集計をやる場合,こんな感じのクエリになる.

Query name: flow_aggregator, Group: LOOPBACK(aggregated_flow)

select
    NFOppositeASN(src_as, dst_as, ipv4_src_addr, ipv4_dst_addr) as opposite_as,
    NFCareer(host, input_snmp, output_snmp, NFDirection(src_as, dst_as)) as career,
    (SUM(in_bytes * sampling_interval) * 8) / 60 as _traffic_bps,
    NFDirection(src_as, dst_as) as flow_direction
from
    flow.win:time_batch(60 sec, 0L)
group by
    OppositeASN(src_as, dst_as, ipv4_src_addr, ipv4_dst_addr),
    NWPoPCareer(host,(code) input_snmp, output_snmp, FlowDirection(src_as, dst_as)),
    FlowDirection(src_as, dst_as)

Query name: traffic_as_per_career

select
    opposite_as,
    career,
    case LAST(_traffic_bps) when null
        then 0
        else LAST(_traffic_bps)
    end as traffic_bps,
    (opposite_as || "." || career || "." || flow_direction) as _gf_key
from
    aggregated_flow.win:time_batch(60 sec, 0L)
group by
    opposite_as,
    career,
    (opposite_as || "." || career || "." || flow_direction)

一度LOOPBACKで集計済みデータを別target(上の例だとaggregated_flow)に流しているのは,後で突発的に解析用のクエリを投入する時にデータを再利用しやすくするため.NorikraのクエリでFluentdのタグを作ってる点がちょっとアレ. ここまで全て上手く動いていれば,GrowthForecastでグラフが作られているはず.

これまでの運用状況など

Norikra

去年末に投入後,約3ヶ月稼働中.単純な時間平均で 10000 event/sec (=10000 flow/sec) 弱程度のeventを流し込んでいる.JVMが数回突然死したり,CPUコアが多い環境で起動しないトラブルに遭遇した*1以外は概ね安定している.現在heapに30GB弱割り当てていて,GCのタイミングで結構な時間止まっているので何とかしたい.

Flow collector

fluent-plugin-netflowが手元の環境だと1プロセスあたり8000 flow/sec 近辺でsocket bufferが埋まる速度に追い付けなくなった.順当に行けばfluent-plugin-multiprosessでポートを分けて負荷を分散させる場面だと思われる.が,これをやるとルータによってNetFlowの送り先が変わってしまうので後々大変そう,ということになり*2nfcollectという超簡易的なNetFlow collectorを書いて凌いだ.

github.com

nfcollectはフローの到着順を保持しない代わりにそこそこのスループットが出るようになっていて,flow-genで試したところ同じ環境で30000 flow/secぐらいまでは何事も無く捌いてくれた.こちらを使った場合でもある時点でfluent-plugin-netflowと同じ問題が発生することに変わりは無いものの,現状30000 flow/secを越えることをはまず無さそうということで一旦目を瞑っている.

その他諸々

当初は単純な集計用途だけを想定していたが,使い始めてから暫くすると,トラブルシューティング的な用途にも使えそうなことが分かった.例えば本来存在するはずの通信が見えない時や,特定ISPから特定アドレス帯への通信が上手くいかないといった時に,その問題に合わせたクエリをその場で書いてNorikraに投入してしばらく待てば,パケットキャプチャより手軽に調査ができる*3.ちょっとしたクエリのサンプル集のようなものを事前に書いておけば,大抵の人は問題無く扱えると思う.

まとめ

NetFlow解析,割と難しいもの扱いされているという噂を耳にしますが,やってみると色々便利なのでまずは試してみると良いと思います.

*1:単純にエラーメッセージちゃんと読んでなかったのが原因.現在はNorikra側でも対策が行われている.

*2:BGPルータの設定変更,あまりやりたい仕事ではないので

*3:ただし対象の通信が平常時からある程度の量流れている場合に限る