世界を動かす技術を、日本語で。

Zeroserve: eBPFを使ってスクリプト可能なゼロコンフィグのウェブサーバー

13時間前原文(su3.io)

概要

zeroserve は、ゼロ設定・高速・小型のHTTPSサーバー。 eBPFミドルウェア による柔軟なリクエスト処理を特徴。 tarファイル一つ でWebサイト全体を配布・ホットリロード可能。 nginxやCaddy と比較して多くのワークロードで高速。 設定ファイル不要、eBPFプログラムが全ての挙動を制御。

zeroserve:次世代ゼロ設定HTTPSサーバーの特徴

  • 小型・高速・ゼロ設定 サーバーとして設計
  • サイト全体を tarファイル一つ で管理・配信
    • ディレクトリをパッケージングし、そのまま配信
    • デプロイはtarファイルの置き換えとSIGHUPシグナル送信のみ
  • HTTP/2TLS 1.3Encrypted Client HelloSNI証明書選択JA4フィンガープリント など最新TLS機能を内蔵
  • io_uring による全I/O処理
    • シングルスレッド・イベントループ駆動
    • 複数プロセスによるスケーリングを想定

eBPFによる柔軟なリクエスト処理

  • .zeroserve/scripts/ 配下のCファイルをeBPFバイトコードにコンパイル
    • ユーザ空間 でサンドボックス実行
    • async-ebpf ランタイムによりJITコンパイル・プリエンプト対応
  • 全リクエストごと にeBPFスクリプト実行
    • リクエスト検査・書き換え、認証、レートリミット、リバースプロキシ等
  • スクリプトチェーン としてファイル名順に実行
    • zs_respondzs_reverse_proxy でチェーンをショートサーキット
  • メタデータ共有 でリクエスト単位の情報やヘッダ追加・テンプレート変数展開
  • メモリ制限 (256KB)、 タイムスライス による暴走防止

設定ファイル不要・プログラムによる全制御

  • 設定ファイルなし、eBPFプログラムが全ての挙動を記述
    • ルーティング、認証、ヘッダ操作、レートリミット、プロキシ制御などを一元管理
  • 全リクエストパス を一つのプログラムで上から下まで見通し良く記述可能

静的ファイル配信・ホットリロード

  • tarファイル をインデックス化し、パス→バイトレンジで直接配信
    • ディスク上に展開せず、 ドキュメントルート不要
    • 配信中の ホットリロード 対応(TLS証明書含む)

eBPFスクリプトの例と機能

  • リクエスト情報取得・ヘッダ追加
    • クライアントIP取得や全レスポンスにカスタムヘッダ付与
  • テンプレート変数展開
    • HTML内のプレースホルダをメタデータで置換
  • 暗号化・エンコード支援
    • SHA-256、HMAC、base64、getrandomなど
  • JSONパース・レスポンス
    • リクエストボディのJSON解析・動的JSONレスポンス生成
  • レートリミット
    • IPやAPIキー単位のトークンバケット方式
  • AWS SigV4/OIDC対応
    • S3アクセスや「Googleでログイン」ゲートもサポート
  • 動的エンドポイント
    • path判定で/health等のAPIレスポンスも容易に実装

ベンチマークとパフォーマンス

  • nginx 1.26Caddy 2.11 との比較
    • Ryzen 7 3700X(8コア)で 1コア単位 の公平な比較
  • 小型静的ファイル(174B)
    • zeroserve: 36,681 req/s, p99 5.4ms
    • nginx: 31,226 req/s, p99 7.8ms
    • Caddy: 12,830 req/s, p99 22ms
  • 大型静的ファイル(100KB)
    • zeroserve: 8,000 req/s, 782MB/s, p99 22ms
    • nginx: 7,600 req/s, 773MB/s, p99 28ms
    • Caddy: 6,084 req/s, 590MB/s, p99 44ms
  • eBPF vs LuaJIT(nginx Luaモジュール)
    • ヘッダ挿入ミドルウェア:zeroserve eBPF(10ms)43,709 req/s > nginx Lua 28,653 req/s
    • 完全動的JSON応答:zeroserve eBPF(10ms)46,945 req/s > nginx Lua 41,231 req/s
    • プリエンプトタイマー間隔 調整でパフォーマンス最適化
  • リバースプロキシ性能(小型レスポンス)
    • zeroserve: 26,486 req/s, p99 8ms
    • nginx: 21,761 req/s, p99 10.5ms
    • Caddy: 7,683 req/s, p99 33ms
  • リバースプロキシ性能(大型ボディ)
    • nginxがバッファリングで優位、zeroserveはやや劣る

