はじめに
およそ1年前にDjangoを使って簡単なWebアプリを開発した。
データ収集からAI開発、Webアプリ公開まで全てPythonで済ませた話
入力されたURLからスクレイピングでデータ取得→学習済み機械学習モデルで推論→答えを返す
という非常にシンプルなものである。アプリはこちら↓
キー判別AI
久しぶりに使ってみたところ、スクレイピング先のサイトの仕様変更により、正しい結果を得られなくなってしまった。これはマズイと思い改修することにした。
久しぶりにコードを覗いてみたが、自分でもよくわからない…開発当時はとにかく動かすことを目標にしていたこともあり、コードも汚いし、デプロイ方法もすっかり分からなくなっている…
というわけで、今後も不定期的に改修することを考え、開発やデプロイをしやすくするためにDockerでの環境構築を実施した。クローンしてdocker-composeすれば開発できて、なおかつコンテナからherokuにデプロイできるようにするのを目標とする。
また、このアプリ自体がDBも使わず非常にシンプルなので、明らかにDjangoを使う必要もないので、この機会にflaskに書き換えちゃいます。
Githubにコードを公開しています。
https://github.com/hatena-hanata/KJA_app
ディレクトリ構造
最終的なディレクトリ構造はこちらです。
KJA_APP
├── Dockerfile
├── Procfile
├── app.py
├── docker-compose.yml
├── requirements.txt
└── src
    ├── modules
    │   ├── module.py
    │   └── music_class.py
    ├── static
    │   └── model
    │       ├── le.pkl
    │       └── model.pkl
    └── templates
環境構築
version: '3'
services:
  web:
    build: .
    ports:
      - "8080:8080"
    volumes:
      - .:/home/KJA_APP
    tty: true
    environment:
      TZ: Asia/Tokyo
    command: flask run --host 0.0.0.0 --port 8080
FROM python:3.8.0
USER root
# install google chrome
RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -
RUN sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list'
RUN apt-get -y update && apt-get install -y google-chrome-stable && apt-get install -yqq unzip
# install chromedriver
RUN wget -O /tmp/chromedriver.zip http://chromedriver.storage.googleapis.com/`curl -sS chromedriver.storage.googleapis.com/LATEST_RELEASE`/chromedriver_linux64.zip
RUN unzip /tmp/chromedriver.zip chromedriver -d /usr/local/bin/
# install heroku cli
RUN curl https://cli-assets.heroku.com/install.sh | sh
# set display port to avoid crash
ENV DISPLAY=:99
# upgrade pip
RUN apt-get update && apt-get install -y \
	&& pip install --upgrade pip
# change dir
WORKDIR /home/KJA_APP
# install module
COPY requirements.txt /home
RUN pip install -r /home/requirements.txt
# flask setting
ENV FLASK_APP '/home/KJA_APP/app.py'
ENV FLASK_DEBUG 1
今回、seleniumでchromeを動かしてスクレイピングするため、最初にchromeとchrome driverをインストールしています。こちらのdockerfileを参考にしました。
また、herokuへのデプロイのために、heroku cliもインストールしています。
最後の2行flask settingでは、flaskに関する設定を行っています。
FLASK_APPでは、flaskのメイン関数のあるpyファイルを指定することで、flask runコマンドでそのファイルが実行されるようになります。
FLASK_DEBUGを1にすることでデバッグモードになり、ファイルの更新がリアルタイムで反映されるようになります。
これにて、git clone→docker-composeですぐに開発ができるようになりました。docker-composeの際にflask runコマンドを実行しているので、http://localhost:8080/ にアクセスすればアプリが走っていることが分かります。

Herokuにデプロイ
Herokuへの会員登録は済ませているものとします。
準備
- requirements.txt
必要なライブラリをrequirements.txtに書き込みます。デプロイのためにgunicornが必要なので、pipで忘れずにインストールしておきましょう。 - Procfile
プロジェクト直下に作成します。以下のように書きます。
左のappがプロジェクト直下のapp.pyのことで、右のappはapp.pyで定義したFlaskインスタンスの変数名?です。
ちょっと何言ってるか分かりづらいと思うので、こちらを読んでください。
https://stackoverflow.com/questions/19352923/gunicorn-causing-errors-in-heroku 
web: gunicorn app:app --log-file -
アプリ作成
ターミナルからでもできますが、ブラウザからの方が分かりやすいので、ブラウザでやります。
- ブラウザからHerokuにログイン
https://id.heroku.com/login - New -> Create new app でアプリを作成
適当に名前をつけます。 - ビルドパックの追加
今回のアプリがpythonであることを明示するために、作成したアプリのページで Settings -> Buildpacks からheroku/pythonを追加します。
(デプロイ時に自動でpythonを追加してくれる場合もあるそうですが、自分のディレクトリ構造が悪かったせいか、pythonプロジェクトと認識されず困ったので、予め手動で追加しておくことをオススメします。)
また、今回はseleniumとchromeを利用したスクレイピングをするため、chromeとchrome driverが必要になります。これもビルドパックの追加で対応できます。
https://qiita.com/nsuhara/items/76ae132734b7e2b352dd#chrome%E3%81%A8driver%E3%81%AE%E8%A8%AD%E5%AE%9A 
デプロイ
ここからはDockerのコンテナに入ってデプロイします。
- herokuにログイン
リンクが出てくると思うので、それをクリックして認証する。 
root@a59194fe1893:/home/KJA_APP# heroku login
- herokuのアプリ用リポジトリにpushする。
urlはhttps://git.heroku.com/[上の2.で作成したアプリ名].gitです。(ブラウザのSettingsでも確認できます)。
下のコマンドは、今のブランチの内容をherokuのリポジトリのmasterブランチにpushする、という意味です。 
root@a59194fe1893:/home/KJA_APP# git push https://git.heroku.com/[上の2.で作成したアプリ名].git master
エラーがでなければこれにてデプロイ完了となります。
最後に
docker-compose便利ですね。