埼玉在住エンジニアのナレッジ帳

webのエンジニアをやっており、日頃の開発で詰まったことについて残していきたいと思っています。https://ikujip.jpの開発も行っているため、そこで使った知識なども載せられればと思います。

Djangoを本番環境に反映

Djangoを本番環境で展開する際に、色々詰まったので僕が対応したやり方を備忘的に記しておきます。

バージョン情報は以下
Python 2.7.10
Django 1.9.6
CentOS 6.8
Apache 2.2.15
※操作はすべてrootユーザで行ってます。

まずは、Apacheなど必要モジュールをインストールして起動させます。

yum install httpd httpd-devel mod_wsgi
service httpd start

chkconfigの設定もしておきましょう。

chkconfig httpd on
chkconfig --list | grep httpd

httpd.confの細かい設定は省きますので、適宜行ってください。

この時、「/etc/httpd/conf.d/wsgi.conf」というファイルができているので、
このファイルを修正します。
※すでに「/var/www/cgi-bin/」にDjangoのプロジェクトが置かれているとします。

vim /etc/httpd/conf.d/wsgi.conf
LoadModule wsgi_module modules/mod_wsgi.so
 
WSGIScriptAlias / /var/www/cgi-bin/testdir1/testdir2(ご自身のdjangoのパスに合わせてください)/wsgi.py
 
<Directory /var/www/cgi-bin/testdir1/testdir2(ご自身のdjangoのパスに合わせてください)>
Order deny,allow
Allow from all
</Directory>

ここで、Pythonのバージョンアップ。
元々サーバには2.6.6が入っていたので、2.7.10を読み込むように設定します。

一応元のバージョン確認

python --version
Python 2.6.6

新バージョンのインストール

cd /usr/src
curl -O https://www.python.org/ftp/python/2.7.10/Python-2.7.10.tgz
tar zxf Python-2.7.10.tgz

展開してインストールします。

cd Python-2.7.10
./configure
make & make altinstall

そして元のバージョンをバックアップして置き換えましょう。

mv /usr/bin/python /usr/bin/python2.6.6
cp /usr/local/src/Python-2.7.10/python /usr/bin/python

そしてyumのファイルも変える必要があります。

vim /usr/bin/yum

先頭の行を以下のようにしてください。

#!/usr/bin/python2.6.6

そしてバージョンを確認します。

python --version
Python 2.7.10

次にpipとvirtualenvのインストールです。

easy_install pip
pip install virtualenv
virtualenv ENV1
cd ENV1
. bin/activate

これでENV1環境に入れるはずなので、次にDjangoのインストールです。

pip install django==1.9.6

次に「wsgi.py」を書き換えて、
virtualenvのPythonを参照するようにします。

vim /var/www/cgi-bin/testdir1/testdir2(ご自身のdjangoのパスに合わせてください)/wsgi.py
import os
 
try:
    from local_wsgi import *
except ImportError:
    import site
    import sys
 
    site.addsitedir("/root/ENV1/lib/python2.7/site-packages")
 
    sys.path.append('/var/www/cgi-bin/testdir1(ご自身のdjangoのパスに合わせてください)')
    sys.path.append('/var/www/cgi-bin/testdir1/testdir2(ご自身のdjangoのパスに合わせてください)')
 
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "プロジェクト名.settings")
 
    activate_env = os.path.expanduser("/root/ENV1/bin/activate_this.py")
    execfile(activate_env, dict(__file__=activate_env))
 
 
from django.core.wsgi import get_wsgi_application
 
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "プロジェクト名.settings")
 
application = get_wsgi_application()

※上記は開発環境と本番環境でPythonの読み込み先を変えるようにしています。
開発環境だけlocal_wsgiという空ファイルを作り、ファイルが存在していれば何もせず、存在しない場合はPythonの読み込み先を変えるようにしています。

さあ、Apacheを再起動してブラウザにアクセスしてみましょう。

service httpd restart

...これでブラウザにアクセスすると、残念ながらアクセスするとApacheのエラーが出ると思います。

エラー内容を見てみましょう。
「/var/log/httpd/error_log」に記載があります。

