2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AWS Lambdaで50MB制限を突破してサードパーティ製ライブラリを使う方法(Layer不要)

Posted at

はじめに

AWS Lambda でサードパーティ製ライブラリを利用する場合、通常は Layer を作成 するのが推奨されています。
しかし、Layer には以下のような制限があります。

  • 1つの Layer は最大 50MB(圧縮時)まで
  • Lambda に設定できる Layer の合計サイズは 250MB まで

scikit-learn や pandas のような重量級ライブラリを使う場合、容量制限にひっかかることも少なくありません。

そこで今回は S3 にライブラリ一式を置いて Lambda 実行時に /tmp に展開して使う方法 を紹介します。
この方法を使えば 50MB 制限を回避でき、必要なライブラリを自由に扱えるようになります。

手順

1. AWS公式の Lambda 環境をクローン

まずは AWS が公開している Lambda のベースイメージを利用します。

git clone https://github.com/aws/aws-lambda-base-images.git
cd aws-lambda-base-images
git checkout python3.12 # 利用環境のバージョンに合わせてください

2. Dockerfile に追記し、必要なライブラリをインストール

Dockerfile.python3.12 を編集し、pip install を追記します。

Dockerfile.python3.12
FROM scratch
ADD x86_64/214c39c8cf002eeeafedbbf3d9eea26835911f69c1c2e85d915322904aa971c7.tar.xz /
ADD x86_64/5348a9630c8b3ba93173487c1a4b5adf6870c16ef653ab7238d538089e39af2d.tar.xz /
ADD x86_64/ba278819ec56a8af670fd42e84172e233aad6a5b8ff8b3a75ce62b9fa02afcd1.tar.xz /
ADD x86_64/bc53fe7cd91383999f362c80316863ea6443c6a3e84abeeec6e2e60882b4308f.tar.xz /
ADD x86_64/cdf4e651a6374c619af769fb233514305e3142168be97059029cb6b52c96ac72.tar.xz /
ADD x86_64/eadc3b32f0f94e591a686ee6b9447e9cdac2557a9a1509018b3a6f10ae4e794b.tar.xz /

ENV LANG=en_US.UTF-8
ENV TZ=:/etc/localtime
ENV PATH=/var/lang/bin:/usr/local/bin:/usr/bin/:/bin:/opt/bin
ENV LD_LIBRARY_PATH=/var/lang/lib:/lib64:/usr/lib64:/var/runtime:/var/runtime/lib:/var/task:/var/task/lib:/opt/lib
ENV LAMBDA_TASK_ROOT=/var/task
ENV LAMBDA_RUNTIME_DIR=/var/runtime

WORKDIR /var/task

# ここ追加に追加
RUN pip install --no-cache-dir scikit-learn pyyaml -t /var/lang/lib/python3.12/site-packages

ENTRYPOINT ["/lambda-entrypoint.sh"]

3. Docker イメージをビルド

/aws-lambda-base-images> wsl
/aws-lambda-base-images$ docker build -t python3.12:local -f Dockerfile.python3.12 .

下記エラーが出る場合は、sudoを付けて管理者権限で実行してください。

ERROR: permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Head "http://%2Fvar%2Frun%2Fdocker.sock/_ping": dial unix /var/run/docker.sock: connect: permission denied

4. コンテナを立ち上げ、site-packages をホスト環境(windows)にコピー

ビルド後のイメージの中にライブラリが展開されています。
下記のコマンドでコンテナを立ち上げ、ライブラリをホスト側にコピーしてください。

# コンテナの起動
/aws-lambda-base-images$ sudo docker run -d --name py312-container python3.12:local
# site-packagesをコピー
/aws-lambda-base-images$ sudo docker cp py312-container:/var/lang/lib/python3.12/site-packages ./site-packages

この site-packages 配下のファイルを zip 化します。

/aws-lambda-base-images$ zip -r ./site-packages.zip ./site-packages -x "*.dist-info*" "*__pycache__*"

.dist-info, _pycache_ は不要なので除外します。

zipコマンドが無ければインストールしてください。

/aws-lambda-base-images$ sudo apt install zip

補足:

.dist-info とは?

Python パッケージを pip でインストールすると、パッケージの情報が .dist-info フォルダとして作られます

内容例:

  • METADATA → パッケージ名やバージョン情報
  • RECORD → パッケージに含まれるファイル一覧
  • INSTALLER → pip でインストールされた情報

これらはLambda 実行には不要 なので zip から除外すると軽量化できる

pycache とは?

  • Python が .py ファイルを実行すると自動で生成する コンパイル済みバイトコード (.pyc) の保存フォルダ
  • 実行速度向上のためだけのキャッシュであり、Lambda にデプロイする場合は不要

こちらも除外すると zip が軽くなる

5. S3 にアップロード

zip 化したファイルを S3 にアップロードします。
スクリーンショット 2025-10-02 012601.png

s3://my-bucket/site-packages.zip

6. Lambda 関数内で /tmp に展開

Lambda 関数の中で S3 からダウンロードし、/tmp に展開します。

import boto3
import zipfile
import sys
import os

s3 = boto3.client('s3')