zeroserveの運用・設計思想

  • 単一tarファイル運用 でセキュリティ・管理性向上
  • スクリプトが唯一の設定、挙動の全体像を把握しやすい
  • 複雑な構成や拡張もeBPFスクリプトで柔軟に対応
  • nginxやCaddyの宣言的設定+スクリプトの二層構造 を一元化

zeroserveのメリット・用途

  • シンプルな静的サイト配信 から APIゲートウェイ認証付きサイト まで幅広く対応
  • デプロイ・ロールバック の容易さ(tarファイル差し替えのみ)
  • 高性能・低フットプリント で小規模から大規模まで運用可能
  • 高度なセキュリティ機能 (TLS 1.3、ECH、JA4等)を標準搭載
  • 将来的な構成変更・拡張 もスクリプト一つで完結

zeroserve導入時の注意点・制約

  • シングルスレッド設計 のため、1プロセスあたりの限界あり
    • スケールはプロセス数で調整
  • バイナリサイズやメモリ制約 に注意
  • 大容量リバースプロキシ用途 ではnginx等が依然優位な場合あり
  • eBPFスクリプト開発 にはC言語知識が必要

まとめ

  • zeroserve は、 ゼロ設定・高速・柔軟性 を兼ね備えた新世代HTTPSサーバー
  • eBPFスクリプト による全リクエスト制御で、 設定ファイル不要全挙動一元管理 を実現
  • 静的サイト配信・APIゲートウェイ・認証付きサイト まで幅広く対応可能
  • nginxやCaddy の代替として、 シンプルさとパフォーマンス を求める現場に最適

Hackerたちの意見

いい感じだね、機能も素晴らしい。ただ、なんか自分にはピンとこないというか、ちょっと人工的に感じるんだよね。メトリクスが偽造されてるのか、便利な機能がちゃんと動くのか、しっかりしたハードニングがあるのかも分からない。 vibe コードで自動生成された README があっても受け入れられるけど、発表のブログ記事まで AI 生成だと、正直言って、ソフトウェアの品質に対する理解が自分と同じかどうか全く分からない。変な世の中だよね。数年前に AI の警告なしで発表されてたら、間違いなく飛びついてたと思う。でも今は、見た目が良いコマンドラインパラメータが並んだ fancy な README を見ると、すぐにそれが幻覚で、コマンドラインパラメータが実際に存在するのか疑っちゃう。

ベンチマークを考えると、小さな静的ファイル(174 B) - 静的サイトの基本: サーバー req/s p99 zeroserve 36,681 5.4 ms nginx 31,226 7.8 ms Caddy 12,830 22 ms zeroserve は単一コアで nginx より約 17% 速く小さなファイルを提供していて、レスポンスも安定してる。HTML ページ、小さな JSON、CSS - これが zeroserve の調整対象。大きな静的ファイル(100 KB): サーバー req/s スループット p99 zeroserve 8,000 782 MB/s 22 ms nginx 7,600 773 MB/s 28 ms Caddy 6,084 590 MB/s 44 ms こういう新興プロジェクトよりも、監査済みで戦闘テストを受けた、ハードニングされたプロジェクトを選ぶかな。リスクを正当化するほどの改善がないと思う。

こんにちは、著者です - async-ebpf みたいな重要な部分は、そのコーディングエージェントがリリースされるずっと前に書かれました。zeroserve を作るときは AI の助けをよく使ってるけど、AI の出力は手動でチェックして責任を持ってるよ :)

