はじめに
先日、機械学習モデルの予測値を返すAPIをDockerで作ってみたという記事を書きましたが、せっかくAPIで予測値を取得できるようになったのでWebページから予測を行いたいと思い、Dockerで構築してみました。今回構築するイメージは下図の通りです。
Docker上に3つのコンテナを立てて、ローカルのブラウザからWordPressにアクセスして、予測結果をWordPressで確認するところまで行いたいと思います。
環境
使用する環境は次の通りです。
$sw_vers
ProductName: Mac OS X
ProductVersion: 10.13.6
BuildVersion: 17G9016
$docker version
Client: Docker Engine - Community
Version: 19.03.4
API version: 1.40
(省略)
Server: Docker Engine - Community
Engine:
Version: 19.03.4
API version: 1.40 (minimum version 1.12)
(省略)
構築
様々な手順があると思いますが、今回はWordPressを構築した後、予測用のコンテナを構築します。それらの接続確認をした後、WordPressの記事作成を行って動作確認をするというような流れで構築していきます。
WordPressの構築
イメージ取得
WordPressと機械学習モデルの予測に用いるコンテナイメージを取得します。
こちら1の記事を参考にして、次の3つのコマンドを実行してイメージを取得します。
$ docker pull mysql:5.7.25
$ docker pull wordpress:4.9.1
docker images
コマンドで取得したイメージを確認します。
$docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mysql 5.7.25 98455b9624a9 7 months ago 372MB
wordpress 4.9.1 28084cde273b 22 months ago 408MB
コンテナ起動
WordPressとMySQLのコンテナを起動します。
こちら2を参考に、起動の前にdocker network
コマンドでネットワークを構築しておきます。
$docker network create test-network
次のコマンドでネットワークが作成されたことを確認します。
$docker network ls
NETWORK ID NAME DRIVER SCOPE
edbb6037a089 bridge bridge local
5f7c06763a14 host host local
1b7e419414a4 none null local
687f0232408b test-network bridge local
作ったネットワーク上にコンテナを起動します。
$docker run --name mysql --network test-network -e MYSQL_ROOT_PASSWORD=test-pw -d mysql:5.7.25
$docker run --name wordpress --network test-network -e WORDPRESS_DB_PASSWORD=test-pw -v $(pwd)/vol:/mnt -p 8080:80 -d wordpress:4.9.1
WordPressについては-v
オプションでローカルのvolという名前のディレクトリをコンテナ中の/mntにマウントしています。これはあとで予測用APIにPOST通信する時に使用するphpスクリプトをローカルで作成してコンテナ内に格納するためです。
また、-p
オプションでポートフォワードを設定して、ローカルの8080番ポートをコンテナ内の80番ポートに転送しています。
ローカルのブラウザで、http://localhost:8080
にアクセスするとWordPressの設定画面が現れるので、好みの言語を設定します。
管理者ユーザの名前やパスワードを設定します。メールアドレスも入力する必要がありますが、適当に入れておけば大丈夫です。
「WordPressをインストール」ボタンをクリックすると、管理者用の画面が現れます。
この時点でブラウザで再度http://localhost:8080/
にアクセスするとおしゃれなページが出てきます。
ここまででWordPressの構築は終了です。
予測用APIを叩く部分はあとで設定します。
予測用APIサーバの構築
構築は機械学習モデルの予測値を返すAPIをDockerで作ってみたで記載した通りなので、省略しますが、コンテナを起動する際には作成したネットワークを指定するようにします。
$docker run -it --name test-api --network test-network --rm -p 5000:5000 -v $(pwd)/vol:/home myflask/mlapi:1.0 /bin/bash
root@b9e8ac7cdb71:/# cd home/
root@b9e8ac7cdb71:/home# python3 api.py
Server is running ...
* Serving Flask app "api" (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
これで予測用APIサーバーを立てられました。
WordPressから予測用APIを叩く
接続確認
ここまででdockerのコンテナが3つ起動していることを確認します。
$docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b9e8ac7cdb71 myflask/mlapi:1.0 "/bin/bash" 8 seconds ago Up 6 seconds 0.0.0.0:5000->5000/tcp test-api
c6b2f713a6a5 wordpress:4.9.1 "docker-entrypoint.s…" 17 minutes ago Up 17 minutes 0.0.0.0:8080->80/tcp wordpress
290950f0f643 mysql:5.7.25 "docker-entrypoint.s…" 18 minutes ago Up 18 minutes 3306/tcp, 33060/tcp mysql
一度WordPressコンテナからcurlコマンドで予測用APIサーバーとの接続を確認します。
docker exec
コマンドでWordPressコンテナに入ってcurlコマンドで予測用のデータを送信します。
$docker exec -i -t c6b2f713a6a5 /bin/bash
root@c6b2f713a6a5:/var/www/html# curl http://test-api:5000/predict -X POST -H 'Content-Type:application/json' -d '{"feature":{"season":[1], "year":[0], "month":[1], "hour":[0], "holiday":[0], "weekday":[6], "workingday":[0], "weather":[1], "temp":[0.24], "feelslike":[0.3], "humidity":[0.8], "windspeed":[0.0]}}'
{"Content-Type":"application/json","prediction":[34.67747315059312],"success":true}
予測値が34.67...と返ってきているので、接続は問題なさそうです。
WordPressのページ作成
それではWordPressの設定に移ります。
新しく固定ページを作成して、以下の内容を記入します。
なお、各項目の数値の選択肢はテキトーに設定しています。
<form action="/ml-submit.php" method="post">
season: <select name="season">
<option value=0>0</option>
<option value=1>1</option>
<option value=2>2</option>
</select>
year: <select name="year">
<option value=0>0</option>
<option value=1>1</option>
<option value=2>2</option>
</select>
month: <select name="month">
<option value=0>0</option>
<option value=1>1</option>
<option value=2>2</option>
</select>
hour: <select name="hour">
<option value=0>0</option>
<option value=1>1</option>
<option value=2>2</option>
</select>
holiday: <select name="holiday">
<option value=0>0</option>
<option value=1>1</option>
<option value=2>2</option>
</select>
weekday: <select name="weekday">
<option value=0>0</option>
<option value=1>1</option>
</select>
workingday: <select name="workingday">
<option value=0>0</option>
<option value=1>1</option>
</select>
weather: <select name="weather">
<option value=0>0</option>
<option value=1>1</option>
<option value=2>2</option>
</select>
temp(0-1): <input type="text" name="temp">
feelslike(0-1): <input type="text" name="feelslike">
humidity(0-1): <input type="text" name="humidity">
windspeed(0-1): <input type="text" name="windspeed">
<input class="submit" type="submit" value="phpを実行" /></form>
これで「公開」をクリックすると、作成したページを見られるようになります。
phpスクリプト作成
作成したページの「予測」ボタンをクリックすると、データが予測用APIに送信されて予測結果が返り、ページに表示されるようにします。ローカルでphpファイルを作成します。
<?php
$data = array(
'feature'=> array(
'season'=> array((int)$_POST['season']),
'year' => array((int)$_POST['year']),
'month' => array((int)$_POST['month']),
'hour' => array((int)$_POST['hour']),
'holiday' => array((int)$_POST['holiday']),
'weekday' => array((int)$_POST['weekday']),
'workingday' => array((int)$_POST['workingday']),
'weather' => array((int)$_POST['weather']),
'temp' => array((float)$_POST['temp']),
'feelslike' => array((float)$_POST['feelslike']),
'humidity' => array((float)$_POST['humidity']),
'windspeed' => array((float)$_POST['windspeed'])
)
);
$data_json = json_encode($data);
$ch = curl_init();
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_POSTFIELDS, $data_json);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_URL, 'http://test-api:5000/predict');
curl_setopt($ch, CURLOPT_PORT, 5000);
$result=curl_exec($ch);
$res_json = json_decode($result , true );
echo 'PREDICTION:'.$res_json['prediction'][0];
curl_close($ch);
echo "<br>更新:".date("Y/m/d H:i:s");
?>
ウェブページで入力したデータをarray形式に格納して、json形式に変換してから予測用APIサーバに送信しています。返り値の予測値を表示するようにしています。なお、phpの作成についてはこちら3を参考にしています。ローカルで作成したファイルをWordPressコンテナに格納します。volディレクトリにphpを格納しておきます。
$ls vol/ml-submit.php
vol/ml-submit.php
volディレクトリはコンテナ上の/mntにマウントしているので、phpが格納されているディレクトリにファイルをコピーします。
root@c6b2f713a6a5:/var/www/html# cp /mnt/ml-submit.php .
root@c6b2f713a6a5:/var/www/html# ls
index.php readme.html wp-blog-header.php wp-config.php wp-includes wp-login.php wp-signup.php
license.txt wp-activate.php wp-comments-post.php wp-content wp-links-opml.php wp-mail.php wp-trackback.php
ml-submit.php wp-admin wp-config-sample.php wp-cron.php wp-load.php wp-settings.php xmlrpc.php
ここまでで構築は完了です。
動作確認
では、WordPressで作成したページで値を適当に入力して、「予測」ボタンを押してみましょう。
「予測」ボタンを押すと、予測値が表示されました。やったー。
真っ白なページに予測値などが表示されていて見た目は悪いですが、Webページ上で予測用APIを叩いて表示することができました。
さいごに
Docker上でWordPressを使って作成したWebページ上で機械学習モデルの予測を行うことができました。まだまだ勉強が足りないところがあったり、見た目が悪かったりしていますが、今後の課題です。今後はAWSなどのクラウド上で動かしてみたいと思っています。