[Rust]GranianとGunicornの比較

TIPS
TIPS

Gunicorn(Python製WSGIサーバーのデファクト)とGranian(Rust製HTTPスタックを内包した次世代サーバー)を、アーキテクチャ、メモリ効率、並行処理性能、実運用上の堅牢性・リスクの4つの軸で比較します。

[Rust]GranianとGunicornの比較
GitHub – emmett-framework/granian: A Rust HTTP server for Python applications
A Rust HTTP server for Python applications. Contribute to emmett-framework/granian development by creating an account on…

1. アーキテクチャの根本的な違い

両者の最大の違いは、「HTTP I/O処理とPythonランタイムの境界線をどこに置いているか」 です。

項目Gunicorn (+ gthread / UvicornWorker)Granian (Rust / PyO3)
I/O・ネットワーク層Python標準ライブラリ(socket, select, asyncio)Rust(Tokio + Hyper)
HTTPパースC拡張(http-parser / httptools)またはPythonRust(Hyper / httparse)
Pythonとの接続同一プロセス内の純粋なPython呼び出しPyO3(C-API経由)によるネイティブバインディング
対応インターフェースWSGI標準(ASGIはUvicornWorkerが必要)WSGI, ASGI, RSGI を単一バイナリでネイティブ対応
HTTPバージョンHTTP/1.1 (HTTP/2はリバースプロキシ任せ)HTTP/1.1, HTTP/2 (TLS終端含む)
  • Gunicorn: 伝統的な「Pre-forkモデル」。マスターがソケットを開き、ワーカーが各自で accept・パース・WSGI実行までをすべてPythonプロセス空間内で行います。
  • Granian: リクエストの受付、TLSハンドシェイク、HTTP/2多重化、ヘッダー解析などの重いI/O処理をRustのスレッドプール(Tokio)がゼロコピーに近い形で処理し、Pythonのコードを実行する直前になって初めてPyO3経由でPythonのGIL(Global Interpreter Lock)を握って呼び出します。
[Rust]GranianとGunicornの比較
[Nginx]Python(Gunicorn+Flask)を実行
NginxとPython(Gunicorn+Flask)を連携させてWebアプリを動作させる方法を解説。Nginxをリバースプロキシとして設定し、WSGIサーバーであるGunicornを介してFlaskを実行する手順がステップバイステップで理解できます。

2. メモリフットプリントとスワップ耐性(2GB VPS視点)

今回のVPS障害のような低メモリ環境において、両者の挙動には明確な差が出ます。

メモリ消費要因GunicornGranian
ランタイム自体の基本消費ワーカーあたり 約 30MB〜50MBワーカーあたり 約 5MB〜15MB
多数のコネクション維持接続数に比例してPythonヒープが増加RustのTokioタスクで維持(1接続あたり数KB)
スレッド追加コストOSネイティブスレッド+Pythonスレッド状態Rustの軽量グリーンスレッド(M:Nモデル)
メモリの自律返却Pythonヒープ断片化でOSにメモリが戻りにくいネットワークバッファはRustのメモリアロケータ(jemalloc等)で即座に返却

プロ目線の実態

  • Rust層の恩恵: コネクションが数千に膨らんだり、巨大なHTTPヘッダーが飛び込んできても、サーバー本体のメモリはほとんど増えません。Gunicornで発生しがちな「通信待ちだけでメモリを食い潰す」現象はほぼ皆無になります。
  • 越えられない壁: Django ORMが取得したモデルインスタンス、PandasのDataFrame、Pythonの辞書オブジェクトはすべてPythonのヒープ領域(OS管理外)に作られます。アプリ側で100MBのデータを展開すれば、Granianであっても物理メモリは100MB消費されます。
[Rust]GranianとGunicornの比較
[Mac]Ubuntuインストール用USBメモリを作成
Macを使ってUbuntuのインストール用USBメモリを簡単かつ安全に作成する方法を解説。GUIツール「balenaEtcher」を使用した手順や、作成後の警告メッセージへの対処法、作成したUSBからの起動方法まで初心者向けに紹介します。

3. スループットとレイテンシ(並行処理性能)

ベンチマーク(wrk / autocannon等)における典型的な性能差です。

  • プレーンなJSON / I/O待ち中心のAPI:
    • Granian が Gunicornの 2〜4倍 のRPS(Requests Per Second)を記録します。
    • 特にFastAPI(ASGI)やHTTP/2環境では、イベントループをRust側で処理するためCPUのボトルネックがPythonから完全に切り離され、p99レイテンシが大幅に平滑化されます。
  • 重いDBクエリや計算処理を伴うDjango (WSGI):
    • ボトルネックが「Pythonコードの実行時間(GIL)」や「DBの応答速度」に移るため、実効スループットの差は 10〜30% 程度に縮まります。

