（備忘録）TensorFlowを使ってオススメの楽曲名を類推するWebアプリ【docker-composeで実行環境作り】

## はじめに
本記事は<a href="https://qiita.com/okubo1504/items/868514e1ad1e2debf6c1">**（備忘録）TensorFlowを使ってオススメの楽曲名を類推するWebアプリ**</a>の続きです。
docker-composeでローカル環境にTensorFlow+Kerasの環境作り、
WEB API叩くところまで整理したいと思います。
自分用に作った記事なので、分かりにくい点や情報、技術が古いかもしれませんがご了承ください:bow:
また何かしらのWebアプリを独りで作りたい方のご参考になれば嬉しいです。

Webアプリ実物は以下のGIFのようになります。
![ezgif.com-crop.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631912/33436db1-85fc-2e62-2a44-adc1018c975e.gif)
検索ボックスに文章で入力すると、ハンバード・ハンバードさんの「同じ話」と言う回答頂きました:clap:
$\tiny{※学習データが少ないため、一部の曲しかヒットしません。。しょぼいです}$:bow_tone1:
$\tiny{※楽譜リンクをクリックすると楽譜の一部が表示されていますが、記事の対象外です}$:no_good_tone1:

## 参考文献

この記事を作るにあたって参考にさせて頂きました:bow_tone1:

