地方エンジニアの学習日記

興味ある技術の雑なメモだったりを書いてくブログ。たまに日記とガジェット紹介。

【Linux】eBPFへ入門していく その1

背景

eBPFに興味はあったもののそれとなくしか理解してなかったのでせっかくなので概要だけでも掴もうと思って書く記事。とりあえず概要とかとよく読むカーネル関数とかをattachしてみて引数を見るみたいなのまでやってみました。

(私の事前の知識レベルとしては「eBPFを使えば、システムコール、パケットの受信など、カーネルで発生する様々なイベントに対して動かしたいコードを動かして遊べる機能だ!」くらいの感じで始めました)

eBPFとは

eBPF(extended Berkeley Packet Filter)とは任意の様々なタイミングでバイトコードを実行できるLinuxカーネルの機能です(BPFはLinux3.15に最初に追加された新機能)。カーネルソースコードを変更したり、カーネルモジュールをロードしたりすることなく、カーネルの機能を安全かつ効率的に拡張するために使用されたりする(独自の命令セット を持つカーネル内部の仮想マシン上で実行できる機能)。カーネル自体に手を入れることや、kprobeを利用するカーネルモジュールを書くことに比べ遥かに簡単に追うことができたりするという印象を持ちました。

ebpf.io

ユーザが定義したプログラムをカーネルランドで実行するための仕組みで。パフォーマンスの計測、セキュリティ、ネットワーク処理などの利用用途があります

また、BPF Compiler Collection (BCC)という、eBPFプログラムの作成や実行を手助けしてくれる便利なツールキットがあります。(ちなみにBCCが使用するものの多くは、Linux4.1以降を必要とするのでCentOS7をそのまま使うのは若干厳しいのでカーネルを上げるなり違うディストロを用意するなどが必要です)

github.com

Brendan Gregg's Blogでの言及もとても面白かったです。

www.brendangregg.com

学ぶ順番も記されており

あたりを行なっていきます。

BPF

歴史的にeBPFの前にBPFがあるとのことですが今はeBPFのことを「BPF」古いBPFのことを「cBPF」と呼ぶことが多いらしいです。BPFの初版はwikiを見てたらなんと 1992年12月とのこと。(年上だったとは...!!!)

ja.wikipedia.org

BPFのドキュメントは以下。BPF設計Q&Aは見ておいても良いかもと思った。(が、見れていないので後で見る)

www.kernel.org

eBPF Foundation

Linux Foundationをスポンサーとした、eBPFプロジェクトのための「eBPF Foundation」という団体もあるらしい。FacebookGoogle、Isovalent、MicrosoftNetflixという名だたる企業が並んでいる。

japan.zdnet.com

昨年はeBPF Summitというイベントも開催されていたらしい。(界隈の盛り上がりをとても感じる。)、eBPFのいくつかの主要なユースケースとして、可観測性、負荷分散の話なんかがされていた模様。

ebpf.io

使っていく

環境構築は以下の記事を参考にBCCでeBPFのコードを書いていく感じで進める。

gihyo.jp

何はともあれhello worldをやってみる。

#!/usr/bin/python3

from bcc import BPF

bpf_text = """
int trace_sys_clone(struct pt_regs *ctx) {
  bpf_trace_printk("Hello, World!\\n");
  return 0;
}
"""

b = BPF(text=bpf_text)
b.attach_kprobe(event="__x64_sys_clone", fn_name="trace_sys_clone")

b.trace_print()

カーネル内部で __x64_sys_clone関数が呼ばれたときに、自分で定義した trace_sys_clone関数を呼び出すという流れです。bpf_trace_printkで中身を出力しているように見えますが実際は/sys/kernel/debug/tracing/trace_pipeへ出力が行われていてb.trace_print()で中身を見にいてっています。

システムコールの入口を知る: get_syscall_fnname

github.com

b = BPF(text=bpf_text)
print b.get_syscall_fnname("inotify_init")

# sys_inotify_init

気になるbccツール

biotop.py

block device (disk) I/O をプロセスごとに表示してくれるツール

PID    COMM             D MAJ MIN DISK       I/O  Kbytes  AVGms
14501  cksum            R 202 1   xvda1      361   28832   3.39
6961   dd               R 202 1   xvda1     1628   13024   0.59
13855  dd               R 202 1   xvda1     1627   13016   0.59
326    jbd2/xvda1-8     W 202 1   xvda1        3     168   3.00
1880   supervise        W 202 1   xvda1        2       8   6.71
1873   supervise        W 202 1   xvda1        2       8   2.51
1871   supervise        W 202 1   xvda1        2       8   1.57
1876   supervise        W 202 1   xvda1        2       8   1.22
1892   supervise        W 202 1   xvda1        2       8   0.62
1878   supervise        W 202 1   xvda1        2       8   0.78
1886   supervise        W 202 1   xvda1        2       8   1.30
1894   supervise        W 202 1   xvda1        2       8   3.46
1869   supervise        W 202 1   xvda1        2       8   0.73
1888   supervise        W 202 1   xvda1        2       8   1.48

cachetop.py

読み取りを含むLinuxページキャッシュのヒット/ミス統計を表示するツール。異常に高い数値が出てるかどうかはすぐにでも見れそうなツール

