エックスサーバでDjango+Pythonを運用!うまくいかない原因はnumpyだった!

2019年2月11日プログラミング

こんにちは!しずかなかずしです。

エックスサーバというレンタルサーバ、使っていますか??

エックスサーバーは、ブログ用にWordPressを立ち上げて利用している方が多いと言われているレンタルサーバー。WordPressのセットアップがとても簡単にできるのが魅力なサービスです。

私のブログ「しずかなかずし」も、エックスサーバーで運営しています。

でも、当然のことながら、WordPress以外の用途にもエックスサーバーは利用可能。ちょっと使ってみましょう!

エックスサーバーでdjangoアプリを動かしてみた

エックスサーバーには、「サブドメイン」という機能も用意されています。例えば、original-domain.com という独自ドメインを使っている場合、subdomain.original-domain.com というサブドメインにて、WordPressとは別のシステムを運用することができるのです。

私は、このサブドメインの仕組みを使ってpython+djangoのアプリを動かそうとしていましたが、以下の記事に書いたように、うまくいかず苦労していたのでした。

djangoは、pythonで本格的なWebアプリが作れる最近流行りのフレームワークです。

上手く動作しない理由はこのdjangoにあるのかと思ったりもしましたが、そうではありませんでした。試行錯誤の末、ようやく原因が特定できたので、今回はその解決方法を解説します。

広告

数値計算用のライブラリnumpyにハマる

動作させたかったのは、以下のようなものです。

関連記事: Excelに英文を打ち込んだら、読み上げた音声ファイルmp3を返すサイトの制作

Excelファイルに英単語や英文を列挙して、このアプリにアップロードすると、テキスト読み上げの機能を使って、mp3ファイルにしてくれる、という便利なツールです。

English Excel to Speech: xls2speech.shizuka-na-kazushi.style

このアプリでは内部でnumpyというpythonの数値計算用のライブラリを参照しています。numpyを使うとAIの機械学習を行ったり、さまざまなビッグデータの解析を行ったりすることができます。その筋の方々にはかなり有名なライブラリですね。

ただし私のアプリ、English Excel to Speechはこれと言った数値計算は行っていません。数値計算的な処理であるText To Speech(TTS)はMicrosoftのWebサービスを使わせてもらっています。ということは、このアプリは数値計算は不要なのです。

では、なぜnumpyが必要かと言うと、アプリが直接使っているpandas というライブラリがnumpyを参照しているためです。pandasはexcelファイルを読み込んでpythonでデータを操作できる便利なライブラリ。上記のアプリでは、excelファイルの中身を取り出すために、pandasを利用しているため間接的にnumpyが必要だったというわけです。

そして、エックスサーバーでpython+djangoアプリが動かない理由は、このnumpyだったということが分かりました。

エックスサーバーでnumpyを使う場合の注意点

numpyには、OpenBLASというマルチスレッド(並列化)の最適化の仕組みがあります

numpyを初期化する際(つまり、includeしているdjangoアプリの初期化の際)に、複数のスレッドを起動するようです。これが原因でエックスサーバーのApache上で、CGI化されたプログラムが途中で停止していました。

色々試した結果、エックスサーバーのApacheでpythonのnumpyを使うには、OpenBLASが使用するスレッドを1にして、マルチスレッド(並列化)を無効化すれば良いことがわかりました。しかも、環境変数で指定ができますので後述のようにCGIのプログラムの中で設定できます。

もっともこの解決方法、

「numpyのアプリをマルチスレッドで最適化して使いたいよー」

という方には解決にはなりませんね。ご注意下さい。

広告

djangoアプリのデプロイ方法(解決版)

それでは、エックスサーバでdjangoアプリを動かす方法をまとめます。OpenBLASの解決方法付きです。

エックスサーバーでpythonのアプリを動かすにはpythonの仮想環境(virtualenv)を使いましょう。

そして、仮想環境にアプリが使うライブラリをインストールします。エックスサーバーは、SSHでログインしたとしてもルート権限はありませんね。ですので、自分のローカルディレクトリに必要なライブラリを全部入れてしまおうという訳です。