* [docker-composeでgunicorn+nginx+flaskを動かしてみた話](https://qiita.com/arata-honda/items/e22c9df83df8ee0e9c4c)
* [[イメージ図付]Nginx+gunicorn+FlaskをDocker化[後編]](https://qiita.com/mintak21/items/00a13e3699588c72b965)
* [DockerでDjangoの開発環境を再構築！！！！](https://qiita.com/nokonoko_1203/items/e345f899ac9ac700d6a8)
* [すぐに使える! 業務で実践できる! Pythonによる AI・機械学習・深層学習アプリのつくり方](https://www.amazon.co.jp/%E3%81%99%E3%81%90%E3%81%AB%E4%BD%BF%E3%81%88%E3%82%8B-%E6%A5%AD%E5%8B%99%E3%81%A7%E5%AE%9F%E8%B7%B5%E3%81%A7%E3%81%8D%E3%82%8B-Python%E3%81%AB%E3%82%88%E3%82%8B-AI%E3%83%BB%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92%E3%83%BB%E6%B7%B1%E5%B1%A4%E5%AD%A6%E7%BF%92%E3%82%A2%E3%83%97%E3%83%AA%E3%81%AE%E3%81%A4%E3%81%8F%E3%82%8A%E6%96%B9-%E3%82%AF%E3%82%B8%E3%83%A9%E9%A3%9B%E8%A1%8C%E6%9C%BA/dp/4802611641)


## TODO マップ
<a href="https://qiita.com/okubo1504/items/868514e1ad1e2debf6c1">**（備忘録）TensorFlowを使ってオススメの楽曲名を類推するWebアプリ**</a>の続きで、
今回は**Web API側の環境構築（実行環境）**です。

|章|区分|状況|内容|言語、FW、環境等|
|---|---|---|---|---|
|序　章|共通|済|<a href="https://qiita.com/okubo1504/items/868514e1ad1e2debf6c1">アプリの概要</a>|Python<br>TensorFlow<br>Keras<br>Google Colaboratory|
|第一章|Web API|済|**（今回）環境構築（実行環境）**|docker-compose<br>Flask<br>Nginx<br>gunicorn|
|第二章|Web API|済|<a href="https://qiita.com/okubo1504/items/8c3374b03cc185fa9068">機械学習</a>|Python<br>TensorFlow<br>Keras<br>Flask|
|第三章|画面|未着手|環境構築|Python<br>Django<br>Nginx<br>gunicorn<br>PostgreSQL<br>virtualenv|
|第四章|画面|未着手|表示、Web API呼出し部分|Python<br>Django|
|第五章|AWS|未着手|AWS自動デプロイ|Github<br>EC2<br>CodeDeploy<br>CodePipeline|
※まだ記事は全然整理できていないので時間ある時につくります。
未着手のまま命尽きるかも:angel_tone2:
またマップは書いてるうちに変わる可能性ありますのでご了承下さい。。

## 環境 <sup>※以下のVerでなくても動くと思いますが、古いのでご注意下さい:no_good_tone2:</sup>
```Ubuntuバージョン:Ubuntuバージョン
$ cat /etc/os-release
NAME="Ubuntu"
VERSION="18.04.4 LTS (Bionic Beaver)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 18.04.4 LTS"
VERSION_ID="18.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=bionic
UBUNTU_CODENAME=bionic
```
```Dockerバージョン:Dockerバージョン
$ docker version
Client: Docker Engine - Community
 Version:           19.03.8
 API version:       1.40
 Go version:        go1.12.17
 Git commit:        afacb8b7f0
 Built:             Wed Mar 11 01:25:46 2020
 OS/Arch:           linux/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          19.03.8
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.12.17
  Git commit:       afacb8b7f0
  Built:            Wed Mar 11 01:24:19 2020
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.2.13
  GitCommit:        7ad184331fa3e55e52b890ea95e65ba581ae3429
 runc:
  Version:          1.0.0-rc10
  GitCommit:        dc9208a3303feef5b3839f4323d9beb36df0a9dd
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683
```
```Docker-Composeバージョン:Docker-Composeバージョン
$ docker-compose version
docker-compose version 1.25.5, build unknown
docker-py version: 4.2.0
CPython version: 3.7.4
OpenSSL version: OpenSSL 1.1.1c  28 May 2019
```
<sup>※なぜかbuild unknown。時間掛かりそうだったので諦めました:sob:</sup>

## 実行環境の作り方
端的に書くと、ちゃんとdocker-composeがインストールされていて、
以降記載の[ディレクトリ構成](#ディレクトリ構成)通りに必要ファイルを配置すれば、
以下のコマンドを実行するだけです。

```:ビルド&backgroundで起動
docker-compose up -d --build
```
初回はビルドに時間かかりますが、コンテナできた後、Web API叩くと以下GIFのようになります。

```:Web_API実行例
http://localhost:7020/recommend/api/what-music/切なくて誰かの幸せ願う歌
```
#### Web API実行例
![Peek 2020-05-16 14-30.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631912/468d3091-ebf7-589d-f44b-649403154e30.gif)
[ツール](https://katblog.manadream.net/index.php/2019/02/11/rest-api-from-restlet-client/)は色々あるので何でも良いと思いますが、GIFのようにJSONで返ってきます。


### ディレクトリ構成
適当に作っています$\tiny{※凝視したらダメです}$:no_good_tone1:
ゴミファイルが多いですが、Githubに置いてあります。
[ソース](https://github.com/S-okubomy/dk_tensor_fw)

```:ディレクトリ構成
dk_tensor_fw
├── app_tensor
│   ├── Dockerfile
│   ├── exeWhatMusic.py
│   ├── inputFile
│   │   └── ans_studyInput_fork.txt
│   ├── mkdbAndStudy.py
│   ├── requirements.txt
│   ├── studyModel
│   │   ├── genre-model.hdf5
│   │   ├── genre-tdidf.dic
│   │   ├── genre.pickle
│   ├── tfidfWithIni.py
│   └── webQueApiRunServer.py
├── docker-compose.yml
├── web_nginx
    ├── Dockerfile
    └── nginx.conf
```

### docker-composeでローカル環境作るのに必要なファイル
```docker-compose.yml
version: '3'
services:
########### Appサーバ設定 ###########
  app_tensor:
    container_name: app_tensor
    # サービス再起動ポリシー
    restart: always
    # ビルドするdockerファイルが格納されたディレクトリ
    build: ./app_tensor
    volumes:
      # マウントするディレクトリ
      - ./app_tensor:/dk_tensor_fw/app_tensor
    ports:
      # ホスト側のポート：コンテナ側のポート
      - 7010:7010
    networks:
      - nginx_network
########### Appサーバ設定 ###########

########### Webサーバ設定 ###########
  web-nginx:
    container_name: web-nginx
    build: ./web_nginx
    volumes:
      # マウントするディレクトリ
      - ./web_nginx:/dk_tensor_fw/web_nginx
    ports:
      # ホストPCの7020番をコンテナの7020番にポートフォワーディング
      - 7020:7020
    depends_on:
      # 依存関係を指定。web-serverの起動より前にapp-serverを起動するようになる
      - app_tensor
    networks:
      - nginx_network
########### Webサーバ設定 ###########
networks:
  nginx_network:
    driver: bridge
```

※ （参考）上記でポート番号を指定していますが、以下のコマンドで確認してます。

```:（参考）空いているポートの調べ方
# 空いているポート調べる（何も表示されなければ空いてる）
netstat -an | grep 7010
```

```:Dockerfile←Apサーバ側(Gunicorn)
FROM ubuntu:18.04

WORKDIR /dk_tensor_fw/app_tensor
COPY requirements.txt /dk_tensor_fw/app_tensor

RUN apt-get -y update \
    && apt-get -y upgrade \
    && apt-get install -y --no-install-recommends locales curl python3-distutils vim ca-certificates \
    && curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py \
    && python3 get-pip.py \
    && pip install -U pip \
    && localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8 \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/* \
    && pip install -r requirements.txt --no-cache-dir

ENV LANG en_US.utf8

CMD ["gunicorn", "webQueApiRunServer:app", "-b", "0.0.0.0:7010"]
```
```requirements.txt
Flask==1.1.0
gunicorn==19.9.0
Keras>=2.2.5
numpy==1.16.4
pandas==0.24.2
pillow>=6.2.0
python-dateutil==2.8.0
pytz==2019.1
PyYAML==5.1.1
requests==2.22.0
scikit-learn==0.21.2
sklearn==0.0
matplotlib==3.1.1
tensorboard>=1.14.0
tensorflow>=1.14.0
mecab-python3==0.996.2
```

```:Dockerfile←Webサーバ側(Nginx)
FROM nginx:latest

RUN rm /etc/nginx/conf.d/default.conf
COPY nginx.conf /etc/nginx/conf.d
```

```nginx.conf
    upstream app_tensor_config {
        # コンテナのサービス名を指定すると名前解決してくれる
        server app_tensor:7010;
    }

    server {
        listen 7020;
        root /dk_tensor_fw/app_tensor/;
        server_name localhost;

        location / {
            try_files $uri @flask;
        }

        location @flask {
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_redirect off;

            proxy_pass http://app_tensor_config;
        }

        # redirect server error pages to the static page /50x.html
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /usr/share/nginx/html;
        }

        # 静的ファイルの要求をstaticにルーティング　←使ってませんので不要です。
        location /static/ {
            alias /dk_tensor_fw/app_tensor/satic/;
        }
    }
```

### 出来上がった環境の確認

#### ビルド&backgroundで起動

```
$ docker-compose up -d --build
```

#### docker-compose イメージ情報を表示

```
$ docker-compose images
Container          Repository           Tag       Image Id       Size  
-----------------------------------------------------------------------
app_tensor   dk_tensor_fw_app_tensor   latest   3b916ea797e0   2.104 GB
web-nginx    dk_tensor_fw_web-nginx    latest   175c2596bb8b   126.8 MB
```
<sup>作り方が悪いのか結構容量大きいような:sweat:</sup>

#### コンテナの一覧表示

```
$ docker-compose ps
   Name                 Command               State               Ports             
------------------------------------------------------------------------------------
app_tensor   gunicorn webQueApiRunServe ...   Up      0.0.0.0:7010->7010/tcp        
web-nginx    nginx -g daemon off;             Up      0.0.0.0:7020->7020/tcp, 80/tcp
```

#### コンテナに接続（Apサーバ側）

```
$ docker-compose exec app_tensor /bin/bash
root@ba0ce565430c:/dk_tensor_fw/app_tensor#
```
Apサーバ側のコンテナに入れました。。。

#### TensorFlowとかKeras入っているか中身を確認
<sup>出力結果の表示が長いのでいくつか省きました:sweat:</sup>

```
root@ba0ce565430c:/dk_tensor_fw/app_tensor# pip3 list
Package                Version
---------------------- -----------
absl-py                0.9.0
Flask                  1.1.0
gunicorn               19.9.0
Keras                  2.3.1
Keras-Applications     1.0.8
Keras-Preprocessing    1.1.2
matplotlib             3.1.1
mecab-python3          0.996.2
numpy                  1.16.4
pandas                 0.24.2
Pillow                 7.1.2
pip                    20.1
python-dateutil        2.8.0
pytz                   2019.1
PyYAML                 5.1.1
requests               2.22.0
requests-oauthlib      1.3.0
rsa                    4.0
scikit-learn           0.21.2
six                    1.14.0
sklearn                0.0
tensorboard            2.2.1
tensorboard-plugin-wit 1.6.0.post3
tensorflow             2.2.0
tensorflow-estimator   2.2.0
（省略）
```
TensorFlow、Kerasなど一通り入っているようです。。。

#### Webサーバ側のコンテナに接続
```
$ docker-compose exec web-nginx /bin/bash
root@d6971e4dc05c:/# 
```
Webサーバ側のコンテナにも入れました。

#### 一応、Webサーバ(Nginx)が起動しているか確認します。

```
root@d6971e4dc05c:/# /etc/init.d/nginx status
[ ok ] nginx is running.
```
Nginxも起動しているようです。
一旦ここまでで実行環境の確認しました。
上記[Web APIの実行例](#web-api実行例)のようにWEB APIが叩ければWEB API側の実行環境できてると思います。。。

## 今後について
今回はWEB API側の実行環境まで少し整理できました。
また、時間のある時に少しづつブラッシュアップ、整理できればと思います:sob:
未定ですが、次回は機械学習部分を整理できればと思います。

|章|区分|状況|内容|言語、FW、環境等|
|---|---|---|---|---|
|序　章|共通|済|<a href="https://qiita.com/okubo1504/items/868514e1ad1e2debf6c1">アプリの概要</a>|Python<br>TensorFlow<br>Keras<br>Google Colaboratory|
|第一章|Web API|済|環境構築（実行環境）|docker-compose<br>Flask<br>Nginx<br>gunicorn|
|第二章|Web API|済|<a href="https://qiita.com/okubo1504/items/8c3374b03cc185fa9068">機械学習</a>|Python<br>TensorFlow<br>Keras<br>Flask|
|第三章|画面|未着手|環境構築|Python<br>Django<br>Nginx<br>gunicorn<br>PostgreSQL<br>virtualenv|
|第四章|画面|未着手|表示、Web API呼出し部分|Python<br>Django|
|第五章|AWS|未着手|AWS自動デプロイ|Github<br>EC2<br>CodeDeploy<br>CodePipeline|