RsyslogとGrafana LokiによるネットワークSyslogの一元監視

Networking tutorial - IT technology blog
Networking tutorial - IT technology blog

クイックスタート: 5分で立ち上げる環境構築

半年前、私たちのインフラチームは、検索インデックスをウォーム状態に保つだけで48GBものRAMを消費していた3ノードのElasticsearchクラスターを廃止しました。その代替として導入したのがGrafana LokiとRsyslogです。この新しい構成では、Cisco Catalyst 9300スイッチ、FortiGate 200Fクラスター、MikroTik CCRエッジルーターなど120台のネットワーク機器から届く毎秒約8,500件のsyslogイベントを処理しながら、メモリ消費量を4GB未満に抑えています。

Docker Composeを使用すれば、Ubuntu 22.04または24.04 LTS上で同様のログ収集パイプラインを数分で立ち上げることができます。

ステップ1: LokiとGrafanaのデプロイ

まず、network-loggingという名前のプロジェクトディレクトリを作成し、以下のdocker-compose.ymlファイルを保存します:

version: "3.8"

services:
  loki:
    image: grafana/loki:2.9.4
    container_name: loki
    ports:
      - "3100:3100"
    command: -config.file=/etc/loki/local-config.yaml
    volumes:
      - ./loki-data:/loki
    restart: unless-stopped

  promtail:
    image: grafana/promtail:2.9.4
    container_name: promtail
    volumes:
      - /var/log/network:/var/log/network:ro
      - ./promtail-config.yaml:/etc/promtail/config.yaml:ro
    command: -config.file=/etc/promtail/config.yaml
    restart: unless-stopped

  grafana:
    image: grafana/grafana:10.4.1
    container_name: grafana
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=adminpass
    restart: unless-stopped

ステップ2: RsyslogをUDP/TCPポート514にバインドする

次に、受信するネットワークトラフィックを受け取れるようホスト側のRsyslogを設定します。/etc/rsyslog.d/10-network-input.confという設定ファイルを新規作成し、以下の設定を記述します:

module(load="imudp")
input(type="imudp" port="514")

module(load="imtcp")
input(type="imtcp" port="514")

# ホストIPごとに受信ログを分割保存: /var/log/network/<DEVICE_IP>/syslog.log
template(name="NetLogPath" type="string" string="/var/log/network/%fromhost-ip%/syslog.log")
*.* ?NetLogPath
& stop

ディレクトリのパーミッションを準備し、サービスを再起動します:

sudo mkdir -p /var/log/network
sudo chmod -R 755 /var/log/network
sudo systemctl restart rsyslog

ステップ3: PromtailでSyslogファイルを監視する

Promtailは、ディスク上の未加工ログファイルを追跡(tail)し、メタデータラベルを付与して、圧縮バッチとしてLokiへ送信します。プロジェクトディレクトリにpromtail-config.yamlとして以下を保存します:

server:
  http_listen_port: 9080
  grpc_listen_port: 0

positions:
  filename: /tmp/positions.yaml

clients:
  - url: http://loki:3100/loki/api/v1/push

