AnyTechの赤川です。
AnyTechでは、「手軽に試せる流体解析AIパッケージ」としてDeepLiquid Lite(以下、Liteと記載)というものを提供しております。DeepLiquid Liteはブラウザベースで稼働するソフトウェアとなっており、その開発メンバーとして参画しています。
今回は、我々が開発に利用している技術を紹介する第二弾として、バックエンド技術についてご紹介しようと思います。
技術スタック
Liteのバックエンドは以下の技術スタックによって構成されています。
- ブランチ運用はgit-flowを採用
- サーバ構築:Docker
- デプロイ:Ansible
- 主なサーバ群
- 外部公開API用サーバ
- 推論実行用サーバ
- カメラ対応サーバ
- データ削除サーバ
- データベース:PostgreSQL
- 開発言語:Python
- フレームワーク:FastAPI
- O/Rマッパー:SQLAlchemy
- マイグレーション:Alembic
- lintなど:flake8、black、isort(pre-commitおよびCIにて適用)
- テスト:pytest(およびpytestのプラグインを使用)
- 定期実行実装用ライブラリ:apscheduler
- CI/CD:GitHub Actions
- CI/CDについては第一弾の記事を参照してください
Docker
Liteのバックエンドでは役割ごとにサーバを立てており、サーバの構築にはDockerを利用しています。Liteは現時点ではオンプレミスサーバ上で稼働するソフトウェアとして提供しており、過剰なアクセスが発生することを想定していないためオーケストレーションツールは導入していません。
Ansible
オンプレミス環境にデプロイする際は、GitHub Actionsにて実行されるCDによってdocker imageが作成されGCP上にアップロードされます。デプロイ時はGCPのデプロイ対象イメージをダウンロードして、Ansibleを利用してデプロイしています。
Liteのデプロイにはお客様の専用端末からアクセスする場合と、AnyTech所有のメンテナンス用PCからアクセスする場合の2つがありますが、どちらの場合もAnsibleを利用してデプロイ可能です。
サーバ内訳
各サーバはDockerによって起動しており、その内訳は大きく4種類に分割しています。
外部公開用APIサーバは、主にフロントエンドとやり取りをするAPIを実装しているサーバになります。推論結果の取得であったり各種設定変更については本サーバを通して実行されることになります。
推論実行用サーバは、撮影された画像に対して搭載されているAIモデルを用いて推論を実行します。推論実行用サーバについては基本的には独立して動作しており、一定間隔で推論が自動実行されるような仕組みとなっています。定期実行についてはapschedulerというライブラリを用いて実装しています。apschedulerを利用することで任意のタイミングで特定の動作を実行する機能を簡単に実装できます。
カメラ対応サーバは、特定のカメラの撮影機能を有しています。カメラの台数が増えるとそれぞれのカメラに対応したサーバが起動される仕組みとなっています。 Liteを利用するにあたり、利用されるカメラはネットワークカメラが主なのですが、回線の状況によってはFPSが急激に低下して映像が取得できない状態に陥ることもあるため、常にストリーミングが取得できる状態であるかをチェックし、必要に応じてキャプチャを再度開くといった処理も行なっています。
データ削除サーバは、過去のデータの削除を担います。Liteは現時点ではオンプレ環境として提供しているため、ストレージ容量に限界があります。また、推論に利用される画像を蓄積するためストレージが圧迫されやすくなっています。そのため、ストレージ容量を一定間隔でチェックしない場合、ストレージ容量が足りなくなり新規推論が実行できない状況になる可能性があります。そのような状況を防ぐために、ユーザが過去のデータの削除をしてもいいという場合は任意のタイミングで削除をできる機能を実装しています。この機能についてもapschedulerを利用した定期実行を採用しています。なお、外部公開用サーバにて推論結果のテーブル情報のエクスポートをCSV形式で取得できる機能があり、それと合わせてストレージ上の画像を別途抽出してもらうことで、独自の分析を行ってもらうこともできるようになっています。そのため、消えてほしくないデータなどはエクスポートや保護機能を利用することで情報を残せる仕組みを提供しています。
このようにサーバが複数に分かれているため、エラーが発生した時にどのサーバで起こったかをいち早く発見する必要があります。そのために、エラーが発生した際に対応するエラーコードが保存されるようになっています。この時、同時にどのようなエラーが起こったのかを専用のテーブルに保存する処理も実行しています。エラーが発生した際はエラーコードがフロントエンドによって取得され、アプリケーション上でどのようなエラーが発生したかを通知する仕組みとなっています。これを利用し、エラーが発生した時にエラーコードを連携いただくことでいち早く原因の解明をすることを可能にしています。
Python
バックエンドの開発はPythonを利用して開発しています。Pythonを採用した理由としては、開発メンバーが従来AI開発をするにあたりPythonを利用しており、API実装のキャッチアップをスムーズに開発できると考えたためです。
Web API実装にはFastAPIを利用しています。FastAPIを採用した理由としては、直感的にAPIの実装をできることに加え、学習コストも低くPythonの利用経験がある場合スムーズにキャッチアップでき、かつシェアも充分あるという点を考慮しました。
O/RマッパーはSQLAlchemyを利用しています。またマイグレーションにはAlembicを利用しています。これらのライブラリの組み合わせは一般的によく利用されている構成かと思います。
lintにはflake8、blackおよびisortを利用しています。これらは開発時にpre-commit機能およびGitHub ActionsのCIにて適用されるようになっています。pre-commitでは、変更対象ファイルにのみチェックが適用され、CIでは全ファイルに対して適用される仕組みとなっています。
テストについてはpytestとそのプラグインを利用して実行しています。利用しているプラグインとしては
- pytest-mock:モック作成に利用
- pytest-cov:カバレッジ取得
- pytest-asyncio:非同期テスト実行
- pytest-freezegun:テスト実行の際の時刻の固定
を主に利用しています。また、pytestのプラグインではないですがAPI結果のモックを行うためにresponsesというライブラリも利用しています。responsesを利用するとAPIを呼び出した結果のモックがとても作りやすくなります。なぜAPIの内部処理ではなくAPIの呼び出し結果そのものをモックする必要があるかというと、例えば推論サーバは推論のタイミングになった時にカメラサーバに撮影依頼をするのですが、推論サーバのテスト実行時にカメラサーバを起動せず単一のサーバとしてテストが実行されます。この場合、APIの内部処理をモックすることが困難なため、APIの結果そのものをモックする必要があります。このようなユースケースの場合、responsesのように直接結果をモックできるライブラリがとても役に立ちます。responsesについての詳細はここでは詳細しますが、requestsの戻り値をモックしたい時はぜひ利用を検討してみてください。
まとめ
今回はLiteのバックエンド技術について紹介させていただきました。バックエンド開発を行う上で技術スタックの選定に少しでも役立てていただければ幸いです。次回はフロントエンド技術についてまとめようと思います。