WSL2を使ってWindows11からDjangoをデプロイしよう

前回の記事では、windowsにWSL2(Windows Subsystem for Linux)をインストールする方法を紹介しました。

では今日はWSL2を使ってDjangoをデプロイする方法を説明していきます。UbuntuサーバーからDjangoをデプロイする方法はこちらの記事を参考にしてください。

※今回はwindows11で作業を行っています。SWL2であれば問題はないと思いますが、Windows11を使うことをお勧めします。

※WindowsからDjangoはデプロイできますが、Hyper-VでNATネットワークをUbuntuにアサインしているためにデフォルトでは、Windowsマシン以外からDjangoアプリにアクセスができません。詳しい対応策はMicrosoftのWSLのGitHub Issueを見てください。

Ubuntuサーバーの起動

では前回の記事でインストールしたUbuntuサーバーを起動します。

Ubutnuサーバーの環境の確認

これから作業するにあたり、Linuxのコマンドに慣れていない人は簡単な早見表などを見ておくとよいです。

例えばCtl+Lで入力画面が一番上に来るのでキレイに見えます。

他に入力している途中でCtl+iを押すと推測したテキストが表示されます。長いファイル名の場合は最初の何文字か書いてCtrl+iで記入を終わられるのに便利です。

また、過去に実行したコマンドを見たい場合は上矢印で見ることができます。

UbuntuサーバーのIPアドレスを見てみます。

ip addr

#結果
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: bond0: <BROADCAST,MULTICAST,MASTER> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether 02:37:69:da:93:42 brd ff:ff:ff:ff:ff:ff
3: dummy0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether 82:6e:ca:74:c1:c6 brd ff:ff:ff:ff:ff:ff
4: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000
    link/ipip 0.0.0.0 brd 0.0.0.0
5: sit0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000
    link/sit 0.0.0.0 brd 0.0.0.0
6: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:15:5d:37:f0:07 brd ff:ff:ff:ff:ff:ff
    inet 172.18.173.157/20 brd 172.18.175.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::215:5dff:fe37:f007/64 scope link
       valid_lft forever preferred_lft forever

ip addrのコマンドで分かるようにeth0のインターフェースに172.18.173.157のIPアドレスがあることが分かります。

WindowsのホストマシンのコマンドプロンプトからPingをかけてみました。

#コマンドプロンプトから
ping 172.18.173.157

リスポンスがあったので、ここからデプロイできそうです。

次にPythonが入っていることを確認します。

python3 --version

#結果
Python 3.10.6

#注意
pythonを実行する際はpythonではなく、python3で入力します。

次にUbuntuソフトウェア、パッケージのアップデートをします。

sudo apt update

Ubutnuユーザーをwww-dataグループに追加します。

sudo adduser dan www-data

#danはUbuntuユーザー名

#結果
Adding user `dan' to group `www-data' ...
Adding user dan to group www-data
Done.

※これからの作業の注意点

Ubuntuユーザーを作成した際にsudoの権限がすでについています。rootに切り替えて作業せず、そのままsudo権限のあるユーザーのまま作業を行いましょう。理由はnginxやファイルの実行の際に権限が与えられていないとエラーが発生するからです。

Djangoデプロイに使うライブラリをUbuntuにインストール

下記のコマンドでUbuntuサーバーにグローバルにインストールしていきます。

sudo apt install python3-pip python3-dev libpq-dev postgresql postgresql-contrib nginx curl virtualenv
  • python3-pip →Pythonのパッケージマネージャー
  • python3-dev →スタティックライブラリ、デベロップメントツール
  • libpq-dev →PostgreSQLで使います
  • postgresql →データベースでPostgreSQLを使用する場合
  • postgresql-contrib →データベースでPostgreSQLを使用する場合
  • nginx →ウェブサーバー(Apacheよりもnginxをお勧めします)
  • curl →URLのテストツール
  • virtualenv →仮想環境の作成に使う(他のアプリのバージョンと干渉させないため)

PostgreSQLのデータベースを作成

ではこちらの参考Djangoのプロジェクトを使用してデプロイさせていくことにします。もしPostgreSQLを使ったことがない人は1時間くらいかけてデータベースのSQLコマンドを練習、理解してみてください。今後データベースのデータの修正やバックアップを取るためにコマンドラインを使っていくことになるので慣れておくとよいです。

