21
17

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

AWS Lambdaでpandasとscikit-learn使おうとしてハマった話 (/tmp からのモジュールの読み込み)

Last updated at Posted at 2018-05-26

機械学習モデルを利用したAPIを作成しようと思い、
Lambdaでpandasscikit-learnを使おうとしてみたら思いのほかハマった。

今回のハマりポイント

  • lambdaのデプロイパッケージ容量制限(ZIPで50MB)
  • Amazon Linuxの環境依存
  • pythonでのモジュールのインポート

lambdaのデプロイパッケージ容量制限(ZIPで50MB)

まず問題になったのはデプロイパッケージ容量だ。
pandasscikit-learnと依存ライブラリを含めると、zipで圧縮しても60MBほどあって、制限を超えてしまう...

そこで、/tmp フォルダ を使う

lambdaではユーザーが使用できる容量512MBの/tmpが割り当てられている。
つまり、デプロイパッケージに入りきらないライブラリを
実行時にs3からダウンロードして/tmpに展開してパスを通した後インポートすればいい。

今回やりたいのはコレだけだけど、付随する問題がいろいろ出てきて死んだ。

Amazon Linuxの環境依存

まず、容量制限に引っかからないpandasと依存ライブラリを含めたデプロイパッケージ(30MBほど)を作成し動作確認してみた。

なんかエラー出る...
Unable to import module 'main': Missing required dependencies ['numpy']

numpyはパッケージに含めているのに、、なんで?

調べてみるとこんな記事が見つかった。
Amazon Linuxと同じオプションでビルドされたPythonを使って安全にLambda functionをデプロイしよう

つまりlambdaで正常に動作させるためには、
lambda実行環境である Amazon Linux でデプロイパッケージを作成する必要がある、ということ。

めんどいかよ

docker環境、以前ec2で使ってたのがあるけど、
アクセスキー紛失してアクセスできなくなったんだよ(え?)

インスタンス作り直すのめんどいなー、と思ってggってたら、
すばらしい人がすでにAmazon Linuxでビルドしたパッケージを公開してくれてた。
https://github.com/chennavarri/aws-lambda-pandas-sample
https://github.com/ryansb/sklearn-build-lambda

...だめでした。
pythonのバージョンがそれぞれ違った。

しかたないのでec2でAmazon Linux環境作ってパッケージ作った。
これを使うとlambda環境まで再現できる。
https://github.com/lambci/docker-lambda

pythonでのモジュールのインポート

ようやくデプロイパッケージができたのでlambdaで実行してみる。
pandasはデプロイパッケージに含めて、sklearn/tmp以下に展開することにした。

コードは単純で、
s3からsklearnのzipファイルを/tmpにダウンロードし、展開するというもの。

main.py
import boto3
import os
import sys
import zipfile
import pandas as pd
from pprint import pprint

s3 = boto3.resource('s3')
bucket = s3.Bucket('data')

def main():
    load_sklearn()
    pprint(os.listdir("/tmp/site-packages/"))

    import sklearn

    return "done"

def load_sklearn():
    bucket.download_file("sklearn-packages.zip", '/tmp/sklearn-packages.zip')

    zip_ref = zipfile.ZipFile('/tmp/sklearn-packages.zip', 'r')
    zip_ref.extractall('/tmp')
    zip_ref.close()

    os.remove("/tmp/sklearn-packages.zip")

def handle(event, context):
    return main()

実行結果 (抜粋)

['bin',
 'scipy-1.1.0.dist-info',
 'sklearn',
 'scipy',
 'scikit_learn-0.19.1.dist-info']
No module named 'sklearn': ModuleNotFoundError

sklearnはちゃんとダウンロードされて展開されているのに、
モジュールが見つからないというエラー...

どうやらlambdaの環境変数でPYTHONPATH/tmp/site-packagesを追加していたのが原因らしい。
PYTHONPATHから/tmp/site-packagesを消し、モジュール追加後にモジュールパスを追加するようにすると正常に動作した。

main.py(抜粋)
def main():
    load_sklearn()
    pprint(os.listdir("/tmp/site-packages/"))

    sys.path.append('/tmp/site-packages') # ←これを追加
    import sklearn

    return "done"

実行結果

"done"

あらかじめ環境変数で指定したモジュールパスについては、実行の初めに探索されて、以降に追加されたモジュールは検知しない、とかなのかな?
import文のタイミングで探索すると思ってたから、最初は詰んだかと思った。
詳しい方はコメントぜひ!

まとめ

cold startだと数秒かかるので実用は厳しい
おすすめを教えてほしい

21
17
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
17

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?