【Raspberry Piではじめるnginx②】リバースプロキシとPythonのFlaskで爆速!API制作
この記事は 【Raspberry Piではじめるnginx①】nginxをインストールしてHTMLを表示させるまで の続きとなります。前回はRaspberry Piにnginxをインストールし、ドキュメントルートを/home/pi/htmlへ変えるところまでを解説しました。 今回は、PythonとFlaskを使ってRESTなアプリケーション(API)をつくり、nginxのリバースプロキシで80ポートからのアクセスをアプリケーションへ渡す仕組みを作っていきます。
下図のようなゴールをめざします。
はじめに
nginxでは、PHPのようなスクリプトを標準では動かすことはできません。ですから動的コンテンツを作ったり、ウェブAPIを動かしたりする場合は、何かしらのアプリケーションを別途に作って動かす必要があります。要するに自前のミニサーバーを用意して、リクエストをさばかなければなりません。
JavaやNode.jsなどで作られる方が多いです。REST APIが簡単に作れるフレームワークも充実してそうです。 私がよく使うPythonではどうすればよいでしょうか? たった数行で作るWebサーバー【Python x HTTPServer】 で紹介しました HTTPServer で簡易的なモノは作れますが本格的にやろうとするとなかなか大変です。そこでFlaskの登場です!FlaskはWebアプリケーションに特化したWebフレームワークやモジュールです。Flaskを使うことで、RESTfulなAPIの処理を簡単にプログラミングできるようになります。
Flaskのインストール
nginxのリバースプロキシを使う前に、簡単なアプリケーションを作っておきます。 爆速でAPIを作るためにFlaskモジュールをインストールしましょう。
$ pip3 install flask
こんな感じでFlaskをインストールできました。
$ pip3 list | grep 'Flask'
Flask 2.1.2
Flaskで爆速API制作
さっそくFlaskでAPIを作っていきましょう。 RESTfulを意識しつつGET処理とPOST処理を学んでいきます。
FlaskでGET処理
はじめに一番簡単なGET処理をFlaskで書いてみます。
from flask import Flask
app = Flask(__name__)
@app.route('/', methods=['GET'])
def hello():
return '{"message": "Hello world!"}'
if __name__ == '__main__':
#app.run() ← これだと外部のクライアントからアクセスできない
app.run(port=5000, host='0.0.0.0')
たった数行で作るWebサーバー【Python x HTTPServer】 で解説したHTTPServerからすると、Flaskをつかえば感動するほど簡単にかけますね!説明はとくにいらないんじゃないかというくらい、わかりやすいですよね! いちおう @api.route('/', methods=['GET'])の部分はデコレータと呼ばれるものです。関数をラッピングして特別な処理を付与してくれます。普通でしたらハンドラのクラスとかを継承したクラスをつくって、関数をオーバーライドして...など複雑な記述を書かなければならないのですが、デコレータのおかげでとても簡潔に書けました。
はい。実行してみましょう。
$ python3 test_flask.py
* Serving Flask app 'test_flask' (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://127.0.0.1:5000 (Press CTRL+C to quit)
内部からGETでアクセス
自身のラズパイから curl でアクセスしてみます。
$ curl 127.0.0.1:5000
{"message": "Hello world!"}
ちゃんとGETできてますね!
外部からGETでアクセス
ラズパイ以外のクライアントマシンからアクセスしてみます。Raspberry Piのホスト名またはIPアドレスを使ってください。
$ curl raspberrypi.local:5000
{"message": "Hello world!"}
外部からアクセスできない?
ラズパイ自身からはアクセスできるのに、ラズパイ以外のLAN内のクラアンとマシンからアクセスできない場合があります。こんな感じで拒絶されました。
$ curl raspberrypi.local:5000
curl: (7) Failed to connect to raspberrypi.local port 5000 after 25 ms: Connection refused
先ほどのプログラム内でコメントしてますが、 api.run() で実行するのではなく api.run(port=5000, host='0.0.0.0') とhost名を明示してあげるとうまくいきます。
URLの可変セクションを受け取る
REST APIな書き方でよくある ドメイン/userinfo/:user_id のようにuser_idが可変である場合に、Flaskで可変セクションを受け取るにはどうすればよいでしょうか? はい。これでイケます。
from flask import Flask
app = Flask(__name__)
@app.route('/userinfo/<int:user_id>')
def show_post(user_id):
return 'user_id: %d' % user_id
if __name__ == '__main__':
app.run(port=5000, host='0.0.0.0')
$ curl raspberrypi.local:5000/userinfo/1000777
user_id: 1000777
「まじかよ!!Flask最高!!!」という声が聞こえてきますね。
わかります!私も思いましたw
ちなみにURLマッピングできる型は次のとおりです。
型 | 意味 |
---|---|
string | スラッシュを除く文字列 |
int | 正の整数 |
float | 浮動小数点数 |
uuid | UUIDフォーマットの文字列 |
path | URLのパス |
POSTリクエストのデータを受け取る
こんどはPOSTリクエストを処理できるようにしてみましょう。 request のインポートをお忘れなく。
from flask import Flask, request
app = Flask(__name__)
@app.route('/login', methods=['POST', 'GET'])
def login():
if request.method == 'POST':
return "username:{}, password:{}".format(request.form['username'], request.form['password'])
return "error!"
if __name__ == '__main__':
app.run(port=5000, host='0.0.0.0')
$ curl -d "username=fuga&password=1234" -X POST raspberrypi.local:5000/login
username:fuga, password:1234
はい。REST APIが爆速でできちゃいましたね。 もちろん本番環境へ実装するには課題はまだまだありますがアプリケーションの部分はこのくらいにし、nginxのリバースプロキシへ進みます。
この他にもHTMLエスケープ処理したり、JSONでやり取りしたり、Cookieを使ったり、セッション管理したり、HTMLテンプレートつかったりとやりたいことが簡単にできます。 Quickstart — Flask Documentation (1.1.x)
nginxリバースプロキシサーバー
APIの準備ができたところで、いよいよnginxリバースプロキシサーバーの設定を説明していきます。
リバースプロキシの用途
さきほどFlaskでつくったAPIは、直接5000ポートへ接続すればアクセス可能でした。 たしかに、LAN環境で自分だけがつかう用途でしたら問題ないですね。 でもレンタルサーバーなどで80ポートなどの主要なポートしか空いていない場合、ファイアーウォールの設定を変えてポートを解放する必要があります。ですが、あまりムダにポートの解放したくはありませんよね? そんなときに nginxのリバースプロキシを使えば、80ポートへのアクセスを内部の5000ポートへ転送できるの です。つまりは、ポートを新たに開放せずともAPIを外部へ公開できるということです。はじめに紹介した図のイメージですね。
もちろん他にもリバースプロキシの使い方はありまして、キャッシュ機能を利用してサーバー負荷を軽減できたりします。こちらは機会があればやってみます。
nginxリバースプロキシの設定
nginxのリバースプロキシを使うために、/etc/nginx/sites-available/default ファイルを少し修正します。
$ sudo vi /etc/nginx/sites-available/default
で設定ファイルを開いて、locationを追記します。
location /login {
proxy_pass http://localhost:5000/login;
}
追記するとこんな感じですね。
server {
...
# root /var/www/html;
root /home/pi/html;
server_name _;
location / {
try_files $uri $uri/ =404;
}
location /login {
proxy_pass http://localhost:5000/login;
}
...
これで /login のリクエストが来たら localhost:5000/login へ転送されます。それ以外のhttpアクセスは今まで通り /home/pi/html のファイルを返します。
さて、設定ファイルを作成したら文法チェックしてnginxを再起動しましょう。
$ sudo nginx -t
$ sudo sytemctl restart nginx
5000のポート番号をつけずにAPIへアクセスできれば成功です!
$ curl -d "username=fuga&password=1234" -X POST raspberrypi.local/login
username:fuga, password:1234
おわり
いかがだったでしょうか?リバースプロキシとFlaskによるAPI制作のおおまかな流れが把握できたら幸いです。
リバースプロキシではさらに細かな設定が可能です。余裕のある方は、こちらの nginxの公式ドキュメント を読まれると良いでしょう。