こんにちは!現場のインフラやアプリのパフォーマンスチューニングで、こんな悩みを抱えたことはありませんか?
「CPU使用率は低いのに、なぜかAPIの応答速度が異常に遅い…」
「ログを見てもエラーが出ていない。一体、裏で何が起きているんだ?」
従来のプロセス監視や、外形監視だけを頼りにしていると、カーネル内部(OSの深部)で起きてきている「本当のボトルネック」を見落としてしまいます。
今回は、Linuxカーネルの超新星「eBPF(Extended Berkeley Packet Filter)」と、古くからの監視の王様「Zabbix」を組み合わせて、カーネルレベルの深淵を覗き見る「次世代オブザーバビリティ」の世界へあなたをご案内します。
これをマスターすれば、今まで「神のみぞ知る」だったカーネルの挙動が手に取るように分かり、毎日の障害調査やパフォーマンスチューニングが劇的に楽になりますよ。肩の力を抜いて、一緒に進んでいきましょう!
—
1. なぜ「Zabbix × eBPF」なのか? 次世代監視のパラダイムシフト
従来の監視の限界
私たちが普段使っているZabbixエージェントや一般的な監視ツールは、OSから見れば「ユーザー空間(User Space)」で動く一つのプロセスに過ぎません。CPU使用率、メモリ空き容量、ディスクI/Oといった「マクロな統計情報」を取るには十分ですが、次のような疑問には答えられません。
- 「どのプロセスが、どのファイルの読み込みでカーネル内部で待たされているのか?」
- 「ネットワークのパケットが、どのシステムコール処理の段階でドロップしたのか?」
eBPFという「カーネルの特等席」
そこで登場するのが eBPF です。eBPFを使うと、Linuxカーネルのソースコードを書き換えることなく、カーネルの安全なサンドボックス内で任意のコード(BPFプログラム)を動かすことができます。
カーネルが動くその瞬間(システムコールの呼び出し、ディスクへの書き込み、パケットの送受信など)に割り込み、データを集められるのです。
組み合わせる最強のメリット
- eBPFの役割: カーネルの奥深くから、超高精度で軽量な「生データ(システムコールのレイテンシなど)」をリアルタイムに抽出する。
- Zabbixの役割: そのデータを集約し、時系列で可視化し、閾値を超えたらSlackやPagerDutyへアラートを飛ばす。
この2つを掛け合わせることで、「カーネルの深部まで見通せる、死角のない監視基盤」が完成します。
—
2. 実践:BPFプログラムでカーネルメトリクスを抽出し、Zabbixへ流し込む
百聞は一見にしかず。実際に手を動かして、カーネル内でファイルが開かれるレイテンシ(応答速度)を測定し、Zabbixに送り込んでみましょう。
今回は、比較的新しいLinuxカーネル(5.x以降)と、PythonのeBPFフロントエンドである `bcc` (BPF Compiler Collection)、そしてZabbix Agent 2のユーザーパラメータ機能を使います。
ステップ1: 必要なパッケージのインストール(Ubuntuの例)
まずは、eBPFを動かすためのツールチェーンとZabbixエージェントをインストールします。
カーネルヘッダーとBCCツールのインストール
sudo apt-get update
sudo apt-get install -y bpfcc-tools python3-bpfcc libbpfcc-dev linux-headers-$(uname -r)
Zabbixエージェント2のインストール(公式リポジトリ設定済みルーズとして)
sudo apt-get install -y zabbix-agent2
ステップ2: カーネルのシステムコールをフックするPythonスクリプトの作成
今回は例として、ファイルを開くシステムコール `sys_enter_openat` をフックし、カーネル内でどれだけ時間がかかったかを測定するシンプルなeBPFスクリプトを作成します。
`/etc/zabbix/scripts/vfs_latency.py` として保存してください。
!/usr/bin/env python3
from bcc import BPF
import time
1. eBPF(C言語のコード)を定義 BPF_HASH(start, u32, u64); struct data_t { // openatシステムコールの開始時 // openatシステムコールの終了時(リターン時) tsp = start.lookup(&pid); // 閾値(例: 1000マイクロ秒以上)を超えた遅延のみを収集することも可能 events.perf_submit(ctx, &data, sizeof(data)); 2. BPFプログラムのコンパイルとアタッチ 3. データを集計して最新の最大遅延を出力する簡易ループ def print_event(cpu, data, size): b[“events”].open_perf_buffer(print_event) 1秒間だけカーネルイベントをキャッチして、最大レイテンシを出力して終了 Zabbixエージェントが、先ほどのPythonスクリプトを定期的に叩いて値を取得できるように設定ファイルを追加します。 `/etc/zabbix/zabbix_agent2.d/ebpf_monitor.conf` を作成: UserParameter構文: UserParameter=<キー名>,<実行するコマンド> > Security Note: Zabbixエージェントの実行ユーザー(通常 `zabbix`)がeBPFプログラムをロードするためには適切な権限(`CAP_SYS_ADMIN`など)が必要です。検証環境では `sudoers` に `zabbix ALL=(root) NOPASSWD: /etc/zabbix/scripts/vfs_latency.py` を設定しておいてください。 設定を反映するため、Zabbixエージェントを再起動します。 sudo systemctl restart zabbix-agent2 Zabbixの管理画面(Web UI)を開き、データが正しく取得できているか確認しましょう。 1. ホストの設定 -> 対象ホストの 「アイテム」 を選択。 3. 「追加」して保存。 おめでとうございます!これで、あなたのZabbixはカーネルのシステムコールレベルのレイテンシを直接監視する次世代システムへと進化しました。 — ここで、現場で本当に役立つ「eBPF監視が救ったインシデント」の話をしましょう。 あるマイクロサービス基盤で、特定のWeb APIのレスポンスが、数時間に1回、数秒間だけ「プチフリーズ」するように遅延するという不具合が発生していました。 「なぜだ……原因が全くわからない」とチーム全体が途方に暮れていました。 そこで今回紹介したような、eBPFを用いた「システムコール単位のレイテンシ分布(ヒストグラム)監視」をZabbixに組み込みました。 具体的には、ディスクへの書き込みを行う `sys_enter_fsync` システムコールのレイテンシをミリ秒単位ではなく、マイクロ秒単位でZabbixに集約し、スパイク(異常な跳ね上がり)を検知できるようにしたのです。 ある日、再びあのプチフリーズが発生しました。Zabbixのグラフがピクッと跳ね上がります。 eBPFが暴いた真犯人は、「特定のコンテナが、ログローテーションの瞬間に誤って巨大なファイルを同期書き込み(`fsync`)し、その背後でLinuxカーネルのI/Oサブシステム全体のロック(bdflush/jbd2)を数秒間ブロックしていた」という現象でした。 ディスク全体の平均I/O使用率は低かったため、従来の `iostat` 監視では絶対に気づけませんでした。しかし、カーネル内部のシステムコールレイテンシを直接測るeBPFならば、その「一瞬のつまり(渋滞)」を正確に捉えることができたのです。 原因がわかってしまえば対策は簡単です。該当プロセスのI/O優先度(ionice)を下げ、バッファリング戦略を見直すことで、不具合は一瞬で解消されました。 — いかがでしたでしょうか? この2つを組み合わせることで、もはや「原因不明のボトルネック」に怯える必要はなくなります。「見えないところが見えるようになる」この快感を、ぜひあなたの現場でも体験してみてください。 毎日の運用監視とトラブルシューティングが、驚くほどクリアで楽しいものに変わるはずです。それでは、次回の次世代オブザーバビリティ解説でお会いしましょう!
openatシステムコールが呼ばれた時刻を記録し、戻ってきたときに差分(レイテンシ)を計算する
bpf_text = “””
include
include
BPF_PERF_OUTPUT(events);
u32 pid;
u64 delta_us;
char comm[TASK_COMM_LEN];
};
int trace_open_entry(struct pt_regs ctx) {
u32 pid = bpf_get_current_pid_tgid() >> 32;
u64 ts = bpf_ktime_get_ns();
start.update(&pid, &ts);
return 0;
}
int trace_open_return(struct pt_regs ctx) {
u32 pid = bpf_get_current_pid_tgid() >> 32;
u64 tsp, delta;
if (tsp != 0) {
delta = (bpf_ktime_get_ns() – tsp) / 1000; // マイクロ秒に変換
struct data_t data = {};
data.pid = pid;
data.delta_us = delta;
bpf_get_current_comm(&data.comm, sizeof(data.comm));
start.delete(&pid);
}
return 0;
}
“””
b = BPF(text=bpf_text)
b.attach_kprobe(event=b.get_syscall_fnname(“openat”), fn_name=”trace_open_entry”)
b.attach_kretprobe(event=b.get_syscall_fnname(“openat”), fn_name=”trace_open_return”)
max_latency = 0
global max_latency
event = b[“events”].event(data)
if event.delta_us > max_latency:
max_latency = event.delta_us
start_time = time.time()
while time.time() – start_time < 1.0:
b.k_perf_buffer_poll(timeout=100)
print(max_latency)
実行権限を付与します。
sudo chmod +x /etc/zabbix/scripts/vfs_latency.py
ステップ3: Zabbixエージェントからの連携設定
UserParameter=kernel.vfs.open.max_latency,sudo /etc/zabbix/scripts/vfs_latency.pyステップ4: Zabbixフロントエンドでの「HelloWorld」確認
2. 「作成」 をクリックして以下のように設定:
4. 「監視データ」 -> 「最新データ」 から、今設定したアイテムの値がグラフ化されていることを確認してください。3. 【実例】従来のプロセス監視では検知できない超低レイテンシ障害の特定
起こっていた現象
eBPF × Zabbixの導入によるブレイクスルー
暴かれたボトルの正体
まとめ