パスワードが検証できなかった。
FastAPIの認証処理がpasslibで書いてある。ログインしようとすると失敗する。ライブラリのバージョン問題だった。passlibとbcryptの最新版の間に、互換性の問題が起きていた。
本番環境の構築はこういうことが起きる。
なぜDockerを使うのか
EC2に直接Pythonをインストールしてデプロイする方法もある。設定は単純だ。
しかし既存のEC2インスタンスにはDXゲーム・orderocr・その施工会社など複数のサービスが同居している。Pythonのバージョン・ライブラリのバージョンがぶつかるリスクがある。
simpleRecは別の専用インスタンス(ap-southeast-2・シドニー)に置くことにした。AWS Rekognitionをシドニーリージョンで使うため、同じリージョンのEC2から呼ぶことでレイテンシを最小化する。
Dockerコンテナにすることで、依存関係の分離と環境の再現性が得られる。ローカルで動けば本番でも動く。
docker-compose.yml
version: '3.8'
services:
app:
build: .
container_name: simplerec-app
ports:
- '8080:8080'
environment:
- AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
- AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
volumes:
- ./backend/service_account.json:/app/service_account.json:ro
restart: always
GoogleスプレッドシートへのアクセスにはGoogle Cloudのサービスアカウントキー(service_account.json)が必要だ。コンテナにボリュームマウントして渡す。.gitignoreに追加してリポジトリには含めない。
nginxの設定
nginxはEC2に直接インストールして、Dockerコンテナの8080番ポートへのリバースプロキシとして動く。
server {
listen 443 ssl;
server_name app.simplerec.jp;
ssl_certificate /etc/letsencrypt/live/app.simplerec.jp/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.simplerec.jp/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_ignore_client_abort on;
client_max_body_size 10M;
}
}
server {
listen 80;
server_name app.simplerec.jp;
return 301 https://$host$request_uri;
}
proxy_ignore_client_abort onはVIGIカメラのfire-and-forget(接続後即切断)に対応するための設定だ。client_max_body_size 10Mは画像アップロードのため。
Let's Encryptで即HTTPS化
カメラがHTTPSでWebhookを送るため、HTTPS対応は必須だ。
apt install certbot python3-certbot-nginx
certbot --nginx -d app.simplerec.jp
certbotのnginxプラグインを使うと、Let's Encryptの証明書取得とnginxの設定更新が自動で行われる。証明書は90日で期限が切れるが、certbot renewをcrontabで自動実行することで更新される。
passlibとbcryptの互換性問題
本題のパスワード問題に戻る。
passlibはFastAPIのチュートリアルでよく使われるパスワードハッシュライブラリだ。しかしpasslib 1.7.xと最新版のbcryptの間に互換性の問題がある。起動時に警告が出て、パスワード検証が失敗するケースがあった。
passlibを使わずにbcryptライブラリを直接使うように変更した。
import bcrypt
def hash_password(password: str) -> str:
return bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode()
def verify_password(password: str, hashed: str) -> bool:
return bcrypt.checkpw(password.encode(), hashed.encode())
passlibの抽象化レイヤーが不要になる。直接使うことでライブラリの互換性問題が消えた。シンプルな実装の方が、余分なレイヤーによる問題を避けられる。
Google Workspace組織ポリシーの問題
Google Cloudでサービスアカウントキーを作成しようとしたとき、「iam.disableServiceAccountKeyCreation」というポリシーでキー作成がブロックされた。法人のGoogle Workspaceアカウント(itoh@simplesystem.co.jp)に組織ポリシーが適用されていたためだ。
個人のGmailでGoogle Cloudプロジェクトを作成してサービスアカウントキーを取得した。Google Sheetsのスプレッドシートにはそのサービスアカウントを「編集者」として招待することで、法人アカウントのスプレッドシートにアクセスできるようにした。
組織ポリシーという見えない壁が、設定の途中で突然現れた。ツールと環境の組み合わせによって生じる問題は、作ってみるまで分からないことがある。
本番運用の現状
VIGIカメラからのWebhookを受けて、AWS Rekognitionで顔認識し、LINEに通知するという一連のフローが動作確認済みだ。開発者本人の顔を99%の信頼度で認識した。
次のフェーズは実際のテナント(店舗)への導入テストだ。環境は整っている。あとは実際に使ってもらってフィードバックを受ける。
*シンプルシステム株式会社 代表 伊藤勝彦*