概要
某Djangoアプリをサーバにデプロイしたときの手順・設定です。
デプロイが完了してから反省したこと。
- Djangoの公式ドキュメントに必要な情報が網羅されているのに回り道した
「Django デプロイ Apache」みたいな感じでググって見つかった記事を参考にしたのですがかえって効率悪かったです。
デーモン実行でなかったり、古かったり、不正確(というか嘘)だったりで約2時間はまりました。涙
公式ドキュメントのデプロイ手順
環境
- 某VPS
- CentOS 7.5.1804
- apache2.4.6
- Django 2.1.7
- Python3.6
また、以下の特徴・留意点があります。
- 当該サーバでは他のサービスも動いているので、Apacheバーチャルホスト上で実行
- デーモンプロセスとして動作させる。
手順
Apacheのインストール
様々なサイトで紹介されているので省略
mod_wsgiのインストール
以下を参考にさせていただきました。(本記事のwsgiの設定はデーモン実行でないタイプです)
centos7へapache2.4とmod_wsgiインストールし、djangoデプロイまで
重要な点として、当該モジュールはpipでもyumでもインストールできるのですが、yumでインストールすると、centos標準のPython2でしか動作しなくなるそうです。本プロジェクトはPython3系(Djago2系以降はPython2は非サポート)なのでpipでインストールします。
wsgiインストール
pip install mod_wsgi
Apache用共有オブジェクトファイル(拡張子so)を探す
locateコマンドで探しました。
$ locate *mod_wsgi*.so
<パッケージインストール先ディレクトリ>/site-packages/mod_wsgi/server/mod_wsgi-py36.cpython-36m-x86_64-linux-gnu.so
共有オブジェクトファイルをApacheのmodulesディレクトリにコピー
cp <soファイルのパス> /etc/httpd/modules/<soファイル名>
soファイル名を控える
のちほどApache設定ファイルのLoadModuleディレクティブで指定するので控えておきます。
当方の場合は以下でした。
mod_wsgi-py36.cpython-36m-x86_64-linux-gnu.so
配置
- プロジェクトソースコードを任意の場所に配置します。
- 仮想環境(venv)を作成・アクティベート・拡張インストール
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
- アプリディレクト全体の所有設定(所有者はApache、グループにユーザ)
後述するバーチャルホスト設定やWSGI設定で指定します。
所有者やパーミションを適宜調整します。
logsディレクトリ作成(option)
ログをプロジェクトルート直下のlogsディレクトリに出力する設定にしているので、logsディレクトリを作成します。apacheユーザが書き込めるように所有者・パーミッションを適切に設定します。
静的ファイルをSTATIC_ROOTに作成
$ python3 manage.py collectstatic
自身がSTATIC_ROOT下に実装したのはcssとjsだけでしたが、このコマンドを実行することでDjango本体と拡張ライブラリによるファイルが配置されます。(例えば,adminやdebug_toolbarやdjango_extensionsなど)
<project_root>/static/ ← 設定ファイルのSTATIC_ROOTで指定したパス
├── admin
├── css
├── js
├── debug_toolbar
└── django_extensions
Apache設定
Apacheの定義ファイルに以下の設定を行います。(Apache本体の設定ファイルhttpd.confで名前ベースの仮想ホスト~NameVirtualHost *:443~を有効化している前提です)
- mod_wsgiの読込み設定(LoadModule)
- 仮想ホスト設定(VirtualHost)
仮想ホストは一般的な設定に加えて2つの重要な設定があります。
- WSGI設定
- DjangoアプリをWSGIで動かす設定です。wsgi.py は django-admin startproject xxxx コマンドを実行した時に設定ディレクトリ直下に自動生成されたものを使っています。
- 静的ファイル配信設定
- この定義がなかったりパスが間違っていたり、collectstatic忘れて静的ファイルが配置されていない場合はCSSやJSが適用されないので変な画面が表示されます。
LoadModule wsgi_module modules/mod_wsgi-py36.cpython-36m-x86_64-linux-gnu.so
<VirtualHost *:443>
ServerName example.com
DocumentRoot /var/www/<project_root>
…省略(SSL定義、カスタムログとか)…
<directory /var/www/<project_root>>
AllowOverride all
Require all granted
Options FollowSymlinks
</directory>
# WSGIをデーモンで実行
WSGIScriptAlias / /var/www/<project_root>/<設定ディレクトリ>/wsgi.py
WSGIDaemonProcess example.com user=apache group=apache processes=2 threads=25 display-name=%{GROUP} python-home=/usr python-path=/var/www/<project_root> lang=ja_JP.utf8
WSGIProcessGroup example.com
<directory /var/www/<project_root>/<設定ディレクトリ>>
<Files wsgi.py>
Require all granted
</Files>
</directory>
# 静的ファイルの配信先を設定
# settings.pyのSTATIC_ROOTで指定したパスと一致していること
Alias /static/ /var/www/<project_root>/static/
<Directory /var/www/<project_root>/static>
Require all granted
</Directory>
</VirtualHost>
- wsgi.pyはstartprojectで作成されるものを利用する(読み込む設定ファイルパスは分離後に合わせる)
トラブルシューティング
いろんな箇所で躓きました。整理すると以下のようにすると良いのではないでしょうか。
準備
- アプリケーションに必要なDjangoや拡張ライブラリのインストール(
$ pip3 install -r requirements.txt
) - 静的ファイルの配置
$ python manage.py collectstatic
を実行 - 権限の確認
- プロジェクトルート以下がWSGIDaemonProcess で指定したユーザ・グループからアクセスできるように設定されているか?
- ALLOWD_HOSTSの確認
- デフォルトだと空になっていて全てのアクセスが拒否されます。127.0.0.1と動作確認するクライアントのグローバルIPを設定します。
- アプリケーションコードに問題がないか?を確認
-
$ python manage.py runserver
で開発モードで実行。ローカルからcurl 127.0.0.1:8000
を実行して正常なレスポンスがあるか?
問題別対処法
Apache再起動でエラー(systemctl restart httpd.service
)
設定ファイルが間違っている可能性が高いです。systemctl status httpd.service
でエラー内容を見て調整します。
例えば、WSGIPythonPathやWSGIPythonHomeというディレクトティブはVirtulaHostでは設定できません。
VirtulaHostのデフォルトサイトにリダイレクトされてしまう
アプリケーションが動作していません。
Apacheのログ(カスタムログを設定していなければ /var/log/httpd/error_log
)を確認します。以下のエラーに遭遇しました。
- wsgi.pyの場所が正しいか?
- 設定ファイルが読み込まれていない ー> パスが
-
WSGIDaemonProcessの
python-path
がプロジェクトのルートディレクトリををさしていない。(あるサイトでは、pythonライブラリのインストール先(site-packages)を指定するという不正な記述がありました) この場合、no module named プロジェクト名
というエラーが出力されてたように思います。
Apacheのerror_logに何も出力されなければwsgi設定はひとまずOKです。
実行エラーが発生する
wsgiがOK(=apacheのerror_logに何も出力されない)になった後のエラーはアプリケーションログを確認します。(要Logging設定) ありがちなもの(というか当方が体験したもの)だと以下のようなものがあります。
- Djangoの必須設定SECRET_KEYが読み込めない。
- ベスト・プラクティスとして当該設定などを構成管理対象外ファイル(例えば
.env
)に設定して、django-environで読み込む方法があります。この場合、そのファイルが存在するか確認します。 - ログ出力先が存在しない・権限不足
- ログをFILE出力指定している場合、出力先ディレクトリが存在するか、書込権限があるかを確認します。
-
no named module <any>
が発生する - 必要なライブラリがインストールされていない可能性があります。requirements.txtの内容を確認して漏れていれば追加インストールします。
- 画面が表示されるがレイアウトがおかしい
-
$ python manage.py collectstatic
を実行していなければ実行する。 - Djangoの設定とApacheの設定の一致を確認します
-
Django設定は以下の定義が整合性とれているか確認します
- BASE_DIRはこの定義をしている設定ファイルから見てプロジェクトルートを指すように
BASE_DIR = `os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))`
STATIC_URL = '/static/'
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
STATIC_ROOT = os.path.join(BASE_DIR,'/var/www/<project_root>/static')