はじめに
最近、自分の作ったWebアプリ(特に機械学習系)を公開する人が増えてきた気がしますが、「お前ら論文読んで理解して、データ集めてモデル作って検証して精度上げて…ってどこにそんな趣味のアプリ作る時間あるねん、なんだ?1日48時間でもあるのか??」と思ったので調べてみたところ、「とりあえず動くもの」なら簡単に作れそうだとわかったので、その作り方を紹介します。
個人的には、機械学習の各種モデルを色々な形に応用し、公開している方を見ると刺激を受けるので、ぜひ今まで敷居が高いと思ってWebアプリを作ってこなかった機械学習エンジニアの方に、Webアプリを作っていただきたいなと思います。
※コードは全て GitHub に上げていますが、一つずつコピペした方が勉強になると思います。
使うもの
以下のものを使います。
名前 | 役割 |
---|---|
Nginx | ロードバランシングやSSL暗号化通信などを提供する、安全性の高いWebサーバー。 |
Flask | RESTful APIを構築するのに最小限な python のフレームワーク。大規模な物を作る場合には Django などが使われる。 |
uWSGI | Webサーバーとアプリケーションをつなぐインターフェースの役割を担う。 |
これらの関係性をまとめると、以下のようになります。Nginx はクライアントの要求を受け取り、ソケットを介して uWSGI サーバーと通信します。その後、uWSGI サーバーが Flask のアプリケーションを呼び起こします。 |
全体像
上記のものを駆使して今回構築する構造は、以下のようになります。
├── etc
│ ├── nginx
│ │ ├── conf.d
│ │ │ ├── default.conf
│ │ │ └── uwsgi.conf
│ │ ├── nginx.conf
│ │ └── uwsgi_params
│ └── systemd
│ └── system
│ └── uwsgi.service
└── usr
└── local
└── app
├── main.py
├── templates
│ └── index.html
├── tmp
└── uwsgi.ini
それでは、実際に作っていきましょう!
AWS
Webアプリを公開するのに欠かせないのはサーバーですが、普通の人は個人サーバーなんて持ってません。そこで、クラウドに頼りましょう。AWS(AmazonWebService)をはじめとする各種サービスは初期費用がかからず従量課金のため、使った分だけ料金を払えば良いサービスです。今回のように趣味程度で利用することができるので、ありがたく使わせていただきましょう。
登録の仕方や各種サービスの使い方を説明していると本題に入れないのでここでは割愛しますが、「AWS EC2 インスタンス作成」などで検索すれば鬼のように記事が出てきます。それだけ愛されているサービスなのです。
インスタンスの作成
今回は、「ubuntu Server 18.04 LTS (HVM), SSD Volume Type」 を使うことにします。
インスタンスは、基本的にAWSの指示通りに作成していきます。注意しなくてはいけないところは、
「セキュリティグループにHTTP接続を追加すること」と「Elastic IPアドレスを設定し、インスタンスを起動するたびにIPアドレスが変わらないようにすること」です。これらについてもたくさんの解説記事が出ているので、やり方がわからない場合は調べて見てください!
接続
AWS の EC2 インスタンスへは ssh を用いて接続します。ここも最初はつまづくポイントですが、いたるところに解説記事が出ているので割愛させていただきます。
環境構築
pip
無事 AWS の EC2 インスタンスに接続することができたら、まず始めに環境構築を行います。試しに python 利用者はお馴染みのコマンド pip
を打ってみましょう。すると、以下の絶望的なメッセージが表示されます。
$ pip
> Command 'pip' not found, but can be installed with:
sudo apt install python-pip
まずは、pip
をインストールしましょう!!今回は OS が Ubuntu なので、apt-get
コマンドを使用します。apt-get
コマンドは、Debian や Ubuntu など、Debian 系のパッケージ管理システムである APT(Advanced Package Tool) ライブラリを利用してパッケージを操作・管理するコマンドです。まずは apt-get
を最新の状態にアップデートしておきましょう。
$ sudo apt-get update
さて、それでは python に欠かせない pip
をインストールします。先ほど言われたコマンドを素直に打ち込みましょう。
$ sudo apt install python-pip
これで無事に pip
はインストールされました。続いて、パッケージをインストールしていきましょう。
Nginx
$ sudo apt-get install nginx
ここで、念のため実際に動かして正しくインストールできているかを確かめます。
$ sudo systemctl start nginx.service
$ sudo systemctl status nginx.service
この時、「Active: active (running) 」と表示されていれば正しく動作しています。q
と入力し、ステータス画面を閉じましょう。
Flask
Flask は python 系のパッケージなので、pip
コマンドを使います。
$ sudo pip install flask
uwsgi
最後に、uwsgi をインストールします。この時 C compiler が必要なので、clang を用います。
$ sudo apt install clang
$ CC=`which clang` pip install uwsgi # コンパイラを指定
$ sudo apt install uwsgi-plugin-python
$ sudo apt-get install libpcre3 libpcre3-dev
これで、最低限必要な材料は揃いました!
設定
それでは各種設定ファイルを編集していきましょう。
Clients-Nginx 間
Nginx の設定ファイル(設定値が書いてあるファイル)は、全て /etc/nginx/
ディレクトリに配置します。デフォルトの設定では、/etc/nginx/nginx.conf
がその設定ファイルの役割をしています。そこで、まずはそのファイルを見てみましょう。
$ sudo vi /etc/nginx/nginx.conf
なお、vim
を使用する際は、コマンドモードで :set number
と打つと行数が表示されるので見やすくなります。
11 http{
12
:
:
61 include /etc/nginx/conf.d/*.conf;
62 include /etc/nginx/sites-enabled/*;
63 }
ここで、上記の http
ブロックが見つかると思います。このブロック内の include
は、Nginx に、どこに website の設定ファイルがあるかを教えています。
ちなみに、上の nginx.conf
はどのようにして Nginx をインストールしたかによって決まっており、今回は Debian 系のパッケージからインストールしているので、上のように書かれています。
また、 ../sites-enabled/
フォルダは、シンボリックリンク(symbolic link)と呼ばれるファイルやフォルダの代用ファイル(中継ファイル)を保持しており、/etc/nginx/sites-available/
に存在するファイルに中継をしています。とりあえずめんどくさそうなので62行目はコメントアウトしておいて、ここでは/etc/nginx/conf.d/*.conf で website の設定をしていきます。
以下のファイルを作成します。
$ sudo vi /etc/nginx/conf.d/default.conf
server {
listen 80;
location / {
include uwsgi_params;
uwsgi_pass unix:///usr/local/app/tmp/uwsgi.sock;
}
}
ここでは色々な設定ができるようですが、ここでは「最低限動くこと」を目指して必要な部分だけを書きます。上記の listen
は、Nginx が外部からリクエストを受け付けるポート番号を指定しています。また、location
は、Nginx が各リソース (/hoge) にアクセスしてきた時にどのように対応するかを設定します。例えば、以下のように設定したとします。
location / { }
location /images/ { }
location /blog/ { }
location /planet/ { }
location /planet/blog/ { }
この時、http://example.com/
にアクセスが来た場合はlocation /
が対応し、http://example.com/planet/blog/
や http://example.com/planet/blog/about/
などにアクセスが来た時には location /planet/
がどちらも対応します。
つまり、今回の /etc/nginx/conf.d/default.conf
では、全てのリソースへのアクセスに対して同じ対応をするように設定されていることがわかります。
その対応とは、「uWSGI に sokect で繋げる」ということです。これを実現するには、include uwsgi_params
を書き、uwsgi_pass
に uWSGI socket のアドレスを指定するだけで大丈夫です。
Nginx-uwsgi 間
$ sudo vi /etc/nginx/conf.d/uwsgi.conf
server {
listen 5050;
error_log /var/log/nginx/error.log warn;
location / {
include uwsgi_params;
uwsgi_pass unix:///usr/local/app/tmp/uwsgi.sock;
uwsgi_ignore_client_abort on;
}
}
uwsgi-Flask 間
まず、Flask アプリケーションを作成(配置)するディレクトリを作成しましょう。
$ sudo mkdir /usr/local/app
続いて、uwsgi
を起動する際のパラメータをまとめたファイルを作成します。
$ sudo vi /usr/local/app/uwsgi.ini
[uwsgi]
# plugin
plugins-dir = /usr/lib/uwsgi/plugins
plugin = python27
chdir = /usr/local/app
# application's base folder
base = /usr/local/app
# python module to import
app = main
module = %(app)
# socket file's location
socket = /usr/local/app/tmp/uwsgi.sock
# permissions for the socket file
chmod-socket = 666
# the variable that holds a flask application inside the module imported at line #6
callable = app
# location of log files
logto = /var/log/uwsgi/%n.log
master = true
processes = 5
vacuum = true
die-on-term = true
このファイルを引数にとることにより、以下の指定方法で各種パラメータを指定でき、スッキリとします。
$ sudo uwsgi --plugins-dir /usr/lib/uwsgi/plugins --plugin python27 --chdir… # 本来
$ sudo uwsgi --ini uwsgi.ini # 上のファイルゆえ
また、以下のファイルを用いて uwsgi を起動できるようにします。
$ sudo vi /etc/systemd/system/uwsgi.service
[Unit]
Description=uWSGI instance to serve uwsgi
After=network.target
[Service]
User=root # 好ましくない
Group=root # 好ましくない
WorkingDirectory=/usr/local/app
Environment="LD_LIBRARY_PATH=/usr/lib"
ExecStart=/usr/bin/uwsgi --ini uwsgi.ini
[Install]
WantedBy=multi-user.target
この時、正しいファイルパーミッションが含まれるようにします。
$ sudo touch /etc/systemd/system/uwsgi.service
$ sudo chmod 664 /etc/systemd/system/uwsgi.service
EC2 を再起動した時などに毎回起動させるのは面倒なので、自動起動を登録しておくと楽です。
$ sudo systemctl enable uwsgi
なお、手動で行うときは以下の通りです。
$ sudo systemctl stop uwsgi.service
$ sudo systemctl start uwsgi.service
$ sudo systemctl status uwsgi.service
Flask アプリケーション作成
ここまでで必要なもののインストール・設定は終わったので、Flask アプリケーションを作成(配置)します。まずは、作成したディレクトリの所有者やグループを変えます。そのために、以下のコマンドを打ちます。
$ chown [オプション] [ユーザー:グループ] ファイル
なお、ユーザー名は
$ ps faux | grep nginx
と入力した時に master_process
と書かれているものを記録し、そのユーザー名を用いて
$ sudo id ユーザー名
と入力すればグループ名がわかります。デフォルトの設定ではユーザー名、グループ名ともに root
になっています。(このまま root
を使用するのは好ましくありませんが、ここでは割愛します。)
※ なお、ここでのユーザー名、グループ名を /etc/systemd/system/uwsgi.service
の該当箇所に書いています。
上記の内容に従って、ディレクトリの所有者やグループを変更していきます。
$ sudo mkdir -p /usr/local/app/tmp
$ sudo chmod 777 /usr/local/app
$ sudo chown root:root /usr/local/app
$ sudo mkdir /var/log/uwsgi
$ sudo chown root:root /var/log/uwsgi
それでは、アプリを作りますが、大体の場合一度ローカル環境で動かしていると思うので、ローカルのデータをそのまま AWS に送りましょう。
# [注意]ローカル環境で行う!
> scp -r -i ~/.ssh/key path/to/app/directory username@aws.example.com:~/
すると、/home/ubuntu/
にデータが移動されるので、これを目的の位置まで移動させます。これで必要なものは揃いました!なお、ここで main.py
や index.html
に何を書けば良いのかわからない場合は、以下のものを貼り付けてください。(機械学習のモデルを用いたい場合は Github を参考にしてください!タグでいくつかの種類に分かれています。)
# coding: utf-8
from flask import Flask,render_template
# 初期化
app = Flask(__name__)
# ルートアクセス時の処理
@app.route('/')
def index():
return render_template('index.html')
if __name__ == "__main__":
app.run()
<!doctype html>
<html>
<head>
<title>sample</title>
<body>
<h1>Hello world.</h1>
<!-- メインコンテンツ -->
</body>
</head>
</html>
その他
ここまでで設定は完了しましたが、動かない場合は
$ sudo cat /var/log/uwsgi/uwsgi.log
でエラーの原因を見てみてください。おそらく Flask のアプリケーションで用いている各種モジュールが見つからない、などのことだと思いますので、適宜必要なものをインストールしてください。
また、以下の nginx のデフォルトページが表示される場合は、
$ sudo rm -v /etc/nginx/sites-enabled/default
$ sudo service nginx restart
によって、デフォルトページへの参照を完全に削除しましょう。
ドメインの取得
Webアプリケーションが作成できたら、IPアドレスのまま公開するのではなく、ドメインを取得して公開したいと考える人が多いと思います。そこで、AWSでのドメインの取得方法(Route 53)を簡単にだけ記します。
上記の場所を選択して、以下で自分の取得したいドメイン名を検索して購入します。もちろんですが、同じドメインは取得できないので、世界中で唯一のドメイン名であることが必要です。(ちなみに「令和」にちなんだドメインはすぐに売り切れてしまったようです。)
しばらく待ち、自分の購入したドメイン名が表示されたら選択し、「Go to Record Sets」を押します。
画面右側の Name に「www」、valueに ElasticIPアドレス を入力します。後は結果が反映されるまで少し待てばドメイン名からアクセスすることが可能になります!!