概要
手元では動いていたコードを Lambda にデプロイしたら、Runtime.ImportModuleError: Unable to import module 'lambda_function': No module named 'requests' のようなエラーで動かないことがあります。手元の環境にはライブラリが入っていても、Lambda の実行環境には入っていないからです。デプロイパッケージを小さくしようとして、依存ライブラリを入れずにアップロードした場合も同じことが起きます。
依存ライブラリを関数のコードと分けて持たせる仕組みが Lambda レイヤーです。ライブラリを決まったフォルダ構成の .zip にまとめてレイヤーとして発行し、関数にアタッチすると、デプロイパッケージに含めなくてもコードから import できます。同じレイヤーを複数の関数で使い回すこともできます。
この記事では、No module named エラーのときの確認ポイントと、レイヤーの仕組み(フォルダ構成・上限・バージョン)を整理します。実践では、requests を使う関数がエラーになることを確かめ、CloudShell でレイヤーを作って関数にアタッチし、動くようにします。

クラウドおさる自分の山では使えた道具を、遠征先の山に持って行き忘れると、作業が始まらぬでござる。道具箱(レイヤー)にまとめて、遠征隊(関数)に持たせればよいのでござるよ。
この記事のメリット
No module namedエラーの原因(実行環境に依存ライブラリが無い)を切り分けられるようになる- Lambda レイヤーの .zip のフォルダ構成と、関数の中での展開先(
/opt)が分かる - 依存ライブラリをレイヤーにまとめて発行し、関数にアタッチできるようになる
- 誤った対処(.zip の中に .zip を入れる、環境変数に書く)を避けられるようになる
執筆者とキャラクター紹介
執筆者:土肥(とひ)
株式会社ジェニュインの代表であり元エンジニア。これまでオンプレミスと AWS 環境のインフラ業務を担当。AWS 認定資格の学習で得た知識を、ハンズオンとともに解説するのが得意。直近では新人の教育も行っており、これから技術を学ぶ方が納得できる解説を心がけています。基礎編・Cloud Practitioner の記事では、解説役としても登場します。


クラウドおさる



はじめましてでござる。弊猿はクラウドおさる。IT インフラが異常に発達した猿山から、雲に乗って地上の AWS を学びに降りてきたのでござる。



基礎編や Cloud Practitioner の記事では、とひさんに素朴な疑問をぶつける役でござるな。Security - Specialty などの記事では、要所でワンポイントや注意点をひとこと添えるでござる。



猿山にもデータセンターや監視当番があって、地上で起きることはだいたい猿山にも同じ仕組みがあるのでござる。例え話がつい猿山に寄るのは、弊猿の癖でござる。
技術解説
No module named のときの確認ポイント
| 確認すること | 内容 |
|---|---|
| ランタイムに入っているか | Python のランタイムには AWS SDK(boto3)などは入っているが、requests などのサードパーティのライブラリは入っていない |
| デプロイパッケージに入っているか | .zip の直下に、ライブラリのフォルダ(例: requests/)があるか |
| レイヤーに入っているか | アタッチしたレイヤーの .zip が、決まったフォルダ(Python なら python/)の下にライブラリを置いているか |
| ランタイムとアーキテクチャ | ネイティブのコードを含むライブラリは、関数のランタイムのバージョンと x86_64 / arm64 に合わせてビルドしたものか |
誤った対処
| 対処 | なぜだめか |
|---|---|
| デプロイパッケージの .zip の中に、依存ライブラリの .zip を入れる | Lambda は .zip の中の .zip を展開しない(試したところ、中の .zip はファイルのまま /var/task に置かれ、中のモジュールは import できませんでした) |
| 環境変数に依存ライブラリへの参照を書く | 環境変数はただの文字列で、ライブラリは入らない |
| コンソールのエディタでライブラリのコードを足す | 現実的ではなく、エディタはパッケージのサイズが大きいと使えない |



道具箱の中に、さらに封をした道具箱を入れても、遠征先では誰も開けてくれぬのでござる。レイヤーにするか、パッケージの直下に入れるか。この 2 つが正解でござるぞ。
Lambda レイヤーの仕組み
レイヤーは、ライブラリやカスタムランタイムなどを入れた .zip ファイルです。関数にアタッチすると、レイヤーの中身が実行環境の /opt ディレクトリに展開されます。Python の場合、.zip の中の python/ フォルダに入れたライブラリは /opt/python に置かれ、そのまま import できます。
layer.zip
└── python/
├── requests/
├── urllib3/
├── idna/
├── charset_normalizer/
└── certifi/| 項目 | 内容 |
|---|---|
| 1 つの関数にアタッチできるレイヤー | 最大 5 個 |
| サイズ | 関数とすべてのレイヤーを展開した合計が 250 MB まで |
| バージョン | レイヤーは発行するたびにバージョンが増え、各バージョンは変更できない。関数はバージョンを指定してアタッチする |
| 互換性 | 発行時に、互換性のあるランタイムとアーキテクチャを指定できる |





道具箱は一度作れば、ほかの遠征隊にも貸せるのでござる。道具を新しくしたら箱の版(バージョン)が上がり、どの版を持たせるかは隊ごとに選ぶのでござるな。
実践
requests で Web ページを取得する関数 dva-layer-fn を作り、依存ライブラリが無いためにエラーになることを確かめます。続けて CloudShell で requests を入れたレイヤー dva-requests を作り、関数にアタッチします。
前提条件
- リージョン:
ap-northeast-1(東京) - Lambda・CloudShell を操作できる IAM ユーザーまたはロールでコンソールにログイン済みであること
作成するリソース一覧
| リソース種別 | リソース名 | 用途 |
|---|---|---|
| Lambda 関数 | dva-layer-fn | requests を使う関数 |
| IAM ロール | dva-layer-fn-role-xxxxxxxx(自動作成) | 関数の実行ロール |
| Lambda レイヤー | dva-requests(バージョン 1) | requests とその依存ライブラリ |
手順 1: requests を使う関数を作る
AWS マネジメントコンソールにログインし、リージョンが ap-northeast-1(東京)であることを確認します。上部の検索バーに Lambda と入力し、「Lambda」を選択します。
「関数を作成」をクリックし、「一から作成」で以下を設定して、画面下部の「関数を作成」をクリックします。
- 関数名:
dva-layer-fn - ランタイム:
Python 3.13 - アーキテクチャ:
x86_64(「その他の設定」の「ARM64 アーキテクチャ」はオフのまま) - 実行ロール: 「その他の設定」の「カスタム実行ロール」はオフのまま(基本的な Lambda アクセス許可の実行ロールが自動で作られます)
「コード」タブのエディタで lambda_function.py を次の内容に置き換え、「Deploy」をクリックします。
import requests
def lambda_handler(event, context):
resp = requests.get("https://aws.amazon.com/", timeout=5)
return {"requests_version": requests.__version__, "status_code": resp.status_code}手順 2: テストしてエラーを確かめる
「テスト」タブで、イベント名に empty と入力し、イベント JSON は {} のまま「テスト」をクリックします。実行結果は「実行中の関数: 失敗」になり、「詳細」を開くと Runtime.ImportModuleError と No module named 'requests' が表示されます。Python 3.13 のランタイムに requests が入っておらず、デプロイパッケージにも入れていないためです。





手元の山では当たり前にあった道具が、遠征先には無かったのでござる。エラーは「道具が見つからぬ」と言っているだけで、コードの間違いではないのでござるな。
手順 3: CloudShell でレイヤーの .zip を作る
コンソール上部の CloudShell のアイコンから CloudShell を開き、python フォルダに requests を入れて .zip にします。
mkdir -p ~/layer/python && cd ~/layer
pip3 install requests -t python/
zip -r -q layer.zip python
unzip -l layer.zip | head -n 8unzip -l の結果で、すべてのファイルが python/ の下に入っていることを確認します。今回の requests とその依存ライブラリ(urllib3・idna・charset_normalizer・certifi)は、CloudShell で入れたものをそのまま使えました(手順 6 で動作を確かめます)。ネイティブのコードを含むライブラリを入れる場合は、関数のランタイムとアーキテクチャに合わせてビルドしたものを使います。
手順 4: レイヤーを発行する
CloudShell で、レイヤーを発行します。
aws lambda publish-layer-version \
--layer-name dva-requests \
--description "requests and its dependencies" \
--zip-file fileb://layer.zip \
--compatible-runtimes python3.13 \
--compatible-architectures x86_64 \
--query '{LayerVersionArn: LayerVersionArn, Version: Version}'Version が 1 のレイヤーバージョンの ARN が表示されます。Lambda のコンソールの左側ナビゲーションで「レイヤー」を選択すると、dva-requests が表示されます。


手順 5: 関数にレイヤーをアタッチする
dva-layer-fn の画面に戻り、「コード」タブの一番下の「レイヤー」セクションで「編集」をクリックします。「レイヤーを編集」の画面で「レイヤーの追加」をクリックすると、「レイヤーを選択」のダイアログが開きます。以下を選択して「追加」をクリックします。
- レイヤーソース: 「カスタムレイヤー」
- カスタムレイヤー:
dva-requests - バージョン:
1


「レイヤーを編集」の画面に戻るので、「保存」をクリックします。
手順 6: もう一度テストする
「テスト」タブで、もう一度「テスト」をクリックします。今度は成功し、requests_version に requests のバージョン、status_code に 200 が返ります。関数のコード(デプロイパッケージ)は変えていませんが、レイヤーが /opt/python に展開されたので、import requests ができるようになりました。


関数のデプロイパッケージは数百バイトのままです。ライブラリを入れ替えたいときは、新しいレイヤーのバージョンを発行して、関数にアタッチするバージョンを変えます。
まとめ
| 困りごと | 対処 |
|---|---|
No module named で関数が動かない | 依存ライブラリをデプロイパッケージの直下に入れるか、レイヤーにしてアタッチする |
| デプロイパッケージを小さく保ちたい・複数の関数で同じライブラリを使いたい | Lambda レイヤー |
| ネイティブのコードを含むライブラリが読み込めない | 関数のランタイムとアーキテクチャに合わせてビルドし直す |
- Python のランタイムに
requestsなどのサードパーティのライブラリは入っていない。入っていなければRuntime.ImportModuleErrorになる - レイヤーは .zip で、Python なら
python/フォルダの下にライブラリを置く。関数の実行環境では/opt/pythonに展開される - 1 つの関数にアタッチできるレイヤーは 5 個まで。関数とレイヤーを展開した合計は 250 MB まで
- レイヤーは発行するたびにバージョンが増え、関数はバージョンを指定してアタッチする
- .zip の中の .zip は展開されない。環境変数に参照を書いてもライブラリは入らない
参照先
- レイヤーによる Lambda 依存関係の管理 – AWS Lambda
- レイヤーコンテンツのパッケージング – AWS Lambda
- Python Lambda 関数にレイヤーを使用する – AWS Lambda
- 関数へのレイヤーの追加 – AWS Lambda
- Lambda クォータ – AWS Lambda
- Python Lambda 関数で .zip ファイルアーカイブを使用する – AWS Lambda