def lambda_handler(event, context):
    # S3からzipを/tmpにダウンロード
    s3.download_file('my-bucket', 'site-packages.zip', '/tmp/site-packages.zip')

    # zipを展開
    with zipfile.ZipFile('/tmp/site-packages.zip', 'r') as zip_ref:
        zip_ref.extractall('/tmp')

    # sys.pathに追加
    sys.path.insert(0, '/tmp/site-packages')

    # インポートして利用
    import sklearn
    import yaml
    
    return {
        'statusCode': 200,
        'body': f'scikit-learn version: {sklearn.__version__}'
    }
{
  "errorMessage": "An error occurred (403) when calling the HeadObject operation: Forbidden",
  "errorType": "ClientError",
  "requestId": "c2f48c85-3e0b-44c3-9beb-4615dc655698",
  "stackTrace": [
    "  File \"/var/task/lambda_function.py\", line 10, in lambda_handler\n    s3.download_file('my-bucket', 'site-packages.zip', '/tmp/site-packages.zip')\n",
    "  File \"/var/lang/lib/python3.12/site-packages/botocore/context.py\", line 123, in wrapper\n    return func(*args, **kwargs)\n",
    "  File \"/var/lang/lib/python3.12/site-packages/boto3/s3/inject.py\", line 223, in download_file\n    return transfer.download_file(\n",
    "  File \"/var/lang/lib/python3.12/site-packages/boto3/s3/transfer.py\", line 406, in download_file\n    future.result()\n",
    "  File \"/var/lang/lib/python3.12/site-packages/s3transfer/futures.py\", line 111, in result\n    return self._coordinator.result()\n",
    "  File \"/var/lang/lib/python3.12/site-packages/s3transfer/futures.py\", line 287, in result\n    raise self._exception\n",
    "  File \"/var/lang/lib/python3.12/site-packages/s3transfer/tasks.py\", line 272, in _main\n    self._submit(transfer_future=transfer_future, **kwargs)\n",
    "  File \"/var/lang/lib/python3.12/site-packages/s3transfer/download.py\", line 355, in _submit\n    response = client.head_object(\n",
    "  File \"/var/lang/lib/python3.12/site-packages/botocore/client.py\", line 602, in _api_call\n    return self._make_api_call(operation_name, kwargs)\n",
    "  File \"/var/lang/lib/python3.12/site-packages/botocore/context.py\", line 123, in wrapper\n    return func(*args, **kwargs)\n",
    "  File \"/var/lang/lib/python3.12/site-packages/botocore/client.py\", line 1078, in _make_api_call\n    raise error_class(parsed_response, operation_name)\n"
  ]
}

こういうエラーがでたら、lambdaの実行ロールにS3にアクセスする権限が付与されていないことが原因なので、下記のように権限を付与してください。
スクリーンショット 2025-10-02 013608.png

スクリーンショット 2025-10-02 013831.png

スクリーンショット 2025-10-02 013946.png
※正確にはs3:GetObjectしかいらないので、jsonでポリシーを設定する場合は、そのように設定したほうが安全

また、下記のようなエラーが出た場合

{
  "errorType": "Sandbox.Timedout",
  "errorMessage": "RequestId: d4751fdf-0c1e-430a-abad-f0e9fbc5a2e6 Error: Task timed out after 3.00 seconds"
}

lambdaのタイムアウトによる強制終了なので、下記のようにタイムアウトの設定時間を延ばしてください。
また、この手法でライブラリを使用する場合、デフォルトのメモリ128MBでは、まず動かないと思うので拡張してください。

スクリーンショット 2025-10-02 015110.png

スクリーンショット 2025-10-02 021944.png
※絶対10分も必要ないです。私がテストした時は20秒で実行できました。

なぜこんな回りくどいことが必要なのか?

Lambda の実行環境は Amazon Linux 系の特殊な環境 で動いています。
例えば私たちの手元(windows や macOS, Ubuntu)で

pip install scikit-learn -t .
zip -r lambda.zip .

としてS3にアップロードし、Lambdaで使おうとすると、よくこんなエラーに出会います。

Unable to import module 'lambda_function': 
  ImportError: /lib64/libc.so.6: version `GLIBC_2.27' not found

つまり、

  • ネイティブライブラリ(C/C++など)の依存関係が Amazon Linux とズレる
  • Windows/macOSでビルドしたwheelが Amazon Linux 上で動かない
  • glibcやlibstdc++のバージョン違いで ImportError が出る

といった問題が起きるのです。

そこで Amazon Linux ベースの Docker イメージ上で pip install して、環境に合ったバイナリを含む site-packages を作る 必要があります。
今回紹介した方法は、この「Amazon Linux 環境に合わせる」という下準備をクリアするためのものです。

メリット・デメリット

メリット

  • Lambda Layer を作らなくても容量制限を回避できる
  • 巨大ライブラリ(scikit-learn, opencv など)も利用可能
  • 必要に応じて S3 上でライブラリをバージョン管理できる

デメリット

  • 毎回 /tmp に展開するため初回実行が遅い
  • /tmp の容量はlambdaのエフェメラルストレージ( 512MB )に制限されているので注意

まとめ

  • Layer では容量制限にひっかかる場合がある
  • S3 + /tmp 解凍方式なら 50MB 制限を突破可能
  • 実行速度・安定性を考えると Layer がベストだが、裏技的に使える方法

「軽量化(不要ファイル削除、--no-cache-dir 指定)」なども組み合わせると、より効率的に使えると思います。

最後に

皆さんはlambdaでサードパーティ製ライブラリを使用する際、どのように対応されていますか?
今回紹介した方法は、結構荒い方法なので、もっとスマートに対応できるようになりたいと思ってます。
良い方法があればコメントで教えてください。


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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?