13:01:01 Buffers MB: 76 / Cached MB: 114 / Sort: HITS / Order: ascending
PID      UID      CMD              HITS     MISSES   DIRTIES  READ_HIT%  WRITE_HIT%
       1 root     systemd                 2        0        0     100.0%       0.0%
     680 root     vminfo                  3        4        2      14.3%      42.9%
     567 syslog   rs:main Q:Reg          10        4        2      57.1%      21.4%
     986 root     kworker/u2:2           10     2457        4       0.2%      99.5%
     988 root     kworker/u2:2           10        9        4      31.6%      36.8%
     877 vagrant  systemd                18        4        2      72.7%      13.6%
     983 root     python                148        3      143       3.3%       1.3%
     981 root     strace                419        3      143      65.4%       0.5%
     544 messageb dbus-daemon           455      371      454       0.1%       0.4%
     243 root     jbd2/dm-0-8           457      371      454       0.4%       0.4%
     985 root     (mount)               560     2457        4      18.4%      81.4%
     987 root     systemd-udevd         566        9        4      97.7%       1.2%
     988 root     systemd-cgroups       569        9        4      97.8%       1.2%
     986 root     modprobe              578        9        4      97.8%       1.2%
     287 root     systemd-journal       598      371      454      14.9%       0.3%
     985 root     mount                 692     2457        4      21.8%      78.0%
     984 vagrant  find                 9529     2457        4      79.5%      20.5%

cpudist.py

タスクごとのオンCPU時間をヒストグラムとして表示するツール

Tracing on-CPU time... Hit Ctrl-C to end.
^C
     usecs               : count     distribution
         0 -> 1          : 0        |                                        |
         2 -> 3          : 1        |                                        |
         4 -> 7          : 1        |                                        |
         8 -> 15         : 13       |**                                      |
        16 -> 31         : 187      |****************************************|
        32 -> 63         : 89       |*******************                     |
        64 -> 127        : 26       |*****                                   |
       128 -> 255        : 0        |                                        |
       256 -> 511        : 1        |                                        |

vfscount.py

vfs_*の関数ごとのコール数を表示

ADDR             FUNC                          COUNT
ffffffff87325781 b'vfs_symlink'                    1
ffffffff8739f971 b'vfs_test_lock'                  2
ffffffff87351fd1 b'vfs_fsync_range'                2
ffffffff87354d21 b'vfs_statfs'                     3
ffffffff87323871 b'vfs_rename'                     3
ffffffff87325aa1 b'vfs_mkdir'                      4
ffffffff87323561 b'vfs_unlink'                     4
ffffffff873287b1 b'vfs_readlink'                  15
ffffffff8731a671 b'vfs_fstat'                    260
ffffffff87310c61 b'vfs_open'                     330
ffffffff873124b1 b'vfs_write'                    404
ffffffff87319601 b'vfs_statx'                    491
ffffffff873a1261 b'vfs_lock_file'                523
ffffffff873195b1 b'vfs_getattr'                  594
ffffffff873194f1 b'vfs_getattr_nosec'            594
ffffffff87312201 b'vfs_read'                    1304

vfsstat.py

vfscountをvmstatみたいに表示してくれるツール

TIME         READ/s  WRITE/s  FSYNC/s   OPEN/s CREATE/s
22:52:16:       813      372        0       66        0
22:52:17:        41       42        0       13        0
22:52:18:         3        2        0        0        0
22:52:19:        23        7        0        8        0
22:52:20:         5        6        0        0        0

memleak.py

解放されなかった未処理のメモリ割り当てをトレースツール

Attaching to pid 5193, Ctrl+C to quit.
[11:16:33] Top 2 stacks with outstanding allocations:
        addr = 948cd0 size = 16
        addr = 948d10 size = 16
        addr = 948d30 size = 16
        addr = 948cf0 size = 16
        64 bytes in 4 allocations from stack
                 main+0x6d [allocs]
                 __libc_start_main+0xf0 [libc-2.21.so]

libbpf

bccで抱える課題を解決するべく「libbpf + BPF CO-RE」という開発環境が今後は主流になるらしい。bccのツールの書き換えも実施されていて今後の動向は見守っていく必要がありそう。ただ現段階では私はbccでさっとかけるくらいで良いので方向変えたりはもう少ししばらくはしない。(ポータビリティとかバイナリサイズが減ったりランタイムオーバーヘッドが減るなどのメリットが挙げられている)

github.com

github.com

bccもある程度触ったらlibbpfgoかrustに入門して触り始めるかしても良いのだろうかな(この辺はよくわかってないので別途検討)

github.com

用途とか実例とか

いろんな会社で実例があったので読んだものとか気になってるものをメモ。

ファイアウォールデバイスドライバー、ネットワークパフォーマンス監視などなど色々な文脈で登場する技術のようです。

Cilium

Cilium は、コンテナ ワークロードのスケーラビリティ、セキュリティ、可視性に関する新たな要件に対応するため、eBPF を基礎として設計されたオープンソース プロジェクト。

cilium.io

おまけ

/sys/kernel/debug/kprobes/list

/sys/kernel/debug/kprobes/listを見るとsystem :: probeに登録されているすべてのプローブを一覧表示することができます。

cat  /sys/kernel/debug/kprobes/list
ffffffffaa8af6d0  k  tcp_init_sock+0x0    [FTRACE]
ffffffffaa3cb990  k  do_writepages+0x0    [FTRACE]

参考記事