はじめに
普段私がLambdaを利用する際は、chaliceを利用してLambda関数の作成とデプロイをしています。
chaliceは便利なのですが、デプロイ時に毎回pip installして必要なpackageを集めているみたいです。
そのため、pandasなどの重めのpackageを利用している場合は、デプロイに時間がかかります。
そこで、あらかじめ利用するpackageを登録しておくLambdaのサービスのLambdaLayersを活用します。
前提
LambdaLayersを作成する際の前提条件などです。
-
LambdaLayersはpackagesをzipで固めたファイルを利用して、あらかじめLambdaが利用できる状態にすること参考 -
packageの配置などフォルダ構成も決まっている -
pandasなどの一部のpackageはAmazonLinux上でpip installする必要がある参考 -
EC2の上でやるのが簡単だけれど、VPCなどの環境がない場合は作成するのが無駄である(VPCそのものに料金がかかる)。 -
Lambdaには/tmpというプログラムがアクセスできる領域がある。
この、 (3) の条件が地味にきつくて、ローカルなどで作成したzipファイルではうまくLambdaが認識してくれないことがありました。
また (4) のEC2上でやっても良いのですが、サーバーレスしか利用していないプライベート開発だと、
お金をかけたく無いためなるべくLambdaのみで完結したいと言う(自分の)要望がありました。
実装
今回の実装の流れ
上記の前提を踏まえて今回は以下の方法でLambdaLayersに必要なzipファイルを作成します。
Lambdaの/tmp領域にpackageをinstallして、zipで固めてS3にアップロードする
実際のLambdaLayersの登録は手作業で行う。
ソースコード
Lambdaで実行するプログラムは以下の通りです。
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_bucketとs3_keyに指定した箇所へzipファイルをアップロードしてくれます。
{
"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を呼び出すことはよくないと思うので、
利用するのはどうしても仕方がない場合にしましょう。