View on GitHub

バックエンドサーバの準備

Home

バックエンドサーバの準備

クライアント側

本番運用環境用 Django プロジェクト設定ファイル

src/backend/config/settings/production.py

from django.core.exceptions import ImproperlyConfigured
from .base import *
import os

# 本番では DJANGO_SECRET_KEY を必須とする(未設定なら起動時に例外で落とす)。
try:
    SECRET_KEY = os.environ['DJANGO_SECRET_KEY']
except KeyError as exc:
    raise ImproperlyConfigured(
        'DJANGO_SECRET_KEY environment variable must be set in production.'
    ) from exc
SIMPLE_JWT = {**SIMPLE_JWT, 'SIGNING_KEY': os.environ.get('JWT_SIGNING_KEY', SECRET_KEY)}

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'recsys_full',
        'USER': os.environ.get('DB_USER'),
        'PASSWORD': os.environ.get('DB_PASSWORD'),
        'HOST': '',
        'PORT': '',
    }
}

DEBUG = False

ALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', '').split(',')
CORS_ALLOWED_ORIGINS = os.environ.get("CORS_ALLOWED_ORIGINS", "").split(",")
CORS_ALLOW_CREDENTIALS = True
CSRF_TRUSTED_ORIGINS = os.environ.get("CSRF_TRUSTED_ORIGINS", "").split(",")

BACKEND_BASE_URL = os.environ.get("BASE_URL", "").split(",")
OMDB_API_BASE_URL = os.environ.get('OMDB_API_BASE_URL', 'https://www.omdbapi.com/')
OMDB_API_KEY = os.environ.get('OMDB_API_KEY')

STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR / 'static'

MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,

    # ロガーの設定
    'loggers': {
        # Djangoが利用するロガー
        'django': {
            'handlers': ['file'],
            'level': 'INFO',
        },
        # onlineアプリケーションが利用するロガー
        'online': {
            'handlers': ['file'],
            'level': 'INFO',
        },
    },

    # ハンドラの設定
    'handlers': {
        'file': {
            'level': 'INFO',
            'class': 'logging.handlers.TimedRotatingFileHandler',
            'filename': os.path.join(BASE_DIR, 'logs/django.log'),
            'formatter': 'prod',
            'when': 'D',        # ログローテーション(新しいファイルへの切り替え)間隔の単位(D=日)
            'interval': 1,      # ログローテーション間隔(1日単位)
            'backupCount': 7,   # 保存しておくログファイル数
        },
    },

    # フォーマッタの設定
    'formatters': {
        'prod': {
            'format': '\t'.join([
                '%(asctime)s',
                '[%(levelname)s]',
                '%(pathname)s(Line:%(lineno)d)',
                '%(message)s',
            ])
        },
    },
}

src/backend/config/wsgi.py

......
import os

from django.core.wsgi import get_wsgi_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings.production')  # <- .productionを追加

application = get_wsgi_application()

src/backend/config/asgi.py

......
import os

from django.core.asgi import get_asgi_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings.production')  # <- .productionを追加

application = get_asgi_application()

インストール済みパッケージ一覧の出力

(recsys_full) $
 cd ~/dev/recsys-full/
 pip freeze > requirements.txt

(recsys_full) $ vi requirements.txt
  1. requirements.txtの下記の行をコメントアウトする。
...(略)...
# torch==2.13.0
# torchaudio==2.11.0
# torchvision==0.28.0
...(略)...

データの初期化

