本日2件のお問い合わせがありました。

✉️FIELDにお問い合わせ

設計・戦略システム開発

サーバーは「選ぶ」から「作る」へ|AIと作った自作Webサーバー FIELD-WS 0.1 を公開します

Webサーバーを何にするか、という議論はずっと「選ぶ」話でした。Apacheにするか、Nginxにするか。自動TLSが欲しければCaddy、HTTP/3ならH2O、JavaならTomcat、PythonならGunicorn。世の中には用途ごとに完成度の高い実装が揃っていて、我々エンジニアの仕事はその中から一番マシなものを選び、設定ファイルで自分の用途に寄せることでした。

ところが、AIと一緒に開発するようになって前提がひとつ変わりました。「無いなら作る」の作るのコストが、現実的な水準まで落ちたということです。

そこで実際に作ってみました。AWS・Docker・WordPress/Laravelに用途を絞った自作Webサーバー FIELD-WS 0.1 です。この記事では、何を作ったのか、何をあえて作らなかったのか、どう動かすのかを実装レベルで書きます。tar.gzを配布していますので、そのままEC2で動かせます。


そもそもWebサーバーにはこれだけの種類がある

まず現在地の確認です。ひとくちにサーバーといっても、レイヤーも役割も違うものが並んでいます。

種類代表例主な役割
WebサーバーApache HTTP ServerHTTP受付、静的ファイル、PHP連携など
Webサーバー/リバースプロキシNginxHTTP、静的配信、リバースプロキシ
高速WebサーバーCaddyHTTP/HTTPS、自動TLS、リバースプロキシ
軽量WebサーバーLighttpd軽量なHTTPサーバー
高性能WebサーバーLiteSpeed / OpenLiteSpeedApache互換、高速化
イベント駆動WebサーバーH2OHTTP/2・HTTP/3など
JavaアプリサーバーTomcat / Jetty / UndertowServlet・JSPを実行
Node.jsNode HTTP / Fastifyアプリ自身がHTTPサーバーになる
PythonGunicorn / UvicornPythonアプリをHTTPで提供
RustHyper / Axum非常に高速なHTTPサーバーを構築できる
Gonet/http / Fiberアプリ自身で高速HTTPサーバーを構築できる

これだけあるのに、なぜ作るのか。理由は単純で、これらはすべて「誰にでも使えること」を前提に作られているからです。汎用であることは長所ですが、同時に、自分の用途では一度も使わない機能のために設定が複雑になり、分岐が増え、メモリを持っていかれる、ということでもあります。

我々の用途はかなり偏っています。AWS(ALB/CloudFrontでTLSは終端済み)・Docker・WordPress/Laravel・公開GETが大半。この4つに絞ってよいなら、汎用サーバーが持っている機能の大半は要りません。


AI時代の「作る」の現実的なコスト

これまで自作Webサーバーは、勉強用のおもちゃか、大企業の研究開発かのどちらかでした。epollのイベントループを書き、HTTPパーサを書き、キャッシュを書き、レートリミットを書き、テストを書く。個人や小さな会社が本業の片手間にやる作業量ではありません。

いま変わったのはここです。設計の判断は人間がやり、実装の手数はAIが引き受ける。この分担にすると、「用途を絞ったサーバー」は現実的な時間で形になります。実際、FIELD-WS 0.1の本体はC言語で236行です。外部ライブラリはゼロ、依存はlibcとpthreadだけです。

重要なのは、AIに「Webサーバーを作って」と投げたわけではないことです。人間側が決めたのは次のような判断でした。

  • TLSはやらない(ALB/CloudFrontが終端しているので二重にやる意味がない)
  • FastCGIはまだやらない(0.1では既存のApache版WordPressへプロキシする)
  • 設定ファイル形式は作らない(環境変数だけにする。Docker前提なら十分)
  • キャッシュはWordPressの都合を知っているものにする(後述)

この「作らないものを決める」作業こそが設計で、ここはAIに任せられません。逆にいえば、ここさえ決めれば実装は進みます。


FIELD-WSの位置づけ

概念としては、冒頭の表の「Webサーバー/リバースプロキシ」の行に並ぶものです。

種類代表例主な役割
高効率Webサーバー/リバプロFIELD-WSHTTP受付、静的配信、リバースプロキシ、WordPress向けキャッシュ、レート制限

ブラウザからHTTPリクエストを受け、静的ファイルを直接返し、WordPressやLaravelへ転送し、応答をキャッシュし、Dockerコンテナとして動く。基本構造はApacheやNginxと同じです。

ただし成熟度はまったく違います。ここを曖昧にすると信用の話になるので、はっきり書いておきます。