postgresをインストールしたときに自動でpostgres というアドミン権限のユーザーが作成されます。

この権限を使って新しいデータベースとユーザーを作成します。

まずは、PostgreSQLのステータスを確認します。

service postgresql status

#結果
14/main (port 5432): down

#ダウンの場合
#PostgreSQLを起動
sudo service postgresql start

#ステータスがonlineになっていればOK

PostgreSQLが起動できたらPostgresのユーザーに切り替えます。

#Ubuntuのユーザーからpostgresに変更
sudo -u postgres psql

これでユーザーが切り替わりSQLの入力モードになったことが分かります。

では、Djangoのsettings.pyに合わせたデータベースを作成していきます。

セミコロンを忘れずに!

CREATE DATABASE asameshidb; asameshidbはDB名

#結果
CREATE DATABASE

\l
                              List of databases
    Name    |  Owner   | Encoding | Collate |  Ctype  |   Access privileges
------------+----------+----------+---------+---------+-----------------------
 asameshidb | postgres | UTF8     | C.UTF-8 | C.UTF-8 |
 postgres   | postgres | UTF8     | C.UTF-8 | C.UTF-8 |
 template0  | postgres | UTF8     | C.UTF-8 | C.UTF-8 | =c/postgres          +
            |          |          |         |         | postgres=CTc/postgres
 template1  | postgres | UTF8     | C.UTF-8 | C.UTF-8 | =c/postgres          +
            |          |          |         |         | postgres=CTc/postgres
(4 rows)

\lのコマンドでDBの一覧が表示されます。

これでpostgresユーザーがオーナーのデータベースが出来ました。

PostgreSQLのコンフィグレーション

もしデータベースのオーナーをpostgres以外に設定したい場合は新しいPosgreSQLのユーザーを作成して所有権を移動させてください。

CREATE USER djangouser WITH PASSWORD 'django1234';

#結果
CREATE ROLE

#passwordはもっと複雑なものにしてください。

ではpostgresの接続をスムースにするためにコネクションパラメーターを変えます。

まずはエンコーディングをutf-8に変えます。

ALTER ROLE djangouser SET default_transaction_isolation TO 'read committed';

#結果
ALTER ROLE

Postgresのタイムゾーンを日本時間に変えます。

Djangoのタイムゾーンがデフォルト(初期設定)でUTCにセットさせているのでそれを変更していない人はSettnigs.pyから変更しておきましょう。

ALTER ROLE djangouser SET timezone TO 'Asia/Tokyo';

#結果
ALTER ROLE

#タイムゾーンの一覧を確認したい人は下記のコマンド
SELECT * FROM pg_timezone_names;

次に、作成したユーザーにdb(データベース)のアドミン権限をつけます。

GRANT ALL PRIVILEGES ON DATABASE asameshidb TO djangouser;

#結果
GRANT

postgresは一旦完了なのでquitと入力してでpostgresから抜け出しましょう。

もしくはCtl+Zでも抜けられます。

Django用に仮想環境を作成

pythonではいくつか仮想環境のライブラリがありますが今回はvirtualenvを使用します。

仮想環境を使ったことがない人は詳しく理解しておいた方が良いですね。

簡単に説明すると同じサーバーで違うバージョンのライブラリを使いたいとき(例えばDjangoの3.0と4.0)に仮想環境をアクティベートして同じマシンでも同時に使用できる様になります。

また、複数人で仕事をするときもバージョンを統一してするために使います。

Pipの準備
pipはpythonパッケージマネージャーでpythonのライブラリをインストールする際に使用しますね。

-HのフラグはsudoのコマンドでユーザーのhomeディレクトリではなくrootのHOMEディレクトリにインストールするように指示します。

まずはpipを最新のものにします。

sudo -H pip3 install --upgrade pip

virtualenvはすでにインストールしているのでこれを使って仮想環境を作成します。(似たものでvenvがあるのでそれでもOKです)

まずは、Djangoのプロジェクトを置く場所を決めましょう。apacheを使ってきた人は/var/wwwのディレクトリに慣れているかもしれません。もちろんそれでもOKです。

