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

リバースプロキシとPythonのFlaskで爆速!API制作
リバースプロキシと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モジュールをインストールしましょう。

shell
$ pip3 install flask

こんな感じでFlaskをインストールできました。

shell
$ pip3 list | grep 'Flask'
Flask              2.1.2

Flaskで爆速API制作

さっそくFlaskでAPIを作っていきましょう。 RESTfulを意識しつつGET処理とPOST処理を学んでいきます。

FlaskでGET処理

FlaskでGET処理
FlaskでGET処理

はじめに一番簡単なGET処理をFlaskで書いてみます。

py
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'])の部分はデコレータと呼ばれるものです。関数をラッピングして特別な処理を付与してくれます。普通でしたらハンドラのクラスとかを継承したクラスをつくって、関数をオーバーライドして...など複雑な記述を書かなければならないのですが、デコレータのおかげでとても簡潔に書けました。

デコレータ?
デコレータ?

はい。実行してみましょう。

shell
$ 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 でアクセスしてみます。

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

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

外部からGETでアクセス

ラズパイ以外のクライアントマシンからアクセスしてみます。Raspberry Piのホスト名またはIPアドレスを使ってください。

shell
$ curl raspberrypi.local:5000
{"message": "Hello world!"}

外部からアクセスできない?

ラズパイ自身からはアクセスできるのに、ラズパイ以外のLAN内のクラアンとマシンからアクセスできない場合があります。こんな感じで拒絶されました。

shell
$ 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で可変セクションを受け取るにはどうすればよいでしょうか? はい。これでイケます。

py
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')
shell
$ curl raspberrypi.local:5000/userinfo/1000777
user_id: 1000777

「まじかよ!!Flask最高!!!」という声が聞こえてきますね。

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

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

ちなみにURLマッピングできる型は次のとおりです。

意味
stringスラッシュを除く文字列
int正の整数
float浮動小数点数
uuidUUIDフォーマットの文字列
pathURLのパス

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

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

こんどはPOSTリクエストを処理できるようにしてみましょう。 request のインポートをお忘れなく。

py
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')
shell
$ 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を外部へ公開できるということです。はじめに紹介した図のイメージですね。

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

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

nginxリバースプロキシの設定

nginxのリバースプロキシを使うために、/etc/nginx/sites-available/default ファイルを少し修正します。

shell
$ 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を再起動しましょう。

shell
$ sudo nginx -t
$ sudo sytemctl restart nginx

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

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

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

おわり

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

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

関連記事

最後までご覧いただきありがとうございます!

▼ 記事に関するご質問やお仕事のご相談は以下よりお願いいたします。
お問い合わせフォーム