10/26(木)-29(日)に開催された『PyCon APAC 2023』にて、CTOの中村が登壇しました。 FastAPIを使用したFinTechサービス開発についてまとめております。
© Susten Capital Management Inc.FinTechの現場でバリバリ活躍するFastAPIの理想と現実PyCon APAC 2023Sho NakamuraX: @sh0nk0↑スライド上げてます
View Slide
お前誰よ?● @sh0nk (github)○ @sh0nk0 (X): スライドもこちらに● Love Beer 🍻● FinTechスタートアップsustenの取締役○ 「おまかせ資産運用」をつくっています● 前職ではML/NLPを使った検索エンジン・推薦システム(recommendation engine)の開発● Python歴:10+年● PyCon参加歴:2014、2016年(7年ぶり、初登壇)2@sh0nk0
本の紹介● 2023年6月発売● FastAPI初心者の方におすすめ!● Web API初学者がメインだが、本番環境で利用できる開発を想定して、AWS/GCPへのデプロイまでカバー● Zennで無料公開している本ベースで加筆したもの3@sh0nk0
本日のトークテーマ● 前半(基礎編):FastAPIの良いところをサクッと学習○ FastAPIを使ってみたいけど まだトライできていない 方● 後半(応用編):FastAPIが「FinTech」「スタートアップ」でどう使われているかの一例をご紹介○ FastAPIに入門してみたけど、 業務で本番環境への投入までには至っていない 方○ FastAPIを本番環境で使っているが、 FinTechでどう使われているか裏側を知りたい 方4@sh0nk0
アンケートタイム● 使ってる方どれぐらいいますか?○ 1:FastAPI現場で使ってます○ 2:Flask or Django(or Django REST Framework)を使っている方?○ 3:WebAPIの開発は未経験です5@sh0nk0
6まずはFastAPIをサクッと理解しちゃおう〜基礎編〜@sh0nk0
どんなFramework?● 他のFrameworkとの違い○ Django REST Framework■ 大規模WebフレームワークであるDjangoでAPIを開発できるようにしたライブラリ■ FastAPIも自動でAPIドキュメントを生成する点でヒントを得ている■ Djangoの上に成り立っている(依存している)ため重量級○ Flask■ かなりシンプルなフレームワーク(Microframework)■ シンプルがゆえドキュメントが技術的過ぎたり、複雑なことをやろうとすると他のライブラリの力を借りることになる7@sh0nk0
どんなFramework?● 他のFrameworkとの違い○ Django REST Framework■ 大規模WebフレームワークであるDjangoでAPIを開発できるようにしたライブラリ■ FastAPIも自動でAPIドキュメントを生成する点でヒントを得ている■ Djangoの上に成り立っている(依存している)ため重量級○ Flask■ かなりシンプルなフレームワーク(Microframework)■ シンプルがゆえドキュメントが技術的過ぎたり、複雑なことをやろうとすると他のライブラリの力を借りることになる誤解を恐れずに言うと...「Django REST Frameworkぐらい色んなこと(例えばバリデーションやAPIドキュメントの自動生成)をFlaskぐらい軽量に扱える」“Battery included”ないいとこ取りWebAPIフレームワーク🔋8@sh0nk0
どんなFramework?● 使われているシーン○ NetflixやUberなどのBig Techも導入■ 例● Netflix Dispatch: システム障害管理ツール (https://github.com/Netflix/dispatch)○ OSSなどでも活用されている■ Chroma: LLMなどで使われるEmbeddingsを保存するベクトルDB9@sh0nk0
FastAPIはどう動いているか?DockerコンテナPydanticバリデーションシリアライズStarlette低レイヤWebフレームワークUvicornASGIServerSQLAlchemyPeeweeなどO/Rマッパーデータベース10● FastAPIは2つの強力なライブラリPydantic/Starletteの上に成り立っている@sh0nk0
FastAPIはどう動いているか?● FastAPIは2つの強力なライブラリPydantic/Starletteの上に成り立っているPydanticバリデーションシリアライズStarlette低レイヤWebフレームワーク11@sh0nk0
FastAPIの3つのおすすめポイント(特長)● ポイント1○ Pydanticによって型ヒントを実行時のバリデーションに利用するため、 型安全● ポイント2○ 型定義に従いSwagger UIが自動生成されるため、フロントエンドとのインテグレーションが容易● ポイント3○ ASGIサーバの利用を前提としているので 高速12@sh0nk0
ポイント1:Pydanticによって型ヒントを実行時のバリデーションに利用するため、型安全● Pythonは動的型付けなので型ヒント(型アノテーション)は基本的に実行時に評価されない● FastAPIではPydanticの力を使って型ヒントを実行時にも利用する13@sh0nk0
ポイント1:Pydanticによって型ヒントを実行時のバリデーションに利用するため、型安全● 例○ リクエストのスキーマ( Pydantic)をこのように定義する○ このようにコールしてみる■ 日付が一桁多くなってしまっている○ このように422エラー(Unprocessable Entity)が返ってくる14@sh0nk0
ポイント1:Pydanticによって型ヒントを実行時のバリデーションに利用するため、型安全● Pydanticスキーマ(モデル)定義の勘所○ Fieldの第一引数により、デフォルトで埋まる値を定義できる○ 第一引数は“...”(Ellipsis)を使うことで、必須パラメータのユーザーによる指定を強制することができる○ “str | None” あるいは “Optional[str]”と書くことで、パラメータを任意入力にすることができる○ 最小・最大の長さ(数値の場合は最小値・最大値)、regexでのvalidationを追加することも可能15@sh0nk0
ポイント2:型定義に従いSwagger UIが自動生成されるため、フロントエンドとのインテグレーションが容易● コード(関数のシグネチャ)を定義するとよしなにSwagger UIを生成してくれる● FastAPIが生成するSwagger UIは実行可能な”動くドキュメント”○ 利用者(例えば、フロントエンドのエンジニア)は実際に動かしながらデバッグが可能16@sh0nk0
ポイント2:型定義に従いSwagger UIが自動生成されるため、フロントエンドとのインテグレーションが容易● 例○ 先程のリクエストスキーマに対し、エンドポイントとなるパスオペレーション関数を定義○ よしなにexampleの値やレスポンス例なども出力してくれる実行17@sh0nk0
ポイント3:ASGIサーバの利用を前提としているので高速● asyncioで非同期化されたコードの書き方をすると、38%の速度向上が見られた○ AWS App Runnerの環境にてRDB(MySQL)へのアクセスを伴う簡単な FastAPIアプリをデプロイし、負荷試験を行った結果asyncioを利用したコードavg.190RPS (10users)asyncioを利用しないコードavg.138RPS (10users)18@sh0nk0
To know more…?● これ以上FastAPIに関する詳細を知りたかったら本をご参照ください!● Zennでも大体無料で読めます● なんならFastAPIは公式ドキュメントめっちゃ充実してます19@sh0nk0
20FinTech @SUSTEN でどう使われているか理想と現実〜応用編〜@sh0nk0
当スライドは、株式会社 sustenキャピタル・マネジメント(以下、当社)のソフトウェア開発に係る情報提供を目的としたものであり、当社の提供する商品・サービスの紹介、勧誘等を目的としたものではありません。
トル22
トルトル23
History FastAPIとSUSTEN開発のあゆみ2023現在まで現場で使い続けてます!2020年春FastAPI導入開発開始!24
そもそも金融でPython大丈夫なの?● 私たちがPython/FastAPIを採用した理由○ Python■ 機械学習との親和性● 社内の資産運用・投資判断チームが利用● 資産運用でのPython(データ分析、クオンツ運用)は今回のトークの対象外○ FastAPI■ 型安全● 金融、特に数値計算において型は重要(後述)■ いくつかのエビデンスによると「速いらしい」■ 実際に触ってみて、「 Swagger UI自動生成すごいし、フレームワークとして伸びそうな予感がする」25@sh0nk0
FastAPIアプリまわりのアーキテクチャWebアプリスマートフォンアプリ金融機関接続勘定システムKYC/AMLシステム外部接続バッチを含む内製システムユーザーとのインターフェイスKYC=Know Your CustomerAML=Anti-Money Laundering口座開設時の審査を行う入出金・口座引落にて、銀行等の金融機関との接続を行う日々の顧客ごとの口座管理(投資信託の買付・売却等)を行う外部接続外部接続WebAPI(FastAPIアプリ)26@sh0nk0
まずはアーキテクチャWebアプリスマートフォンアプリ金融機関接続勘定システムKYC/AMLシステム外部接続バッチを含む内製システムユーザーとのインターフェイスKYC=Know Your CustomerAML=Anti-Money Laundering口座開設時の審査を行う入出金・口座引落にて、銀行等の金融機関との接続を行う日々の顧客ごとの口座管理(投資信託の買付・売却等)を行う外部接続外部接続WebAPI(FastAPIアプリ)27金融ドメインのAPIは「ミッションクリティカル」なAPIになりがち@sh0nk0
型ヒント● FastAPIでは、型ヒントを至る所で活用○ Pydanticの強力なサポートが得られる○ Web APIのリクエスト・レスポンスの定義に活用理想28@sh0nk0
型ヒント● どれぐらい厳密に型ヒントを用いるか?○ すべての変数に定義することもできる○ しかし、■ 静的型付け言語ほど型に関するIDEのサポートが完全とも言い難い■ コードを書くスピードと、全体のコードの可読性も重要○ → 変数定義・代入など比較的自明な部分には、基本的には型ヒントは不要とする■ 明示的にしたい、型が複雑な箇所では適宜付加可能現実変数定義には型ヒントを無理に付けない29@sh0nk0
型ヒント● どれぐらい厳密に型ヒントを用いるか?○ すべての変数に定義することもできる○ しかし、■ 静的型付け言語ほど型に関するIDEのサポートが完全とも言い難い■ コードを書くスピードと、全体のコードの可読性も重要○ → 変数定義・代入など比較的自明な部分には、基本的には型ヒントは不要とする■ 明示的にしたい、型が複雑な箇所では適宜付加可能現実変数定義には型ヒントを無理に付けない30「静的型付け言語の型チェックと同等のチェックを目指す」までは目指さない基本的には「関数の引数及び返り値には必ず型ヒントを」ぐらいの緩めのルールで運用@sh0nk0
型ヒント● 課題:メソッドを拡張する際に、一つ引数を追加。滅多に呼ばれないパスの呼び出し元のメソッドの修正漏れがあったため、実行時にエラーが発生してしまった○ ユニットテストの拡充も考えられるが、完全に網羅することは難しい修正31@sh0nk0
型ヒント● 静的解析ツールを導入:mypyの活用○ SUSTENでは途中からの導入だったため、導入当初から全てのアラートを拾い上げるには大掛かりなリファクタリングが必要となり難しかった■ disable_error_code オプションを指定し、手っ取り早く導入できる部分だけ導入。その後少しずつ改善する方針エラー32@sh0nk0
型ヒント● 静的解析ツールを導入:mypyの活用○ SUSTENでは途中からの導入だったため、導入当初から全てのアラートを拾い上げるには大掛かりなリファクタリングが必要となり難しかった■ disable_error_code オプションを指定し、手っ取り早く導入できる部分だけ導入。その後少しずつ改善する方針エラーFastAPI/Pydanticによる型ヒントだけではなく、mypyなどの静的解析ツールの力も借りて型チェックを強化33@sh0nk0
Tips:数値計算● 金融の計算では小数点が登場することがしばしば○ 小数点(float)は丸め誤差が発生する● Decimalの利用○ 勘定系システムではDecimalを使う○ MySQLなどはdecimal型をサポートしているので、そのままdecimalとして保存○ Decimalを利用する際は、インスタンス作成時にも一度もfloatを介さないように注意■ 直接小数点の数値を与える場合はstrとして与える34@sh0nk0
Swagger UI● 開発時の「スキーマ駆動開発」で利用バックエンドエンジニアがFastAPIでエンドポイントを定義フロントエンドの開発などAPIの利用者が開発時・デバッグに利用理想35@sh0nk0
Swagger UI● ビジネスメンバーが使うツールとしても活用○ 以下のような操作が、非エンジニアでも簡単に(意外と)扱える■ セレクトボックスによるパラメータの指定■ ファイルのダウンロード■ パラメータやエンドポイントに自由に説明が加えられる現実36@sh0nk0
Swagger UI● ビジネスメンバーが使うツールとしても活用○ 以下のような操作が、非エンジニアでも簡単に(意外と)扱える■ セレクトボックスによるパラメータの指定■ ファイルのダウンロード■ パラメータやエンドポイントに自由に説明が加えられる現実37「こいつ…動くぞ!」(非エンジニアでも動かせる)自動生成されるAPIドキュメント(Swagger UI)により、FastAPIは「業務を支えるツール」にもなり得る@sh0nk0
非同期化● asyncio を活用して高速化するために、async/awaitを使ってコードを書く理想38@sh0nk0
非同期化● 最初から導入しないと、なかなか非同期化していくのが難しい○ asyncioに慣れていないとどこに async/awaitを書いていけば良いかが難しい■ チームの非同期化に関する知識の差分があるため、導入の障壁がある○ asyncioに対応していないライブラリがある■ 例:SQLAlchemyはasyncioに対応しているが、古いバージョンでは対応しておらず、新しいSQLAlchemy 2.0 Styleの書き方が求められる現実39@sh0nk0
非同期化● asyncioを使った書き方ではないコードでも十分本番環境でパフォーマンスを出すことができる○ FastAPIはasyncioを使わずとも、”よしなに” threading処理をやってくれる○ asyncioを使う場合はIOバウンドな処理(DBや他のAPIへのアクセス)を伴うシーンだけで十分● 代替策:インフラの力を借りる○ SUSTENではAmazon ECSを使っているが、アクセス増への対策としてはコンテナのAutoScalingによる富豪的なリソース確保で十分対応できているECSTask #1ECSTask #2ECSTask #3・・・現実40@sh0nk0
非同期化● asyncioを使った書き方ができていないコードでも十分本番環境でパフォーマンスを出している○ FastAPIはasyncioを使わずとも、”よしなに” threading処理をやってくれる○ asyncioを使う場合はIOバウンドな処理(DBや他のAPIへのアクセス)を伴うシーンだけで十分● 代替策○ SUSTENではAmazon ECSを使っているが、アクセス増への対策としてはコンテナのAutoScalingによる富豪的なリソース確保で十分対応できているECSTask #1ECSTask #2ECSTask #3・・・現実41これからFastAPIを導入する場合は今後のメンテコストと照らし、asyncioを使うのか検討使う場合は無理に全体で対応せず、バランスを見るのがGood「非同期化はFastAPI導入のハードルにはならない」FastAPI公式docより引用@sh0nk0
Tips:エラーハンドリング● 課題:パスオペレーション関数(router)内でHTTPExceptionを毎回吐くのは面倒42@sh0nk0
Tips:エラーハンドリング● 対応策:エラーハンドラをFastAPIに登録しておけば、ExceptionをraiseするだけでHTTPエラーを吐き出すことができる○ 内部的な関数やライブラリが Exceptionを吐いてもWeb APIのレスポンス自体(json型等)が壊れないように、ベースクラスの Exceptionも保険として拾っておくOriginalのエラー←のエラーハンドラを利用43@sh0nk0
まとめ● イマドキの金融システムは ”普通の” Web技術の結晶でできている○ レガシーじゃないよ! 電子決済系、仮想通貨系以外でも FinTech屋はいます● FastAPIを駆使すれば、様々な業務の効率化ができる○ 例えばSwagger UIでの業務支援● Pythonでも、型ヒントとFastAPIのバリデーション、静的解析ツールなどを有効活用し適切なテストを書けば、「ミッションクリティカル」な世界でも十分戦える● 型ヒントにしろ非同期化にしろ、「完璧を求めすぎない」がFastAPIとうまく付き合うコツ44@sh0nk0
\We are hiring!/X: @sh0nk0
Appendix
トルトル
GitレポジトリWeb APIバッチDBモデル共通ライブラリ金融機関連携モジュール勘定システムモジュールsubmodulesubmodule
Dockerと開発環境● Local環境はdocker composeを利用● デプロイ環境はAWSを利用○ 開発・ステージング・本番環境ではPull Requestマージをトリガーにして、Github ActionsによりECRにDocker imageをpushしたのち、ECSにデプロイ(CD)○ 本番環境は「検証されたイメージをデプロイする」方針により、ステージング環境のDockerイメージを指定してデプロイ開発環境ECRstaging/本番環境ECRLocal環境Github ActionsGithub Actions
デプロイの現実● ECS○ 基本的にローカル環境と異なる設定はすべて環境変数に逃がす。ECSの場合はTask Definitionで定義○ 環境変数の中でもcredentialsなどのsecretsはAWS SecretsManagerで管理
監視の現実● Datadogインテグレーション○ リアルタイムLogとProfileが見られる
監視の現実● Datadogインテグレーション○ サイドカーによって実現■ 1. ECSの同一タスク上にDatadog Agent用のコンテナを立てる■ 2. FastAPIアプリ側にインストールしたトレーサーライブラリ(dd-trace)がDatadog Agentにプロファイルを送信■ 3. AgentはDatadog上で表示できるアプリのトレース情報および環境情報を送信Amazon ECSECS TaskFastAPI Appコンテナ(ddtraceインストール済み)DatadogAgentコンテナトレース・ログ情報アプリ・環境情報①② ③
Swagger UI● ただし、DBに直接アクセスできるような超法規的ツールなので、セキュリティの担保が必要○ これはAPI Gatewayのレイヤーでカバー
ユニットテスト● ユニットテストでクオリティを担保するのは重要● 基本的な「同値クラス分割」や「境界値分析」に基づくテスト● 金融の世界での具体例○ 生年月日周り■ 口座開設には厳密な年齢制限がある○ NISAの枠管理■ 年間の投資額には上限がある○ 休日のチェック■ 世界の株式市場は複数あるので、市場の休日は OR条件で判断しなければならない
ユニットテスト● 複数のenum、条件分岐などを網羅するためにpytestのparameterized testを積極活用● テストカバレッジとしても、90%超えの結果
ユニットテストの現実(cont.)● DB関係のテストはSQLiteを用いて実際に入出力を行う○ conftest (Pytestの設定ファイル)にSQLiteのsessionを定義し、FastAPIがサポートするDI(Dependency Injection)を利用し本番DBとの切り替えを行う● AWS関係のIO(S3やSNS)はmock(boto3)を活用● Mockオブジェクトも活用○ mockerだと静的なmockの生成も容易○ 時刻はfreezegun
セキュリティ● FastAPIで無理にやろうとしない○ ログインまわりではIDaaSを活用(餅は餅屋)○ IDaaS連携もインフラレイヤーがカバー( AWS API Gateway)○ 特にログインまわりは車輪の再発明をしない
躓きポイント1● Git submoduleの多重import○ 依存先のmoduleがupdateされたときに、片方を最新化するのを忘れがち○ pythonでは同名のモジュールは 2つimportできないので、どちらかのコードが走ることになるため、コードのバージョン差分によって問題が発生する可能性があるWeb APIバッチDBモデル共通ライブラリ金融機関連携モジュール勘定システムモジュールsubmodulesubmodule