分散学習って、速いはずなのに急に遅くなることがあります。しかも厄介なのは、全体が止まって見えるのに、原因は1台だけというパターン。今回紹介する記事は、そんな「見えない犯人」を eBPF と fleet fan-out という仕組みで、4台のGPUノードをまたいで一発で特定したという話です。かなり面白いです。しかも、中央の収集サーバーなしでやっているのが筋がいい。
元記事の著者たちは Ingero という eBPF ベースの観測ツールを作っています。
eBPF はざっくり言うと、Linux カーネルの中で起きていることを安全に“のぞき見る”ための仕組みです。ログをただ見るだけより、「どこで詰まったか」をかなり細かく追えます。
もともと Ingero は 単一ノード向けでした。1台のマシン上で、GPUの遅延やCUDA呼び出しの状況を調べるには十分。でも distributed training になると話が変わります。
たとえば 4台構成の DDP(Distributed Data Parallel)ジョブが遅いとき、まず知りたいのは 「どのノードが足を引っ張っているのか」。でも現実には、
nvidia-smi を見るdstat を見るみたいな、地味でつらい作業になりがちです。
正直、これって「問題を解く」というより「探偵ごっこ」なんですよね。しかも時間が経つと症状が消えてしまう。そこが本当にしんどい。
記事では、Ingero v0.9.1 に 3つの機能を追加したと説明しています。重要なのは、どれも 既存の各ノード上のエージェントの上に乗っていて、新しい中央サービスを追加していないことです。
各イベントに node tag を付けるようにしたそうです。
つまり、どのイベントがどのノードで起きたかを明示する仕組みです。
例としてはこんな感じです。
sudo ingero trace --node gpu-node-01
イベントIDも gpu-node-01:4821 のように node-namespaced になり、複数ノードのデータをマージしても衝突しません。
torchrun のワークロードでは、RANK や LOCAL_RANK、WORLD_SIZE も自動検出できるとのこと。ここは地味ですが、かなり実用的です。分散学習で毎回手動設定するのは、ぶっちゃけ面倒すぎます。
これが今回の主役です。
各ノードの Ingero エージェントは、すでに HTTPS の dashboard API を持っています。そこで新しい fleet client が、同じSQLクエリを全ノードに並列投げして、結果をまとめるようにしたわけです。
イメージとしては、
という感じです。
しかもセキュリティ面もちゃんとしていて、TLS 1.3 や mTLS(相互TLS、つまりクライアントとサーバーが両方身元確認する仕組み)に対応しています。
--no-tls もありますが、これは明示的なオプトインで、信頼できるVPC内での利用を想定しているようです。
記事中の例では、こんなSQLでノードごとのイベントを比較しています。
SELECT node, source, count(*) as cnt, avg(duration)/1000 as avg_us
FROM events GROUP BY node, source
その結果、gpu-node-01 だけが明らかにおかしい。
他のノードは source 3 の平均が2ms前後なのに、gpu-node-01 は 18.4ms と突出していました。
こういう「1台だけ変」なケースは、経験上ほんとうに厄介ですが、逆に言うと 見つかれば勝ちです。
続けて ingero explain を実行すると、因果関係まで出してくれます。
結果は、gpu-node-01 で cuLaunchKernel と cuMemAlloc が遅くなっていて、その根本原因が sched_switch events + heavy block I/O と示されました。
要するに、checkpoint の書き込みなどのブロックI/OがCPUを奪い、training process を待たせていたということです。
これはかなり“あるある”です。GPUが悪いわけじゃなく、周辺のI/Oが足を引っ張る。分散学習あるあるの中でも、かなり嫌なやつです。
現場によっては、ノード間のHTTP通信ができないこともあります。
たとえば air-gapped cluster(外部ネットワークから隔離された環境)や、厳しいセキュリティ制約のあるVPCです。
その場合は、各ノードの SQLite database を集めて、ingero merge で1つにまとめられます。
scp gpu-node-01:~/.ingero/ingero.db node-01.db
scp gpu-node-02:~/.ingero/ingero.db node-02.db
ingero merge node-01.db node-02.db -o cluster.db
ingero explain -d cluster.db
さらに ingero export --format perfetto で Perfetto に出せるJSONも作れるそうです。
Perfetto はタイムラインでイベントを見るための可視化ツールで、Chrome Trace Event Format に対応しています。
個人的には、こういう「あとから見返せる形」にしてくれるのはかなり良いと思います。運用の現場では、その場で直せないことも多いので、記録が資産になります。
ここがこの記事の思想としていちばん面白いところです。
多くの observability 基盤は、中央に collector を置いて、そこへメトリクスやログを送ります。Prometheus、Datadog、Honeycomb みたいな王道のやり方ですね。
でも著者たちは、あえてそれを避けています。
理由は主に3つあります。
Ingero は zero-config の単一バイナリを目指しているので、中央集約基盤を足すと思想が崩れる。
これはすごくわかります。便利さのためにシステムを足していくと、だんだん「何を監視するための監視だっけ?」となりがちです。
4〜50ノードくらいなら、協調分散システムを作るより、素直に並列HTTPリクエストを投げてローカルでまとめる方が簡単だし十分実用的、という判断です。
これはかなり現実的だと思います。巨大な分散クエリ基盤を作るより、まず“使える”ことが大事ですから。
1台つながらなくても、他のノードの結果は返す。
この「全部成功しないとダメ」ではない設計は、現場目線で良いです。むしろ、つながらないノードこそ怪しいことが多いので、失敗情報自体が診断材料になります。
分散システムで地味に厄介なのが clock skew、つまりマシンごとの時刻のズレです。
イベントの順番をまたいで比較するのに、時間がズレていたら話になりません。
Ingero では、eBPF のタイムスタンプが bpf_ktime_get_ns() に基づいていて、これは各マシンの CLOCK_MONOTONIC を使います。
そこで fleet client が、クエリと並行して NTP風のオフセット推定を行い、時刻差を見積もるそうです。
しかも、これが クエリの遅延を増やさないというのが賢い。ここは設計としてかなりきれいです。
率直に言うと、今回のポイントは「GPUの話」そのものより、分散デバッグをいかに軽くするかにあります。
普通なら、distributed training の可観測性をちゃんとやろうとすると、
みたいに、だんだん大掛かりになります。もちろんそれはそれで強い。でも、**“今この1回の遅さの原因が知りたい”**だけなら、もっと軽い道が欲しい。
この記事のアプローチは、その「軽い道」をかなりうまく作っていると思います。
特に印象的なのは、
「1つのSQLクエリを、複数ノードへ飛ばす」
という発想です。
これ、見た目はシンプルですが、かなり強いです。
SQLという慣れた形のまま、複数ノードの観測結果を横断比較できる。しかも中央集約なし。
派手さはないけど、実務ではこういう地味なシンプルさが一番効くことが多いんですよね。
この記事は、分散学習の「どこが遅いのか分からない」を、各ノード上の eBPF エージェント + fleet fan-out query で素早く解決した話でした。
結果として、4ノードのうち1台だけが遅いことを即座に見つけ、その原因が CPU contention due to block I/O だと突き止めています。
個人的には、これはかなり実戦向きのアイデアだと思います。
「壮大な観測基盤を作る前に、まず今あるエージェントを賢く使う」という姿勢がいい。現場では、こういう割り切りの良さが効くんですよね。