製品成熟度特徴
Apache非常に高い多機能・互換性重視
Nginx非常に高い静的配信・リバースプロキシに強い
Caddy高い自動HTTPSが強い
H2O高いHTTP/2・HTTP/3に強い
FIELD-WSMVPAWS・Docker・WordPress/Laravel用途へ特化

FIELD-WS 0.1は「思想を実測できるMVP」であって、Nginxの即時置き換えを保証する完成版ではありません。「Nginxと並ぶ製品」と名乗るには、機能追加だけでなく、脆弱性検査・異常系試験・長時間負荷試験・複数EC2での本番運用実績が要ります。そこはまだ通過していません。

目指している位置は、汎用の新しいWebサーバーではなく、こちらです。

AWS・Docker・WordPress・Laravelに特化し、Nginxより設定が簡単で、公開GETを高速に処理するWebサーバー

この定義なら、独立した製品として成立すると考えています。


実装したもの

0.1で動いているのは次の範囲です。

  • epollによるノンブロッキングI/O
  • sendfileによる静的ファイルのzero-copy配信(カーネル空間からそのまま送る)
  • CPUコア数に応じたworkerプロセスとSO_REUSEPORT(カーネルが接続を各workerへ分散する)
  • HTTP/1.1 keep-alive、GET、HEAD
  • /api/など任意のprefixでのリバースプロキシ
  • upstream応答のインメモリキャッシュ(容量上限・TTL・簡易LRU)
  • WordPress管理画面・ログインCookie・WooCommerceカート・POSTのキャッシュ回避
  • IP単位のtoken bucketによるレート制限
  • /health、Prometheus形式の/metrics、token付きのキャッシュpurge
  • パストラバーサル対策、16 MiBのupstream応答上限、セキュリティヘッダ

WordPress向けのキャッシュ回避は、汎用サーバーの設定では地味に事故るところです。ここをサーバー側が最初から知っているのがFIELD-WSの実利です。/wp-admin/wp-login.php、POSTリクエスト、wordpress_logged_in_ Cookieを持つリクエスト、WooCommerceのカートは自動的にキャッシュを迂回します。さらに、upstreamがSet-Cookieを返した応答はキャッシュに入れません(ログイン状態の取り違えを起こさないため)。

記事を更新したときの全パージはこれだけです。

curl 'http://127.0.0.1:8080/__fieldws/purge?token=YOUR_TOKEN'

実装していないもの(あえて)

