kWatanabe 記事一覧へ

kWatanabe の 技術帖

某企業でOSや仮想化の研究をやっているインフラ系エンジニア。オンプレとクラウドのコラボレーションなど、興味ある技術を綴る。

FastAPI と Uvicorn で REST API 対応のスクリプトを簡単に動かす

f:id:kWatanabe:20211204145741p:plain https://fastapi.tiangolo.comより

  • 私生活の情報をAPI化すれば、世にある監視ツールで快適な生活を送れるのでは考えた
  • プライベート情報を公開するのは怖いので、ローカルでサクッとAPIを作れる環境を用意した

API監視による快適生活

以前、Zabbix で某ワクチン接種サイトの更新を拾って、Slack で飛ばす環境を構築した。

kwatanabe.hatenablog.jp

その後、この環境を育てていって、今は株価や天気を世の中の公開APIから拾って、Slackへ通知する仕組みを確立したのだけど、ふと私生活の情報をAPI化すればもっと捗るのでは無いかと思いついた。

調べてみたところ、FastAPI という素敵なWebフレームワークがあって、これを使うと Python でちょっとしたスクリプトを書く感覚で API が作れると分かったので、試してみることにした。

検証

検証環境

単純に動かす

APIアプリに特化したPythonフレームワークのFastAPIを導入する。 また、FastAPI で推奨となっている ASGI 対応 AP サーバの uvicornを併せて導入する。

fastapi.tiangolo.com

www.uvicorn.org

$ sudo apt install python3 python3-fastapi uvicorn python3-uvloop python3-httptools

ちなみに Debian 10 以前では FastAPI は apt で提供されていないので pip で導入する。

$ sudo python3 -m pip install fastapi

任意の場所にサンプルコードを設置。仮に、/srv/uvicorn とする。

$ sudo mkdir -p /srv/uvicorn
$ sudo vim /srv/uvicorn/main.py

内容はシンプルに、/ を GET すると Hello, World. が JSON で返ってくる API を作る。

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def read_root():
    return { "Hello": "World" }

試運転。

$ cd /srv/uvicorn
$ uvicorn main:app --host 0.0.0.0 --port 8000

アクセスしてみる。

$ curl http://localhost:8000/
{"Hello":"World"}

以上おわり、楽チン過ぎる。

Uvicorn を Gunicorn のワーカーとして動かす

Uvicorn のドキュメントによると、 WSGI 対応 AP サーバの Gunicorn のワーカーとして動かすことが推奨されているので、Uvicorn を Gunicorn 配下で動くように変更する。

gunicorn.org

まず、Gunicorn を入れる。

$ sudo apt insatll gunicorn

なお、Debian 10 以前では、Python2 バージョンが入ってしまうので以下のようにする。

$ sudo apt install gunicorn3

Gunicorn と Uvicorn を連携させて、再度試運転。

$ cd /srv/uvicorn
$ gunicorn main:app --workers 4 --worker-class uvicorn.workers.UvicornWorker --bind 0.0.0.0:8000

アクセスしてみる。

$ curl http://localhost:8000/
{"Hello":"World"}

いやっほー!

リバースプロキシ配下に置く

実運用では AP サーバ 単体を露出させることは無いだろうし、SSL の終端なども必要だろうということで、リバースプロキシとして nginx をかませる。 静的コンテンツとの区別やAPIバージョンごとにAPサーバを分けたい場合などのため、/api/v1 プレフィックスがついているものは Gunicorn へ飛ばすようにする。

nginx.org

nginx を入れる。

$ sudo apt install nginx

nginx.confserver ディレクティブに以下を追記する。

...
location ^~ /api/v1/ {
    proxy_pass http://GunicornサーバのIPアドレス:8000;
}
...

アプリで FastAPI クラスを作成する際にプレフィックスを指定する。

...
app = FastAPI( root_path = "/api/v1/" )
...

nginx につないでみる。

$ curl http://nginxのIPアドレス/api/v1/
{"Hello":"World"}

完璧すぎる。

CORS に対応させる

調子に乗って、もう一歩。今回のように Zabbix から監視したい程度ならともかく、普通はAPIを素で使うことはあまりないだろうし、大概は何らかのフロントエンドアプリと疎通することになると思うので、CORS へ対処する必要がある。

FastAPI では、オリジンとして許可するドメインFastAPI() クラスに与えることで解決できる。こんな感じ。

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI( root_path = "/api/v1/" )

origins = [
    "http://excample.com",
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

@app.get("/")
def read_root():
    return { "Hello": "World" }

おわりに

これで FastAPI を使って API アプリを作るための殻ができあがった。あとは好きに API を実装すればいい。

URIとHTTPメソッドによるルーティングはデコレータ (@app.get("/"))、リクエストボディのバリデーションは型ヒント、レスポンスはディクショナリを渡してあげれば (return { "Hello": "World" })、面倒な HTTP 通信やJSONのパースなどは全部やってくれる。

まさにちょっとした Python スクリプトを作る感覚で簡単な API は一瞬で実装できる。いい世の中になった。