自動化無しに生活無し

WEBとかAIとかLinux関係をひたすら書く備忘録系ブログ

DjangoをLinux(Ubuntu)サーバーにデプロイする方法【Nginx+PostgreSQL】

thumbnail

DjangoをLinuxサーバーにデプロイできれば、クラウドとは違ってハードウェアの性能を余すこと無く使うことができる。

高度なAIを運用したり、それなりにセキュリティが求められるウェブアプリを運用する予定であれば実機のLinuxサーバーにデプロイしたほうが良い。

デプロイまでの流れ

  1. PostgreSQLとNginxのインストール
  2. settings.pyの修正
  3. ホームディレクトリにプロジェクト一式を設置
  4. 必要になるライブラリのインストール(Gunicornなど)
  5. 静的ファイルを公開ディレクトリに配置
  6. システム管理デーモン(systemd)にサーバー起動を登録
  7. Nginxの設定書き換え
  8. 動作チェック

PostgreSQLとNginxのインストール

まずは対象サーバーにNginxとPostgreSQLを導入する。下記コマンドを実行すればOK

sudo apt install postgresql nginx

下記コマンドをそれぞれ実行し、その下の画像のようになれば正常に動作している。

sudo systemctl status nginx.service
nginx正常稼働
sudo systemctl status postgresql.service
PostgreSQL正常稼働

PostgreSQLにおいては、別途Django側からアクセス為にユーザーとパスワードを決める必要がある。PostgreSQLの操作方法等に関しては下記を参照

PostgreSQLインストールから、ユーザーとDBを作る

settings.pyの修正

デプロイに向け、settings.pyを修正する必要がある。下記コードのTODOで書かれた部分を修正する。
 
"""
Django settings for config project.

Generated by 'django-admin startproject' using Django 2.2.3.

For more information on this file, see
https://docs.djangoproject.com/en/2.2/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.2/ref/settings/
"""

import os

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
PROJECT_NAME = os.path.basename(BASE_DIR)

# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'ここにSECRET_KEYのランダム文字列が入る'

# SECURITY WARNING: don't run with debug turned on in production!
# TODO:DEBUGはFalseにしておく
DEBUG = False

# TODO:ALLOWED_HOSTSはデプロイ先のIPアドレス、もしくはドメイン名を指定する
ALLOWED_HOSTS = [ "ここにドメイン名もしくはIPアドレスを指定" ]

# Application definition

INSTALLED_APPS = [
    '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 = 'config.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR,"templates")],
        '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 = 'config.wsgi.application'

# TODO:ここのデータベースの設定を修正する。
# Database
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases
DATABASES = { 
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'データベースの名前を書く',
        'USER': 'アクセスするユーザー名',
        'PASSWORD':'アクセスするユーザーのパスワード',
        'HOST': 'localhost',
        'PORT': '', 
    }   
}

# Password validation
# https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators

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',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/2.2/topics/i18n/

LANGUAGE_CODE = 'ja'

TIME_ZONE = 'Asia/Tokyo'

USE_I18N = True

USE_L10N = True

USE_TZ = True


# TODO:静的ファイルとメディアファイル(staticとmedia)の指定を修正する
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.2/howto/static-files/

STATIC_URL = '/static/'
STATICFILES_DIRS = [os.path.join(BASE_DIR, "static")]
STATIC_ROOT = '/var/www/{}/static'.format(PROJECT_NAME)

MEDIA_URL   = "/media/"
if DEBUG:
    MEDIA_ROOT  = os.path.join(BASE_DIR, 'media')
else:
    MEDIA_ROOT  = "/var/www/{}/media".format(PROJECT_NAME)

以下、解説。

DEBUG = Falseにする

DEBUGモードを無効化しておかないと、エラーが出た時にウェブアプリケーションの情報が全て表示される。

例えば、クライアントに本来見せてはならないviews.pyがエラーによってブラウザに表示されてしまうと、views.py側の処理を逆手に取り、攻撃を仕掛けることは容易。

他にも、使用しているフレームワークのバージョン、DBの構造やパスワードなどが知られてしまうリスクがある。

一方で、DEBUGモードを無効化しておくと、エラーが出てもServer Error 500などという文言が出るだけ。

DEBUGモードではない時、エラーの詳細は見えない

他にも、プロジェクトディレクトリ内部にファイル保存を許したり、DBがSQLiteでもOKだったり、ホスト名何でもOKなど、問題ありまくり。

ALLOWED_HOSTSの指定

前述のDEBUG = Falseに関係していて、ウェブアプリを公開するサーバーのホスト名(IPアドレス、ドメイン名)の指定をしないとエラーが出る。

これはHTTP Host header攻撃を防ぐ際に有効。ドメインを入力する際はwwwから始まるFQDNを入力する。

