3
3

More than 3 years have passed since last update.

LambdaLayersをLambdaで作る

Posted at

はじめに

普段私がLambdaを利用する際は、chaliceを利用してLambda関数の作成とデプロイをしています。

chaliceは便利なのですが、デプロイ時に毎回pip installして必要なpackageを集めているみたいです。
そのため、pandasなどの重めのpackageを利用している場合は、デプロイに時間がかかります。

そこで、あらかじめ利用するpackageを登録しておくLambdaのサービスのLambdaLayersを活用します。

前提

LambdaLayersを作成する際の前提条件などです。

  1. LambdaLayerspackageszipで固めたファイルを利用して、あらかじめLambdaが利用できる状態にすること参考
  2. packageの配置などフォルダ構成も決まっている
  3. pandasなどの一部のpackageAmazonLinux上でpip installする必要がある参考
  4. EC2の上でやるのが簡単だけれど、VPCなどの環境がない場合は作成するのが無駄である(VPCそのものに料金がかかる)。
  5. Lambdaには/tmpというプログラムがアクセスできる領域がある。

この、 (3) の条件が地味にきつくて、ローカルなどで作成したzipファイルではうまくLambdaが認識してくれないことがありました。
また (4) のEC2上でやっても良いのですが、サーバーレスしか利用していないプライベート開発だと、
お金をかけたく無いためなるべくLambdaのみで完結したいと言う(自分の)要望がありました。

実装

今回の実装の流れ

上記の前提を踏まえて今回は以下の方法でLambdaLayersに必要なzipファイルを作成します。

Lambda/tmp領域にpackageinstallして、zipで固めてS3にアップロードする
実際のLambdaLayersの登録は手作業で行う。

ソースコード

Lambdaで実行するプログラムは以下の通りです。

lambda_function.py
import json
import subprocess
import zipfile
import boto3
from pathlib import Path


def lambda_handler(event, context):
    # 引数の確認
    if "package_list" not in event:
        return {"error": "[package_list]が存在しません"}
    package_list = event['package_list']
    if type(package_list) is not list:
        return {"error": "[package_list]がlistではありません"}    
    if "s3_bucket" not in event:
        return {"error": "[s3_bucket]が存在しません"}
    if "s3_key" not in event:
        return {"error": "[s3_key]が存在しません"}

    # pythonと言うディレクトリを作成し、そこにpackageをインストール
    mkdir_cmd = "mkdir -p /tmp/python"
    pip_install_cmd = f"pip install -t /tmp/python {' '.join(package_list)}"
    process = (subprocess.Popen(f"{mkdir_cmd} && {pip_install_cmd}", stdout=subprocess.PIPE, shell=True).communicate()[0]).decode('utf-8')
    print('commands...\n'+process)

    # Pathオブジェクトを生成
    p = Path("/tmp/python")

    # zipにinstallしたpackageを追加    
    with zipfile.ZipFile('/tmp/python_lib.zip', 'w', compression=zipfile.ZIP_DEFLATED) as new_zip:
        for f in list(p.glob("**/*")):
            new_zip.write(f, arcname=str(f).replace("/tmp/", ""))

    #S3オブジェクトを取得しupload
    s3 = boto3.resource('s3')
    bucket = s3.Bucket(event['s3_bucket'])
    bucket.upload_file('/tmp/python_lib.zip', event['s3_key'])
    return {
        "success": f"s3://{event['s3_bucket']}/{event['s3_key']}に出力しました"
    }

Lambda設定

インストールするpackageに合わせて適宜変えてください。

  • メモリ: 512[MB]
  • timeout: 5[min]
  • Role: アップロードするS3にアクセス権限を付与。

引数例

実行する際に、以下の引数を渡すとpackage_listに含まれるpackageをインストールします。
また、s3_buckets3_keyに指定した箇所へzipファイルをアップロードしてくれます。

event
{
  "package_list": ["pandas", "lxml", "requests", "beautifulsoup4"],
  "s3_bucket": "<s3のバケット名>",
  "s3_key": "lambda_layer/python_lib.zip"
}

上記の例を利用してzipファイルを作成した場合は、およそ43.1[MB]程度のzipファイルが作成されます。

おわりに

ということで、上記のソースコードをLambdaにコピーして実行すれば、
LambdaLayersの登録に必要なzipを作成できるようになりました。

注意:今回は仕方なしにPythonからshellを実行しています。
基本的に、Pythonからshellを呼び出すことはよくないと思うので、
利用するのはどうしても仕方がない場合にしましょう。

3
3
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
3
3