AI can fly !!

AI がやりたい Web エンジニアのアウトプット (AI の知識は無い)

【Gunicorn】インストールから systemd での起動まで【Django】

gunicorn

はじめに

Django アプリケーションを本番環境で運用するにあたり、 WSGI サーバとして Gunicorn を使用する際の設定手順をまとめました。

本番運用には他にも Nginx などの Web サーバが必要ですが、今回は Gunicorn を systemd でサービス化し、 systemctl コマンドで操作するところまでの説明です。

TL;DR

  • 全部 Gunicorn の公式ドキュメントに載ってるよ

docs.gunicorn.org

動作環境

OS Version
CentOS 7.7.1908
Language Version
Python 3.8.1
Application Version
Gunicorn 20.0.4
Django 3.0.3

他の WSGI サーバとの比較

Gunicorn の他に、 Python Web アプリケーションで人気のある WSGI サーバとして、 uWSGI や Waitress などがあります。

今回初めて WSGI サーバを構築するので正しい評価ではないと思いますが、色々調べた結果、以下の理由で Gunicorn を選択しました。

  • 他の WSGI サーバと比べて、シンプルな実装でサーバリソースが軽量である
  • uWSGI の方が大幅にカスタマイズ可能で高機能だが、 Gunicorn も機能としては必要十分である
  • 初めて WSGI サーバを構築するなら、手軽で利用事例も多い Gunicorn の方がおすすめ

Gunicorn のインストールと起動

インストール

1. PyPI からインストール

まずは、 Python の仮想環境下で pip で Gunicorn をインストールします。

今回は既に Django アプリケーションが作成済みの状態を想定しているので、カレントディレクトリは Django プロジェクトルートになります。

# 仮想環境をアクティベート
. venv/bin/activate

# Gunicorn をインストール
pip install gunicorn

2. Gunicorn で Django をテスト起動

Gunicorn をインストールしたら gunicorn コマンドで Django アプリケーションをテスト起動します。

gunicorn [Django Project Name].wsgi:application

Web ブラウザを起動して http://localhost:8000 を開き、 Django アプリケーションが表示されればインストールは成功です。

コマンド

Gunicorn は gunicorn コマンドで起動します。

gunicorn コマンド

gunicorn [Options] $(MODULE_NAME):$(VARIABLE_NAME)
Target Explanation
MODULE_NAME WSGI モジュール名(カレントディレクトリからのドット付きフルパスで、拡張子は不要)
VARIABLE_NAME WSGI モジュール内の WSGI オブジェクト名

今回使用する主なオプションを以下にまとめました。

Options Default Value Explanation
-b $(HOST):$(PORT), --bind $(HOST):$(PORT) 127.0.0.1:8000 バインドするサーバソケットを指定
-c $(PATH), --config $(PATH) None 設定ファイルのパスを指定
-e $(KEY)=$(VALUE), --env $(KEY)=$(VALUE) [] 環境変数の設定
-d, --daemon False Gunicorn プロセスをデーモン化

設定ファイル

Gunicorn の設定ファイルは、拡張子付きの Python ソースファイルになります。

例えば、 gunicorn.conf.py などです。

CLI で実行する gunicorn コマンドのオプションと同様に、設定したいオプションと値を記述します。

import multiprocessing

bind = "127.0.0.1:8000"
daemon = True
workers = multiprocessing.cpu_count() * 2 + 1

設定の優先順位

同じオプションに対して複数の設定が存在した場合、適用される値は優先度の高い順に

  1. CLI で指定した値
  2. 設定ファイルの値
  3. フレームワーク固有の値

となります。

Django アプリケーションの起動

通常起動

起動

gunicorn [Django Project Name].wsgi:application

通常起動では Gunicorn 起動中はコンソールが専有され、常にログが表示されます。

開発時やテストを行う際に使用することが多いと思います。

停止

Ctrl + C

デーモン起動(バックグラウンド実行)

起動

gunicorn --daemon [Django Project Name].wsgi:application