変な世界だよね。もしこれが数年前にAIの注意書きなしで発表されてたら、間違いなく飛びついてたと思う。でも今は、見た目が良いコマンドラインパラメータがたくさんあるおしゃれなREADMEを見ると、すぐにそのREADMEが幻覚で、コマンドラインパラメータが実際に存在するのか疑っちゃう。残念だよね。最近、ffmpeg-wasmプロジェクトがあったんだ。試してみたら、ちゃんと動いた。でも、あれは雰囲気でコーディングされたAIだった。AIは本当に無理。たとえ動いてもね。できるだけオールドスクールの時代に留まることにした。賢い人たちがソフトウェアを公開し、賢い人たちがそれを維持する。AIなんて必要ない。それが俺のニッチだ。もしかしたら消えてしまうかもしれないけど、それでもその方がいい。(あ、賢い人たちがドキュメントを書いてくれるならね。多くの賢い人たちはドキュメントを書くのが嫌いなんだ。ソフトウェアがドキュメントなしで出てきたら、どんなに素晴らしいドキュメントがあっても、俺の時間には値しないってずっと前に決めた。これは主にアプリケーション側に関して言ってる。Linuxのドキュメントはめったに見ないけど、他の人たちはそれもそんなにひどくないって言ってるから、どうだろうね。)

アイデアはいいね。eBPF ディレクトリに .c ファイルの代わりに .rs ファイルを入れられたら、もっと安心できると思う。もう Rust プロジェクトなんだから! :) それと、なぜかこれがカーネルアクセラレートされたウェブサーバーになると思ってたんだ。もしそれが eBPF を使って安全にできるなら、すごいことだよね!それに、シングルスレッド? incoming connection queue をフォークして共有するのは、Linux では基本的に簡単だから、Rust でもほんの数行で済むはず。SO_REUSEPORT を使えば、カーネルが残りをやってくれるよ。ちなみに、io_uring を推進するなら、kTLS も推進すべきだと思う。ハンドシェイク後にユーザースペースの SSL を流さずに済むなら、設計がかなりシンプルになるよ。

こんにちは、ありがとう!フォークと SO_REUSEPORT を実装するよ。こういうことには nftables を使ってるから、まだ自分では必要なかったんだ :)

こういうのを見るのが大好きだよ。LLMs がなければ存在しなかったかもしれないアイデアを探求するのが、比較的安くて早くできるからね。でも、これからの教訓は、nginx は単体でもかなり印象的だってこと。あと、これが目に留まった: nginx や Caddy の代替として設計されていて、デザインの賭けは設定に関するものなんだ。これらのサーバーは宣言的な設定言語を提供してくれる - location ブロック、リライトルール、マップディレクティブ、try_files - そして、宣言的な言語が限界に達すると、オプションのスクリプトランタイムが横に追加される(Lua や Caddy のプラグイン)。動作は二つのレイヤーに分かれてしまう: 自分の制御フローを静かに成長させるディレクティブと、リクエストライフサイクルのどこかで実行されるスクリプト。人々はコードよりも設定を好むから、賭けが間違ってると思う。ビルトインで十分にニーズを満たしてるし、C コードを書く必要はないんだ。

AI が「人間の言葉 -> 機械の効果」をどんどん可能にしているから、変わる価値があると思う。AI にとってはもっとエルゴノミックかもしれないね。そういうシフトが明確に良いアイデアになるまでには時間がかかるかもしれないけど、AI はどちらでも動かせるから。

複雑さを抽象化して、マクロを使って「設定ファイル」構成を実現するのはどう?

なんでそんなにLLMSを持ち上げたいの?記事を書いたのがLLMの助けを借りたからって、実験をLLMにやらせてるわけじゃないでしょ。

次の96時間以内に、LLMを使えば、誰かがnginxやcaddyの設定ファイルをzeroserveが使える関連コードに「パック」する翻訳者を作るだろうね。もっとシンプルに言えば、kubernetesクラスター内のすべてのIngressマニフェストを拾ってパックを再構築することもできる。要するに、ツールと設定の間のインターフェースはただのAPIで、システムオペレーターはすでに高次の構造でシステムの状態を説明してるし、設定を構成する具体的なバイトはその名残なんだ。