正直に列挙します。0.1時点で未実装なのは次です。

  • HTTPSの直接処理(ALB/CloudFrontでTLS終端する前提
  • HTTP/2・HTTP/3
  • FastCGIによるPHP-FPMへの直接接続
  • WebSocket
  • gzip・Brotli圧縮
  • 詳細なアクセスログ
  • graceful reload
  • 細かい設定ルール全般
  • 長期間の実運用検証

このうちTLSは「未実装」というより設計判断です。AWSに載せる時点でTLSはALBかCloudFrontが終端しており、その内側でもう一度TLSを張る必要はありません。証明書の更新も、暗号スイートの追従も、AWS側の仕事にしておくほうが安全です。

一方でFastCGIは、次の開発でやる予定のものです。PHP-FPMへ直接FastCGIで繋げば、構成からApacheを完全に外せます。0.1では公式のApache版WordPressイメージをそのまま使い、FIELD-WSは前段だけを担当しています。


Docker対応版の構成

Docker Composeで、FIELD-WS・WordPress・MySQLをまとめて起動できるようにしました。

ALB
 ↓ :8080
FIELD-WS
 ↓ 内部通信
WordPress
 ↓
MySQL 8.4

公開されるのはFIELD-WSの8080番だけで、WordPressとMySQLはComposeの内部ネットワークに閉じています。ホスト側にポートを出していないので、外から直接叩くことはできません。

FIELD-WSのコンテナ側もそれなりに絞めてあります。

read_only: true
tmpfs:
  - /tmp:size=16m,mode=1777
security_opt:
  - no-new-privileges:true
cap_drop:
  - ALL

ルートファイルシステムは読み取り専用、権限昇格は禁止、Linux capabilityは全部剥奪、実行ユーザーはuid 10001の非rootです。ヘルスチェックはバイナリ自身の--healthcheckが担当します(コンテナにcurlを入れずに済みます)。


使い方

1. ダウンロードして展開する

fieldws-0.1.0.tar.gz をダウンロード(12KB)

tar -xzf fieldws-0.1.0.tar.gz
cd fieldws

念のため、配布物のSHA-256はこちらです。

161d5bc509b5596ce4b073de450baddf804164e9dd73f3ebe48ba3d69cac6d10  fieldws-0.1.0.tar.gz

2. .envを作ってパスワードを変える

cp .env.example .env
nano .env

.env秘密値3つを必ず変更してください。空のままだとComposeが起動を拒否します(:?指定にしてあります)。

# Generate with: openssl rand -hex 32
FIELDWS_ADMIN_TOKEN=replace-with-random-value
WORDPRESS_DB_PASSWORD=replace-with-random-value
MYSQL_ROOT_PASSWORD=replace-with-random-value

値はopenssl rand -hex 32で作るのが早いです。

3. 起動して確認する

docker compose up -d --build
docker compose ps
curl http://127.0.0.1:8080/health

okが返れば起動しています。あとはブラウザでhttp://EC2のIP:8080/を開くと、WordPressの初期設定画面が出ます。

本番では8080番をインターネットへ直接公開しないでください。 EC2のSecurity Groupで、ALBのSecurity Groupからのみ8080を許可する構成にします。

Docker無しで動かす場合

単体バイナリなので、ビルドして直接起動することもできます。

make
FIELDWS_ROOT="$PWD/www" FIELDWS_PORT=8080 ./fieldws
curl http://127.0.0.1:8080/health

EC2(Amazon Linux 2023)へsystemdサービスとして入れる場合は、同梱のスクリプトが入れてくれます。

chmod +x deploy/install-amazon-linux-2023.sh
sudo ./deploy/install-amazon-linux-2023.sh
curl http://127.0.0.1:8080/health

ALBのtarget groupはHTTPの8080、ヘルスチェックパスは/healthにします。設定変更後はsudo systemctl restart fieldws、ログはsudo journalctl -u fieldws -fで見られます。


設定は環境変数だけ

独自の設定ファイル形式は作りませんでした。Docker前提なら環境変数で足りますし、「設定ファイルの文法を覚える」というコストを利用者に払わせないためです。

環境変数初期値内容
FIELDWS_LISTEN0.0.0.0listenアドレス
FIELDWS_PORT8080listenポート
FIELDWS_ROOT./www静的ファイルのドキュメントルート
FIELDWS_WORKERSCPU数workerプロセス数
FIELDWS_UPSTREAMhost:port。空ならプロキシ無効
FIELDWS_PROXY_PREFIX/api/プロキシ対象のprefix
FIELDWS_CACHE_TTL30キャッシュ秒数
FIELDWS_CACHE_MB64プロセスごとのキャッシュ上限MiB
FIELDWS_RATE300IPごとの毎秒補充token
FIELDWS_BURST600IPごとの最大burst
FIELDWS_ADMIN_TOKENpurge用token。空ならpurgeを拒否

WordPressの前段として使うなら、この形になります。

FIELDWS_PROXY_PREFIX=/ \
FIELDWS_UPSTREAM=wordpress:80 \
FIELDWS_CACHE_TTL=30 \
FIELDWS_CACHE_MB=256 \
FIELDWS_ADMIN_TOKEN='十分に長いランダム値' \
./fieldws

動作の確認とベンチも同梱しています。

make test
./bench.py -c 64 -n 100000

本番投入の前に必要なこと

MVPだと書いた以上、何が足りないかも書いておきます。実運用へ上げる前に必要な次段階は、upstreamの完全非同期化、chunkedリクエスト/レスポンス対応、cache stampede防止、複数worker間の共有キャッシュ、graceful reload、アクセスログ、fuzzing、そしてTLS/HTTP/2/HTTP/3です。

いま安心して使えるのは、「ALBの内側で、公開GETが大半のWordPressサイトを、Dockerで動かす」という、まさに設計時に想定した範囲だけです。逆にいうと、その範囲であれば、設定は環境変数11個で済み、コンテナは非rootの読み取り専用で回り、キャッシュはWordPressの都合を最初から知っています。


サーバーは「選ぶ」から「作る」へ

言いたいことは、自作サーバーがNginxより優れている、という話ではまったくありません。用途を絞り込めば、自分たちの条件にちょうど合うものを作るという選択肢が、現実的なコストの側に入ってきたということです。

これはWebサーバーに限りません。社内ツール、変換バッチ、監視の仕組み、業務システムの一部分。「既製品を入れて、合わない部分を運用でカバーする」が当たり前だったところに、「用途を絞って作る」が並びました。判断の材料が増えたわけです。

そして、この作り方で一番時間を使うのは、コードを書く工程ではありません。何を作らないかを決める工程です。AIが実装を引き受けてくれる分、設計の解像度がそのまま成果物の質になります。ここは人間の仕事として、むしろ重くなったと感じています。

FIELD-WSは今後も開発を続けます。次はPHP-FPMへのFastCGI直結で、構成からApacheを外すところです。

それでは、みなさん効率の良いAIライフを。

Yamamoto Yuya

プロフェッショナルとしての高いスキルと知識を持ち、誠実さと責任感を大切にする。常に向上心を持ち、新たな挑戦にも積極的に取り組む努力家。