この記事はTech KAYAC Advent Calendar 2019の8日目の記事です。
こんにちは!技術部の ひめの です。
クライアントワーク事業部のフロントエンドで2年半 → サーバーサイドエンジニアで1年ちょっと働いています。趣味でラーメンを食べ漁っています。
つい先日の re:Invent 2019 の発表で、RDS + Lambda のコネクションプール周りやコールドスタート問題の軽減の話もあり、どんどん AWS Lambda で解決できる幅が広がっているのを感じます。
Lambda のセッションについてはクラスメソッドさんの Developers.IO にて紹介されてます。
- [速報]コールドスタート対策のLambda定期実行とサヨナラ!! LambdaにProvisioned Concurrencyの設定が追加されました #reinvent
- [速報]これでLambdaのコネプー問題も解決?!LambdaからRDS Proxyを利用できるようになりました(まだプレビュー) #reinvent
今回はそんな AWS Lambda で、opencv-python
を動かすためにやったことついて紹介します。
(実装だけ見たい方は GitHub をごらんください)
まって、その選択大丈夫?
先に述べておきますが、 opencv-python
を Lambda で実行するという選択はかなり慎重に考えたほうが良いです。
- 予算(EC2インスタンスたてたくない、など)
- リソース(サーバーの管理リソースを割きたくない、など)
- 技術的要因
- GPU(CUDA関連の関数など)を利用しない
- レスポンスに関してある程度の遅延が許される
- マシンスペックで解決できるチューニングなどを考慮しない
など、ある程度の条件下でないとこの構成はおすすめできません。実行速度のチューニングやGPUを利用する関数を利用するなら、問答無用で GPU つきの AMI を借りて非同期実行させるほうが健全です。
「動かせるんじゃね?」と興味本位でやってみた結果動いた、と念頭において読み進めていただけると幸いです。
opencv-python って何?
opencv-python
は、OpenCVという画像処理に特化したライブラリを、Python 上で実行できるようにしたバインディングパッケージです。
https://pypi.org/project/opencv-python/
利用用途はたくさんありますが、画像を用いた機械学習を実施する前処理として画像処理が必要なときに利用するケースが考えられます。
実装方法
サンプルコードをGitHubにアップしていますので、詳細を見たい方はそちらもご確認ください。
opencv-python
はOpenCV のビルドされたファイルが必要です。
しかし、これはビルド環境と実行環境を一致させる必要があります。
そのために今回はserverless-opencv-python
を利用しました。この Serverless Framework 用のプラグインを利用することで、requirements.txt
や Pipfile
をもとにパッケージを zip 形式でまとめてデプロイできます。
また、特定のDockerイメージ上でのパッケージングと、対象パッケージのみ Lambda Layer にアップロードする設定が存在します。
今回はこの設定を利用して、 sls deploy
コマンドで
lambci/lambda:build-python3.7
のコンテナ上でopencv-python
をインストールする- OpenCV のコアファイルのダウンロードとビルド
- パッケージングして Lambda と Lambda Layer にデプロイ
というフローを構築することができました。
手順
1から環境構築する手順について軽く説明します。
ディレクトリを作成して Serverles Framework の初期設定をしましょう。
$ mkdir serverless-opencv $ cd serverless-opencv $ npx sls create -t aws-python3 -p sls-opencv Serverless: Generating boilerplate... Serverless: Generating boilerplate in "/.../serverless-opencv/sls-opencv" _______ __ | _ .-----.----.--.--.-----.----| .-----.-----.-----. | |___| -__| _| | | -__| _| | -__|__ --|__ --| |____ |_____|__| \___/|_____|__| |__|_____|_____|_____| | | | The Serverless Application Framework | | serverless.com, v1.58.0 -------' Serverless: Successfully generated boilerplate for template: "aws-python3"
つぎに、 serverless-python-requirements
を追加します。
$ cd sls-opencv $ npx sls plugin install -n serverless-python-requirements
serverless.yml
の最後の行に次の設定が追加されているのを確認してください。
sls
コマンドからでなくても、 npm
/yarn
でインストール可能です。その場合は、次の行を serverless.yml
に追加してください。
# ... plugins: - serverless-python-requirements
次に、 serverless-python-requirements
の設定を serverless.yml
に追加します。
custom: pythonRequirements: layer: true dockerizePip: true dockerImage: lambci/lambda:build-python3.7 usePipenv: false slim: true strip: false useDownloadCache: true useStaticCache: true
それぞれの設定項目については npm のページを参考ください。
今回ポイントとなる設定は次の3つです。
layer
:有効化すると AWS Lambda Layer に Python パッケージをデプロイします。dockerizePip
:有効化すると、 Docker 上で pip コマンドを実行します。dockerImage
:dockerizePip
有効時に、利用する Docker イメージを選択します。
設定後、必要な Python パッケージを Pipenv 経由などでディレクトリにダウンロードし、 requirements.txt
を作成します。
$ cat requirements.txt boto3==1.10.16 botocore==1.13.16 docutils==0.15.2 jmespath==0.9.4 numpy==1.17.4 opencv-python==4.1.1.26 python-dateutil==2.8.0 s3transfer==0.2.1 six==1.13.0 urllib3==1.25.7
Layer との疎通確認用に、opencv-python
をインポートし、バージョン情報をレスポンスとして返すように handler.py
内のコードを変更しておきます。
import json import cv2 def hello(event, context): body = { "message": "Go Serverless v1.0! Your function executed successfully!", "input": event, "cv2": cv2.__version__ } response = { "statusCode": 200, "body": json.dumps(body) } return response # Use this code if you don't use the http event with the LAMBDA-PROXY # integration """ return { "message": "Go Serverless v1.0! Your function executed successfully!", "event": event } """
serverles.yml
内の hello.py
をハンドラとしているファンクションを API Gateway で提供します。
# ... functions: hello: handler: handler.hello # The following are a few example events you can configure # NOTE: Please make sure to change your handler code to work with those events # Check the event documentation for details events: - http: path: hello method: get # ...
AWS のプロファイル設定や、デプロイに必要な情報を serverless.yml
に記述して、デプロイしてみます。
$ sls deploy --profile=xxx
AWS コンソールで Lambda の画面へアクセスし、該当の関数と Layer が表示されているのを確認できればデプロイは成功です。
この状態でローカル環境で API Gateway のエンドポイントに curl してみます。
$ curl https://xxx.api-endpoint.<region>.amazonaws.com/dev/hello
JSON形式で API Gateway のイベントデータと python-opencv
のバージョンがリクエストボディで返ってくれば、疎通確認完了です。
注意点
実際に Lambda 上で実行することは可能ですが、.serverless/pythonRequirements.zip
を解答したファイルサイズは約200MBになります。これは Layer の容量制限(解凍後で最大250MB)になんとか入っているというサイズ感です。
Web アプリケーションフレームワークとして Flask を検討しようにも入るかどうか...といったところです。
サイズを食っているのは OpenCV のビルドファイルと numpy
(計算処理に特化したパッケージ)です。どちらも容量削減するのは結構厳しいです。
今回の方法だとOpenCVのGUIライブラリも入っているため、サイズをもう少し抑えたいのであれば opencv-python-headless
の方を使ったほうが Layer サイズを少なくすることができます。
さいごに
今回は AWS Lambda で opencv-python
を動作させる方法について紹介しました。
動作自体は可能ですが、Lambda のアーキテクチャと OpenCV の特性を考えると、あまりおすすめできる構成ではありません。日本語の記事どころか他言語の記事もみあたらないのも納得いきます。ご利用は計画的に。
さいごまでお読みいただきありがとうございました!