scrape_configs:
  - job_name: network_syslog
    static_configs:
      - targets:
          - localhost
        labels:
          job: network_devices
          __path__: /var/log/network/*/*.log
    pipeline_stages:
      - regex:
          expression: '^/var/log/network/(?P<device_ip>[^/]+)/.*\.log$'
          source: filename
      - labels:
          device_ip: ""

コンテナを起動します:

docker compose up -d

ブラウザでhttp://<your-server-ip>:3000にアクセスしてGrafanaを開きます。admin / adminpassでログインし、データソースとしてLoki(http://loki:3100)を追加したら、{job="network_devices"}でログストリームのクエリを実行できます。

詳細解説: アーキテクチャとパイプラインの仕組み

ElasticsearchやOpenSearchのような従来のツールは、受信したテキスト文字列を転置インデックス(Inverted Index)にパースして格納します。これは自由形式の検索クエリには適していますが、インターネット全体を対象としたスキャン攻撃などで、ノイズの多いファイアウォールから毎秒4万件ものドロップイベントが吐き出されるような環境では急速に破綻します。RAM使用率が高騰し、ディスクI/Oが激しく消耗し、クラスターのメンテナンスが専任業務と化してしまうのです。

なぜフロントゲートにRsyslogを採用するのか?

RsyslogはC言語で記述されており、カーネル空間に近いユーザー空間で動作します。そのため、LogstashやVectorのようなJavaやGoベースのコレクターよりも、高頻度なUDPパケットをはるかに効率的に取り込むことができます。仮にアクセススイッチが激しくリンクフラッピングを起こし始めても、Rsyslogはインメモリのリングバッファにログ行を保持し、CPUコアを圧迫することなく処理を継続します。

Lokiの設計思想: テキストではなくラベルをインデックス化する

Lokiは標準的なインデックス作成モデルを根本から覆しています。ログ本文には手を加えず、チャンク単位でgzip圧縮してディスクやS3互換ストレージに直接書き込みます。インデックス化されるのは、job、device_ip、environmentといったストリームのメタデータラベルのみです。

この設計アプローチにより、月間のストレージ使用量を1.2TBから約140GBにまで削減できました。Grafanaがチャンク境界をまたいでgrepのような並列フィルタリングを実行するため、クエリの実行速度も高速に維持されます。

ネットワーク機器の設定手順

ポート514経由でUbuntuホストへイベントを転送するように各ネットワーク機器を設定します:

# Cisco IOS / IOS-XE
logging host 192.168.10.50 transport udp port 514
logging trap informational
logging facility local6
logging source-interface GigabitEthernet0/0

# Fortinet FortiGate (CLI)
config log syslogd setting
    set status enable
    set server "192.168.10.50"
    set mode udp
    set port 514
    set facility local7
    set format default
end

# MikroTik RouterOS v7
/system logging action add name=central-syslog target=remote remote=192.168.10.50 remote-port=514 src-address=192.168.10.1
/system logging add action=central-syslog topics=critical,error,warning,info

高度な活用法: LogQLによるパースとアラート通知

Grafana内で必要に応じてパースやフィルタリングを行うことで、生のテキストストリームは強力な情報源に変化します。

読み取り時のフィールド抽出

Ciscoのsyslogメッセージは%FACILITY-SEVERITY-MNEMONIC: Descriptionという規則的なフォーマットに従っています。事前にスキーマ定義を用意しなくても、LogQLのパターンマッチングを使えばクエリ実行時にフィールドをパースできます:

{device_ip="192.168.10.1"} 
  |= "%LINK-3-UPDOWN:"
  | pattern "<_> %LINK-3-UPDOWN: Interface <interface>, changed state to <status>"

このクエリは、interfaceフィールドとstatusフィールドを動的に抽出します。これらをGrafanaのテーブルパネルに直接バインドすることで、キャンパスコア全体で発生しているポートのフラッピングを迅速に特定できます。

生ログからのメトリクスストリーム生成

生のログを集計して、Prometheusスタイルの時系列データを作成することも可能です:

sum by (device_ip) (
  rate({job="network_devices"} |= "action=deny" [1m])
)

これにより、デバイスごとの毎秒ファイアウォールパケット破棄数が算出されます。突発的なスパイクを検知すれば、内部マルウェアによるスキャンや境界部への総当たり攻撃を数秒以内に検知できます。

リアルタイムアラートルールの設定

Lokiの設定にアラートルールを追加することで、ルーティングの切断を即座に検知できます:

groups:
  - name: network-alerts
    rules:
      - alert: BGPNeighborDown
        expr: |
          count_over_time({job="network_devices"} |= "%BGP-5-ADJCHANGE:" |= "Down" [2m]) > 0
        for: 0m
        labels:
          severity: critical
        annotations:
          summary: "{{ $labels.device_ip }} 上でBGPピアセッションが切断されました"

実践的アドバイス: 堅牢化と本番環境チューニング

このパイプラインを本番運用する中で、早期に対処しておくべき3つのボトルネックが判明しました。

1. 高カーディナリティなラベルを避ける

リモートクライアントIP、MACアドレス、TCPポート番号のような動的な値をPromtailのラベルとして抽出してはいけません。Lokiはユニークなラベルの組み合わせごとに個別のインデックスストリームを生成します。1万件の一意なクライアントIPが存在すると瞬時に1万本のストリームが作成され、Lokiのインデックスが肥大化して検索パフォーマンスが著しく低下します。ラベルにはsite、role、device_ipのようなカーディナリティの低い値のみを使用してください。

2. RsyslogのUDPソケットバッファを調整する

突発的なトラフィックバーストが発生すると、OSの受信バッファが飽和し、Linuxカーネルは受信UDPデータグラムを警告なしにドロップします。以下のコマンドでドロップ数を確認できます:

netstat -su | grep "receive errors"

このカウンターが増加している場合は、/etc/sysctl.d/99-syslog.confでカーネルの受信バッファを25MBに引き上げます:

net.core.rmem_default = 26214400
net.core.rmem_max = 26214400

再起動せずにsysctlの設定値を反映させます:

sudo systemctl -p /etc/sysctl.d/99-syslog.conf

3. コンパクターによるログ保持期間(Retention)の強制適用

ネットワーク機器は膨大な量の稼働ログを出力します。ストレージを保護するために、loki-config.yamlで明示的な保持期間を設定し、古いチャンクを自動削除するように構成します:

compactor:
  working_directory: /loki/compactor
  compactor_ring:
    kvstore:
      store: inmemory
  retention_enabled: true

schema_config:
  configs:
    - from: 2024-01-01
      store: tsdb
      object_store: filesystem
      schema: v13
      index:
        prefix: index_
        period: 24h

limits_config:
  retention_period: 30d

これにより、コンプライアンス要件を満たす30日分の検索可能な監査ログを維持しながら、NVMeディスクの使用量を一定範囲内に抑えることができます。

Share: