AWS
OpenCV
Python3
lambda

AWS LambdaのLayer用にOpenCVをビルド(Python3)

Lambda Layers

AWS Lambdaのデプロイパッケージには50MBのサイズ制限がある。Numpy・SciPy・OpenCVをベタにビルドすると、それだけでお腹いっぱい。
そこでLambda Layers。展開後のサイズで250MBまで使える。
AWS公式は現在Numpy・SciPyのみ提供している。OpenCVも当然あっていいはずなのに今はまだないので、丸1日かけてビルドした。

準備

要Docker。

使うOpenCVのバージョンを決める。決めたら、ソースツリーをzipで固める。ファイル名はopencv-master.zipとopencv_contrib-master.zip。もしHEADでよければ、GitHubでDownload ZIPしたそのまんま。

ビルド

以下のDockerfileとdocker-compose.ymlと同じところに、opencv-master.zipとopencv_contrib-master.zipを置いておく。

Dockerfile
FROM lambci/lambda:build-python3.6
ENV AWS_DEFAULT_REGION ap-northeast-1

RUN yum update -y
RUN sed -i -e 's/enabled=0/enabled=1/' /etc/yum.repos.d/epel.repo
RUN yum install cmake3 -y
RUN pip install --upgrade pip
RUN pip install numpy

ENV HOME /home/hoge
RUN mkdir $HOME
WORKDIR $HOME

COPY opencv-master.zip .
RUN unzip opencv-master.zip
RUN mv opencv-master opencv
COPY opencv_contrib-master.zip .
RUN unzip opencv_contrib-master.zip
RUN mv opencv_contrib-master opencv_contrib

RUN mkdir $HOME/opencv/build
WORKDIR $HOME/opencv/build
RUN cmake3 -D CMAKE_BUILD_TYPE=RELEASE -D BUILD_SHARED_LIBS=NO \
    -D BUILD_LIST=aruco,features2d,imgcodecs,imgproc,python3,xfeatures2d \
    -D OPENCV_EXTRA_MODULES_PATH=~/opencv_contrib/modules \
    -D PYTHON3_EXECUTABLE=/var/lang/bin/python \
    -D CMAKE_INSTALL_PREFIX=~/bar_dist ..
RUN make install

RUN mkdir -p $HOME/foo/python
RUN mv $HOME/bar_dist/lib $HOME/foo/python/
WORKDIR $HOME/foo
CMD zip -ry9 ~/cv2_ll.zip * && cp ~/cv2_ll.zip /share
docker-compose.yml
version: '2'
services:
  app:
    build:
      context: .
    volumes:
      - .:/share

上記のDockerfileなどなどを置いたディレクトリで、

docker-compose build
docker-compose up

カレントディレクトリにcv2_ll.zipが生成される。

使い方

cv2_ll.zipをAWSコンソールでLayerとして登録する。自分のLambda FunctionにこのLayerをつける。このとき、AWS公式で提供されているNumpy・SciPyのLayerも同時につける必要がある。

importに時間がかかるので、ハンドラモジュールのグローバルでimportする必要がある。ハンドラ関数内でimport cv2しようとすると、デフォルトの制限時間の3秒以内に終わらない。

OpenCVモジュールの取捨選択

BUILD_LISTを編集する。ちなみに上のリストでビルドすると、2019/01/08のHEADで展開後サイズが49,254,022バイトとなる。

共有ライブラリ?

上の例では静的リンクしてあるので、メモリを余計に食っているはず。Lambda FunctionでLD_LIBRARY_PATHを見ると/opt/libに通っているので(Layerは/optに展開される仕組み)、もしここに.soを配置すれば使えるはず。

追記:
むしろメモリを余計に食った。import cv2しただけの状態で54MB vs 58MB。

参考

AWS Lambda で OpenCV と Pillow を使う環境を準備する
Using Packages and Native nodejs Modules in AWS Lambda