Gunicorn
Pythonアプリケーション用のシンプルで使いやすいWSGI HTTPサーバー。最も文書化されたPython Webサーバー。簡単な設定と管理が特徴。
アプリケーションサーバー
Gunicorn
概要
Gunicorn(Green Unicorn)は、Pythonアプリケーション用のシンプルで使いやすいWSGI HTTPサーバーです。最も文書化されたPython Webサーバーとして、簡単な設定と管理が特徴的です。最も広く使用されるPython Webサーバーであり、シンプルさを重視する場合に選択されることが多く、uWSGIより劣るパフォーマンスながら設定の簡単さで人気があります。
詳細
Gunicornは2010年にリリースされ、PythonのWSGI(Web Server Gateway Interface)アプリケーションを本番環境で実行するためのHTTPサーバーとして広く採用されています。Rubyの Unicorn サーバーからインスパイアされ、プリフォーク・ワーカーモデルを採用しています。Django、Flask、FastAPIなど主要なPythonウェブフレームワークと互換性があり、本番環境での安定性に定評があります。
主要な技術的特徴
- プリフォーク・ワーカーモデル: マスタープロセスが複数のワーカープロセスを管理
- WSGI準拠: Python標準のWebアプリケーションインターフェース
- 柔軟な設定: コマンドライン、設定ファイル、環境変数による設定
- 複数のワーカータイプ: 同期・非同期ワーカーサポート
- グレースフルリスタート: 無停止でのアプリケーション更新
- シンプルな監視: プロセス監視とシグナルハンドリング
用途
- Django、Flask、FastAPIアプリケーション
- REST API サーバー
- マイクロサービス
- Webアプリケーション本番環境
- コンテナ化アプリケーション
- 負荷分散環境のバックエンド
メリット・デメリット
メリット
- 設定の簡単さ: 最小限の設定で稼働可能
- 豊富なドキュメント: 詳細な公式ドキュメントとコミュニティサポート
- 安定性: 長期間の本番運用実績
- 柔軟性: 多様な設定オプションとワーカータイプ
- 監視の容易さ: シンプルなプロセス構造
- フレームワーク互換性: 主要Pythonフレームワークとの高い互換性
デメリット
- パフォーマンス: uWSGIと比較して低いパフォーマンス
- メモリ使用量: プロセスベースによる高いメモリ消費
- スケーラビリティ: 大規模環境での制限
- 静的ファイル配信: 静的ファイル配信には向かない
- Windows非対応: Windows環境での使用不可
インストール・基本設定
前提条件
# Python バージョン確認(3.7以上推奨)
python --version
python3 --version
# pip 確認
pip --version
pip3 --version
インストール方法
pip インストール
# 基本インストール
pip install gunicorn
# 特定のバージョン
pip install gunicorn==21.2.0
# 非同期ワーカー用依存関係
pip install gunicorn[eventlet] # Eventlet ワーカー
pip install gunicorn[gevent] # Gevent ワーカー
# または個別インストール
pip install eventlet
pip install gevent
仮想環境での設定
# 仮想環境作成
python -m venv venv
source venv/bin/activate # Linux/Mac
# venv\Scripts\activate # Windows
# Gunicorn インストール
pip install gunicorn
# requirements.txt への追加
echo "gunicorn==21.2.0" >> requirements.txt
基本的なアプリケーション起動
最小限のWSGIアプリケーション
# app.py
def application(environ, start_response):
"""基本的なWSGIアプリケーション"""
data = b'Hello, World!\n'
status = '200 OK'
response_headers = [
('Content-type', 'text/plain'),
('Content-Length', str(len(data)))
]
start_response(status, response_headers)
return iter([data])
Flaskアプリケーション例
# flask_app.py
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/')
def hello():
return jsonify({'message': 'Hello from Gunicorn!'})
@app.route('/health')
def health():
return jsonify({'status': 'healthy'}), 200
if __name__ == '__main__':
app.run(debug=True)
基本起動コマンド
# 基本起動
gunicorn app:application
# Flask アプリケーション
gunicorn flask_app:app
# ワーカー数指定
gunicorn --workers 4 flask_app:app
# ホストとポート指定
gunicorn --bind 0.0.0.0:8000 --workers 4 flask_app:app
設定とカスタマイズ
コマンドライン設定
# 包括的な設定例
gunicorn flask_app:app \
--bind 0.0.0.0:8000 \
--workers 4 \
--worker-class sync \
--worker-connections 1000 \
--timeout 30 \
--keepalive 2 \
--max-requests 1000 \
--max-requests-jitter 100 \
--preload \
--daemon \
--pid /var/run/gunicorn.pid \
--access-logfile /var/log/gunicorn/access.log \
--error-logfile /var/log/gunicorn/error.log \
--log-level info
設定ファイル(gunicorn_config.py)
# gunicorn_config.py
import multiprocessing
import os
# サーバー設定
bind = "0.0.0.0:8000"
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = "sync"
worker_connections = 1000
timeout = 30
keepalive = 2
# パフォーマンス設定
max_requests = 1000
max_requests_jitter = 100
preload_app = True
# ログ設定
accesslog = "/var/log/gunicorn/access.log"
errorlog = "/var/log/gunicorn/error.log"
loglevel = "info"
access_log_format = '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s" %(D)s'
# プロセス設定
user = "www-data"
group = "www-data"
pid = "/var/run/gunicorn.pid"
daemon = False
# セキュリティ設定
limit_request_line = 4094
limit_request_fields = 100
limit_request_field_size = 8190
# ワーカー設定
worker_tmp_dir = "/dev/shm" # RAM ディスク使用
# 環境変数設定
raw_env = [
'DJANGO_SETTINGS_MODULE=myproject.settings.production',
'SECRET_KEY=your-secret-key'
]
# サーバーフック
def when_ready(server):
server.log.info("Server is ready. Spawning workers")
def worker_int(worker):
worker.log.info("worker received INT or QUIT signal")
def pre_fork(server, worker):
server.log.info("Worker spawned (pid: %s)", worker.pid)
def post_fork(server, worker):
server.log.info("Worker spawned (pid: %s)", worker.pid)
環境変数による設定
# 環境変数設定
export GUNICORN_CMD_ARGS="--bind=0.0.0.0:8000 --workers=4"
gunicorn flask_app:app
# .env ファイル
echo "GUNICORN_CMD_ARGS=--bind=0.0.0.0:8000 --workers=4" > .env
# Docker環境での使用
docker run -e GUNICORN_CMD_ARGS="--bind=0.0.0.0:8000 --workers=4" myapp
ワーカータイプとパフォーマンス
同期ワーカー(sync)
# 基本的な同期ワーカー設定
bind = "0.0.0.0:8000"
workers = 4
worker_class = "sync"
worker_connections = 1000
timeout = 30
非同期ワーカー(gevent)
# Gevent ワーカー設定
bind = "0.0.0.0:8000"
workers = 2
worker_class = "gevent"
worker_connections = 1000
timeout = 30
# Gevent使用時の追加設定
import gevent.monkey
gevent.monkey.patch_all()
非同期ワーカー(eventlet)
# Eventlet ワーカー設定
bind = "0.0.0.0:8000"
workers = 2
worker_class = "eventlet"
worker_connections = 1000
timeout = 30
スレッドワーカー(gthread)
# スレッドワーカー設定
bind = "0.0.0.0:8000"
workers = 2
worker_class = "gthread"
threads = 4
worker_connections = 1000
timeout = 30
パフォーマンス最適化
# 高性能設定
import multiprocessing
import os
# CPU最適化
workers = min(multiprocessing.cpu_count() * 2 + 1, 8)
worker_class = "gevent"
worker_connections = 2000
# メモリ最適化
max_requests = 1000
max_requests_jitter = 100
preload_app = True
# システム最適化
worker_tmp_dir = "/dev/shm" # RAM ディスク
keepalive = 2
timeout = 60
# 接続最適化
backlog = 2048
本番環境設定
Systemd サービス設定
# /etc/systemd/system/gunicorn.service
[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target
[Service]
Type=notify
User=www-data
Group=www-data
RuntimeDirectory=gunicorn
WorkingDirectory=/var/www/myapp
ExecStart=/var/www/myapp/venv/bin/gunicorn \
--access-logfile - \
--workers 3 \
--bind unix:/run/gunicorn.sock \
myapp.wsgi:application
ExecReload=/bin/kill -s HUP $MAINPID
KillMode=mixed
TimeoutStopSec=5
PrivateTmp=true
[Install]
WantedBy=multi-user.target
Systemd ソケット設定
# /etc/systemd/system/gunicorn.socket
[Unit]
Description=gunicorn socket
[Socket]
ListenStream=/run/gunicorn.sock
SocketUser=www-data
SocketGroup=www-data
SocketMode=0660
[Install]
WantedBy=sockets.target
Docker設定
# Dockerfile
FROM python:3.11-slim
WORKDIR /app
# システム依存関係
RUN apt-get update && apt-get install -y \
gcc \
&& rm -rf /var/lib/apt/lists/*
# Python依存関係
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# アプリケーションコピー
COPY . .
# 非rootユーザー作成
RUN useradd --create-home --shell /bin/bash app
RUN chown -R app:app /app
USER app
EXPOSE 8000
# ヘルスチェック
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1
CMD ["gunicorn", "--config", "gunicorn_config.py", "app:app"]
docker-compose.yml
version: '3.8'
services:
web:
build: .
ports:
- "8000:8000"
environment:
- DJANGO_SETTINGS_MODULE=myproject.settings.production
volumes:
- ./logs:/app/logs
restart: unless-stopped
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./ssl:/etc/ssl
depends_on:
- web
restart: unless-stopped
Nginx との連携
Nginx プロキシ設定
# /etc/nginx/sites-available/myapp
upstream app_server {
server unix:/run/gunicorn.sock fail_timeout=0;
}
server {
listen 80;
server_name example.com www.example.com;
client_max_body_size 4G;
keepalive_timeout 5;
# 静的ファイル配信
location /static/ {
alias /var/www/myapp/static/;
expires 1y;
add_header Cache-Control "public, immutable";
}
location /media/ {
alias /var/www/myapp/media/;
expires 1y;
add_header Cache-Control "public, immutable";
}
# アプリケーション
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_buffering off;
proxy_pass http://app_server;
}
# エラーページ
error_page 500 502 503 504 /500.html;
location = /500.html {
root /var/www/myapp/static/;
}
}
ロードバランシング設定
upstream app_servers {
server 127.0.0.1:8000 weight=1 fail_timeout=0;
server 127.0.0.1:8001 weight=1 fail_timeout=0;
server 127.0.0.1:8002 weight=1 fail_timeout=0;
keepalive 32;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://app_servers;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
}
監視・ログ・トラブルシューティング
ログ設定
# 詳細ログ設定
import logging
import sys
# ログフォーマット
log_format = '%(asctime)s [%(process)d] [%(levelname)s] %(message)s'
access_log_format = '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s" %(D)s %(p)s'
# ログファイル
accesslog = "/var/log/gunicorn/access.log"
errorlog = "/var/log/gunicorn/error.log"
loglevel = "info"
# コンソール出力
if "--log-file" not in sys.argv:
accesslog = "-" # stdout
errorlog = "-" # stderr
統計・監視設定
# gunicorn_config.py での統計設定
import os
# StatsD設定
statsd_host = os.environ.get('STATSD_HOST', 'localhost:8125')
statsd_prefix = 'myapp.gunicorn'
# プロメテウス連携
def child_exit(server, worker):
"""ワーカー終了時の処理"""
server.log.info(f"Worker {worker.pid} exited")
def when_ready(server):
"""サーバー準備完了時の処理"""
server.log.info("Server is ready. Spawning workers")
# カスタム監視ログ
server.log.info(f"Workers: {server.cfg.workers}")
server.log.info(f"Worker class: {server.cfg.worker_class}")
プロセス管理
# プロセス状況確認
ps aux | grep gunicorn
# シグナル送信
kill -HUP <master_pid> # 設定リロード
kill -TTIN <master_pid> # ワーカー追加
kill -TTOU <master_pid> # ワーカー削除
kill -USR1 <master_pid> # ログローテーション
kill -TERM <master_pid> # グレースフル停止
kill -QUIT <master_pid> # 即座停止
# PIDファイル使用
kill -HUP $(cat /var/run/gunicorn.pid)
ヘルスチェック
# health_check.py
#!/usr/bin/env python3
import sys
import requests
import time
def health_check():
try:
response = requests.get('http://localhost:8000/health', timeout=5)
if response.status_code == 200:
print("OK: Application is healthy")
return 0
else:
print(f"ERROR: Health check failed with status {response.status_code}")
return 1
except requests.exceptions.RequestException as e:
print(f"ERROR: Health check failed: {e}")
return 1
if __name__ == '__main__':
sys.exit(health_check())
自動化スクリプト
#!/bin/bash
# deploy.sh
set -e
APP_DIR="/var/www/myapp"
VENV_DIR="$APP_DIR/venv"
PID_FILE="/var/run/gunicorn.pid"
echo "Starting deployment..."
# 仮想環境activate
source $VENV_DIR/bin/activate
# 依存関係更新
pip install -r requirements.txt
# データベースマイグレーション
python manage.py migrate
# 静的ファイル収集
python manage.py collectstatic --noinput
# Gunicorn グレースフルリスタート
if [ -f $PID_FILE ]; then
echo "Reloading Gunicorn..."
kill -HUP $(cat $PID_FILE)
else
echo "Starting Gunicorn..."
systemctl start gunicorn
fi
echo "Deployment completed successfully"