【Django】Nginx + Gunicorn でサブドメインを設定する方法

「example.com」というドメイン名ですでに運用しているところに追加で「sub.example.com」も使いたいという想定で書きます。

また、Django の処理には Gunicorn を使っています。

  1. ネームサーバーでサブドメインの設定をする
  2. サブドメイン用のディレクトリを作る
  3. 仮想環境と Django プロジェクトを作成する
  4. Gunicorn をインストールする
  5. UNIX ドメインソケットを設定する
  6. Nginx の設定ファイルにサブドメインの処理を追記する
  7. とりあえず図にしてみた

1. ネームサーバーでサブドメインの設定をする

ネームサーバーでサブドメインを登録します。

フィールド
エントリ名sub
種別別名 (CNAME)
@
DNSチェックする
TTLの設定なし

さくらインターネットを使っている場合は会員メニュー > ドメイン > ゾーン表示の画面で登録できます。

2. サブドメイン用のディレクトリを作る

任意の場所にサイトをホストするためのディレクトリを作成します。

例えば「example.com」というディレクトリの配下にさらにサブドメイン用に「sub」ディレクトリ、その配下に「html」と作る場合は下記の様な形。

$ cd var/www/example.com
$ mkdir sub
$ cd sub
$ mkdir html

ディレクトリの管理者を変更します。

$ sudo chown -R $USER:$USER /var/www/example.com/sub/html/

注意

一度上記の管理者の変更を飛ばした際、後で Django の「python manage.py startapp アプリ名」を実行した時に「ImportError: Couldn't import Django. Are you sure it's installed and available on your PYTHONPATH environment variable? Did you forget to activate a virtual environment?」というエラーが出ました。

「pip install django」を実行した際に django モジュールが仮想環境内に上手くインストールされなかったことが原因です。

3. 仮想環境と Django プロジェクトを作成する

Python の仮想環境を作成、起動します。

$ cd var/www/example.com/sub/html/
$ python3.9 -m venv dj_proj_venv
$ cd dj_proj_venv
$ source bin/activate

Django を pip install し、プロジェクトとアプリケーションを作成します。

注)ローカルPCで開発したものを Git で本番環境にクローンする様な場合はこのステップは不要です。

(dj_proj_venv) $ pip install django
(dj_proj_venv) $ django-admin startproject dj_proj
(dj_proj_venv) $ cd dj_proj
(dj_proj_venv) $ python manage.py startapp dj_app

settings.py の INSTALLED_APPS にアプリケーションを追記します。

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'dj_app.apps.DjAppConfig', # 追記分
]

settings.py の ALLOWED_HOSTS にサブドメインを含むドメインを記述します。

ALLOWED_HOSTS = ['sub.example.com']

4. Gunicorn をインストールする

Gunicorn をインストールします。

$ pip install gunicorn

5. UNIX ドメインソケットを設定する

そして Nginx と Gunicorn の接続に Unix ソケットを使える様、Linux サーバーの Systemd という機能で設定します。

.socket ファイルの作成

サブドメイン用に「example_sub.socket」というファイルを作成します。

# example_sub.socket
[Unit]
Description=gunicorn socket
[Socket]
ListenStream=/run/example_sub.sock
[Install]
WantedBy=sockets.target
項目メモ
Descriptionログ出力の際などに使われる
ListenStreamリクエストを待ち受けるポートの指定。
Nginx 設定ファイルの pass_proxy で指定するものと同じ。
WantedBysockets.target

.service ファイルの作成

サブドメイン用に「example_sub.service」というファイルを作成します。

上記の「example_sub.socket」へ送られてきたリクエストの引き渡し先を定義します。

