Django デプロイ作業(Part.6)
この記事では「Djangoのデプロイ作業」の基本的な流れを「Virtual Box」を利用して行います。作業内容は以下の通りです。
ローカル側(開発環境側)での作業
- 「settings.py」を編集する
- 利用しているパッケージを「requirements.txt」に出力する
- Githubへプロジェクトをpushする
サーバー側(本番環境側)での作業
- Virtual BoxにRocky Linux を準備する
- DBMS(この記事ではPostgreSQL)のインストールと設定
- gitのインストールと設定
- Githubからプロジェクトをクローンする
- python仮想環境の準備
- gunicornのインストールと設定
- Webサーバー(この記事ではnginx)のインストールと設定
サーバー側(本番環境側)での作業
Gunicornの準備
Gunicornのインストールと設定①
Djangoの実行にはGunicornというソフトウェアが必要です。これは「PythonのWeb Server Gateway Interface(WSGI)を実装するHTTPサーバー」です。DjangoアプリケーションとWebサーバー間のインターフェースとして機能してくれます。
python仮想環境(この記事ではenv)を持つディレクトリ(この記事ではPosディレクトリ)に移動してGunicornをインストールします。
(env) [django@localhost Pos]$ pip install gunicorn
インストール後、Gunicornを利用してDjangoアプリケーションが実行できるかを確認します。そのために一時的に任意のポート(この記事では8000番)のファイアウォールを開放します。
ルートユーザーに切り替え次のコマンドを入力します。(一般ユーザーからsudoを付けて実行も可能です。)
(env) [django@localhost pos]$ deactivate
[django@localhost pos]$ su –
Password:
[root@localhost ~]# firewall-cmd –add-port=8000/tcp –permanent
success
[root@localhost ~]# firewall-cmd –reload
success
ルートユーザーを抜けてpython仮想環境に戻ります。
[django@localhost Pos]$ source env/bin/activate
(env) [django@localhost Pos]$ cd pos
viエディタで「settings_product.py」を開いて「ALLOWED_HOSTS」に本番環境で利用するIPアドレスを入力します。IPアドレスは「ifconfig」コマンドで確認します。この記事作成時の筆者の環境では「192.168.2.155」でした。(固定IPアドレスにするのが望ましいですが、デプロイの流れを確認する記事なので固定IPアドレスの設定は行っていません。)
from .base import *
# SECURITY WARNING: keep the secret key used in production secret!
# 新しいシークレットキーを設定します。
SECRET_KEY = 'xv!*********************************************7@f)'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False
# 環境によって変更
ALLOWED_HOSTS = ["192.168.2.155 "]
# Database
# https://docs.djangoproject.com/en/4.1/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'djangodatabase', # データベース名
'USER': 'djangouser', # ユーザー名
'PASSWORD': 'password', # パスワード
'HOST': '127.0.0.1', # localhost
'PORT': '5432', # 5432
}
}
settingsフォルダ内のbase.pyに次の4行を追記します。
(「base.py」は「元settings.py」)
import os
STATIC_ROOT = os.path.join(BASE_DIR, ‘static’)
MEDIA_URL = ‘/media/’
MEDIA_ROOT = os.path.join(BASE_DIR, ‘media’)
from pathlib import Path
# ここがひとつ目#######################
import os
BASE_DIR = Path(__file__).resolve().parent.parent.parent
INSTALLED_APPS = [
'monthlyaccount.apps.MonthlyaccountConfig',
'depositbook.apps.DepositbookConfig',
'cashbook.apps.CashbookConfig',
'accountlist.apps.AccountlistConfig',
'consumptiontax.apps.ConsumptiontaxConfig',
'dailyaccount.apps.DailyaccountConfig',
'bills.apps.BillsConfig',
'customerlist.apps.CustomerlistConfig',
'menulist.apps.MenulistConfig',
'mainpage.apps.MainpageConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'pos.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
WSGI_APPLICATION = 'pos.wsgi.application'
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
LANGUAGE_CODE = 'ja'
TIME_ZONE = 'Asia/Tokyo'
USE_I18N = True
USE_TZ = True
STATIC_URL = '/static/'
# ここがふたつ目#######################
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
# ここがみっつ目#######################
MEDIA_URL = '/media/'
# ここがよっつ目#######################
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
ここまで入力ができると、WSGIサーバーからのレスポンスを確認できます。次のように入力してALLOWED_HOSTSに指定しているアドレスにファイアウォールで許可したポートを付加してブラウザでアクセスします。
起動します。
(env) [django@localhost pos]$ gunicorn –bind 0.0.0.0:8000 pos.wsgi:application
この記事の場合は「192.168.2.155:8000」でアクセスします。
ブラウザで次のように表示されました。
今はまだCSSが適用されていませんが、nginxをインストールして設定するときに設定を編集します。その事前準備としてプロジェクト内の静的ファイルを収集します。これは、先ほどbase.pyに追記した「STATIC_ROOT = os.path.join(BASE_DIR, ‘static’)」と「MEDIA_ROOT = os.path.join(BASE_DIR, ‘media’)」によって収集する箇所を指定しています。
(env) [django@localhost pos]$ python manage.py collectstatic
150 static files copied to ‘/home/django/pos/Pos/static’.
staticフォルダを確認できます。
(env) [django@localhost Pos]$ ls
README.md env main.py pos requirements.txt static
(env) [django@localhost Pos]$ ls static/
accountlist bills consumptiontax dailyaccount mainpage monthlyaccount
admin cashbook customerlist depositbook menulist
開いたファイアウォールを閉じておきます。
(env) [django@localhost pos]$ deactivate
[django@localhost pos]$ su –
Password:
[root@localhost ~]# firewall-cmd –remove-port=8000/tcp –permanent
success
[root@localhost ~]# firewall-cmd –reload
success
Webサーバー(nginx)の準備
Webサーバーソフトのインストールとファイアウォールの設定
Webサーバーの「nginx」をインストールします。
[root@localhost ~]# dnf install nginx
Total download size: 640 k
Installed size: 1.8 M
Is this ok [y/N]: y
Rocky Linux を起動したときにnginxも自動で起動するように設定します。
[root@localhost ~]# systemctl enable nginx
nginxを起動します。
[root@localhost ~]# systemctl start nginx
[root@localhost ~]# systemctl status nginx
ファイアウォールの設定から80番ポートを開きます。
[root@localhost ~]# firewall-cmd –add-service=http –permanent
success
[root@localhost ~]# firewall-cmd –reload
success
Gunicornの設定②
gunicorn設定ファイル(「gunicorn.socket」と「gunicorn.service」)の作成と設定を行います。Rocky Linux で設定ファイルは/etc/systemd/systemディレクトリに作成します。
[root@localhost ~]# vi /etc/systemd/system/gunicorn.socket
[Unit]
Description=gunicorn socket
[Socket]
ListenStream=/run/gunicorn.sock
[Install]
WantedBy=sockets.target
[root@localhost ~]# vi /etc/systemd/system/gunicorn.service
(注意)ExecStartの箇所でフォルダの移動を指定しています。必須ではありませんが、この記事の場合「pos.wsgi:application」の場所が深いため、移動の設定が必要になります。
–chdir /home/django/pos/Pos/pos
次の部分はwsgiの準備されているディレクトリを指定して、
「ディレクトリ名.wsgi:application」と記述します。
今回の記述例: pos.wsgi:application
[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target
[Service]
User=django
Group=django
WorkingDirectory=/home/django/pos/Pos/pos/pos
ExecStart=/home/django/pos/Pos/env/bin/gunicorn --workers 3 --bind unix:/run/gunicorn.sock --chdir /home/django/pos/Pos/pos pos.wsgi:application
[Install]
WantedBy=multi-user.target
新規作成した設定ファイルが読み込まれるようにreloadします。
[root@localhost ~]# systemctl daemon-reload
[root@localhost ~]# systemctl start gunicorn.socket
[root@localhost ~]# systemctl enable gunicorn.socket
Created symlink /etc/systemd/system/sockets.target.wants/gunicorn.socket → /etc/systemd/system/gunicorn.socket.
[root@localhost ~]# systemctl status gunicorn.socket
Webサーバーの設定
Gunicornの設定ファイルが作成できたので、最後にWebサーバー(この記事ではnginx)の設定を行います。
設定ファイル(nginx.conf)を開いて次の一行を追記します。これはサーバーの名前が長すぎるときにエラーを吐き出さないようにするために追記します。
[root@localhost ~]# vi /etc/nginx/nginx.conf
http {}の中にserver_names_hash_bucket_size 64;
続けて「nginx.conf」に呼び込まれるオリジナルの設定ファイルを作成します。
「nginx.conf」にはinclude /etc/nginx/conf.d/*.conf;という記述があり、「/etc/nginx/conf.dディレクトリ」内に作成した〇〇.confというファイルを読み込めるように仕組まれています。
この記事では「djangoapp.conf」という名前で作成しています。
[root@localhost ~]# vi /etc/nginx/conf.d/djangoapp.conf
server {
listen 80 default_server;
server_name 192.168.2.155;
location /static/ {
root /home/django/pos/Pos;
}
location /media/ {
root /home/django/pos/Pos;
}
location / {
# gunicorn.socketを作成するとRocky Linux のrunディレクトリにgunicorn.sockというファイルが自動で生成されます。
proxy_pass http://unix:/run/gunicorn.sock;
# ホスト名が呼ばれます。
proxy_set_header Host $http_host;
# 送信元のアドレスが呼ばれます。
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
}
}
設定ファイルのテストを行います。
[root@localhost ~]# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
何かしらの間違いがある場合は設定ファイルの行数を出力してくれます。(下の例では「gunicorn.socket」の14行目にミスがあります。)
Gunicornの設定ファイルの再読み込みとnginxの再起動を行います。
[root@localhost ~]# systemctl restart gunicorn.socket
[root@localhost ~]# systemctl restart gunicorn.service
[root@localhost ~]# systemctl reload nginx.service
上の3つのコマンドを入力するときにエラーが出た場合は次のコマンドを入力してから打ち直します。
[root@localhost ~]# systemctl daemon-reload
SELinuxの設定を変更します。
[root@localhost ~]# vi /etc/selinux/config
Rocky Linux を再起動して、忘れずにPostgreSQLも起動します。
[root@localhost ~]# reboot
[django@localhost ~]$ su –
Password:
[root@localhost ~]# systemctl start postgresql.service
[root@localhost ~]#
指定したIPアドレスへアクセスします。(今回は192.168.2.155)
PCでの表示
スマートフォンでの表示はうまくいっていません。Rocky Linux のhome/djangoディレクトリに、その他のユーザーの実行権限を付与します。
ホームディレクトリのその他のユーザーへの実行権限の付与
[root@localhost ~]# ls
anaconda-ks.cfg
[root@localhost ~]# cd ../
[root@localhost /]# ls
afs boot etc lib media opt root sbin sys usr
bin dev home lib64 mnt proc run srv tmp var
[root@localhost /]# cd home
[root@localhost home]# ls -l
total 4
drwx—––. 5 django django 4096 Oct 15 13:52 django
[root@localhost home]# chmod 701 django/
[root@localhost home]# ls -l
total 4
drwx—–x. 5 django django 4096 Oct 15 13:52 django
[root@localhost home]#
スマートフォンで再度アクセスします。今度はCSSが適用されています。
その他、このような表示になった場合は
502 Bad Gatewayが表示されたら「gunicorn.socket」や「gunicorn.service」の設定ミスが疑えます。その他にもnginxの新規に作成した.confも確認します。
その他にも、うまくいかない場合に次の権限付与も検討します。
「manage.py」に755の権限を付与します。
[root@localhost pos]# chmod 755 manage.py
Djangoのデプロイ作業については以上となります。
ブックマークのすすめ
「ほわほわぶろぐ」を常に検索するのが面倒だという方はブックマークをお勧めします。ブックマークの設定は別記事にて掲載しています。