mod_wsgi で flask アプリをデプロイ (pipenv 使用）

## はじめに

mod_wsgi (+ Apache) を使ったFlaskアプリのデプロイは[公式のドキュメント](https://flask.palletsprojects.com/en/0.12.x/deploying/mod_wsgi/)にも書かれているポピュラーな方法ですが、私が読んだ限りでは説明が初心者には不親切です．スクリプトが要所要所で端折られてるため，いまいち何をやったらいいのかわかりませんでした．

さらに私はFlaskアプリのパッケージ管理として Pipenv を使おうとしてたのですが、mod_wsgi, flask に Pipenv を組み合わせる方法となると英語で検索してもなかなかヒットしませんでした．

実際に丸一日くらい苦心してやってみたところ，上記の組み合わせでアプリをデプロイするにはやや複雑な手順が必要だったので、自分への備忘録も兼ねてここに記しておきたいと思います．

## 環境構成
 * OS: Ubuntu 18.04
 * Web Server: Apache 2.4
 * Python: Python 3.6

## デプロイへの道のり

#### 1. Apache, mod_wsgi をインストール

```
$ sudo apt install apache2
$ sudo apt install libapache2-mod-wsgi-py3
```

Python 3 を使う場合は `libapache2-mod-wsgi` ではなく `libapache2-mod-wsgi-py3` を指定する必要があるみたいです．ここ結構なハマりポイントだと思います．

#### 2. Apache の公開フォルダにFlaskアプリを追加する

`/var/www/html` 配下にFlaskアプリを追加します．今回は説明を簡単にするために Hello, World を表示するだけのプログラムにしましょう．

html 配下に`wsgi-scripts`フォルダを作成して`hello.py`という名前で以下のファイルを保存します．

```/var/www/html/wsgi-scripts/hello.py
from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello from flask!'

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

書けたら

```
$ pipenv install
$ pipenv install flask
```

で仮想環境を作成しておきましょう．

#### 3. wsgi ファイルを追加する

flaskアプリと同じフォルダにwsgiファイルも追加します．これが本番サーバーで稼働させるときに必要になるファイルです．なんかちょっと不思議な中身に見えますが，これがwsgiスクリプトというものらしいです．複雑に思えますが，最終行以外はmod-wsgiとpipenvを使うための準備です．

```/var/www/html/wsgi-scripts/myapp.wsgi
activate_this = '/home/hoge/.local/share/virtualenvs/wsgi-scripts-XXXXXX/bin/activate_this.py'
with open(activate_this) as file_:
  exec(file_.read(), dict(__file__=activate_this))

import sys
sys.path.insert(0, 'var/www/html/wsgi-scripts')

from hello import app as application
```

ここで `activate_this` に該当するパスは個々人の環境に合わせて修正する必要があります．要はvirtualenvのパスなのですが、確認方法としては `pipenv shell` を起動したときに出てくるパスをコピーすればいいです．

#### 4. conf ファイルを追加する

ここからは Apacheの設定です．`/etc/apache2/conf-available/`に`wsgi.conf`を追加しましょう．

```/etc/apache2/conf-available/wsgi.conf`
WSGIDaemonProcess myapp

WSGIProcessGroup myapp
WSGIApplicationGroup %{GLOBAL}

WSGIScriptAlias /myapp /var/www/html/wsgi-scripts/myapp.wsgi

<Directory /var/www/html/wsgi-scripts>
    Require all granted
</Directory>
```

純粋にwsgiを動かすだけなら WSGIScriptAlias だけ書けばいいのですが、Flaskアプリをうごかす場合は他の設定も書く必要があります．

書けたら以下のコマンドでサイトを有効化します．

```
$ sudo a2enconf wsgi
$ sudo sytemctl reload apache2
```

これで http://localhost/myapp にアクセスすれば，`hello from flask` が表示されるはずです．

## おまけ: Plotly Dash をデプロイする
Flaskアプリの一つであるPlotly Dashをデプロイする場合は少し変更が必要です

まずpyファイルを以下のように変更します．

```hello_dash.py
from flask import Flask
import dash
import dash_html_components as html

server = Flask(__name__)

@server.route('/')
def index():
    return 'Hello Flask app'

app = dash.Dash(__name__, server=server, routes_pathname_prefix='/myapp/')
app.layout = html.Div('My Dash app')

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

さらにwsgiファイルの最終行を以下のように変更します

```myapp.wsgi
from hello_dash import server as application
```

最後に　confファイルも以下のように変更します

WSGIScriptAlias のパスを `/` に変更するのがミソです。これをしないとJSでエラーが出てしまいます。

```wsgi.conf
WSGIDaemonProcess myapp

WSGIProcessGroup myapp
WSGIApplicationGroup %{GLOBAL}

WSGIScriptAlias / /var/www/html/wsgi-scripts/myapp.wsgi

WSGIPassAuthorization On

<Directory /var/www/html/wsgi-scripts>
    Require all granted
</Directory>
```

`WSGIPassAuthorization` は `dash_auth` を使う際に必要です

これでDashアプリが動きます。