Django事始め: 3_Apache連携

# 目次
[Django事始め: 1_環境構築](http://qiita.com/roy29fuku/items/3fae6a1b474676cdb703)
[Django事始め: 2_プロジェクト作成](http://qiita.com/roy29fuku/items/306399ca5eb6f63ed691)
[Django事始め: 3_Apache連携](http://qiita.com/roy29fuku/items/578de62fbdd65f8ffbaa)
[Django事始め: 4_MySQL連携](http://qiita.com/roy29fuku/items/519becda21565e5a71a3)

前回は開発用簡易サーバでDjangoの起動をブラウザから確認しました。
後々公開することを考えるとapacheなどのウェブサーバと連携する必要があります。

ここではApache + mod_wsgi + Djangoという形にします。
公式でもオススメされている方法です。

[Django を Apache と mod_wsgi とともに使うには？](https://docs.djangoproject.com/ja/1.10/howto/deployment/wsgi/modwsgi/)

ただ、いろいろウェブで調べましたが中々うまくいかず大変なところでした。
多分他の人がこの記事を読んでもきっとどこかでハマるんだと思います。

うまくいかない時は一度公式を読むことをお勧めします。しんどいですが、一次情報が一番確実です。

[mod_wsgi公式](https://modwsgi.readthedocs.io/en/master/)

ではなるべく細かく書き残しておきます。

# 環境
- CentOS 7.1
- Apache 2.4.6
- pyenv 1.0.7
- anaconda 3-4.3.0
- django 1.10.5
- mariadb-5.5.52



# ちょこっと解説
## Apacheとは
ウェブサーバです。多分一番使われてます。
デフォルトではPythonを解釈する機能が付いていないので、別途カスタマイズする必要があります。

## mod_wsgiとは
wsgiはウィズギーと読むらしいです。
Apacheのモジュールで、Pythonを実行できるようにしてくれます。

# Apacheインストール
サーバにapacheが入っていなければまずインストールします。

```
yum install httpd httpd-devel
systemctl start httpd
systemctl enable httpd
```

念のためブラウザにIPアドレス打ってapacheが動いているか確認しましょう。

# mod_wsgiインストール
インストール時にPythonと紐付ける必要があります。
pyenvを使っている場合はそれを伝えないといけません。

うっかりyumで手軽にインストールしたら組み込みのPythonと紐付いて、結局やり直しになるので注意どころです。

ソースをダウンロード、展開します。

```
wget https://github.com/GrahamDumpleton/mod_wsgi/archive/4.5.14.tar.gz
tar -zxvf 4.5.14.tar.gz
cd mod_wsgi-4.5.14/
```

*自分がプロジェクトで使いたいPythonのパスを指定します！
pyenvの場合はpyenv/versions/ 以下にダウンロードしたPythonが入っています。
anaconda3-4.3.0を使いたい場合は以下のように指定します。バイナリファイルを指定する必要があります。

```
./configure --with-python=/usr/local/bin/pyenv/versions/anaconda3-4.3.0/bin/python
make
make install
```

# mod_wsgiインストールエラー
以下のようなエラーが出ることがあります。

```
/bin/ld: /usr/local/bin/pyenv/versions/3.6.0/lib/libpython3.6m.a(abstract.o): relocation R_X86_64_32S against `_Py_NotImplementedStruct' can not be used when making a shared object; recompile with -fPIC
```

この場合はPythonを再コンパイルする必要があります。

```
CONFIGURE_OPTS="--enable-shared" CFLAGS="-fPIC" pyenv install X.X.X
```

その後改めてインストールします。一旦` make clean`で設定をリセットします。

```
make clean
./configure CFLAGS=-fPIC --enable-shared
make
make install
```


# Apache設定
 `/etc/httpd/conf/httpd.conf`に追記、

全体の記述はこんな感じです。
適当に置き換えてください。
/home/django/ 以下にxxxというプロジェクトを作った場合です。

```
LoadModule wsgi_module modules/mod_wsgi.so
WSGIScriptAlias / /home/django/xxx/xxx/wsgi.py
WSGIPythonHome /usr/local/bin/pyenv/versions/anaconda3-4.3.0/
WSGIPythonPath /home/django/xxx

<Directory /home/django/xxx/xxx>
  <Files wsgi.py>
    Require all granted
  </Files>
</Directory>

Alias /static/ /home/django/xxx/static/
<Directory /home/django/xxx/static>
  Require all granted
</Directory>
```

それぞれの項目を解説します。

* WSGIScriptAlias: Apacheに来たリクエストをどこにつなぐか、です。ここでは/（ルート）に来たリクエストをDjangoでつくったプロジェクトに飛ばしています。
* WSGIPythonHome: mod_wsgiに使わせたいPythonのパスを記述します。インストールの時と違い、ディレクトリを指定することに注意です。
* WSGIPythonPath: プロジェクトのパスを指定します。
* Directory: 指定のディレクトリにapacheのアクセス許可を与えます。
* Alias /static/ /home/django/xxx/static/: Djangoが静的ファイルを探す場所を指定しています。


*注意
Apache2.4系では
`Require all granted`
ですが、それより前のバージョンでは以下のように書いてください。

```
Order allow,deny
Allow from all
```


# mod_wsgi.soの依存関係を満足させる

今のままだとmod_wsgi.soはpythonを必要としているのに、見つけられていないです。
詳細をlddコマンドで確認します。

```
ldd /etc/httpd/modules/mod_wsgi.so
	linux-vdso.so.1 =>  (0x00007fff8c9fe000)
	libpython3.6m.so.1.0 => not found
	libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f4c9e548000)
	libdl.so.2 => /lib64/libdl.so.2 (0x00007f4c9e344000)
	libutil.so.1 => /lib64/libutil.so.1 (0x00007f4c9e141000)
	librt.so.1 => /lib64/librt.so.1 (0x00007f4c9df38000)
	libm.so.6 => /lib64/libm.so.6 (0x00007f4c9dc36000)
	libc.so.6 => /lib64/libc.so.6 (0x00007f4c9d875000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f4c9e9a8000)
```

libpython3.6m.so.1.0を見つけられていません。
自分が使うPythonのディレクトリ以下を検索して、シンボリックリンクを貼りましょう。

```
ls /usr/local/bin/pyenv/versions/anaconda3-4.3.0/lib/ | grep python
libpython3.6m.so
libpython3.6m.so.1.0
libpython3.so
python3.6
ln -s /usr/local/bin/pyenv/versions/anaconda3-4.3.0/lib/libpython3.6m.so.1.0 /lib64/
```


# Apache起動確認 => エラー解消
ここまで来たら一旦Apapcheを再起動します。
`systemctl restart httpd`

おそらくエラーが出るのでエラーログを見ながらブラウザにアクセスします。
`tail -f /var/log/httpd/error_log`

エラー別に原因を記します。

## WSGIPythonHomeの設定が間違っている
```
[Thu Mar 09 16:32:51.315916 2017] [core:notice] [pid 9301] AH00094: Command line: '/usr/sbin/httpd -D FOREGROUND'
Could not find platform independent libraries <prefix>
Could not find platform dependent libraries <exec_prefix>
Consider setting $PYTHONHOME to <prefix>[:<exec_prefix>]
Fatal Python error: Py_Initialize: Unable to get the locale encoding
ModuleNotFoundError: No module named 'encodings'

Current thread 0x00007ff5dd6d8840 (most recent call first):
Could not find platform independent libraries <prefix>
Could not find platform dependent libraries <exec_prefix>
Consider setting $PYTHONHOME to <prefix>[:<exec_prefix>]
Fatal Python error: Py_Initialize: Unable to get the locale encoding
ModuleNotFoundError: No module named 'encodings'
```
WSGIPythonHomeの場所を確認しましょう。使いたいPythonはpyenvのものか、システムのものか、バージョンは合っているかを確認しましょう。

## モジュールが足りない
```
[Thu Mar 09 15:53:52.797174 2017] [wsgi:error] [pid 9535] [client 192.168.10.1:65448] mod_wsgi (pid=9535): Target WSGI script '/home/django/plagiarism/plagiarism/wsgi.py' cannot be loaded as Python module.
[Thu Mar 09 15:53:52.797231 2017] [wsgi:error] [pid 9535] [client 192.168.10.1:65448] mod_wsgi (pid=9535): Exception occurred processing WSGI script '/home/django/plagiarism/plagiarism/wsgi.py'.
[Thu Mar 09 15:53:52.800163 2017] [wsgi:error] [pid 9535] [client 192.168.10.1:65448] Traceback (most recent call last):
[Thu Mar 09 15:53:52.800196 2017] [wsgi:error] [pid 9535] [client 192.168.10.1:65448]   File "/usr/local/bin/pyenv/versions/anaconda3-4.3.0/lib/python3.6/site-packages/django/db/backends/mysql/base.py", line 25, in <module>
[Thu Mar 09 15:53:52.800199 2017] [wsgi:error] [pid 9535] [client 192.168.10.1:65448]     import MySQLdb as Database
[Thu Mar 09 15:53:52.800213 2017] [wsgi:error] [pid 9535] [client 192.168.10.1:65448] ModuleNotFoundError: No module named 'MySQLdb'
```

MySQLdbをimportしろとのこと。
インストールしましょう。そのままの名前じゃないことに注意。
`pip install mysqlclient`


## Pythonの指定ができていない
```
[Thu Mar 09 16:46:51.598855 2017] [mpm_prefork:notice] [pid 9476] AH00163: Apache/2.4.6 (CentOS) mod_wsgi/4.5.14 Python/xxx configured -- resuming normal operations
```
Python/xxxの部分が自分が使いたいPythonのバージョンと異なる場合はmod_wsgiと使いたいPythonが紐付いていません。一回mod_wsgiをインストールしなおしましょう。

## mod_wsgi.soの依存関係が満足でない
```
3月 12 15:42:31 ip-172-31-32-130 systemd[1]: Starting The Apache HTTP Server...
3月 12 15:42:31 ip-172-31-32-130 httpd[22078]: httpd: Syntax error on line 353 of /etc/httpd/conf/httpd.conf: Syntax error on line ...: Cannot load modules/mod_wsgi.so into server: libpython3.6m.so.1.0: cannot open shared object file: No such file or directory
```

mod_wsgi.soはpythonを必要としているのに、見つけられていないです。
詳細をlddコマンドで確認します。

```
ldd /etc/httpd/modules/mod_wsgi.so
	linux-vdso.so.1 =>  (0x00007fff8c9fe000)
	libpython3.6m.so.1.0 => not found
	libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f4c9e548000)
	libdl.so.2 => /lib64/libdl.so.2 (0x00007f4c9e344000)
	libutil.so.1 => /lib64/libutil.so.1 (0x00007f4c9e141000)
	librt.so.1 => /lib64/librt.so.1 (0x00007f4c9df38000)
	libm.so.6 => /lib64/libm.so.6 (0x00007f4c9dc36000)
	libc.so.6 => /lib64/libc.so.6 (0x00007f4c9d875000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f4c9e9a8000)
```

libpython3.6m.so.1.0を見つけられていません。
自分が使うPythonのディレクトリ以下を検索して、シンボリックリンクを貼りましょう。

```
ls /usr/local/bin/pyenv/versions/anaconda3-4.3.0/lib/ | grep python
libpython3.6m.so
libpython3.6m.so.1.0
libpython3.so
python3.6
ln -s /usr/local/bin/pyenv/versions/anaconda3-4.3.0/lib/libpython3.6m.so.1.0 /lib64/
```

## OpenSSLのバージョンが古い？
アクセスするとDjangoのページは表示されるが以下のエラーが出る場合。

```
You are linking against OpenSSL 1.0.0, which is no longer support by the OpenSSL project. You need to upgrade to a newer version of OpenSSL.
```

`openssl version`で確認するバージョンと表示がずれているっぽいです。

散々調べましたが、これと同じ状況でAnacondaのopensslを使ってしまっている？
[pyenv の Anaconda/Miniconda 対応について](http://qiita.com/yyuu@github/items/7de957838437eaecdc8a#pyenv-%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B-anacondaminiconda-%E5%AF%BE%E5%BF%9C%E3%81%AE%E5%95%8F%E9%A1%8C%E7%82%B9)

`pip install -U cryptography`
で解決。




# 静的ファイルの公開
```
Not Found: /static/admin/css/base.css, referer: http://xxx.xxx.xxx.xxx/
```
開発サーバでDjangoが用意しているテンプレートなどを使っている場合、
ウェブサーバで公開する時は改めて設定が必要です。こちらはエラーが出ていなくても設定しておきましょう。

自分のプロジェクトディレクトリ下にDjangoのファイルをコピーなりシンボリックリンクを貼るなりすれば良いのですが、Djangoに専用のコマンドがあるのでそれを使いましょう。

プロジェクトディレクトリのsettings.pyに以下の形で静的ファイルを置く場所を決めます。
`STATIC_ROOT = '/home/django/xxx/static/'`
以下のコマンドで指定したディレクトリに静的ファイルをコピーしてくれます。
`python manage.py collectstatic`


---

エラー解消はドット疲れますが、これを乗り越えないと公開できません。
思い出しただけで辛くなりました。

---

# mod_wsgiアンインストール
利用するPythonのバージョンを変更したい場合は一度mod_wsgiをアンインストールして、再度上記手順を辿る必要があります。  
アンインストールするには、mod_wsgi.soを削除すれば良いです。  

# 参考
- [No module named MySQLdb](http://stackoverflow.com/questions/454854/no-module-named-mysqldb)
- [The staticfiles app](https://docs.djangoproject.com/ja/1.10/ref/contrib/staticfiles/#django-admin-collectstatic)
- [Django での static files の扱い方まとめ](http://hideharaaws.hatenablog.com/entry/2014/12/12/230825)
- [【apache】2.4系はアクセス許可の設定の書き方が違うので注意](https://www.softel.co.jp/blogs/tech/archives/3142)
- [How to uninstall mod_wsgi when installed with make?](http://stackoverflow.com/questions/38403948/how-to-uninstall-mod-wsgi-when-installed-with-make)
- [Python3 + mod_wsig3.3 + Apache2.2 でHelloWorldをブラウザ表示](http://istks.net/1127)
- [え？君せっかく Python のバージョン管理に pyenv 使ってるのに Vim の補完はシステムライブラリ参照してるの？](http://lambdalisue.hatenablog.com/entry/2014/05/21/065845)
- [ApacheでPythonを動かしてみた(mod_wsgiをソースからインストール）](http://h2shiki.hateblo.jp/entry/2016/04/07/000506)

# vhosts
最終的にはドメインを取得してapacheのvhostsを使うことになると思います。  
ひとまずうまくいった設定だけメモしておきます。  

```httpd.conf
LoadModule wsgi_module modules/mod_wsgi.so
WSGIPythonHome /usr/local/bin/pyenv/versions/anaconda3-4.3.0/
```

```vhosts.conf
<VirtualHost *:80>
  ServerName  MY_DOMAIN
  WSGIDaemonProcess PROCESS_GROUP user=apache group=apache python-path=/path/to/project
  WSGIProcessGroup PROCESS_GROUP
  WSGIScriptAlias / /path/to/project/app/wsgi.py process-group=PROCESS_GROUP

  <Directory /path/to/project/app/>
    <Files wsgi.py>
      Require all granted
    </Files>
  </Directory>

  Alias /static/ /path/to/project/static/
  <Directory /path/to/project/static/>
    Require all granted
  </Directory>
  CustomLog  /path/to/project/log/access_log common
  ErrorLog   /path/to/project/log/error_log

</VirtualHost>
```

環境に合わせて適宜読み替えます。