mod_wsgi (pid=28251): Target WSGI script '/var/www/cgi-bin/test1/test2/wsgi.py' cannot be loaded as Python module.
mod_wsgi (pid=28251): Exception occurred processing WSGI script '/var/www/cgi-bin/test1/test2/wsgi.py'.
Traceback (most recent call last):
  File "/var/www/cgi-bin/test1/test2/wsgi.py", line 22, in <module>
    execfile(activate_env, dict(__file__=activate_env))
IOError: [Errno 13] Permission denied: '/root/ENV1/bin/activate_this.py'

どうやら「/root/ENV1/bin/activate_this.py」に権限がないらしい。

色々試して「activate_this.py」にだけ755権限の権限を与えてもダメでした。
ディレクトリごと権限を与える必要があるようです。
やりたくないですがrootに755の権限を与えてみました。
root直下に作らなきゃよかったな...

chmod 755 /root

再度アクセスするとまたエラーが...
今度のエラー内容は下記のものでした。

mod_wsgi (pid=28294): Target WSGI script '/var/www/cgi-bin/test1/test2/wsgi.py' cannot be loaded as Python module.
mod_wsgi (pid=28294): Exception occurred processing WSGI script '/var/www/cgi-bin/test1/test2/wsgi.py'.
Traceback (most recent call last):
  File "/var/www/cgi-bin/test1/test2/wsgi.py", line 25, in <module>
    from django.core.wsgi import get_wsgi_application
  File "/root/ENV1/lib/python2.7/site-packages/django/__init__.py", line 1, in <module>
    from django.utils.version import get_version
  File "/root/ENV1/lib/python2.7/site-packages/django/utils/version.py", line 7, in <module>
    from django.utils.lru_cache import lru_cache
  File "/root/ENV1/lib/python2.7/site-packages/django/utils/lru_cache.py", line 28
     fasttypes = {int, str, frozenset, type(None)},
                     ^
 SyntaxError: invalid syntax

シンタックスエラーが出ているのですが、これはPythonのバージョンが2.7でない時に発生するようです。

あれ?ちゃんと2.7を読み込んでるはずなのになんでろう...
本当に2.6の方を読み込んでるの?と疑問に思い、Djangoソースコードに以下の一文を入れました。

print(sys.version_info)

wsgi.py」に入れると良いと思います。

これで実行し、再度エラーログを見ると以下の一文が...

[error] (2, 6, 6, 'final', 0)

確かに旧バージョンを読み込んでいました。
「mod_wsgi」が旧バージョンの方を見ているらしく、再度コンパイルをしても直らず...
こちらの対応が一番時間がかかりました。
結果的には一度mod_wsgiをアンインストールし、再度入れ直してコンパイルすることで直りました。

ではmod_wsgiをアンインストールします。

yum remove mod_wsgi

この時点で「/etc/httpd/conf.d/wsgi.conf」は「/etc/httpd/conf.d/wsgi.conf.rpmsave」に変更されます。

mod_wsgiをインストールして展開してください。

cd /usr/src
wget https://github.com/GrahamDumpleton/mod_wsgi/archive/4.2.8.tar.gz -O mod_wsgi-4.2.8.tar.gz
tar xzf mod_wsgi-4.2.8.tar.gz
cd mod_wsgi-4.2.8
./configure --with-python=/usr/local/bin/python2.7
make & make install

コンパイルが終わったら、
wsgi.confを元に戻しましょう。

cd /etc/httpd/conf.d/
cp wsgi.conf.rpmsave wsgi.conf

これでブラウザにアクセスすると、また別なエラーが!
「Bad Request (400)」と表示されます。
あと一息。

settings.pyのALLOWED_HOSTSでアクセス制限をかけていないでしょうか。
もしかけている場合は、

ALLOWED_HOSTS = ['ドメイン名']

としてみてください。
私はそれで上手く表示されました。

いやー、サーバで表示させるだけで大変でした。
まだDjangoは静的ファイルの扱いとかデータベースの設定もあるのに最初にこんなに詰まるとは。
でもまぁ、Djangoの実行の流れはわかったから今後は大丈夫かな。