【ラズパイではじめるnginx②】リバースプロキシとPythonのFlaskで爆速!API制作

リバースプロキシとPythonのFlaskで爆速!API制作

この記事は【ラズパイではじめるnginx①】nginxをインストールしてHTMLを表示させるまでの続きとなります。前回はRaspberry Piにnginxをインストールし、ドキュメントルートを/home/pi/htmlへ変えるところまでを解説しました。

今回は、PythonとFlaskを使ってRESTなアプリケーション(API)をつくり、nginxのリバースプロキシで80ポートからのアクセスをアプリケーションへ渡す仕組みを作っていきます。

下図のようなゴールをめざします。

ゴールのイメージ
ゴールのイメージ

はじめに

nginxでは、PHPのようなスクリプトを標準では動かすことはできません。ですから動的コンテンツを作ったり、ウェブAPIを動かしたりする場合は、何かしらのアプリケーションを別途に作って動かす必要があります。要するに自前のミニサーバーを用意して、リクエストをさばかなければなりません。

JavaやNode.jsなどで作られる方が多いと思います。REST APIがカンタンに作れるフレームワークも充実してそうです。

私がよく使うPythonではどうすればよいでしょうか?PythonでさくっとWebサーバー!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処理

FlaskでGET処理
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')

PythonでさくっとWebサーバー!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)

http://127.0.0.1:5000 でAPIへアクセスできるようですね。

なにやら警告が出ていますが、あくまで開発テストのサーバとのこと。絶対そのまま使わないで、本番環境の場合は WSGI server を使ってねと書かれてます。

内部からGETでアクセス

自身のラズパイから curl でアクセスしてみます。

$ curl 127.0.0.1:5000
{"message": "Hello world!"}

ちゃんとGETできてますね!

外部からGETでアクセス

ラズパイ以外のクライアントマシンからアクセスしてみます。ラズパイのホスト名または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最高!!!」という声が聞こえてきますね。

Flask最高!!!
Flask最高!!!

わかります!私も思いましたw

POSTリクエストのデータを受け取る

POSTリクエストのデータを受け取る
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テンプレートつかったりとやりたいことがカンタンにできます。

興味ある方はぜひFlaskのクイックスタートをご参考ください。

nginxリバースプロキシサーバー

APIの準備ができたところで、いよいよnginxリバースプロキシサーバーの設定を説明していきます。

プロ棋士
プロ棋士

リバースプロキシの用途

さきほどFlaskでつくったAPIは、直接5000ポートへ接続すればアクセス可能でした。

たしかに、LAN環境で自分だけがつかう用途でしたら問題ないですね。

でもレンタルサーバーなどで80ポートなどの主要なポートしか空いていない場合、ファイアーウォールの設定を変えてポートを解放する必要があります。ですが、あまりムダにポートの解放したくはありませんよね?

そんなときにnginxのリバースプロキシを使えば、80ポートへのアクセスを内部の5000ポートへ転送できるのです。つまりは、ポートを新たに開放せずともAPIを外部へ公開できるということです。はじめに紹介した図のイメージですね。

80ポートを5000ポートへ転送
80ポートを5000ポートへ転送

もちろん他にもリバースプロキシの使い方はありまして、キャッシュ機能を利用してサーバー負荷を軽減できたりします。こちらは機会があればやってみたいと思います。

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 sytemctl restart nginx

5000のポート番号をつけずにAPIへアクセスできれば成功です!

$ curl -d "username=fuga&password=1234" -X POST raspberrypi.local/login
username:fuga, password:1234

串で一杯やろう!
串で一杯やろう!

おわり

いかがだったでしょうか?リバースプロキシとFlaskによるAPI制作のおおまかな流れが把握できたら幸いです。

リバースプロキシではさらに細かな設定が可能です。余裕のある方は、こちらの nginxの公式ドキュメント を読まれると良いでしょう。

「キッチンノート.fun」という料理サイトを立ち上げました!このサイトで紹介していた料理記事は、そちらへ移動しました。
記事に関するご質問などがあれば、
Twitter または お問い合わせ までご連絡ください。
関連記事