flask+apacheでAPI公開

# 3行で
* 自分で作った機械学習モデルを簡単にweb上にAPIとして公開したい。
* flaskを使うとpythonのアプリケーションがwebに公開できる。
* apacheを使う場合、.confにmod_wsgiの設定を書く必要がある。設定例を公開する。

# 目標
ターミナルから

```bash
curl https://example.com/yourapp
```

の結果として、Contentに

```
Hello World!!!!
```
と表示させる

# 環境
|   項目   | 使用環境 |
|:-----------|:------------|
| クラウドサービス  | AWS EC2       | 
| OS    | ubuntu 18.04 LTS  | 
| サーバーソフト     | Apache/2.4.29 (Ubuntu)      | 
| 認証局      | let's encrypt       | 


# 必要な作業
1. apacheのインストール
2. (オプション)https対応
3. mod_wsgiのインストール
4. apacheの設定変更
    1. ディレクトリリスティングの無効化
    2. ドキュメントルートの設定
    3. mod_wsgiを読み込む設定
5. flaskのインストール
6. flaskでアプリ作成
7. wsgiファイルの書き換え

# 全体通しての注意
apacheが思い通りの挙動をしない場合、ログを見ながらトラブルシューティングすることが大事です。
ubuntuの場合デフォルトではログは`/var/log/apache2`配下にあります。例えば

```bash
tail /var/log/apache2/error.log
```
でログを確認してください。
apacheが起動しなくなった場合、確認するのは`systemd`のログです。これを確認するには、以下のコマンドが使えます。

```bash
journalctl | grep apache2
```