エックスサーバで仮想環境を作成しpipを使う

私が借りているサーバは、python2.7とpython3.4がインストール(2019年2月11日現在)されています。

以下の手順は、SSHでサーバーにログインする準備ができている前提です。

まずは、以下のコマンドでpythonの仮想環境を作ります。(私の場合、バージョン3系を使います)

$ pyvenv-3.4 venv

「venv」は自分で付けた仮想環境の名前です。この名前でディレクトリが作成されます。

次に、仮想環境を有効化します。

$ . venv/bin/activate

その後、pipのインストールです。

$ curl https://bootstrap.pypa.io/get-pip.py > get-pip.py
$ python get-pip.py

これでpipが使えるようになりました。pipはライブラリ(パッケージ)のインストールや管理をやってくれるpythonの仮想環境には無くてはないプログラムです。

pipがひと度利用できるようになれば、話は簡単ですね。

アプリで利用するライブラリは、pip install xxxx とコマンドを実行すれば作成した仮想環境にすべてインストールされます。

djangoアプリのファイルのコピー

次に、djangoのアプリをコピーします。

エックスサーバの場合、サブドメインで利用するファイルは、以下のような決まった名前のサブフォルダに入れる必要があります。

[HOME]/original-domain/public_html/subdomain/

このディレクトリにdjangoの全てのファイルをコピーします。
サブドメインではない場合は、public_htmlフォルダにコピーすればOKです。

広告

subdomainのディレクトリに.htaccessの配置

subdomain用に、Apacheサーバの設定ファイルを作ります。

内容は、以下のようなファイル.htaccessを配置します。

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ /index.cgi/$1 [QSA,L]

この設定は apache のCGIの設定で、簡単に言うと、「このURLに来た全てのリクエストを、index.cgiというpythonのファイルで処理しますよ」という意味です。もしサブドメインを使用しない場合は、public_htmlのトップフォルダに.htaccessを配置します。

エックスサーバーのサブドメインの仕組みは、URLがサブドメインでも、サブフォルダ形式でも両方利用可能です。

しかし、私が試したところ、上記の.htaccessだと、djangoのアプリにブラウザからアクセスする際に、

https://original-domain.com/subdomain/

ではなく、

https://subdomain.original-domain.com/

でアクセスしないと、上手く行きませんでした。。。(多分、エックスサーバの運用側の設定のせいだと思います)

index.cgiの用意

ここがポイントでした。以前の解決できなかったときのブログ記事とほぼ同様のファイルなのですが、環境変数を1つ追加しています。

#!/home/XXXX/YYYY/public_html/ZZZZ/venv/bin/python
# -*- coding: utf-8 -*-

import sys, os

# Python のモジュール検索パスをカスタマイズする
sys.path.insert(0, "/home/XXXX/YYYY/public_html/ZZZZ/venv/bin")

# カレントディレクトリをプロジェクトのディレクトリに移す (必要なら)
os.chdir("/home/XXXX/YYYY/public_html/ZZZZ")

# 環境変数 DJANGO_SETTINGS_MODULE を設定する
os.environ['DJANGO_SETTINGS_MODULE'] = "ZZZZ.settings"

# numpy のスレッド(OpenBLAS)をsingle threadに制限する??
os.environ['OPENBLAS_NUM_THREADS'] = "1"

from wsgiref.handlers import CGIHandler
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()
CGIHandler().run(application)

追加したのは、OPENBLAS_NUM_THREADSの行です。これが、上記のnumpyの課題を解決するためのおまじない、OpenBLASの最適化スレッドを1つに限定するための設定です。

まとめ

エックスサーバーで、pythonのWebフレームワークdjangoのアプリを運用できます。ただし、そのアプリがnumpyを使う場合は注意が必要。マルチスレッドの最適化を無効化(1スレッドにする)するCGIスクリプトを使うと解決できます。

これで解決できました!