eBPF入門――Linuxカーネルを安全に拡張する技術がクラウドの監視・セキュリティ・オブザーバビリティをどう変えるのか
はじめに
Kubernetes上でマイクロサービスを運用していると、PodのIPアドレスが頻繁に入れ替わる環境では、iptablesベースのネットワーク制御のルール数が増大して更新コストが問題になったり、プロセス単位で動くエージェント型の監視の網羅性に限界が生じたりするケースがあります。「どのコンテナがどのファイルを開いたのか」「どのサービス間でレイテンシが発生しているのか」を、なるべく低いオーバーヘッドで知りたい——そんな課題に対して、いまクラウドインフラの根幹で使われているのが eBPF(extended Berkeley Packet Filter) です。
eBPFとは一言でいえば、Linuxカーネル内で安全にサンドボックス化されたプログラムを実行できる技術です。カーネルのソースコードを変更したりカーネルモジュールをロードしたりすることなく、カーネルそのものを「プログラマブル」にできます(ebpf.io公式)。よく使われる比喩は、「eBPFはLinuxカーネルに対して、JavaScriptがブラウザにもたらしたのと同じ変化をもたらす」 というもの。静的な存在だったカーネルが、誰でも安全に拡張できるプラットフォームへと変わった、という意味です。
本記事では、eBPFの仕組みを図解したうえで、監視(オブザーバビリティ)・セキュリティ・ネットワーキングの3分野での活用を、Cilium・Falco・Pixieなどの具体例とともに解説します。Linuxや分散システム、ホームラボの運用に興味があるエンジニアの方に読んでいただきたい入門記事です。
eBPFとは何か――仕組みの図解
eBPFプログラムは イベント駆動 で動きます。カーネルやアプリケーションが特定の フックポイント(アタッチポイント) を通過した瞬間に実行される仕組みです。主なフックには、NICにパケットが到着した直後に最速で処理できる XDP、トラフィックコントロールの tc、カーネル内のほぼ任意の関数にアタッチできる kprobe、ユーザー空間の関数用の uprobe、安定したトレースポイントである tracepoint、セキュリティフックの LSM などがあります。さらにLinux 6.12では sched_ext がメインライン入りし、eBPFでスケジューラ自体を実装できるまで適用範囲が広がっています。
プログラムがカーネルで動くまでの流れは次のとおりです。
flowchart LR
A[Cソース] -->|clang/LLVM| B[eBPFバイトコード]
B -->|bpf syscall| C{Verifier}
C -->|安全でない| X[拒否]
C -->|合格| D[JITコンパイル]
D --> E[ハードニング]
E --> F[フックにアタッチ]
F <-->|eBPFマップ| G[ユーザースペース]
C風のコードをclang/LLVMでeBPFバイトコードにコンパイルし、bpfシステムコールでカーネルにロードします。ここで最大の特徴が Verifier(検証器) による静的検証です。ロード時に「プログラムが必ず有限時間で完了すること(bounded loops)」「未初期化変数の使用やメモリ境界外アクセスをしないこと」「複雑性の上限内であること」などが証明され、不合格ならロード自体が拒否されます。これにより、代表的なクラッシュ要因(未定義動作・境界外アクセス・無限ループ)がロード時の段階で排除され、カーネルモジュールよりも高い安全性が得られます。ただしVerifierは安全性の検証であり、プログラムの動作内容の正当性を保証するものではありません。なお公式ドキュメントは、Verifierは「安全性ツール」であってプログラムの「何をしているか」を検査するセキュリティツールではない、と明記しており、ロードにはrootやCAP_BPFといった特権の管理が重要です。検証を通過したプログラムは JITコンパイル によりCPU固有の命令に変換され、ネイティブコードに近い速度で実行されます(実際の性能はプログラム内容とフック頻度に依存します)。
カーネル内のプログラムとユーザースペースの間でデータをやり取りするのが eBPFマップ です。ハッシュテーブル・配列・LRU・Ring Buffer・LPM(最長一致)などのデータ構造があり、プログラム間の状態共有やユーザー空間からのシステムコール経由での読み書きに使われます。この「カーネルで収集・フィルタし、マップ経由でユーザー空間に渡す」構造こそが、低オーバーヘッドなオブザーバビリティを実現する鍵です。
主要プロジェクト比較(表)
eBPFと一口に言っても、その上に構築されたプロジェクトは分野ごとに大きく異なる性格を持っています。ここでは代表的なプロジェクトを比較表にまとめます。
| プロジェクト | 分野 | 仕組み | 主な用途 | 特徴 |
|---|---|---|---|---|
| Cilium(+Hubble/Tetragon) | ネットワーキング/セキュリティ/監視 | CNI + eBPFデータプレーン | K8sネットワークポリシー、kube-proxy置換 | アイデンティティベースのL3〜L7ポリシー、ClusterMesh、Hubbleによるフロー可視化 |
| Falco | ランタイムセキュリティ | eBPFプローブ(CO-RE)でsyscall監視 | 侵入検知・コンプライアンス | CNCF卒業、YAMLルールエンジン、特権昇格やシェル実行をリアルタイム検知 |
| Pixie | オブザーバビリティ | 動的eBPFプローブによる自動計装 | K8sのメトリクス/トレース/ログ | コード変更不要、クラスタ内完結、PXLスクリプトでdebug as code |
| bpftrace | トレーシング | 高レベル言語→LLVM→eBPF | 一時的な性能解析・ワンライナー | awk/DTrace風の記法、kprobe/uprobe/tracepoint対応 |
| Katran | ネットワーキング | XDPベースのロードバランサ | 高性能L4ロードバランシング | XDPによる最速パスでのパケット処理 |
| Parca | オブザーバビリティ | eBPFによる継続的プロファイリング | 本番でのCPU/メモリプロファイル収集 | 低オーバーヘッドで常時プロファイリング |
| BCC | トレーシング | Python+C、多数の実用ツール同梱 | 本番性能解析の定番ツール群 | 大規模本番での活用実績で知られる、opensnoop/biolatencyなど |
Cilium は、従来のiptablesがIP/ポートベースであり、エンドポイントが頻繁に入れ替わるマイクロサービス環境ではルール数の増大により更新コストが課題になりうるという問題意識から生まれました。IPではなくサービス/Pod/コンテナのアイデンティティでポリシーを適用し、HTTPメソッドやURLパス、gRPCまでL7レベルでの制御を実現しています。kube-proxy置換の負荷分散、XDP/DSR/MaglevによるNorth-Southトラフィック処理も特徴で、この実績はGKEのDataplane V2への採用につながり、大規模Kubernetesクラスタにおけるiptablesのスケール課題への現実的な選択肢となっています。Hubbleはその上に乗る可視化レイヤーで、サービス依存グラフやp95/p99レイテンシ、ポリシー拒否をPrometheus/Grafana連携で見せられます。
Falco はCNCFを卒業したランタイムセキュリティの代表格です。システムコールをリアルタイムにパースし、YAMLルールで評価してアラートを出します。現行の既定ドライバはModern eBPFプローブ(CO-RE)ベースに移行しており、CO-REが実用の中心になっていることがうかがえます。Pixie はNew Relicから寄贈されたCNCFプロジェクトで、コード変更ゼロで動的eBPFプローブによる自動計装を行い、テレメトリをクラスタ外に転送しない設計で転送コストを回避します。
bpftrace はawk/DTrace風の高レベル言語でワンライナーから書ける手軽さが強みです。Katran と Parca はそれぞれXDPによるロードバランシング、eBPFによる継続的プロファイリングと、eBPFの適用範囲の広さを示す好例です。
なお、CO-RE/BTF(Compile Once, Run Everywhere)により、BTF情報を持つカーネル間では、一度コンパイルしたプログラムを再コンパイルなしで動かせる移植性が高まっています(カーネルの型情報や有効な機能が前提条件になります)。さらに sched_ext —— eBPFでLinuxスケジューラ自体を実装・差し替え可能にする仕組み —— がLinux 6.12でメインライン入りし、Steam Deckのような特定ワークロード向けのスケジューラ調整にも活用が進んでいます。eBPFの適用範囲はネットワークと監視を超え、カーネルの中枢へと拡大し続けているのです。
実践ステップ――bpftraceで今日からカーネルを観測する
それでは、実際にeBPFを体験してみましょう。Linuxマシン(またはRaspberry Pi 5のようなaarch64マシン)があれば、そのままFollowできます。
- 対象読者: Linux・ホームラボ運用者
- 必要環境: Linux Kernel 4.x以降(機能によっては5.x以降推奨)、root権限、BTFが有効なカーネル
1. 環境確認(カーネル / BTFの確認)
まず、お使いの環境がeBPF(bpftrace)に対応しているかを確認します。
# カーネルバージョンの確認
uname -r
# BTFが有効か確認(/sys/kernel/btf/vmlinux が存在すればOK)
ls -l /sys/kernel/btf/vmlinux
/sys/kernel/btf/vmlinux が存在すればBTF(BPF Type Format)が有効なカーネルの目安となり、CO-RE対応バイナリが動作する可能性が高くなります(利用する機能によってカーネルバージョン要件は異なります)。Raspberry Pi OS(64bit)の最近のカーネルではBTFが有効になっているため、Raspberry Pi 5(aarch64)でもbpftraceを試せます。実際の対応状況はお使いのカーネルとbpftraceのバージョンをご確認ください。
2. bpftraceのインストール
多くのディストリビューションではパッケージマネージャから導入できます。
# Debian / Ubuntu / Raspberry Pi OS
sudo apt update && sudo apt install bpftrace
# バージョン確認
bpftrace --version
Raspberry Pi 5(aarch64アーキテクチャ)でも、標準リポジトリからそのままインストール可能です。aarch64はJITコンパイラが対応しているアーキテクチャの一つであり、x86-64と同様にネイティブ命令へ変換されて実行されるため、「ARMだから遅い・動かない」ということはありません。ホームラボのK8sノード上での観測にもそのまま活用できます。
3. 最初の一歩(Hello World)
sudo bpftrace -e 'BEGIN { printf("hello eBPF\n"); }'
hello eBPF と表示されれば、eBPFプログラムのロード→Verifierによる検証→JITコンパイル→実行、という一連の流れが正常に動作しています。
4. システムコール集計(プロセス別のファイルオープン集計)
より実用的な例として、どのプロセスがどれだけの頻度でファイルをオープンしているかを集計します。
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_openat { @[comm] = count(); }'
@[comm] = count() というマップ(BPFマップのひとつ)にプロセス名ごとのオープン回数がカウントされていきます。数秒待ってから Ctrl+C で終了すると、プロセス名ごとの集計結果が表示されます(以下は実行例のイメージです。環境により結果は異なります)。
@[systemd]: 12
@[bash]: 3
@[bpftrace]: 1
アプリケーション側に一切変更を加えることなく、カーネルレベルのイベントをリアルタイムに集計できました。これはeBPFの「比較的低いオーバーヘッドで継続的なトレーシングができる」という特徴を体感できる最初の例です(本番での常時実行には事前のオーバーヘッド計測が推奨されます)。
5. 発展: レイテンシ計測とプローブの一覧確認
慣れてきたら、kprobe/kretprobeを組み合わせたレイテンシ計測に挑戦してみましょう。
# read()の所要時間ヒストグラム
sudo bpftrace -e 'kprobe:vfs_read { @start[tid] = nsecs; } kretprobe:vfs_read /@start[tid]/ { @ns[comm] = hist(nsecs - @start[tid]); delete(@start[tid]); }'
また、フック可能なプローブの一覧は bpftrace -l で確認できます。tracepoint、kprobe、uprobeなど、どのイベントにアタッチできるかを眺めてみると、観測の対象範囲の広さを実感できるはずです。
6. 注意点と次のステップ
- Verifierログの読み方: 複雑なプログラムを書くと、Verifierに拒否されることがあります。ログを丁寧に読み、無限ループや未初期化変数の使用がないか確認しましょう。
- カーネルバージョン差異: カーネル間の構造体差異はCO-REで解消されますが、利用可能なフックポイントや機能はカーネルバージョンによって異なります。
- 本番投入時はオーバーヘッド計測: eBPFは低オーバーヘッドですが、フック頻度の高い箇所に設定する場合は必ず事前にオーバーヘッドを計測しましょう。
次のステップとしては、70以上の実用ツールが同梱されたBCC(biolatency、tcpretrans など)の活用や、libbpf(C)・cilium/ebpf(Go)・aya(Rust)を使った自作eBPFプログラムの開発へ進むと、観測だけでなくネットワーク処理やセキュリティ強制への応用も可能になります。
ケーススタディ――eBPFが選ばれた現場の理由
Ciliumによるiptables置換え
従来のkube-proxyはiptablesベースで、エンドポイントが頻繁に入れ替わる大規模なマイクロサービス環境ではルール更新のスケールが課題として指摘されてきました。CiliumはeBPFデータプレーンでこれを置き換えます。East-Westトラフィックはsocketレベルのconnect()書き換えでロードバランシングし、North-SouthはXDP/DSR/Maglevで処理。さらにIPベースではなくサービス/Pod/コンテナのアイデンティティでL3〜L7のポリシーを適用でき、例えば「/public/.*へのGETのみ許可」といったHTTPパス/メソッド単位の制御が可能です。Hubbleを合わせれば、サービス依存グラフ、DNS失敗率、HTTPエラー率、p95/p99レイテンシ、ポリシー拒否を可視化できます。
Falco・Tetragon的ランタイムセキュリティ
Falco(CNCF卒業、Sysdig作成)はLinuxシステムコールをモダンeBPFプローブ(CO-RE)でリアルタイムにパースし、YAMLルールエンジンで評価。特権昇格、/etcや/usr/binへの不正書き込み、シェルやSSHバイナリの実行などを検知した瞬間にアラートを出し、falcosidekickでSIEM等へ転送できます。カーネルモジュール不要のCO-REドライバがデフォルトになったことで、導入障壁も下がっています。
Pixieの自動計装とVerifier拒否との戦い
Pixieはコード変更ゼロで動的eBPFプローブによりHTTPトレースを含むメトリクス・ログを自動取得し、テレメトリをクラスタ外へ転送しない設計で、大規模環境での転送コスト増大を抑える設計思想です。ただし自作プローブを書く際はVerifierとの戦いが付き物です。Verifierはロード時に「必ず有限時間で完了するか」「境界外アクセスや未初期化変数はないか」を全実行パスで証明できないプログラムを拒否します。実践のコツは、ループに明示的な上限を設け、複雑性上限内に収め、拒否されたらログの解析結果に従ってプログラムを小さく分割することです。
よくある質問(FAQ)
eBPFは安全なのか? カーネルをクラッシュさせたりしない?
Verifierがロード時に、有限時間での完了と禁止操作の非実行を静的検証し、合格後にJITコンパイルとハードニング(メモリ読み取り専用化、Spectre緩和など)で多層防御します。ただしVerifierは安全性ツールであり、プログラムの「何をしているか」を検査するセキュリティツールではないため、ロード権限(root/CAP_BPF)の管理が重要です。
eBPFを使うにはカーネルのアップデートや再起動が必要?
不要なのが最大の利点で、ランタイムでカーネル機能を拡張できます。ただし利用できる機能はカーネルバージョンに依存し、CO-REとBTFにより、BTF情報を持つカーネル間での移植性は大幅に緩和されています(カーネル側の型情報が前提です)。
Cilium・Falco・Pixieはどう使い分ける?
CiliumはネットワーキングとL3〜L7ポリシー、Falcoはホスト/コンテナの異常挙動検知、Pixieはアプリの自動計装された監視が担当です。三者は競合ではなく補完関係にあり、同じKubernetesクラスタに併存できます。
WindowsやmacOSでも使える?
eBPF自体はLinuxカーネル技術です。ただしlibbpfやaya(Rust)などのツールチェーンはクロスプラットフォームで、macOSでの開発は可能です。Microsoftのebpf-for-windowsという別実装も存在しますが、本記事の範囲外です。
まとめ
eBPFは「カーネルを安全に再プログラムする」技術として、監視・セキュリティ・ネットワーキングの常識を塗り替えました。Verifier/JIT/マップという土台の上に、Cilium(ネットワーク)、Falco(セキュリティ)、Pixie/bpftrace/BCC(オブザーバビリティ)というエコシステムが育ち、CO-RE/BTFやsched_ext(Linux 6.12メインライン入り)により適用範囲は拡大し続けています。クラウドネイティブ時代のインフラエンジニアにとって、eBPFの理解は重要なスキルになりつつあります。
今日から試せる3アクション:
sudo apt install bpfcc-toolsしてopensnoopを実行し、ファイルオープンを可視化する- bpftraceのワンライナーでsys_enter_openatをトレースし、eBPFプログラムの動きを体感する
- kind/minikubeにCilium + Hubbleを導入し、サービスマップの可視化を体験する
まずはopensnoop一発から始めてみてください。
参考文献・出典
- ebpf.io(公式ポータル): https://ebpf.io/
- LinuxカーネルBPFドキュメント: https://docs.kernel.org/bpf/
- Ciliumドキュメント: https://docs.cilium.io/
- Falco: https://falco.org/
- Pixie: https://px.dev/
- bpftrace: https://github.com/bpftrace/bpftrace
- sched_ext (scx): https://github.com/sched-ext/scx
- Brendan Gregg, BPF Performance Tools: https://www.brendangregg.com/bpf-performance-tools-book.html