4. 運用・保守性の比較(メリットと地雷)

現場で置き換えを検討する際の最大の判断材料となる項目です。

Gunicorn の強みと弱み

  • 強み:
    • 圧倒的な歴史と実績(トラブル時の知見が世界中に蓄積されている)。
    • 設定オプションの豊富さ(--max-requests, --max-requests-jitter, --preload, --graceful-timeout など、プロセスライフサイクル制御が極めて洗練されている)。
  • 弱み:
    • WSGIとASGIで設定やワーカークラスがバラバラになる。
    • HTTPスタック自体が古く、HTTP/2非対応。リバースプロキシ(Nginx)の前段配置が必須。

Granian の強みと弱み

  • 強み:
    • pip install granian だけでCコンパイラ不要で入り、Rustの依存関係はホイールに含まれている。
    • WSGI(Django)とASGI(FastAPI)を全く同じコマンド体系で統一管理できる。
    • リバースプロキシなしでもHTTP/2を直接喋れるため、ローカル開発や内部通信のオーバーヘッドが小さい。
  • 弱み(運用上のリスク):
    • プロセスの自己再起動制御(max-requests 相当)がGunicornほど細やかでない:
      Granianにも --respawn-failed-workers などの監視はありますが、Gunicornのように「500リクエスト処理したら安全にプロセスを自決させてリークを防ぐ」というPython特有のメモリ断片化対策は、外側(systemdの RuntimeMaxSec など)に依存する設計思想です。
    • PyO3境界での予期せぬパニック:
      Pythonの拡張Cモジュール(C-APIを直接叩く特殊なライブラリや非標準なシグナルハンドラ)と組み合わせた際、Rust層で未捕捉の例外が起きた場合のトラブルシューティング難易度が上がります。

5. 比較総括表

項目GunicornGranian
開発言語Python (一部C)Rust + Python
安定性・運用実績★★★★★
業界標準・極めて堅牢
★★★★☆
実運用導入が急速に拡大中
スループット / レイテンシ★★★☆☆
標準的
★★★★★
爆速・低遅延
省メモリ性 (基盤部分)★★☆☆☆
数十MB〜100MB
★★★★★
数MB〜十数MB
メモリリーク防衛機能優秀
max-requests
△
systemd等の外部制御が必須
Django (WSGI) との相性最高
標準構成
良好
WSGIインターフェースで動作
FastAPI (ASGI) との相性△
UvicornWorkerが別途必要
最高
ネイティブ最適化

結論:使い分けるべきか?

もし導入を試すなら、まずは FastAPIで動いているサービスから granian --interface asgi で検証導入し、安定性を確認した上でDjango(WSGI)への水平展開を検討するのが最もリスクの低いアプローチです。

  1. Djangoメインで、バックグラウンド処理やDBアクセスが重いサービス:
    • Gunicornを継続利用(設定最適化)するのが最も安全。
      今回実施した --workers 1~2, --max-requests 300, systemdの MemoryMax を適用したGunicornは、極めて枯れており予測可能性が高いです。無理にGranianに置き換えても、Django ORM側のメモリ消費が主因である場合は劇的な劇薬にはなりません。
  2. FastAPIや、I/Oバウンド(外部APIの中継・WebSocket・高頻度ポーリング)なサービス:
    • Granian への移行を強く推奨。
      Uvicorn + Gunicorn の組み合わせに比べ、CPU使用率・メモリ消費ともに大幅に削減され、明らかなパフォーマンス向上が得られます。

Q よくある質問 (FAQ)
Q. Granianを導入すれば、DjangoやFastAPIアプリ全体のメモリ消費量を劇的に削減できますか?
A.
サーバー自体の基本消費メモリや接続維持コストは数MB〜十数MBへと劇的に削減されますが、Django ORMやPandasなどPythonアプリ側がヒープ領域に展開するデータ(モデルインスタンスやオブジェクト)の消費メモリは削減できないため、アプリの実装依存の部分は残ります。
Q. GunicornからGranianに移行する際、運用管理面で最も注意すべき違いは何ですか?
A.
Gunicornに搭載されている「一定リクエスト処理後にプロセスを自動再起動してメモリリークを防ぐ機能(max-requests)」がGranianにはないため、systemdのRuntimeMaxSecなどの外部ツールを用いてプロセスライフサイクルを制御する必要があります。

コメント

タイトルとURLをコピーしました