DBの設定

PostgreSQLの操作時に必要になるユーザー名とパスワードを入力する。

PostgreSQLのアクセスにはpsycopgが必要になるためpip3コマンドでインストールさせておく。

sudo pip3 install psycopg2

上記コマンドが正常に動作しない場合は、下記コマンドを実行してから実行する

sudo apt install libpq-dev python3-dev

もし、pip3コマンドがないと言われたら、下記コマンドを実行してpip3コマンドを使えるようにする

sudo apt install python3-pip

静的ファイル、メディアファイルの指定

静的ファイルは開発時に開発者が格納する物、CSSとかJSとか画像ファイルとかも含まれる。一方でメディアファイルはウェブアプリを利用するクライアントが投稿する画像等のファイルを扱う。

いずれもウェブサーバーの公開ディレクトリに当たる、/var/www/以下を指定する。

ホームディレクトリにプロジェクト一式を設置

SSH(scp)が使える環境であれば下記コマンドを実行して、プロジェクト一式をコピーする。

scp -r ./* ユーザー名@ホスト名:~/Documents/

scpコマンドが使えない場合は、USBメモリなどを使用して、サーバーのホームディレクトリの任意の場所にコピーしておく。上記に倣って~/Documents/などでも可。

必要になるライブラリのインストール(Gunicornなど)

デプロイ先のUbuntuで下記コマンドを実行。ライブラリをインストールさせる。

sudo pip3 install django django-environ gunicorn

その他、必要になるライブラリを適宜追加でインストールさせる。

sudo pip3 install django-allauth 

virtualenvを使用している場合

まずプロジェクトディレクトリに移動したあと、下記コマンドを実行する。

sudo pip3 freeze > requirements.txt

requirements.txtなどに必要になるライブラリが全て記録される。その上でサーバーにプロジェクト一式をコピーした上、下記コマンドを実行、ライブラリをインストールさせる

sudo pip3 install -r requirements.txt

静的ファイルを公開ディレクトリに配置

予め、デプロイ先となるUbuntuにて、静的ファイルの配信先になるプロジェクトのディレクトリを作っておく。

sudo mkdir /var/www/プロジェクト名

プロジェクトのディレクトリの所有者はDjangoのプロジェクトが配置されている場所の所有者に当たるユーザーの名前、グループはwww-dataを指定する。

sudo chown ユーザー名:www-data /var/www/プロジェクト名

続いて、下記コマンドを実行して静的ファイルの配信を行う。

python3 manage.py collectstatic

これで、Djangoのstaticディレクトリに含まれているファイルは、全て/var/www/プロジェクト名/staticの中に書き込まれる。

システム管理デーモン(systemd)にサーバー起動を登録

まず、systemdが自動起動するために必要なファイルを作る。下記コマンドを実行

sudo vi /etc/systemd/system/プロジェクト名.service

上記ファイルに、下記内容を書き込む。

[Unit]
Description = gunicorn daemon(プロジェクト名)
Requires    = プロジェクト名.socket
After       = network.target

[Service]
User        = ユーザー名
Group       = www-data
WorkingDirectory    = /home/ユーザー名/Documents/プロジェクト名
ExecStart           = /usr/local/bin/gunicorn \
                        --bind unix:/run/gunicorn/プロジェクト名.sock \
                        config.wsgi:application
[Install]
WantedBy    = multi-user.target

systemdが待ち受けるソケットの定義ファイルを作る

sudo vi /etc/systemd/system/プロジェクト名.socket

上記ファイルに下記内容を書き込む。

[Unit]
Description     = gunicorn socket (プロジェクト名)

[Socket]
ListenStream    = /run/gunicorn/プロジェクト名.sock

[Install]
WantedBy        = sockets.target

systemdにサービスを管理登録する

sudo systemctl enable プロジェクト名.socket
sudo systemctl enable プロジェクト名.service

サービスを起動する。

sudo systemctl start プロジェクト名

ステータスをチェックする。下記画像のように表示されればOK

正常にsystemdが動いている

もしActive:failedなどが表示されれば、併記されているエラー文から該当箇所を修正し、下記コマンドを実行してリロードする。

sudo systemctl daemon-reload

その上でプロジェクト名のサービスをリスタートさせる

sudo systemctl restart プロジェクト名

Nginxの設定書き換え

デプロイの最終段階。nginxの設定をGunicornが動作させるように編集する。

sudo vi /etc/nginx/sites-available/プロジェクト名

下記を書き込む。IPアドレスは適宜変更。

server {
    listen 80; 
    server_name 192.168.XXX.XXX;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /var/www/プロジェクト名;
    }   
    location /media/ {
        root /var/www/プロジェクト名;
    }   
    location / { 
        include proxy_params;
        proxy_pass http://unix:/run/gunicorn/プロジェクト名.sock;
    }   

    client_max_body_size 100M;
}

末端のclient_max_body_sizeの値はお好みで。デフォルトでは1MB程度しかアップロードできないので、ファイルアップロード系ウェブアプリの場合は指定必須。

mediaとstaticはSTATIC_URL、MEDIA_URLに対応したURIである。

availableで作ったnginxの設定ファイルは、enabledにシンボリックリンクを作ることで有効化出来る。以下のコマンドを実行して設定ファイルを有効化させる

sudo ln -s /etc/nginx/sites-available/プロジェクト名 /etc/nginx/sites-enabled

デフォルトの設定ファイルは不要なのでシンボリックリンクを削除して無効化する。無効化後は再起動。

sudo unlink /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginx

マイグレーション

DBを使用しているタイプのウェブアプリであれば、PostgreSQLへのマイグレーションを忘れなく。

python3 manage.py migrate

動作チェック

デプロイしたUbuntuにて、下記コマンドを実行して、IPアドレスを調べる。

ip a

表示されたIPアドレスを元に、http://192.168.XXX.XXX にアクセスする。ブラウザに表示されればデプロイ完了。

結論

実機のサーバーにデプロイするメリットは、自由度が高く、高性能な点にある。

特に、Heroku無料プランはラズパイゼロと同じぐらい低スペック。ストレージもない状況では画像認識系AIがまともに動いてくれるはずがない。

有料プランに加入して毎月お金を支払うのであれば、3~5万円程度で中古デスクトップを購入してLinuxサーバーに仕立てるほうが経済的だと私は思う。3~5年あれば十分元がとれるし、何より処理速度が段違い。

ただ、実機にデプロイする際には、開発者にLinuxサーバー関係の知識が要求される。一般的なコマンド作業、SSH、vim、デーモン、権限云々の話が出てくる。

サーバーとしてまともに運用するのであれば、IPアドレスの固定化とかもしないといけない。一般人向けにするのであればドメインの話も必要になる。

【補足1】UbuntuのIPアドレスを固定化させるには?

まず、ルーターがDHCPで割り当てているローカルIPアドレスの範囲を確認する必要がある。

例えば、DHCPで割り当てているIPアドレスが192.168.11.2から192.168.11.30の場合、固定IPアドレスとして割り当てられるのはそれ以外の192.168.11.31からである。

このDHCPの範囲の確認方法はルーターの機種によって異なるが、PCのIPアドレスが192.168.11.10などだった場合、ルーターのIPアドレスは192.168.11.1である。(IPアドレスの第三オクテットまで共通、第四オクテットは1である場合が多い)

ルーターのIPアドレスをブラウザに直接入力してアクセスする。ルーターのパスワードとIDの入力をして、ルーターの管理画面でDHCPの設定を確認することができる。

続いて、UbuntuのIPアドレスを指定する方法は、下記記事を元に行うと良い。

サーバー版Ubuntu 20.04のインストールから設定、SSHログインまで【固定IPアドレス、タイムゾーン、bashrcなど】

もし、上記の方法では難しいと思う場合、Ubuntuの設定からネットワーク、優先設定の歯車のアイコンをクリックしてGUIで設定ができる。下記画像のようにする。

メソッドは手動、アドレスを記入、DNSとしてルーターのIPアドレスを指定する。これでIPアドレスの固定化ができる。

【補足2】このデプロイはOSに直接Pythonライブラリをインストールしている

このデプロイはOSに直接Pythonライブラリをインストールしている。

仮想開発環境、virtualenvのようなライブラリのバージョンを一括管理する物を使っていないので、OSの更新に合わせてライブラリも自動的に更新されてしまう問題がある。

virtualenvを使用したデプロイを行いたい場合、下記記事を参考にすると良いだろう。

DjangoをAWSのEC2(Ubuntu)にデプロイする

途中までAWS仕様だが、それ以外はUbuntuとなんらかわりはない。

【補足3】Herokuにデプロイするには?

なるべくお金をかけずに、制作したウェブアプリをインターネットから誰でも自由にアクセスできるようにしたい場合、Herokuを使うと良いだろう。

1万行のDB、約20GBのストレージ、毎月1000時間のウェブサーバー稼働時間が無料で提供されている。(ストレージ利用の際には事前にカード登録しておく必要がある。)

DjangoをDEBUG=FalseでHerokuにデプロイする方法

DjangoをHeroku+Cloudinary(基本無料ストレージ)の環境にデプロイする【ウェブアプリのデモを一般公開したい場合などに】

スポンサーリンク

シェアボタン

Twitter LINEで送る Facebook はてなブログ