今回はNginxと、特定のユーザーを使ってデプロイするので/home/user (※userはUbuntuのユーザー名)にDjangoのプロジェクトを置くようにしましょう。

では、ユーザーのディレクトリに移動します。

cd /home/asameshi

#みなさんの場合は自分のユーザー名の場所

cd /home
ls
#lsで存在するフォルダーが見れるのでそれで
Ctl + i で推測入力することも可能です。

pwd
#現在のファイルパスが表示されます。


Djangoプロジェクトの作成

では、自分で作成したDjangoのプロジェクトを/home/userに置きます。

もしWindowsのファイルエクスプローラーでファイルをドラッグ&ドロップしたい場合は下記のファイルパスを参考にしてください。

\wsl.localhost\Ubuntu\home\ユーザー名

ファイルエクスプローラーを開くとLinuxのディレクトリがあるのでそこからアクセスも可能です。

一番のおすすめのやり方はGitとGitHubを使った方法です。

その場合は下記のコマンドを参考にしてください。

#Gitがインストールされているか確認
git --version

#結果
git version 2.34.1

#正しいパスにいることを確認
pwd

#GitHubリポからコードをクローンする
git clone git@github.com:TraitOtaku/JS-Hiroba-Backend.git

ここで、SSHキーがGitHubに登録されていないとセキュリティのエラーでリポのクローンができないので、こちらの記事を参考にしてSSHキーをGitHubに登録しましょう。コマンドだけ記載しておきます。

cd ~/.ssh

ssh-keygen -t rsa

下記にいくつか質問されるのですべてエンターで飛ばしていきます。もし合言葉を設定したい場合は入力することも可能です。

SSHキーだけでもセキュリティが強化されるので不要だとは思いますが。

次にcatコマンドでパブリックキーを閲覧します。

cat id_rsa.pub

#表示されたキーをハイライトして右クリックすることで自動でコピーされます。

では記事に従い、SSHキーを登録できたところで再度Git cloneのコマンドを実行します。

cd /home/dan

git clone git@github.com:TraitOtaku/JS-Hiroba-Backend.git

#結果
dan@asameshi:~$ git clone git@github.com:TraitOtaku/JS-Hiroba-Backend.git
Cloning into 'JS-Hiroba-Backend'...
remote: Enumerating objects: 267, done.
remote: Counting objects: 100% (267/267), done.
remote: Compressing objects: 100% (195/195), done.
remote: Total 267 (delta 79), reused 257 (delta 69), pack-reused 0
Receiving objects: 100% (267/267), 1.21 MiB | 5.22 MiB/s, done.
Resolving deltas: 100% (79/79), done.

これでリポジトリのクローンができました。

Windowsのファイルエクスプローラーを見ると確かにリポがクローンされていることが確認できました。

もし手動でファイルを置きたい場合はここにおいてください。

仮想環境をアクティベート

下のコマンドでvirtualenvで仮想環境を作成します。envの所はディレクトリ名なので自分で好きなものを決めてください。

cd Djangoのプロジェクトディレクトリ

virtualenv env

#virtualenvのライブラリがない場合
sudo apt install python3-virtualenv

ls
#結果
command  env  js_hiroba  manage.py  memo.txt  requirements.txt  static

では、/env/binフォルダにあるactivateを実行して仮想環境を起動します。

source env/bin/activate

#envが付きました!
(env) dan@asameshi:~/JS-Hiroba-Backend$

lsでDjangoのプロジェクトのルートディレクトリを参照します。

Pythonのプログラムを作業するにあたりrequirements.txtというファイルを作成することが慣習になります。このファイルがPythonのプロジェクトで使用するライブラリの一覧になります。

ではこのテキストファイルを使ってDjangoプロジェクトに必要なライブラリをインストールしていきます。

pip install -r requirements.txt

#結果
Defaulting to user installation because normal site-packages is not writeable
Collecting asgiref==3.5.2
  Downloading asgiref-3.5.2-py3-none-any.whl (22 kB)
