Python 3 + mod_wsgi + Django
CentOS 6.5, Python 3.6.1, Django 1.11.1
私がPythonについて持っている知識というのはこの程度のことしかない。
にわかというレベルですらないが、このたび、PythonでちょっとしたWebアプリを作ってみる機会があった。
以下は記事タイトルの環境を構築した時のメモである。
Python 3.6をインストールする
私が対象にした環境にはPython 2.6がインストールされており、これはyumが内部的に使用しているらしい。
そこで、2.6は残したまま、3.6をインストールすることにした。
$ wget https://www.python.org/ftp/python/3.6.1/Python-3.6.1.tgz $ tar zxf Python-3.6.1.tgz $ cd Python-3.6.1 # 64bit 環境で Python3 と mod_wsgi の組み合わせの場合、-fPIC が必要みたい $ ./configure CFLAGS=-fPIC --enable-shared --prefix=/usr/local $ make $ sudo make install # Python の .so を読み込むパスを通しておく $ export LD_LIBRARY_PATH=/usr/local/lib $ /usr/local/bin/python3.6 --version Python 3.6.1
virtualenvを使ってPython Webアプリの稼働環境を構築する
virtualenvはRubyのrbenv的なもの。
pipはgemみたいなもんかな?
という認識。
ここでは、/var/www/python/
以下にPythonのWebアプリをデプロイするつもり。
この環境を venv という仮想環境として、venv内では Python 3.6 を使うこととする。
$ curl -kLO https://bootstrap.pypa.io/get-pip.py $ sudo LD_LIBRARY_PATH=/usr/local/lib /usr/local/bin/python3.6 $ sudo LD_LIBRARY_PATH=/usr/local/lib /usr/local/bin/pip3.6 install virtualenv $ sudo mkdir -p /var/www/python $ sudo chown myuid:mygid /var/www/python $ cd /var/www/python $ virtualenv venv --python=/usr/local/bin/python3.6 --no-site-packages $ source venv/bin/activate (venv) $ which python /var/www/python/venv/bin/python
Djangoをインストールする
Djangoってのは、Python Webアプリのフレームワーク。RoRみたいなもん。
という認識。
# pipでDjangoをインストール (venv) $ pip install Django # test_siteというプロジェクトを新規作成 (venv) $ django-admin.py startproject test_site (venv) $ cd test_site # 雛形プロジェクトがSQLiteを使うようになっているが、今は使わないのでコメントアウト (venv) $ vim test_site/settings.py DATABASES = { # 'default': { # 'ENGINE': 'django.db.backends.sqlite3', # 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), # } } # ひとまず内包のHTTPサーバーを起動してみる(バックグラウンド起動させる) (venv) $ python manage.py runserver & Performing system checks... System check identified no issues (0 silenced). You have 13 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions. Run 'python manage.py migrate' to apply them. May 5, 2017 - 00:00:00 Django version 1.11.1, using settings 'myproject.settings' Starting development server at http://127.0.0.1:8000/ Quit the server with CONTROL-C. ^M # ローカルからHTTP経由でアクセスしてみる $ curl -s http://localhost:8000/ | grep "<h1>" <h1>It worked!</h1> # 動いているみたいなので、シャットダウン $ fg 1 python manage.py runserver ^C(venv) $
mod_wsgiを使ってApache経由でアクセスできるようにする
mod_wsgiはよくあるApacheの拡張モジュール。
ApacheへのリクエストをPythonに処理させるために必要。
ソースから直接コンパイルするより、pipでインストールした方が良いらしい。
(venv) $ pip install mod_wsgi
これでvenv仮想環境内にmod_wsgiがインストールされたはずなので、ファイルパスを検索する。
$ find /var/www/python/venv -name 'mod_*.so' /var/www/python/venv/lib/python3.6/site-packages/mod_wsgi/server/mod_wsgi-xxx.so
このパスをコピペして、httd.confに設定する。
$ sudo vim /etc/httpd/conf/httpd.conf LoadModule wsgi_module /var/www/python/venv/lib/python3.6/site-packages/mod_wsgi/server/mod_wsgi-xxx.so WSGIPythonHome /var/www/python/venv WSGIPythonPath /var/www/python/venv/lib/python3.6/site-packages WSGIScriptAlias /test-site /var/www/python/test_site/test_site/wsgi.py
- WSGIPythonHomeは、Apacheに使わせるPythonのホームディレクトリー
- WSGIPythonPathは、実行中にPythonに通させるパス
- WSGIScriptAliasは引数を2つ取り、1つめのURLがアクセスされたら、2つめのwsgiスクリプトに移譲するという設定
と思われる。
さて、これでhttpd.confの設定確認を行ったところ、下記のようなエラーが発生した。
$ apachectl configtest httpd: Syntax error on line 9999 of /etc/httpd/conf/httpd.conf: Cannot load /var/www/python/venv/lib/python3.6/site-packages/mod_wsgi/server/mod_wsgi-xxx.so into server: /var/www/python/venv/lib/python3.6/site-packages/mod_wsgi/server/mod_wsgi-xxx.so: undefined symbol: forkpty
どうも、環境によってなのか、Python3でmod_wsgiを使うとこのようなエラーが発生するそうな。
事前にApacheにとあるライブラリーをロードさせておくと良いらしい。
$ sudo vim /etc/sysconfig/httpd # 以下2行を追加 export LD_PRELOAD=/usr/lib/libutil.so # これはいらない場合もある export LD_LIBRARY_PATH=/usr/local/lib # Python を -fPIC でコンパイルした場合は .so が読み込まれるようにしておく(設定箇所はここじゃなくても良い) $ sudo apachectl restart
この libutil.so ってやつは、私の環境には既に入っていた。
Apache経由でDjangoにアクセスしてみる
さて、お楽しみの時間だ。
みんな大好きコーディング。
まず、プロジェクト初期化時に自動生成されたsettings.py内のALLOWED_HOSTSという設定項目に、開発中(デバッグモード時)にどこからアクセスされるのか、という設定を行う。
$ vim /var/www/python/test_site/test_site/settings.py ALLOWED_HOSTS = ['127.0.0.1', 'localhost']
ALLOWD_HOSTSの設定はおそらく、実際にアクセスされた時のHTTPヘッダーのホスト名と比較されるものと思われる。
上記設定ではローカルホストからしかアクセスできないので、実際には、クライアントからアクセスされる時のサーバーのホスト名を設定してやらなければならないだろう。
次に、自動生成されたwsgi.pyをカスタマイズする。
と言っても、プロジェクトのパスを追加するだけである。
$ vim /var/www/python/test_site/test_site/wsgi.py import os import sys # 追加 from django.core.wsgi import get_wsgi_application sys.path.append('/var/www/python/test_site') # 追加 sys.path.append('/var/www/python/test_site/test_site') # 追加 os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test_site.settings") application = get_wsgi_application()
テスト用にview.pyという名のコントローラーを作ってみる。
ここでは、動作確認用に、単にHelloテキストを返すだけとする。
$ touch /var/www/python/test_site/test_site/view.py $ vim /var/www/python/test_site/test_site/view.py from django.http import HttpResponse def hello(request): return HttpResponse('Hello Django\n', content_type='text/plain')
最後に、urls.py(こちらも自動生成)をカスタマイズして、URLマッピングを行う。
urlpatternsには、urlレコードを配列で登録する。
urlは引数を3つ取る。
1つ目は、アクセスされるURLだが、ここでは正規表現 ^$
として、トップページがパスなしでアクセスされた時を指定している。
2つ目は、そのURLがアクセスされた時に実行される関数を指定する。ここでは、カレントディレクトリーからimportしたview.pyの中のhello関数を指定している。
$ vim /var/www/python/test_site/test_site/urls.py from django.conf.urls import url from django.contrib import admin from . import view # 追加 urlpatterns = [ url(r'^$', view.hello, name='hello') # 元々あった設定を削除して追加 ]
動作確認。
$ curl localhost/test-site/ Hello Django