「example.com」というドメイン名ですでに運用しているところに追加で「sub.example.com」も使いたいという想定で書きます。
また、Django の処理には Gunicorn を使っています。
- ネームサーバーでサブドメインの設定をする
- サブドメイン用のディレクトリを作る
- 仮想環境と Django プロジェクトを作成する
- Gunicorn をインストールする
- UNIX ドメインソケットを設定する
- Nginx の設定ファイルにサブドメインの処理を追記する
- とりあえず図にしてみた
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 で指定するものと同じ。 |
WantedBy | sockets.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 | |
WorkingDirectory | manage.py があるディレクトリのフルパス? |
ExecStart | systemctl 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 ソケットを指定。 |
7. とりあえず図にしてみた
自分の理解のためにいろんなケースを図にしてみました。
1 つのアプリを Gunicorn のデフォルトポートで実行する場合
まずは一番シンプルな形。サブドメインとかなく、一つのドメインで一つのアプリを公開する形。
Gunicorn のデフォルトの bind 先は 127.0.0.1:8000 だそうなので、そこに対して Nginx から proxy_pass した場合です。

1 つのアプリを UNIX ドメインソケットで実行する場合
UNIX ドメインソケットの方が処理が速いということで(?)大半の記事で UNIX ドメインソケットを使用しているっぽいです。なので自分もこの方法を取っています。
この場合、当記事にも書いてある様に Systemd の .socket ファイルと .service ファイルを作成する必要があります。たぶん。実は必須ではないのかも。わかりません。

2 つのアプリを別々の UNIX ドメインソケットで実行する場合
複数のアプリをサブドメインで分けるという事でこの記事で紹介した方法がこちらです。
別々のソケットを用意し、そこに各サブドメインから proxy_pass で繋いであげれば OK です。

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