# apacheのインストール、https対応
前に書いた自分の記事を参考にできます。
[ubuntu18.04+apacheでhttps対応](https://qiita.com/nabion/items/d96d3734352c8055a417)

# mod_wsgiのインストール

--------------------
### 注意
flask公式を見ると、`apt-get install libapache2-mod-wsgi`でインストールしろという記述がありますが、これをやるとpython2系のmod-wsgiが入ってしまいます。python3系を使いたい場合は避けましょう。
https://flask.palletsprojects.com/en/1.1.x/deploying/mod_wsgi/

--------------------

apacheでpythonのwebアプリを作るにはmod_wsgiが必要です。

>mod_wsgi パッケージは、Python WSGI 仕様をサポートする任意の Python ウェブアプリケーションをホストすることができる、使いやすい Apache モジュールを実装しています。
https://modwsgi.readthedocs.io/en/develop/
(deepl訳)

そこでmod_wsgiをインストールするわけですが、我らpythonの民はソースからビルドすると寿命が減ってしまうので、pipでインストールします。
https://pypi.org/project/mod-wsgi/
ちょっと前置きが長いですが、System Requirementsの項に
>If you are running Debian or Ubuntu Linux with Apache 2.4 system packages, regardless of which Apache MPM is being used, you would need both:
apache2
apache2-dev
https://pypi.org/project/mod-wsgi/

とあるので、apache2-devをインストールしてからmod-wsgiをpipでインストールします。

```
sudo apt install apache2-dev
pip3 install mod-wsgi
```
動作確認の前にapacheの設定を変更します。

# apacheの設定変更
## ディレクトリリスティングの無効化
まずは[こういうやつ](https://www.google.com/search?q=index+%E8%A1%A8%E7%A4%BA&sxsrf=ALeKk00N2bJzes0LhV1Rt23d2rUdaNjo9g:1590074726911&source=lnms&tbm=isch&sa=X&ved=2ahUKEwi13o36ocXpAhWNHHAKHeP2BpsQ_AUoAXoECCgQAw&biw=1920&bih=937)が表示されないように設定を変えます。
`/etc/apache2/apache2.conf`を見ると

```conf:apache2.conf
<Directory /var/www/>
        Options Indexes FollowSymLinks
        AllowOverride None
        Require all granted
</Directory>
```
のようになっていると思いますが、`Options`の項から`Indexes`を消します

```conf:apache2.conf
<Directory /var/www/>
        Options FollowSymLinks
        AllowOverride None
        Require all granted
</Directory>
```

これでOKです。
参考
>Indexes
もし、URL がディレクトリにマップするリクエストであって、 且つ DirectoryIndex で指定したファイル (例えば、index.html) が ディレクトリ内に無ければ、mod_autoindex が ディレクトリ内の一覧を整形して返します。
https://httpd.apache.org/docs/2.4/ja/mod/core.html#options

## ドキュメントルートの設定
自分のドメインにアクセスされた際、どのディレクトリをルートディレクトリ（ドキュメントルート）として扱うか設定できます。
デフォルトでは`/var/www/html/`になっています。
Apache2 Ubuntu Default Pageとして表示されているものの正体は`/var/www/html/index.html`だったわけですね。

この設定は`/etc/apache2/apache2.conf`(と、let's encryptのcertbotを使った場合`/etc/apache2/sites-available/000-default-le-ssl.conf`)内でできます。例えば`/home/ubuntu/apache_docroot`をドキュメントルートにしたい場合は

```conf:apache2.conf
<Directory /home/ubuntu/apache_docroot>
        Options FollowSymLinks
        AllowOverride None
        Require all granted
</Directory>
```
を書き加え`VirtualHost`ディレクティブ内(certbotを使った場合`/etc/apache2/sites-available/000-default-le-ssl.conf`にあります)に下記を加えます

```conf:000-default-le-ssl.conf
DocumentRoot /home/ubuntu/apache_docroot
```
デフォルトで`DocumentRoot /var/www/html`の記述があるので、どこに書けばいいかはわかると思います。

## mod_wsgiを読み込む設定
まず、pipでインストールしたmod_wsgiのインストール先をapacheに教える必要があります。

これは

```bash
python -c "import sys; print(sys.path)"
```
を実行して、表示されるディレクトリのどれかの配下にある`.so`ファイルです。
私の環境では以下の場所にありました。

```
/home/ubuntu/.local/lib/python3.6/site-packages/mod_wsgi/server/mod_wsgi-py36.cpython-36m-x86_64-linux-gnu.so
```
これも`VirtualHost`ディレクティブ内に書き込みます

```conf:000-default-le-ssl.conf
LoadModule wsgi_module /home/ubuntu/.local/lib/python3.6/site-packages/mod_wsgi/server/mod_wsgi-py36.cpython-36m-x86_64-linux-gnu.so
```

次に、`mod_wsgi`の挙動を決める`.wsgi`ファイルを作成します。
例として`/home/ubuntu/apache_docroot`配下に`yourapp`などというディレクトリを作り`hello.wsgi`という名前で以下の内容を書いたファイルを保存してください。

```python:/home/ubuntu/apache_docroot/yourapp/hello.wsgi
def application(environ, start_response):
    status = '200 OK'
    output = b'Hello World!'

    response_headers = [('Content-type', 'text/plain'),
                        ('Content-Length', str(len(output)))]
    start_response(status, response_headers)

    return [output]
```
`VirtualHost`ディレクティブ内にこの`hello.wsgi`を読みにかせるよう`WSGIScriptAlias`という項に記述します。
https://example.com/yourapp というURLを踏んだ際に`hello.wsgi`を読みに行かせたい場合、以下のように書きます

```
WSGIScriptAlias /yourapp /home/ubuntu/apache_docroot/yourapp/hello.wsgi
```
1つ目の引数は設定したいURLのドキュメントルートからの相対パス、2つ目の引数は`.wsgi`ファイルの絶対パスです。

ここまで書き換えて最終的に、`VirtualHost`ディレクティブは以下のようになります。

```conf:000-default-le-ssl.conf
<VirtualHost *:443>
        ServerAdmin webmaster@localhost
        #DocumentRoot /var/www/html
        DocumentRoot /home/ubuntu/apache_docroot

        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined

        #for flask
        LoadModule wsgi_module /home/ubuntu/.local/lib/python3.6/site-packages/mod_wsgi/server/mod_wsgi-py36.cpython-36m-x86_64-linux-gnu.so
        WSGIDaemonProcess hello user=ubuntu
        WSGIScriptAlias /tegaki_api /home/ubuntu/apache_docroot/tegaki_api/hello.wsgi

ServerName example.com
SSLCertificateFile hogehoge.pem
SSLCertificateKeyFile fugafuga.pem
Include /etc/letsencrypt/options-ssl-apache.conf
</VirtualHost>
```

`WSGIDaemonProcess hello user=ubuntu`はflaskのhelloという（これから作る）アプリがubuntuという名前のユーザー権限で動作するように書き加えています。

ここまで出来たら、設定を反映させてapacheを再起動すると、`.wsgi`ファイルの挙動を確認できます。
コマンドは

```
cd /etc/apache2/sites-enabled
sudo a2dissite 000-default-le-ssl.conf
cd ../sites-available/
sudo a2ensite 000-default-le-ssl.conf
sudo /etc/init.d/apache2 restart
```
です。

# flaskのインストール
いよいよflaskをインストールします。
これは実に簡単。（仮想環境使わないなら）以下のコマンドだけでできます。

```
easy_install flask
```
easy!

# flaskでアプリ作成
flask部分を作ります。`/home/ubuntu/apache_docroot/yourapp`配下に`hello.py`として下記のコードを書いておきます。

```python:/home/ubuntu/apache_docroot/yourapp/hello.py
from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return "Hello World!!!!"

if __name__ == '__main__':
    app.run()
```

# wsgiファイルの書き換え
URLを踏まれたときの挙動を定義している実体はflaskになったので、`.wsgi`ファイルも書き換えます。

```python:hello.wsgi
import sys
sys.path.insert(0, '/home/ubuntu/apache_docroot/yourapp')
from hello import app as application
```
前項で作った`hello.py`がインポートできるようにパスを通し、`application`という名前でインポートできればOKです。

これでapacheを再起動すれば

```bash
curl https://example.com/yourapp
```

の結果として、Contentに

```
Hello World!!!!
```
が表示されます。

# 参考
[Apache2.4セキュリティ「ディレクトリ一覧表示対策」](https://www.ritolab.com/entry/2)
[[Python] mod_wsgiを使ってPython3.6をApacheで動かす（CentOS6系）](https://www.yoheim.net/blog.php?q=20170206)