はじめに
普段私が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
を呼び出すことはよくないと思うので、
利用するのはどうしても仕方がない場合にしましょう。