Collecting Django==4.1.4
  Downloading Django-4.1.4-py3-none-any.whl (8.1 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 8.1/8.1 MB 5.3 MB/s eta 0:00:00
Collecting django-cors-headers==3.13.0
  Downloading django_cors_headers-3.13.0-py3-none-any.whl (13 kB)
Collecting djangorestframework==3.14.0
  Downloading djangorestframework-3.14.0-py3-none-any.whl (1.1 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.1/1.1 MB 5.3 MB/s eta 0:00:00
Collecting psycopg2-binary==2.9.5
  Downloading psycopg2_binary-2.9.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.0 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.0/3.0 MB 5.9 MB/s eta 0:00:00
Collecting pytz==2022.6
  Downloading pytz-2022.6-py2.py3-none-any.whl (498 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 498.1/498.1 kB 5.3 MB/s eta 0:00:00
Collecting sqlparse==0.4.3
  Downloading sqlparse-0.4.3-py3-none-any.whl (42 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 42.8/42.8 kB 6.6 MB/s eta 0:00:00
Installing collected packages: pytz, sqlparse, psycopg2-binary, asgiref, Django, djangorestframework, django-cors-headers
  WARNING: The script sqlformat is installed in '/home/dan/.local/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
  WARNING: The script django-admin is installed in '/home/dan/.local/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
Successfully installed Django-4.1.4 asgiref-3.5.2 django-cors-headers-3.13.0 djangorestframework-3.14.0 psycopg2-binary-2.9.5 pytz-2022.6 sqlparse-0.4.3

もしrequirements.txtがない場合は必要なライブラリを手動でインストールしましょう。

Gunicornのインストール

次にデプロイで使用するGunicornをインストールします。すでにインストールされている場合はその旨を言われるので確認しておきましょう。Gunicornの説明はこちらの記事をどうぞ。

pip install gunicorn 

#すでにインストールされている場合
Requirement already satisfied: asgiref<4,>=3.5.2 in ./env/lib/python3.10/site-packages (from django) (3.5.2)
Requirement already satisfied: sqlparse>=0.2.2 in ./env/lib/python3.10/site-packages (from django) (0.4.3)
Requirement already satisfied: setuptools>=3.0 in ./env/lib/python3.10/site-packages (from gunicorn) (59.6.0)
Installing collected packages: gunicorn
Successfully installed gunicorn-20.1.0

#requirements.txtを更新する場合
pip freeze > requirements.txt

settings.pyのコンフィグレーション

では、Djangoが正しく起動できるために、settings.pyのコンフィグレーションを確認します。

今回はコマンドラインのテキストエディタのnanoを使ってUbuntuサーバーに上げたファイルをいじりますが、オリジナルを編集してアップロードしなおしてもOKです。

cd js_hiroba/
#もしくは
cd djangoプロジェクトフォルダ

ls
sudo nano settings.py

下記のラインが正しく記載されているか確認しましょう。

ALLOWED_HOSTS = ['127.0.0.1', '172.18.173.157']

    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.postgresql_psycopg2',
            'NAME': 'asameshidb',
            'USER': 'djangouser',
            'PASSWORD': 'django1234',
            'HOST': '127.0.0.1',
            # 'PORT': '5432',
        }
    }

ALLOWED_HOSTS = [ *] で全てのIpを許可しますが、セキュリティ上、良くないのでこれはやめましょう。

Staticファイルの扱い

同じくsettings.pyです。

下記のようにosモジュールをインポートしてStaticファイル(CSS,JS,画像ファイル)等の保管場所を指定してあげます。

import os
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static/')

データベースのマイグレーション

ではmanage.pyがあるディレクトリから下記のコマンドで接続したDBにDjangoのモデルで作成したテーブルの枠組みをmigration(マイグレーション:移動)します。

python3 manage.py makemigrations
python3 manage.py migrate

#DBテーブルが作成されたことが表示されます。

スーパーユーザーの作成

次にDjangoのアドミンパネルに入るためのスーパーユーザを作成します。

python3 manage.py createsuperuser

collectstaticの実行

ではmanage.pyから下記のコマンドを実行して、Staticファイルをデプロイ用に一か所にまとめます。

python3 manage.py collectstatic

#yesで実行

ファイヤーウォールの設定

では、このDjangoプロジェクトをテストで公開するために下記のコマンドでサーバーのポートを開けます。

sudo ufw allow 8000

#結果
Rules updated
Rules updated (v6)

Djangoのテストサーバーでテスト

では下記のコマンドでDjangoにおまけでついてきたウェブサーバーで実際にpublicのネットワークから見れるか確認しましょう。

ちなみに自宅のLAN環境の場合はポートフォワーディングをして外からのアクセスを許可しないといけません。

ではWindowsのブラウザからDjangoのアプリにアクセスしてみます。

http://172.18.173.157:8000/admin

Ipはip addrで出たIPを記載してください。:8000はポート番号になります。

これで、windowsからUbuntuでホスティングされているDjangoにアクセスすることができました。

しかし実際のデプロイではpython manage.py runserverで起動されるデベロップメント用のテストサーバーは使用しません。

Gunicornを使ってテスト

では、今度はgunicornを使って同じようにDjangoアプリをデプロイできるか試してみましょう。

※このproject.wsgiのprojectはDjangoのアプリ名です。settings.pyが入っているディレクトリ名ですね。

gunicorn --bind 0.0.0.0:8000 js_hiroba.wsgi

※必要に応じてパスを移動(pwdで確認)
gunicorn --bind 0.0.0.0:8000 Djangoのプロジェクト名.wsgi

[2023-01-24 14:43:41 -0500] [3720] [INFO] Starting gunicorn 20.1.0
[2023-01-24 14:43:41 -0500] [3720] [INFO] Listening at: http://0.0.0.0:8000 (3720)
[2023-01-24 14:43:41 -0500] [3720] [INFO] Using worker: sync
[2023-01-24 14:43:41 -0500] [3721] [INFO] Booting worker with pid: 3721

では同じようにWindowsから同じようにブラウザにアクセスします。

http://172.18.173.157:8000/admin/

GunicornでDjangoアプリがデプロイのテストができました。

スタイルが抜けている部分は無視しておいて大丈夫です。

では同じようにCtrl +CでGunicornを止めておきます。

Systemdソケットとサービスファイルの作成

ではここからgunicorn様にsystemd socket(ソケット)とサービスファイルを作成します。

さっき、gunicornが動くことが分かったのですが、よりコントロールのある設定をしていきます。

下記のコマンドでソケットファイルを作成します。

sudo nano /etc/systemd/system/gunicorn.socket

中身はこのようになります。

[Unit]
Description=gunicorn socket
[Socket]
ListenStream=/run/gunicorn.sock
[Install]
WantedBy=sockets.target

次に、systemdサービスファイルを作成します。

sudo nano /etc/systemd/system/gunicorn.service

中はこんな感じです。

Nanoの使い方が分からない人は調べておきましょう。Ctrl+Oで上書きします。

UserはUbuntuのユーザー名になります。

[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target
[Service]
User=dan
Group=www-data
WorkingDirectory=/home/dan/JS-Hiroba-Backend
ExecStart=/home/dan/JS-Hiroba-Backend/env/bin/gunicorn \
          --access-logfile - \
          --workers 3 \
          --bind unix:/run/gunicorn.sock \
          js_hiroba.wsgi:application
[Install]
WantedBy=multi-user.target
  • User=Ubuntuのユーザー名
  • Group=www-data(これからUbuntuのユーザーをwww-dataに追加します。)
  • WorkingDirectory /home/Ubuntuユーザー/Djangoプロジェクト
  • ExecStart=/home/dan/Djangoプロジェクト/env/bin/gunicorn
  • その下で、Djangoプロジェクト.wsgi:application

次に下のコマンドでGunicornソケットを実行します。これで自動でソケットファイルが作成されます。

sudo systemctl start gunicorn.socket

#結果
System has not been booted with systemd as init system (PID 1). Can't operate.
Failed to connect to bus: Host is down

ここで、systemdが起動されていないというエラーが出ました。下記にLinuxの重要なSystemdtosystemctlについて簡単に説明します。

systemd は init systemで、起動時に実行されるアプリケーションのことです。systemdはLinuxの多くのディストリビューターで使われる新しいスタンダードとなるシステムマネージャー(管理システム)になります。Linuxのアドミン作業でsystemdを使っていくことになるので知っておいて損はありません。systemd は比較的新しいシステムなので、Linuxディストロによってはインストールされていないこともあります。また、今回のWSL2ではデフォルトでインストールされていないようです。

では起動時にsystemdが使えるようにUbuntuのコンフィグをいじります。

sudo nano /etc/wsl.conf

#下記の行を追加してセーブします。

[boot]
systemd=true

ではUbuntuサーバーを再起動するために、Windowsのコマンドラインから下記のコマンドを実行します。

wsl --shutdown

#これでUbuntuのコマンドラインインターフェースが落ちます。

では再度WSL2のUbuntuを起動しなおしましょう。

再度、Gunicornのソケットファイルを作成します。

sudo systemctl start gunicorn.socket

これでエラーが出なければOKです。

gunicorn.socketをアクティブにします。

sudo systemctl enable gunicorn.socket

#結果
Created symlink /etc/systemd/system/sockets.target.wants/gunicorn.socket → /etc/systemd/system/gunicorn.socket.

では下記のコマンドでプロセスのステータスを確認しましょう。

sudo systemctl status gunicorn.socket

#結果
● gunicorn.socket - gunicorn socket
     Loaded: loaded (/etc/systemd/system/gunicorn.socket; enabled; vendor preset: enabled)
     Active: active (listening) since Tue 2023-01-24 15:34:40 EST; 1min 4s ago
   Triggers: ● gunicorn.service
     Listen: /run/gunicorn.sock (Stream)
     CGroup: /system.slice/gunicorn.socket

Jan 24 15:34:40 asameshi systemd[1]: Listening on gunicorn socket.

次に、/runディレクトリ直下にgunicorn.sockファイルがあるか確認します。

file /run/gunicorn.sock

#結果
/run/gunicorn.sock: socket

※もしここで、systemctl statusのコマンドでエラーが出るか、gunicorn.sockファイルが見つからないメッセージが出た場合はgunicornソケットが作成されていないことが原因になるかと思います。下記のコマンドでgunicornソケットのログを出しましょう。

sudo journalctl -u gunicorn.socket

次のステップに進む前に下記のファイルに問題が無いか確認してください。

/etc/systemd/system/gunicorn.socket

ソケットアクティベーションのテスト

Gunicorn.scoketユニットをスタートしただけだとgunicorn.serviceはアクティブになりません。それはソケットが接続を受けていないからです。

下記のコマンドでステータスをチェックしましょう。

sudo systemctl status gunicorn

#結果
○ gunicorn.service - gunicorn daemon
     Loaded: loaded (/etc/systemd/system/gunicorn.service; disabled; vendor preset: enabled)
     Active: inactive (dead)
TriggeredBy: ● gunicorn.socket

gunicorn.service; disabledになっているのでGunicornを起動します。

sudo systemctl start gunicorn

sudo systemctl status gunicorn

#結果
● gunicorn.service - gunicorn daemon
     Loaded: loaded (/etc/systemd/system/gunicorn.service; disabled; vendor preset: enabled)
     Active: active (running) since Tue 2023-01-24 15:43:52 EST; 29s ago
TriggeredBy: ● gunicorn.socket
   Main PID: 611 (gunicorn)
      Tasks: 4 (limit: 19139)
     Memory: 120.5M
     CGroup: /system.slice/gunicorn.service
             ├─611 /home/dan/JS-Hiroba-Backend/env/bin/python /home/dan/JS-Hiroba-Backend/env/bin/gunicorn --access-log>
             ├─612 /home/dan/JS-Hiroba-Backend/env/bin/python /home/dan/JS-Hiroba-Backend/env/bin/gunicorn --access-log>
             ├─613 /home/dan/JS-Hiroba-Backend/env/bin/python /home/dan/JS-Hiroba-Backend/env/bin/gunicorn --access-log>
             └─614 /home/dan/JS-Hiroba-Backend/env/bin/python /home/dan/JS-Hiroba-Backend/env/bin/gunicorn --access-log>

Jan 24 15:43:52 asameshi systemd[1]: Started gunicorn daemon.
Jan 24 15:43:52 asameshi gunicorn[611]: [2023-01-24 15:43:52 -0500] [611] [INFO] Starting gunicorn 20.1.0
Jan 24 15:43:52 asameshi gunicorn[611]: [2023-01-24 15:43:52 -0500] [611] [INFO] Listening at: unix:/run/gunicorn.sock >
Jan 24 15:43:52 asameshi gunicorn[611]: [2023-01-24 15:43:52 -0500] [611] [INFO] Using worker: sync
Jan 24 15:43:52 asameshi gunicorn[612]: [2023-01-24 15:43:52 -0500] [612] [INFO] Booting worker with pid: 612
Jan 24 15:43:52 asameshi gunicorn[613]: [2023-01-24 15:43:52 -0500] [613] [INFO] Booting worker with pid: 613
Jan 24 15:43:52 asameshi gunicorn[614]: [2023-01-24 15:43:52 -0500] [614] [INFO] Booting worker with pid: 614
Jan 24 15:43:53 asameshi gunicorn[613]:  - - [24/Jan/2023:20:43:53 +0000] "GET / HTTP/1.1" 400 143 "-" "curl/7.81.0"

ソケットのアクティベーションのメカニズムをテストするには、curlコマンドを使ってソケットに接続シグナルを送ります。

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

#結果
<!DOCTYPE html>
<html lang="en">
<head>
  <meta http-equiv="content-type" content="text/html; charset=utf-8">
  <meta name="robots" content="NONE,NOARCHIVE">
  <title>DisallowedHost
          at /</title>
  <style type="text/css">
    html * { padding:0; margin:0; }
    body * { padding:10px 20px; }
    body * * { padding:0; }
    body { font:small sans-serif; background-color:#fff; color:#000; }
    body>div { border-bottom:1px solid #ddd; }
    h1 { font-weight:normal; }
    h2 { margin-bottom:.8em; }
    h3 { margin:1em 0 .5em 0; }
-------

もし400エラーが返ってきた場合はsetting.pyのDEBUGがFalseになっていることが原因の一つになる可能性があります。テストのためにDEBUG = Trueにして問題がなければFalseに戻すようにしておきましょう。

では、Gunicornのステータスをもう一度確認してみましょう。

sudo systemctl status gunicorn

#修正したら下記のコマンド
sudo systemctl daemon-reload

#で、gunicornのプロセスもリスタートしましょう。
sudo systemctl restart gunicorn

では、次に進む前にここまでで問題ないことを確認しましょう。

NginxのGunicornにパスするプロキシを設定する

では、gunicornも設定ができたところで、次にプロセスにnginxのトラフィックを設定してきます。

nginxはウェブサーバーでフロントエンドのアプリケーションをデプロイする際にも使うので是非慣れておきたいですね。

まずは、Nginxのsites-availableディレクトリに新しいサーバーブロックを作成します。

ファイル作成前に理解しておきたい部分を説明します。

  • サーバーブロックとは各ウェブサイトのコンフィグをファイルごとに分けているものを指します。
  • sites-availableはサイト(Djangoアプリなど)のサーバーブロックを保管するディレクトリになります。
  • /etc/nginxにsites-enableというディレクトリがあります。ここに実際に公開したいサイトをシンボリックリンクでリンクしておきまsす。これはWindowsのショートカットのようなイメージになります。
  • なので、nginxのサーバーブロックを編集するする際にはいつもsites-available内のファイルを編集することになります。

先に学習の為にnginxのデフォルトのコンフィグを見てみましょう。

cd /etc/nginx
ls
sudo nano nginx.conf

#この部分を見てください。

http {
        include /etc/nginx/conf.d/*.conf;
        include /etc/nginx/sites-enabled/*;
}

このnginx.confがnginxが最初に参照するコンフィグになります。

ここで分かるように/etc/nginx/sites-enabled/*のすべてのファイルが読み込まれるようになります。

では、これが理解できたところで、/etc/nginx/sites-availableにサーバーブロックを作成してsites-enabledにリンクさせましょう。

sudo nano /etc/nginx/sites-available/django-api

#django-apiの部分は自分のサイト名にするのが一般的

下記にベーシックなサーバーブロックのコンフィグ例を記載します。

server {
    listen 80;
    server_name 172.18.173.157;
    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /home/dan/JS-Hiroba-Backend;
    }
    location / {
        include proxy_params;
        proxy_pass http://unix:/run/gunicorn.sock;
    }
}
  • listen 基本は80番ポートになります。もし81番ポートを使いたい場合は81を記載します。その場合ドメインの後にポートを記載してDjangoアプリにアクセスすることになります。例:172.18.173.157:81、asameshicode.com:81など
  • server_nameはユーザーがURLに打ち込むドメイン名です。 パブリックIPからアクセスさせるにはIPアドレスを記載します。ドメインがある場合はドメイン名(asameshicode.com www.asameshicode.com)を記載します。
  • location /static/ にstaticファイルのファイルパスを指定します。

もしドメインを購入して、SSL(HTTPS)から公開したい場合は下記の様になります。

※この場合はSSLの取得と、DNSでドメインの設定が必要になります。先に80ポートからIPアドレスで公開できるか試しておきましょう。Let’sEncryptでSSLを無料で取得する方法はこの記事を参照してください。

server {
    listen 80;
    listen [::]:80;
    server_name asameshi-api.cloud www.asameshi-api.cloud;
    return 301 https://asameshi-api.cloud$request_uri;
}


server {
    listen 443 ssl;
    server_name asameshi-api.cloud www.asameshi-api.cloud;

    ssl_certificate /etc/letsencrypt/live/asameshi-api.cloud/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/asameshi-api.cloud/privkey.pem;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /home/dan/JS-Hiroba-Backend;
    }
    location / {
        include proxy_params;
        proxy_pass http://unix:/run/js-hiroba-backend.sock;
    }
}

では、サーバーブロックのファイルができたところでシンボリックリンクでsites-enabledディレクトリ内にリンクしてあげます。

sudo ln -s /etc/nginx/sites-available/django-api /etc/nginx/sites-enabled

#django-apiは自分のサーバーブロックのファイル名に変更しておくこと

では、nginxのコンフィグレーションをテストしてみましょう。

sudo nginx -t

#結果
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

okが返ってきたら大丈夫です。

ここでエラーがでなければ、再度nginxをリスタートします。

sudo systemctl restart nginx

では、最初に開けた8000番のポートはデベロップメント用なので閉じてあげます。

sudo ufw delete allow 8000

#結果
sudo ufw status

#結果
Status: inactive

もしufw statusのコマンドでファイヤーウォールがinactiveの場合はアクティベートしてあげます。

sudo ufw enable

それから、通常のアクセスは80番と443番のポートなので HTTPとHTTPSのポートを下記のように許可します。

sudo ufw allow 'Nginx Full'

#結果
Rule added
Rule added (v6)

必要に応じてサービスを再起動します。

sudo systemctl restart nginx
sudo systemctl restart gunicorn

これでDjangoのデプロイは一応できました!

ここで大きな問題があることに気が付きました。

課題①UbuntuをホストしているWindowsからはDjangoアプリにアクセスできるが、他のネットワークデバイスからはアクセスできない。

課題②Ubuntuサーバーを起動する度にIPアドレスが変わる。

最初は、固定IPにすればよいのではと思いましたがこの問題はGitHubでも大きなトピックになっているようでした。

https://github.com/microsoft/WSL/issues/4150

理由のうちの一つとしてWSL2のネットワークコンフィグレーションはWindowsのHyper-Vで行っていることとと、NATネットワークを使用していることが原因です。

UbuntuのコマンドラインでこのようにデフォルトゲートウェイのIPを見つけました。

ip r | grep default

#結果
default via 172.18.160.1 dev eth0 proto kernel

ではwindowsの方でネットワークの詳細を確認しました。

やはりHyper-Vのバーチャルネットワークアダプターを使用していることが分かりました。

この課題に関しては別途、記事にしようと思います。

結論

WindowsからUbuntuなどのLinuxディストロを使ってデプロイするには、バーチャルマシンのVMWareやVirtualBoxの方がネットワークコンフィグレーションが簡単であることが分かりました。VirtualBoxではネットワークアダプターの設定がUI上で変更できるのでとても簡単です。もちろん、VirtualBoxの方が使用するRAMが多くなりますが、設定が楽なのは確かです。