offline$ cp bkup/data/* data/

リポジトリのプッシュ

$
 cd ~/dev/recsys-full/
 git add *
 git add .*
 git commit -m "deploy backend"
 git push
 git status

バックエンドサーバ側

リポジトリの clone

rsl@*:$ mkdir ~/dev/
rsl@*:$ cd ~/dev/
rsl@*:$ git clone git@github.com:recsyslab/recsys-full.git
rsl@*:$ cd recsys-full/
rsl@*:$ ls
README.md  requirements.txt  src

パッケージのインストール

rsl@*:$
 python3.12 -m venv ~/venv/recsys_full
 source ~/venv/recsys_full/bin/activate

(recsys_full) rsl@*:$
 pip install --upgrade pip
 pip --version
pip 26.2.1 from /home/rsl/venv/recsys_full/lib/python3.12/site-packages/pip (python 3.12)

(recsys_full) rsl@*:$
 pip install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu124
 pip install -r ~/dev/recsys-full/requirements.txt
 pip install gunicorn

(recsys_full) rsl@*:$ pip freeze
...(略)...
gunicorn==26.2.0
...(略)...
torch==2.13.0
torchaudio==2.11.0
torchvision==0.28.0
...(略)...

ログ出力先ディレクトリの作成

rsl@*:backend$
 mkdir logs/
 mkdir media/
 mkdir static/

rsl@*:backend$ ls
api  config  logs  manage.py  media  static

環境変数の設定

(recsys_full) rsl@*:backend$
 python -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())"
<DJANGO_SECRET_KEY>
rsl@*:$ sudo vi ~/.profile

~/.profile

...(略)...
# Django用環境変数
export DB_USER=rsl
export DB_PASSWORD=<DB_PASSWORD>
export DJANGO_SETTINGS_MODULE=config.settings.production
export DJANGO_SECRET_KEY='<DJANGO_SECRET_KEY>'
export ENCRYPTION_KEY=<FERNET_KEY>
export ALLOWED_HOSTS=recsys-full.vercel.app,recsys-full.recsyslab.org,***.***.***.***  # <- ***.***.***.***はバックエンドサーバのIPアドレス
export CORS_ALLOWED_ORIGINS=https://recsys-full.vercel.app,https://recsys-full.recsyslab.org
export CSRF_TRUSTED_ORIGINS=https://recsys-full.vercel.app,https://recsys-full.recsyslab.org
export OMDB_API_KEY=<OMDB_API_KEY>
rsl@*:$
 less ~/.profile
 diff ~/.profile-org ~/.profile
 source ~/.profile
 env

データベース環境の構築

postgres=# CREATE ROLE rsl WITH LOGIN PASSWORD 'rsl-pass';
postgres=# CREATE DATABASE recsys_full ENCODING 'UTF8';
postgres=# ALTER DATABASE recsys_full OWNER TO rsl;
postgres=# \l
postgres=# \c recsys_full
recsys_full=# CREATE EXTENSION IF NOT EXISTS pg_trgm;

マイグレーションの実行

(recsys_full) rsl@*:backend$ python manage.py makemigrations accounts
Migrations for 'accounts':
  api/accounts/migrations/0001_initial.py
    + Create model CustomUser

(recsys_full) rsl@*:backend$ python manage.py makemigrations online
Migrations for 'online':
  api/online/migrations/0001_initial.py
    + Create model Genre
    + Create model User
    + Create model Movie
    + Create model MovieGenre
    + Create model ReclistMovieSimilarity
    + Create model ReclistPopularity
    + Create model ReclistBPR
    + Create model UserMovieRating

(recsys_full) rsl@*:backend$ python manage.py migrate
Operations to perform:
  Apply all migrations: accounts, admin, auth, contenttypes, online, sessions, token_blacklist
Running migrations:
  Applying online.0001_initial... OK
  Applying contenttypes.0001_initial... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0001_initial... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying auth.0012_alter_user_first_name_max_length... OK
  Applying accounts.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying sessions.0001_initial... OK
  Applying token_blacklist.0001_initial... OK
  Applying token_blacklist.0002_outstandingtoken_jti_hex... OK
  Applying token_blacklist.0003_auto_20171017_2007... OK
  Applying token_blacklist.0004_auto_20171017_2013... OK
  Applying token_blacklist.0005_remove_outstandingtoken_jti... OK
  Applying token_blacklist.0006_auto_20171017_2113... OK
  Applying token_blacklist.0007_auto_20171017_2214... OK
  Applying token_blacklist.0008_migrate_to_bigautofield... OK
  Applying token_blacklist.0010_fix_migrate_to_bigautofield... OK
  Applying token_blacklist.0011_linearizes_history... OK
  Applying token_blacklist.0012_alter_outstandingtoken_user... OK
  Applying token_blacklist.0013_alter_blacklistedtoken_options_and_more... OK

データの登録

rsl@*:offline$
 psql recsys_full -U postgres -c "\copy m_movies (movie_id, title, year, imdb_id, tmdb_id) from 'data/movies.csv' with delimiter E'\t' csv header encoding 'UTF8'"
 psql recsys_full -U postgres -c "\copy m_genres (genre_id, genre_name) from 'data/genres.csv' with delimiter E'\t' csv header encoding 'UTF8'"
 psql recsys_full -U postgres -c "\copy m_movies_genres (id, movie_id, genre_id) from 'data/movies_genres.csv' with delimiter E'\t' csv header encoding 'UTF8'"

 psql recsys_full -U postgres -c "\copy t_users (user_id, email_encrypted, email_hash) from 'data/users.csv' with delimiter E'\t' csv header encoding 'UTF8'"
 psql recsys_full -U postgres -c "\copy t_users_movies_rating (id, user_id, movie_id, rating, rated_at) from 'data/ratings.csv' with delimiter E'\t' csv header encoding 'UTF8'"

 psql recsys_full -U postgres -c "\copy r_reclist_popularity (id, target_genre_id, rank, movie_id, score) from 'data/reclist_popularity.csv' with delimiter E'\t' csv header encoding 'UTF8'"
 psql recsys_full -U postgres -c "\copy r_reclist_movie_similarity (id, base_movie_id, rank, movie_id, score) from 'data/reclist_movie_similarity.csv' with delimiter E'\t' csv header encoding 'UTF8'"
 psql recsys_full -U postgres -c "\copy r_reclist_bpr (id, user_id, movie_id, score, rank) from 'data/reclist_bpr.csv' with delimiter E'\t' csv header encoding 'UTF8'"