それはちょっと疑わしいな。どんな設定ファイルフォーマットも最初はシンプルに始まるみたいだよ。YAMLを見てみて、基本はかなり理にかなってた。でも人々はアンカーやエイリアスでより派手にしたいと思ったんだ。GitLabだって独自の条件分岐や変数があって、ちょっとしたハックみたいなもんだ(特定の場所でしか機能しない)。ApacheもXMLベースの設定フォーマットでこれに陥ったしね。だから、設定管理のためにたくさんの「特注」プログラミング言語ができちゃった。もちろん、企業の人たちはこれを直接編集しないから、Ansibleのワークフローをスクリプト化してリモートで手術を行うんだ。残念ながら、そんなことをすっ飛ばして、サーバーにLuaやPythonなどのインタプリタを埋め込んで設定管理をやればよかったのに。特注の設定ファイルをプログラムで編集するよりも簡単だったはず。確かに、特注の試みは一般的な言語とは違って特定の用途に最適化されてるって言う人もいるけど、それは最初からその機械を必要としなかった狭い範囲の玩具の例にしか当てはまらないよ!WindowsのINIファイルを覚えてる?昔はコードはコードで、データはデータだった良き時代の話さ…。

面白いアイデアだけど、静的ファイルに集中するのはどうかと思う。最近はそれのためにサーバーを立ち上げる人は少ないよ。

先週やったよ(ゴーストを静的に変換した)けど、自己完結型のバイナリの方が速いんじゃないかと思ってたから、これは自分のために作られた感じがする。でも、典型的なユーザーじゃないのは認めるよ。

ドメインによるね。大規模なデータセットを効率的に提供する静的ファイルフォーマットを使ってる科学がたくさんあるよ。例えば、https://zarr.dev/ と https://parquet.apache.org/ みたいな。

昨日やったよ。

なんでtarボールなの?

まあ、「One tarball, served in place」っていうセクションの最初の段落によると、サイト全体が一つのtarファイルなんだ。zeroserveは読み込み時にそれをインデックス化して、パス→バイト範囲マップを作って、tarボール自体に対してバイト範囲の読み込みを発行してファイルを提供する。ディスクに解凍されることはない。サイトはその一つのファイルの中に完全に存在してるから、余計なロケーションルールで露出するドキュメントルートもないし、デプロイは一回の原子的なファイルスワップで済む。一方で、これはLLMの正当化かもしれないね。コピーには「正しい形」とか「表面が広い」みたいな-ismが散りばめられてるし。

バイト範囲でリソースにアクセスするのに簡単なフォーマットで、みんながツールを持ってるし、圧縮もしないんだよね。

techempowerのウェブサーバーベンチマークの終焉は、こういう新しいものが自分を証明するチャンスを失ったってことだね。編集:どうやら私が遅れをとってるだけで、新しいホットなものは https://www.http-arena.com/leaderboard/ みたいだ。頑張って!

何言ってるの?死んでないよ!ここにあるよ:https://www.techempower.com/benchmarks/#section=data-r23。最後のベンチマークは2025年2月だったけど、彼らはそんなに頻繁にはベンチマークを実行しないんだ、年に一回かそれ以下だよ。

LLMのUI/UXは最悪だね。こんなのに週末の努力を注ぐのがどれだけ難しいのか。

すごいね!これを他のBPFプログラムタイプ、例えばXDPプログやソケットマップに結合してL7 HTTP機能を下に統合するのも面白そう。

面白い新しいコンセプトだね。実際の問題は開発者のコミットメントとコミュニティだよ。CaddyやNginxの人たちは、自分たちの製品をサポートするために常に努力してるからね。かなりの集中力と注意が必要になるだろうね。

eBPFにウェブサーバーってあるの?

eBPFについて考えてたら、これに出くわしたんだ。なんて偶然!すごく面白いアイデアだし、無駄のないベンチマークもありがとう!このアーキテクチャが動的なコンテンツやロジックを持つウェブサーバーにも移植できるのかな。