🐝

入門 eBPF 番外編:bpftrace + systemdでexecveログを常時収集する

に公開
2025/12/17

この記事は ↓ の続きです。

前回は、

  • execve.bt を作る
  • sudo bpftrace execve.bt を叩いて、短時間だけログを眺める

というお試し用の使い方でした。

今回はそれを一歩進めて、

bpftrace + systemd で、execve のログを常時取りっぱなしにする

ためのセットアップをまとめます。

最後に、

  • 「ログを止めたいときどうするの?」
  • 「ログファイルを消したいときどうするの?」

もまとめてあります!

1. 今回のゴール

  • VM のどこかに execve.bt を置いている
  • systemd のサービスとして bpftrace を登録
  • VM 起動時に自動で bpftrace が動き始める
  • ログは /var/log/bpftrace/execve_YYYYMMDD.log みたいな日付付きファイルに追記されていく
  • 止めたいときは systemctl stop で一発停止できる

※パスやファイル名は自分の環境に合わせて変えてください!

ここでは例として、

  • スクリプト → /opt/watchlog/bpftrace/execve.bt
  • ログディレクトリ → /var/log/bpftrace/

という前提で書きます!

2. 前提 execve.bt を用意する

前回の記事のおさらいです。

sudo mkdir -p /opt/watchlog/bpftrace
sudo vim /opt/watchlog/bpftrace/execve.bt
execve.bt
tracepoint:syscalls:sys_enter_execve
{
    time("%Y-%m-%d %H:%M:%S ");
    printf("pid=%d comm=%s filename=%s\n", pid, comm, str(args->filename));
}

保存したら、念のため権限も整えておきます(ここでは root 実行前提)

sudo chown -R root:root /opt/watchlog
sudo chmod -R 755 /opt/watchlog

3. ログ出力先ディレクトリを作る

ログ置き場として /var/log/bpftrace を作ります。

sudo mkdir -p /var/log/bpftrace
sudo chown root:root /var/log/bpftrace
sudo chmod 755 /var/log/bpftrace

今回はシンプルに

  • root が bpftrace を叩く
  • /var/log/bpftrace/ 配下にログを追記
    という想定で行います。

4. bpftrace を起動するラッパースクリプトを作る

日付ごとにログファイルを分けるために、bash スクリプト側でファイル名を決めるようにします。

sudo vim /usr/local/bin/bpftrace-execve.sh

中身の例

#!/usr/bin/env bash
set -euo pipefail

LOG_DIR=/var/log/bpftrace
mkdir -p "$LOG_DIR"

# 起動した日付ごとにログファイルを分ける
LOG_FILE="$LOG_DIR/execve_$(date +%Y%m%d).log"

# ここで bpftrace 実行(標準出力+標準エラーをファイルに追記)
exec /usr/bin/bpftrace /opt/watchlog/bpftrace/execve.bt >> "$LOG_FILE" 2>&1

保存したら、実行権限を付けます。

sudo chmod +x /usr/local/bin/bpftrace-execve.sh
sudo chown root:root /usr/local/bin/bpftrace-execve.sh

ポイントはこの行です

LOG_FILE="$LOG_DIR/execve_$(date +%Y%m%d).log"
  • $(date +%Y%m%d) が bash 上で展開 されるので
    例 >> /var/log/bpftrace/execve_20251217.log

"$LOG_FILE" 2>&1 で標準出力・標準エラーをそのファイルに追記します。

5. systemd ユニットファイルを作る

次に、systemd のサービスとして登録します。
/etc/systemd/system/bpftrace-execve.service を作成します。

sudo vim /etc/systemd/system/bpftrace-execve.service
[Unit]
Description=bpftrace: log execve() system calls
After=network.target

[Service]
Type=simple
User=root

# bpftrace を起動するラッパースクリプト
ExecStart=/usr/local/bin/bpftrace-execve.sh

# ログ本体はスクリプト側でファイルに書き出すので、
# systemd 側では journal にだけ流しておく
StandardOutput=journal
StandardError=journal

Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

StandardError=journal について

StandardOutput=journal / StandardError=journal としておくと、

  • sudo systemctl status bpftrace-execve.service
  • sudo journalctl -u bpftrace-execve.service

から、サービスの起動・停止・エラーなどを確認できます。

execve のログ本体は /var/log/bpftrace/execve_YYYYMMDD.log に出る、
運用ログは journal に残る、という分担です。

なぜ unit ファイルに $(date ...) を書かないの?

最初は

StandardOutput=append:/var/log/bpftrace/execve_$(date +%%Y%%m%%d).log

のように書きたくなりますが、
systemd ユニットはシェルではないので $(date ...) は展開されません。

そのため、日付付きファイル名のような「シェルっぽい処理」は
bash スクリプト(bpftrace-execve.sh)の側に全部寄せる構成にしています。

なぜ絶対パスなの?

  • ExecStart= のコマンドは、基本カレントディレクトリを期待しないほうが安全
  • bpftrace や execve.bt の場所がはっきりしているほうが、後から見たときに分かりやすい

/usr/bin/bpftrace や /opt/watchlog/bpftrace/execve.bt のように絶対パスで書いておくのが定石です。

6. systemd に読み込ませて起動する

ユニットファイルを書いたら、systemd に再読み込みさせます。

sudo systemctl daemon-reload

続いて、サービスを有効化 + 即起動します。

sudo systemctl enable --now bpftrace-execve.service

状態を確認する

sudo systemctl status bpftrace-execve.service

active (running) になっていれば成功です🐤

7. ログがちゃんと出ているか確認する

ログディレクトリを覗いてみましょう。

ls -l /var/log/bpftrace

例

-rw-r--r-- 1 root root  12345 XXX XX XX:XX execve_YYYYMMDD.log

中身をtailしてみます。

sudo tail -f /var/log/bpftrace/execve_YYYYMMDD.log

別ターミナルから、適当にコマンドを叩きます。

ls
uname -a
python3 -V

ログ側にこんな感じで流れてくれば成功です。

xxxx-xx-xx xx:xx:xx pid=1234 comm=ls filename=/usr/bin/ls
xxxx-xx-xx xx:xx:xx pid=1235 comm=uname filename=/usr/bin/uname
xxxx-xx-xx xx:xx:xx pid=1236 comm=python3 filename=/usr/bin/python3.12

8. ログをオフにする方法

「常時取りっぱなし」にしたら、次に気になるのがここですよね。

いま撮ってるログを止めたいとき、どうすればいいのかを、パターン別にまとめます。

8.1 systemd サービスを一時停止する

いま作ったサービスを一旦止めるだけならこれです。

sudo systemctl stop bpftrace-execve.service

これで bpftrace プロセスが終了し、ログも流れなくなります。

状態確認

sudo systemctl status bpftrace-execve.service

inactive (dead) や inactive (stopped) になっていれば OK です。

8.2 起動時に自動で動いてほしくない場合

VM を再起動したときに もう自動で動いてほしくない 場合は、disable します。

sudo systemctl disable bpftrace-execve.service

これで、次回ブート時にはこのサービスは起動されません。

  • stop … 今だけ止める
  • disable … そもそも自動起動しないようにする

というイメージになります。

Discussion