なぜ Vector を用いたか?
大量のパケットをリアルタイムで解析し、後段の BigQuery で解析できる形にして保存しておきたい。でも、Kafka を導入してシステムを大きく複雑にすることはしたくないし、パケット解析に必要な処理ロジックも Java などでコンシューマを書かないといけないほど複雑なものでもない。 ──
そんな状況で”ちょうどよい”選択肢になったのが、パケットのバイト列を Lua で解析させることもできる「Rust 製の高速なログ/イベント処理エージェント Vector」 でした。
この記事では、Vector × Lua で秒間 50 万パケット以上を処理したアーキテクチャと、その裏側にある設計・実装のポイントを紹介します。
Vector の概要と特徴
Vector は Rust で実装された高性能なログ/イベント処理エージェントです。高速・軽量でありながら堅牢、かつ柔軟性の高いアーキテクチャを備えています。
1. Source / Transform / Sink の明確な三分割
Vector は後発ツールならではの、よく整理された設計になっており、ログ/イベント処理に関わる各処理が下記の 3 つのコンポーネントに整理されていて分かりやすくなっています:
- Source:入力
- Transform:変換
- Sink:出力
例えば、複数行に渡るログの認識と切り出しは Source で行い、フィルタリングや特定の項目の抽出、加工や整形といった処理は Transform に集約され、バッファリングは Sink が担当するなど、役割の境界が明確です。この役割分担により、ログ/イベント処理パイプラインの構造を直感的に理解しやすく、設定の見通しが良くなります。
2. Rust 製による高速性とメモリ安全性
Vector は 公式で対応するSource、Transform、Sinkの種類も多く、他のログフォワーダー(fluentbit / fluentd など)と機能面で遜色ないだけでなく、Rust による高速性とメモリ安全性を武器により堅牢で信頼性の高いランタイムを提供しつつ、大量データのリアルタイム処理を低コストで実現できる点が大きな特長です。
Sink における一時的なバッファリングはメモリベース、またはディスクベースのいずれも選択でき、バーストがあるログ/イベントに対しても一時的な滞留で耐えることができます。
また、各コンポーネントが提供する送達保証のレベルもコンポーネントごとに明記されており、Pull 型の Source コンポーネントや Sink コンポーネントの多くで自動リトライに対応しています。
3. ログ転送だけでなく、リアルタイムなイベント処理にも”ちょうどよい”
Kafka は圧倒的なスケールと再処理の柔軟性を持ちますが、構成が重く、導入・運用コストも高くなりがちです。一方、Vector は Kubernetes 上で簡単に水平スケール可能で、Helm チャート等を使えば数分でデプロイできる手軽さが魅力です。バーストもある大量イベントをリアルタイムに処理したいが、Kafka のような大規模分散基盤までは不要 ─ そんな場面で、Vector は現場に“ちょうどよい”選択肢になります。
Vector は Rust の非同期モデルにより、高スループットを維持しながら複数のログ/イベント処理パイプラインを 1 つの Vector ポッドで並行に実行することができます。
この Vector ポッドを HPA などを使い、Kubernetes 上で水平スケールさせることで、シンプルな構成で大量のデータ処理が可能になります。
4. 「高速・堅牢」と「拡張性と柔軟性」の両立
また、「高速・堅牢」に加えて「拡張性と柔軟性」も両立しやすい点も特徴です。Transform として Lua スクリプトを組み込むことで、ログ/イベント処理の途中で任意のロジックを差し込みやすく、業務固有のフォーマット解析や変換を自在に実装できます。
こちらについては、本記事で紹介する案件で Lua Transform を用いてパケットアナライザのような処理を実現していますので、詳しく後述します。
本記事で紹介する適用事例
本記事で紹介する案件では、「オンプレミスのネットワーク上に大量に流れる、ある種類のパケットを収集し、後から解析できる形で保存したい」というニーズに応えるために Vector を採用しました。オンプレミスのネットワークから継続的に流れてくるパケットの中から、必要なフィールドを抽出し、BigQuery で扱いやすい JSON/CSV 形式に整形して蓄積しておくために Vector を使用しています。
Kafka を導入するほどの複雑な基盤は不要であり、パケットを解析するためにアプリ側で Java や Go などのコンシューマを書くほどでもない ──。Vector で Lua を用いれば、パケットヘッダやペイロードからのフィールド抽出は十分に実現可能であり、「受信したパケットをその場で解析し、必要な項目を整形して保存する」という要求に対して Vector は最適でした。
実際、Vector のディスクベースのバッファリングは、バーストのある一時的な滞留にも対応でき、構成を大きく複雑化させることなく、必要十分な信頼性とスループットを確保することができました。
処理方式の構成
適用案件における全体的な構成は、オンプレミス側の装置から送出されるパケットを GRE トンネルで Google Cloud 側に運び、GCE VM 上の Go プログラムで GRE を終端したあと、GKE 上の Vector に渡して処理する、という流れになっています。具体的には、オンプレ装置 → GRE トンネル → GCE VM 上の Go → GKE 上の Vector → Cloud Storage → BigQuery 外部テーブル、という 1 本のデータフローです。
GCE VM 上では gopacket を利用した Go プログラムを動作させていますが、ここで行う処理はあくまで「GRE ヘッダーを剥がして中の UDP パケットを取り出す」ことに限定しています。抽出した UDP パケットはペイロードには手を加えず、送信先アドレスのみを GKE 上の Vector Service に向けて書き換え、UDP ソケット経由でそのまま転送します。これにより、GRE トンネルの制約上、スケールアウトできない GCE 側の VM には GRE トンネル終端としての最小限の責務だけを持たせてできるだけ軽くし、パケット内容の解析やログ形式の変換といった複雑で変更が入りやすいロジックは、水平スケールさせやすい後段の Vector に集約する設計としています。
GKE 上の Vector は、ディスクバッファを利用する前提で StatefulSet としてデプロイしつつ、HPA によるオートスケールが効くように構成しています。各 Pod は PVC をバッファとして利用し、一時的に下流への書き込みが追いつかない場合でもイベントを保持しながら自動リトライが行えるようになっています。設計上は各 StatefulSet ごとに数 GB 程度の滞留であれば余裕を持って耐えられるバッファ容量にしていますが、Vector 自体の処理性能が高いため、実際にそこまでバッファが溜まるケースはほとんどありません。適用した案件では、秒間 50 万パケット以上のトラフィックを問題なく捌けることを確認しており、過度に複雑なキューイング基盤を導入しなくても、シンプルな構成で高スループットなパイプラインを実現できています。
Vector 内部のイベント処理パイプラインの構造は、一部簡略化すると下図のような構成にしました。
まとめ ー 後編(実践編)へ続く
今回紹介した構成では、Kafka のような大規模ストリーム基盤を使わずとも、Vector と Lua だけで秒間 50 万パケット規模の解析処理を実現できました。Rust 製の高速・堅牢なランタイムと、Source/Transform/Sink の明快な構造により、パイプライン全体をシンプルかつ拡張可能な形で組める点が、Vector の大きな強みです。
特に、「単一イベント内で完結する解析」や「バイトレベルの加工を含む軽量な整形処理」 であれば、専用アプリを用意したり複雑な分散基盤を構える必要はありません。Vector の Lua Transform を活用することで、要求に対して“必要十分”で、なおかつ運用しやすいパイプラインを実装することができます。
一方で、複数イベントの集約・相関や外部システムとの連携など、イベント境界を跨ぐ処理には専用アプリケーションや別基盤が必要になる場合があります。適材適所で Vector を活用すれば、「Kafka ほど重くなく、Fluent 系より柔軟な」ちょうどよいイベントストリームの処理基盤を実現できるはずです。
続く後編(実践編)では、Vector を触ったことがある人ほど「そこが困るんだよ!」と感じるポイントや、これから使う人にとっては“先回りして回避すべき落とし穴”について詳しく紹介します。