本番運用時に手動で gunicorn コマンドを実行する場合は、デーモンモードで起動することが主だと思います。

停止

プロセスを調べて kill コマンドで停止します。

いちいちプロセスを調べて停止するのは面倒ですが、後述する systemd でサービス化を行えば systemctl コマンドで起動・停止ができるようになります。

systemd による起動と停止

1. Unit 作成

systemd で Gunicorn を起動するための Unit を作成します。

servicesocket の拡張子以外のファイル名は揃える必要があるので注意してください。

正直 Linux はあまり自信が無いので、 Gunicorn の 公式ドキュメント を参考にしましょう…

docs.gunicorn.org

Service

/etc/systemd/system/gunicorn.service

[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target

[Service]
Type=notify
# the specific user that our service will run as
User=[User Name]
Group=[Group Name]
RuntimeDirectory=gunicorn
WorkingDirectory=[/ から Django Project Root までパス]/[Django Project Root]
ExecStart=[/ から gunicorn コマンドまでのパス]/gunicorn --config gunicon.conf.py [Django Project Name].wsgi:application
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID
KillMode=mixed
TimeoutStopSec=5
PrivateTmp=true

[Install]
WantedBy=multi-user.target

ベースは Gunicorn の 公式ドキュメント にあったものを使用しています。

WorkingDirectoryExecStart を対象の Django アプリケーションのパスに変更しましょう。

Socket

/etc/systemd/system/gunicorn.socket

[Unit]
Description=gunicorn socket

[Socket]
ListenStream=/run/gunicorn.sock
# Our service won't need permissions for the socket, since it
# inherits the file descriptor by socket activation
# only the nginx daemon will need access to the socket
# User=www-data
# Optionally restrict the socket permissions even more.
# Mode=600

[Install]
WantedBy=sockets.target

こちらもベースは Gunicorn の 公式ドキュメント にあったものです。

特に変更点はありませんが、強いて言うなら User くらいでしょうか。

2. Socket の起動

Unit を作成したら、 systemctl コマンドで起動します。

systemctl コマンドを使用する際は、ユーザの権限に応じて sudo コマンドも併用してください。

systemctl enable --now gunicorn.socket

これで gunicorn.socket で指定した /run/gunicorn.sockトラフィックが流れると、自動的に gunicorn.service が起動されるようになります。

Gunicorn を systemd の Socket から起動する場合、監視している UNIX ドメインソケット(今回の例だと /run/gunicorn.sock )へトラフィック流すように、 Web サーバ側で設定をする必要があります。

systemctl コマンド

起動 / 停止 / 再起動

# 起動
systemctl start gunicorn.socket

# 停止
systemctl stop gunicorn.socket

# 再起動
systemctl restart gunicorn.socket

自動起動

# 有効化
systemctl enable gunicorn.socket
# 自動起動有効化と同時に起動
systemctl enable --now gunicorn.socket

# 無効化
systemctl disable gunicorn.socket

状態確認

# gunicorn.socket の状態確認
systemctl status gunicorn.socket

# gunicorn.service の状態確認
systemctl status gunicorn

3. 動作確認

curl コマンドで、設定した UNIX ドメインソケットへリクエストを送信して動作を確認します。

curl --unix-socket /run/gunicorn.sock localhost

コンソール上に Django アプリケーションの HTML が表示されれば、 Gunicorn は正常に動作しています。

また、 systemctl status gunicorngunicorn.service の状態も確認しておきましょう。

おわりに

今回取り上げた Socket から Service を起動する方法の他に、 Service 単体で Gunicorn を常時起動しておき、 Web サーバから Gunicorn がバインドされているURI ( e.g. 127.0.0.1:8000 ) へ転送する方法もあるようです。

ただ、Socket の使用有無について色々調べましたが、いまいちそれぞれのメリット・デメリットが分かりませんでした…

Gunicornの 公式ドキュメント には Socket からの起動方法が書かれていたので、今回は Socket 有の方法を紹介してみました。

どなたか詳しい方、systemd について分かりやすく教えてください!