Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

機械学習モデルを利用した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だと数秒かかるので実用は厳しい
おすすめを教えてほしい

atilol
プログラマー
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away