New Relic の eBPF APM が x86 だけでなく ARM64 の Linux サーバーでも利用できるようになったので、一家に一台は転がっているであろう Raspberry Pi 5 上で Go サーバーを動かし、大量に負荷をかけると New Relic 上でどのように見えるのかを試してみました。
はじめに
以前から New Relic を利用している方にとって、New Relic における eBPF の活用といえば、これまでは Pixie の印象が強かったかもしれません。
Pixie は Kubernetes 環境で強力に動作する一方で、eBPF = Kubernetes 向けの機能というイメージを持っている方も多いのではないでしょうか。
しかし、New Relic の eBPF Agent(eBPF APM) は Kubernetes に依存せず、一般的な Linux ホスト上でもアプリケーションをコード変更なしで観測できるエージェントです。
昨年12月に eBPF APM が一般提供(GA)され、それに伴い x86 に加えて ARM64 アーキテクチャもサポートされました。
eBPF APM General Availability and eBPF Network Metrics Public Preview
そうです!!
ARM でも使えるようになったんです。
ということは、同じARM の Raspberry Pi も見えるはずなんです!
ということで今回は、一家に一台は必ず転がっている Raspberry Pi を使って、徹底的に負荷をかけながら、New Relic 上でどんな悲鳴を上げるのかを確認してみました!
eBPF 自体の説明はあまりしていません。そのものの仕組みをきちんと理解したい方は、お母さんでもわかる eBPF を参照ください。
今回のゴール
- アプリケーションを修正せずに、Linux ホスト上で動く HTTP サーバーの状態を New Relic 上で観測できること
- Raspberry Pi 5 のような ARM64 環境でも、eBPF Agent による Host / Process / Network の可視化ができること
特に今回は、次のような観点で確認しました。
- 高負荷をかけたときに CPU / Network / Process の変化が見えるか
- アプリケーションに APM Agent を入れなくても、どこまで見えるか
検証環境
Mac(wrk)
|
| HTTP
v
Raspberry Pi 5
|
| Go HTTP Server :9000
|
+-- New Relic Infrastructure Agent
+-- New Relic eBPF APM
とてもシンプルな構成で、Mac から wrk で Raspberry Pi 上の Go HTTP サーバーに負荷をかけ、その状態を New Relic 上で確認します。
早速どんな感じに見えたの?
途中の設定方法を知りたい!!という方はかなり少数だと思うので、最後にまとめて設定内容を記載してます。
まずは integrations and Agents から eBPFを検索して、手順に従いインストールします。

クリックすると APM を導入して見られるものと同じ、transaction time, throughput, Erros が確認できます。一覧画面の内容は APM と遜色はなさそうです。
高負荷をかけているので、transaction timeがスパイクしていることがわかります。
続いて分散トレースと Transactions ですが、eBPF は Linux Kernel から情報を取得しているので、リクエストがあったことを外側から確認はできるが、アプリケーション内部で何があったのかは取得できず、処理の中身までは表示されませんでした。



最後に New Relic Infrastructure Agent で取得したデータもこのように詳細まで確認ができます。