# example_sub.service
[Unit]
Description=gunicorn daemon
Requires=example_sub.socket
After=network.target
[Service]
User=root
Group=root
WorkingDirectory=/var/www/example.com/sub/html/dj_proj_venv/dj_proj
ExecStart=/var/www/example.com/sub/html/dj_proj_venv/bin/gunicorn --workers 3 --bind unix:/run/sample_django.sock dj_proj.wsgi:application
[Install]
WantedBy=multi-user.target
項目メモ
Descriptionログ出力の際などに使われる
Requires対応する .socket ファイル
Afterユニットが開始する順序 (ユニットの起動する順番) を設定。
After で指定したユニットがアクティブになると、このユニットを開始する。
User
Group
WorkingDirectorymanage.py があるディレクトリのフルパス?
ExecStartsystemctl start した時に実行するコマンド。
「$ gunicorn [OPTIONS] [WSGI_APP]」のフォーマットで記述。
上記ファイルでは venv 内の gunicorn のフルパスを指定し、オプションとして --workers と --bind を指定した上で Django の wsgi を実行している。
--workers:worker プロセスの数
--bind:bind 対象のサーバーソケット(.sock)。gunicorn のデフォルトは 127.0.0.1:8000
WantedBy大抵 multi-user.target で大丈夫

socket の待ち受けを開始します。

$ systemctl start example_sub.socket
$ systemctl start example_sub.service

6. Nginx の設定ファイルにサブドメインの処理を追記する

「sub.example.com」と「example.com」の処理が記述されています。

それぞれの proxy_pass の記述を見るとわかりますが、「sub.example.com」へのリクエストの場合は example_sub.sock のソケット、「example.com」へのリクエストの場合は example.sock のソケットへ処理が回されます。

server {
        listen 80;
        listen [::]:80;

        server_name sub.example.com;

        root /var/www/example.com/sub/html;
        index index.html;

        location / {
                proxy_set_header Host $http_host;
                proxy_set_header X-Forwarder-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Proto $scheme;
                proxy_pass http://unix:/run/example_sub.sock;
        }

        location /static {
                alias /var/www/example.com/sub/html/static;
        }

}

server {
        listen 80;
        listen [::]:80;

        server_name example.com;

        root /var/www/example.com/html;
        index index.html;

        location / {
                proxy_set_header Host $http_host;
                proxy_set_header X-Forwarder-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Proto $scheme;
                proxy_pass http://unix:/run/example.sock;
        }

        location /static {
                alias /var/www/example.com/html/static;
        }

}
ファイル内の概念解説
listenリクエストを受け入れる IP アドレスやポートの指定。
UNIX ドメインソケットのパスでも可。
server_nameリクエストを受け入れるサーバー名(ドメイン名)の指定。
複数のサーバー名をスペース区切りで設定可。
proxy_set_headerプロキシサーバーへ送るリクエストヘッダーの各フィールドの追加や書き換え。
$proxy_add_x_forwarded_forクライアントからのリクエストヘッダーの X-Forwarded-For フィールドに $remote_addr が追加されたもの。
クライアントからのリクエストヘッダーに X-Forwarded-For が無かった場合は $remote_addr と同じ。
$schemeリクエストスキーム。http か https。
proxy_passプロキシサーバーの指定。Gunicorn につなげるための UNIX ソケットを指定。
参照 Nginx Documentation

7. とりあえず図にしてみた

自分の理解のためにいろんなケースを図にしてみました。

1 つのアプリを Gunicorn のデフォルトポートで実行する場合

まずは一番シンプルな形。サブドメインとかなく、一つのドメインで一つのアプリを公開する形。

Gunicorn のデフォルトの bind 先は 127.0.0.1:8000 だそうなので、そこに対して Nginx から proxy_pass した場合です。

Django + Nginx + Gunicorn での設定

1 つのアプリを UNIX ドメインソケットで実行する場合

UNIX ドメインソケットの方が処理が速いということで(?)大半の記事で UNIX ドメインソケットを使用しているっぽいです。なので自分もこの方法を取っています。

この場合、当記事にも書いてある様に Systemd の .socket ファイルと .service ファイルを作成する必要があります。たぶん。実は必須ではないのかも。わかりません。

Django + Nginx + Gunicorn での設定

2 つのアプリを別々の UNIX ドメインソケットで実行する場合

複数のアプリをサブドメインで分けるという事でこの記事で紹介した方法がこちらです。

別々のソケットを用意し、そこに各サブドメインから proxy_pass で繋いであげれば OK です。

Django + Nginx + Gunicorn での設定

2 つのアプリを別々のポートで実行する場合

ただ、上記が出来るならそもそも UNIX ドメインソケットを使わなくても localhost(127.0.0.1)のポートを分ければ良いんじゃないかと思いました。自分では実際に試してませんが多分いける気がします。

Django + Nginx + Gunicorn での設定

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です