AWS LambdaでNumpy、Scipyを使ってみる!Serverless Frameworkとプラグインを使えばパッケージ管理が簡単!
Numpy、ScipyはLambdaのLayer機能を利用することで今回紹介する方法を使わなくても利用できます。AWSがNumpy、ScipyをホストしたLayerを提供しているので対象のLambdaのレイヤに設定することで利用できます。 詳細はこちらをご確認ください。
どうも!大阪オフィスの西村祐二です。
機械学習やデータ分析でよく目にするライブラリとして、Numpy、Scipyがあると思います。
今回、そのライブラリをAWS Lambda上で動かしてみた、という話になります。
通常、上記を実現するためにいくつか注意点があるのですが、Serverless Frameworkとプラグインのserverless-python-requirementsを使えば、その注意点をほぼ気にすることなく実現できたのでブログにまとめておきます。
AWS Lambdaで外部モジュールを利用する際の注意点
外部モジュールも含めてzipに固めてデプロイする必要がある
Lambdaで外部モジュールを使用する場合は、その外部モジュールをデプロイパッケージに含めてデプロイする必要があり、コードを修正するたびにzipに固めてデプロイパッケージを作成する必要があります。また実行プログラムから外部モジュールをimportする際のパスなど気をつける必要があります。(パッケージ直下に外部モジュールファイルをおくなど)
1つのデプロイパッケージに容量制限がある(2018/10/25現在、50MB)
Lambda 関数デプロイパッケージのサイズ (圧縮 .zip/.jar ファイル)に制限があり、2018/10/25現在、50MBとなっています。また、なるべくデプロイパッケージのサイズは最小限にしておくことが推奨されています。
他のLambdaの制限については下記ドキュメントを参照ください。 https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/limits.html
Pure Pythonなライブラリじゃない場合、Amazon Linux環境でビルドしたファイルをパッケージングする必要がある
Lambdaで利用したい外部モジュールの内部でC言語を使っている場合などPure Pythonじゃないライブラリの場合、Amazon Linux環境でインストール(ビルド)したファイルを使ってパッケージングする必要があります。 Numpy、Scipyはそれに該当します。
MacやWindowsにインストールしたモジュールファイルをデプロイパッケージに固めてデプロイしてもエラーとなります。
Amazon LinuxのDockerイメージを利用する方法やlambci/docker-image
というLambda関数の実行環境とほぼ同等のDockerイメージを使う方法があります。
上記3つのことをLambdaをデプロイする際に、つまりコードを更新する際に気をつける必要があり、少々面倒なところであります。はじめAWS SAMとDockerを使ったオレオレデプロイスクリプトを作っていましたが、そのスクリプトのメンテや案件専用につくったので汎用性がないところなどいろいろとつらくなっていたところ、Serverless Frameworkとプラグインのserverless-python-requirementsを使えば、その注意点をほぼ気にすることなく実現できました。
やってみる
AWSのクレデンシャルの設定は完了している前提で進めていきます。
環境
- node.js:8.12.0
- serverless framework:1.32.0
- serverless-python-requirements:4.2.4
Serverless Frameworkをインストール
$ npm install -g serverless
プロジェクト作成
$ serverless create \ --template aws-python3 \ --name numpy-test \ --path numpy-test
python3環境セットアップ
$ cd numpy-test $ virtualenv venv --python=python3 Running virtualenv with interpreter /usr/local/bin/python3 Using base prefix '/usr/local/Cellar/python3/3.6.1/Frameworks/Python.framework/Versions/3.6' New python executable in /Users/username/scratch/numpy-test/venv/bin/python3.6 Also creating executable in /Users/username/scratch/numpy-test/venv/bin/python Installing setuptools, pip, wheel...done. $ source venv/bin/activate (venv) $
Numpy、Scipyインストール
(venv) $ pip install numpy (venv) $ pip install scipy
requirements.txt
に書き出しておきます。プラグインはこのファイルを参照して外部モジュールを含めたデプロイパッケージを作成してデプロイしてくれます。
(venv) $ pip freeze > requirements.txt (venv) $ cat requirements.txt numpy==1.15.3 scipy==1.1.0
Lambda関数のプログラムを作成
今回、Numpy、Scipyが動作することだけを確認したかったので、簡単なプログラムにしています。
Numpyでは行列、Scipyでは積分のプログラムを実行しています。
具体的には、Scipyはquad()は与えられた関数を[区間の始まり, 区間の終わり]の区間で定積分し、2つの値を返します。変数1には積分した結果が、変数2には積分計算をした際の誤差が返されます。
最初の4行はserverless-python-requirements でデプロイパッケージ容量を削減する機能をつかうために記載しています。
try: import unzip_requirements except ImportError: pass import numpy as np from scipy import integrate def func(x): return 2*x + 5 def main(event, context): a = np.arange(15).reshape(3, 5) print("numpy array:") print(a) result, err = integrate.quad(func, 0, 5) print(f'scipy:積分結果:{result}\n誤差:{err}') if __name__ == "__main__": main('', '')
ローカルで実行してみる
(venv) $ python handler.py numpy array: [[ 0 1 2 3 4] [ 5 6 7 8 9] [10 11 12 13 14]] scipy:積分結果:50.0 誤差:5.551115123125783e-13
上記が、Lambdaを実行したときの出力結果となれば成功です。
プラグインをインストール
(venv) $ npm install --save serverless-python-requirements
serverless.ymlを編集する
デプロイファイルを設定していきます。
今回は下記のように設定しました。
service: numpy-test provider: name: aws runtime: python3.6 plugins: - serverless-python-requirements custom: pythonRequirements: dockerizePip: non-linux slim: true zip: true package: include: - handler.py exclude: - '**' functions: numpy: handler: handler.main
特徴のある設定項目を見ていきます。
- 7,8行目
- 利用するプラグインを指定します。
-
12行目(dockerizePip: non-linux)
-
Dockerイメージ
lambci/lambda:build-python3.6
を使って外部モジュールをパッケージングしてくれます。設定はtrue
でもいいもみたいです。 -
13行目(slim: true)
-
外部モジュールから
strip
、.so
ファイル、__pycache__
ディレクトリとdist-info
ディレクトリなど削除してくれます。 -
14行目(zip: true)
-
numpy、scipy、scikit-learnなどのような大きなライブラリを圧縮してくれる機能のようです。
-
17行目(include)
- パッケージに含めたいファイルを指定します。
- 19行目(exclude)
- パッケージに含めたくないファイルを指定します。今回includeで指定したファイル以外はLambda関数として使わないので
'**'
としてすべて除外しておきます。(外部モジュールは含まれます。)
デプロイしてみる
dockerを使いますのでdockerデーモン
を起動しておてください。
未インストールの場合は下記リンクなどからインストールし、起動しておいてください。
https://docs.docker.com/docker-for-mac/install/
(venv) $ sls deploy Serverless: Adding Python requirements helper... Serverless: Generated requirements from /Users/nishimura.yuji/study/sls/numpy-test/requirements.txt in /Users/nishimura.yuji/study/sls/numpy-test/.serverless/requirements.txt... Serverless: Installing requirements from /Users/nishimura.yuji/study/sls/numpy-test/.serverless/requirements/requirements.txt ... Serverless: Docker Image: lambci/lambda:build-python3.6 Serverless: Zipping required Python packages... Serverless: Packaging service... Serverless: Excluding development dependencies... Serverless: Removing Python requirements helper... Serverless: Injecting required Python packages to package... Serverless: Uploading CloudFormation file to S3... Serverless: Uploading artifacts... Serverless: Uploading service .zip file to S3 (42.02 MB)... Serverless: Validating template... Serverless: Updating Stack... Serverless: Checking Stack update progress... ......... Serverless: Stack update finished... Service Information service: numpy-test stage: dev region: us-east-1 stack: numpy-test-dev api keys: None endpoints: None functions: numpy: numpy-test-dev-numpy
パッケージサイズは42.02 MBとなり50M制限にひっかかることはなさそうです。
試してみる
下記コマンドでターミナルからLambdaを実行できます。
(venv) $ sls invoke -f numpy --log null -------------------------------------------------------------------- START RequestId: Version: $LATEST numpy array: [[ 0 1 2 3 4] [ 5 6 7 8 9] [10 11 12 13 14]] scipy:積分結果:50.0 誤差:5.551115123125783e-13 END RequestId: REPORT RequestId: Duration: 0.77 ms Billed Duration: 100 ms Memory Size: 1024 MB Max Memory Used: 289 MB
やりましたね。ローカルで実行した値と同じであり、AWS Lambda上でNumpy、Scipyがうまく動作していることがわかります。
さいごに
いかがだったでしょうか。
AWS LambdaでNumpy、Scipyを使ってみました。デプロイする際に気をつける必要がある項目については、Serverless Frameworkとプラグインのserverless-python-requirementsを使うことでほぼ気にすることなく、デプロイすることができます。これでLambda関数のロジック部分の開発に注力できますね。
誰かの参考になれば幸いです。
追記
lambda上でnp.show_config()
の出力結果を知りたいとコメントいただきましたので、試してみました。
np.show_config()
を追記して実行した結果が下記になります。
ここらへんの知識がなく詳しいことはわかりませんが、OpenBLASなどは使える?みたいです。
null -------------------------------------------------------------------- START RequestId: Version: $LATEST numpy array: [[ 0 1 2 3 4] [ 5 6 7 8 9] [10 11 12 13 14]] scipy:積分結果:50.0 誤差:5.551115123125783e-13 blas_mkl_info: NOT AVAILABLE blis_info: NOT AVAILABLE openblas_info: libraries = ['openblas', 'openblas'] library_dirs = ['/usr/local/lib'] language = c define_macros = [('HAVE_CBLAS', None)] blas_opt_info: libraries = ['openblas', 'openblas'] library_dirs = ['/usr/local/lib'] language = c define_macros = [('HAVE_CBLAS', None)] lapack_mkl_info: NOT AVAILABLE openblas_lapack_info: libraries = ['openblas', 'openblas'] library_dirs = ['/usr/local/lib'] language = c define_macros = [('HAVE_CBLAS', None)] lapack_opt_info: libraries = ['openblas', 'openblas'] library_dirs = ['/usr/local/lib'] language = c define_macros = [('HAVE_CBLAS', None)] END RequestId: REPORT RequestId: Duration: 1.08 ms Billed Duration: 100 ms Memory Size: 1024 MB Max Memory Used: 291 MB