まとめ
Raspberry Pi 5 上で Go HTTP サーバーを動かし、Mac から wrk で負荷をかけながら eBPF Agent とInfrastructure Agent で観測してみました。
結果として、ARM 環境でも以下は十分に確認できました。
- Host の CPU / Memory / Network
- Process 単位の CPU / Memory
- Go サーバーへの負荷
- Network traffic の変化
-
serverプロセスがどれだけ CPU を使っているか
特に良かったのは、Go サーバーに New Relic APM Agent を入れていないにもかかわらず、New Relic 上でプロセス単位の負荷を確認できた点です。
アプリケーションをすぐに修正できない環境でも、Linux ホスト側にエージェントを導入することで、
- どのプロセスが重いのか
- ネットワーク通信量がどう変化しているのか
- 高負荷時にホスト全体がどう振る舞うのか
を確認できるのが、eBPF Agent の大きなメリットです。
Raspberry Pi は本番環境ではありませんが、eBPF が何を見ているのかを学ぶにはかなり面白い環境でした。
実際の手順(やってみたい方向け)
おそらくやりたい方は少数だと思うので設定内容はこちらにまとめております。
Raspberry Pi のセットアップ
今回は環境構築を極力シンプルにしています。
- Raspberry Pi Imager で Ubuntu Server LTS を書き込み
- SSH 有効化
- ネットワーク設定 実際に設定した方はこちら
Raspberry Pi 上に Go HTTP サーバーを立てる
まず、負荷をかける対象として Go の HTTP サーバーを用意します。
最初は goroutine を大量に生成するような極端なコードも試しましたが、それだとサーバー自身が不安定になり、eBPF の観測対象としてはノイズが大きくなりました。
そのため、最終的には「少しだけ I/O 待ちがある普通の HTTP サーバー」にしています。
package main
import (
"log"
"net/http"
"runtime"
"sync/atomic"
"time"
)
var total atomic.Uint64
func main() {
log.SetFlags(log.LstdFlags | log.Lmicroseconds)
srv := &http.Server{
Addr: ":9000",
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
total.Add(1)
// 擬似 I/O 待ち
time.Sleep(5 * time.Millisecond)
w.WriteHeader(http.StatusOK)
w.Write([]byte("ok\n"))
lat := time.Since(start)
if lat > 50*time.Millisecond {
log.Printf("[遅延] latency=%s goroutines=%d remote=%s",
lat,
runtime.NumGoroutine(),
r.RemoteAddr,
)
}
}),
}
go func() {
t := time.NewTicker(1 * time.Second)
defer t.Stop()
var prev uint64
for range t.C {
now := total.Load()
log.Printf(
"[統計] RPS=%d goroutines=%d",
now-prev,
runtime.NumGoroutine(),
)
prev = now
}
}()
log.Println("[起動] HTTP server :9000")
log.Fatal(srv.ListenAndServe())
}
このサーバーでは、リクエストごとに 5ms の待ち時間を入れています。
これは CPU を無理やり消費させるためではなく、実際の Web API で発生しがちな DB / 外部 API / I/O 待ちに近い状態を簡易的に再現するためです。
また、1秒ごとに以下をログ出力しています。
- RPS
- goroutine 数
- 50ms を超えたリクエストの遅延ログ
Go サーバーを起動する
Raspberry Pi 上で以下を実行します。
nohup を使ってバックグラウンド起動し、ログは server.log に出力します。
go build server.go
nohup ./server > server.log 2>&1 &
起動後は、:9000 が LISTEN していることを確認します。
ss -ltnp | grep 9000
以下のように :9000 が LISTEN していれば OK です。
LISTEN 0 4096 *:9000 *:* users:(("server",pid=30772,fd=3))
疎通確認として、Raspberry Pi 上で以下を実行します。
curl http://127.0.0.1:9000
以下のように返ってくれば Go サーバーは起動しています。
ok
New Relic Infrastructure Agent と eBPF Agent を導入する
次に New Relic のエージェントを導入します。
今回使ったのは次の2つです。
- New Relic Infrastructure Agent
- New Relic eBPF Agent
New Relic Infrastructure Agent
Infrastructure Agent は、ホスト全体の情報を取得するためのエージェントです。
主に以下のような情報を取得します。
- CPU
- Memory
- Disk
- Network
- Process
今回のように Raspberry Pi 全体の CPU 使用率やメモリ使用量、ネットワーク通信量を見るには Infrastructure Agent が必要です。
New Relic eBPF Agent
eBPF Agent は、Linux カーネルレベルの情報を利用して、より詳細なプロセスやネットワークの状態を観測します。
今回の主役はこちらです。
特に確認したかったのは、アプリケーションに New Relic APM Agent を組み込まなくても、Go サーバーのプロセスやネットワークの状態が New Relic 上で見えるかどうかです。
New Relic CLI の profile を設定する
New Relic CLI v2 以降では、newrelic install の前に profile 設定が必要です。
ここで少しハマりました。
sudo newrelic profile configure --profile default
--profile default を指定しないと、環境によっては以下のようなエラーになる場合があります。
FATAL the --profile argument is required
eBPF APM の設定を確認する
インストール後、/etc/newrelic-ebpf-agent/newrelic-ebpf-agent.conf を確認します。
sudo vim /etc/newrelic-ebpf-agent/newrelic-ebpf-agent.conf
今回、最終的に動作した主要設定は以下です。
NEW_RELIC_LICENSE_KEY=xxxxxxxxxxxxxxxxxxxxxxxx
DEPLOYMENT_NAME=c10k_raspai
OTLP_ENDPOINT="otlp.nr-data.net:4317"
NEW_RELIC_LOG_LEVEL=INFO
NEW_RELIC_LOG_FILE_PATH=/var/log/newrelic-ebpf-agent
注意点として、今回の環境では OTLP_ENDPOINT に https:// を付けず、以下の形式にしました。
OTLP_ENDPOINT="otlp.nr-data.net:4317"
設定を変更したら、newrelic-ebpf-agent と newrelic-ebpf-agent-client を再起動します。
sudo systemctl restart newrelic-ebpf-agent
sudo systemctl restart newrelic-ebpf-agent-client
再起動後は、どちらのサービスも active (running) になっていることを確認します。
sudo systemctl status newrelic-ebpf-agent
sudo systemctl status newrelic-ebpf-agent-client
eBPF APM のログを確認する
eBPF Agent のログは /var/log/newrelic-ebpf-agent/ 配下に出力されます。
sudo ls -l /var/log/newrelic-ebpf-agent/
newrelic_client.log や newrelic_agent_*.log を確認すると、eBPF Agent / Client の起動状況や接続状況を確認できます。
sudo tail -f /var/log/newrelic-ebpf-agent/newrelic_client.log
また、OTLP エンドポイントへの TLS 接続確認として、openssl s_client で otlp.nr-data.net:4317 に接続できるかも確認しました。
echo | openssl s_client \
-connect otlp.nr-data.net:4317 \
-servername otlp.nr-data.net
証明書情報が表示されれば、ネットワーク的には New Relic 側へ到達できています。
Mac に wrk をインストールする
負荷生成には wrk を使いました。
Mac では Homebrew でインストールできます。
brew install wrk
もし Xcode のライセンスエラーが出る場合は、以下を実行します。
sudo xcodebuild -license accept
その後、再度 brew install wrk を実行します。
brew install wrk
wrk でランダム負荷をかける
一定の負荷をランダムでかけるために、50〜200 connections 程度の、ある程度安定した負荷を継続的にかける形にしました。
#!/bin/bash
TARGET="http://192.168.10.115:9000"
END_TIME=$(( $(date +%s) + 7200 )) # 2時間
while [ $(date +%s) -lt $END_TIME ]; do
CONN=$(( RANDOM % 150 + 50 )) # 50–200
DURATION=$(( RANDOM % 40 + 20 )) # 20–60秒
THREADS=2
echo "[LOAD] connections=${CONN}, duration=${DURATION}s"
wrk \
-t${THREADS} \
-c${CONN} \
-d${DURATION}s \
${TARGET}
SLEEP=$(( RANDOM % 20 + 20 )) # 20–40秒
echo "[SLEEP] ${SLEEP}s"
sleep ${SLEEP}
done
TARGET には Raspberry Pi の IP アドレスを指定します。
Mac から実行する場合、127.0.0.1 を指定すると Mac 自身を見に行ってしまうため注意してください。
負荷実行中は、wrk.log に実行結果を出力しました。
bash ./test.sh | tee wrk.log
実行結果としては、リクエスト数、平均レイテンシ、最大レイテンシ、Requests/sec などを確認できます。
Go サーバー側のログを見る
Raspberry Pi 側では、Go サーバーのログを確認します。
tail -f server.log
負荷をかけると、RPS と goroutine 数が上昇します。
実際のログでは、RPS が上がるにつれて goroutine 数も増え、一定以上の遅延が出たリクエストについては [遅延] ログが出力されました。
2026/01/04 17:40:57.066238 [統計] RPS=19277 goroutines=4408
2026/01/04 17:40:58.058522 [統計] RPS=19179 goroutines=4290
2026/01/04 17:40:59.057436 [統計] RPS=20725 goroutines=4259
2026/01/04 17:40:59.854870 [遅延] latency=55.546995ms goroutines=4400 remote=192.168.10.107:55169
このログだけでも、Go の net/http が多くの接続を捌くために goroutine を増やしていることが分かります。ここまでできれば、NewRelicの画面が添付したようになっておりますので、ぜひ確認して見てください!
New Relic株式会社のQiita Organizationでは、
新機能を含む活用方法を公開していますので、ぜひフォローをお願いします。


