Lambda で No module named エラーになるときの確認ポイント ── 依存ライブラリをレイヤーにまとめる

目次

概要

手元では動いていたコードを Lambda にデプロイしたら、Runtime.ImportModuleError: Unable to import module 'lambda_function': No module named 'requests' のようなエラーで動かないことがあります。手元の環境にはライブラリが入っていても、Lambda の実行環境には入っていないからです。デプロイパッケージを小さくしようとして、依存ライブラリを入れずにアップロードした場合も同じことが起きます。

依存ライブラリを関数のコードと分けて持たせる仕組みが Lambda レイヤーです。ライブラリを決まったフォルダ構成の .zip にまとめてレイヤーとして発行し、関数にアタッチすると、デプロイパッケージに含めなくてもコードから import できます。同じレイヤーを複数の関数で使い回すこともできます。

この記事では、No module named エラーのときの確認ポイントと、レイヤーの仕組み(フォルダ構成・上限・バージョン)を整理します。実践では、requests を使う関数がエラーになることを確かめ、CloudShell でレイヤーを作って関数にアタッチし、動くようにします。

依存ライブラリをレイヤーにまとめ、関数にアタッチして /opt から読み込ませる
依存ライブラリをレイヤーにまとめ、関数にアタッチして /opt から読み込ませる
クラウドおさる

自分の山では使えた道具を、遠征先の山に持って行き忘れると、作業が始まらぬでござる。道具箱(レイヤー)にまとめて、遠征隊(関数)に持たせればよいのでござるよ。

この記事のメリット

  • 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 まで
バージョンレイヤーは発行するたびにバージョンが増え、各バージョンは変更できない。関数はバージョンを指定してアタッチする
互換性発行時に、互換性のあるランタイムとアーキテクチャを指定できる
レイヤーの .zip の python/ が、実行環境の /opt/python に展開される
レイヤーの .zip の python/ が、実行環境の /opt/python に展開される
クラウドおさる

道具箱は一度作れば、ほかの遠征隊にも貸せるのでござる。道具を新しくしたら箱の版(バージョン)が上がり、どの版を持たせるかは隊ごとに選ぶのでござるな。

新卒・未経験の方へ
クラウドおさる

AWS を仕事にしたい方へ ── 新卒・未経験から

この記事を書いているのは、AWS の設計・構築を仕事にしている現役エンジニアです。運営元の株式会社ジェニュインでは、新卒・未経験の方も募集しています。異業種や文系からインフラエンジニアになり活躍しているメンバーもいます。

  • ✓システム開発・WEB エンジニア / インフラ構築などの職種で、未経験から応募できます
  • ✓未経験から入社した社員の声を含む社員アンケートを掲載
  • ✓応募フォームから 1 分で応募できます

実践

ハンズオンガイド8 ステップ画面 4 枚

コンソールの横に開いて、チェックしながら進めましょう。

ガイドを開く

別タブで開きます。チェックはこのブラウザに保存されます。

requests で Web ページを取得する関数 dva-layer-fn を作り、依存ライブラリが無いためにエラーになることを確かめます。続けて CloudShell で requests を入れたレイヤー dva-requests を作り、関数にアタッチします。

前提条件

  • リージョン: ap-northeast-1(東京)
  • Lambda・CloudShell を操作できる IAM ユーザーまたはロールでコンソールにログイン済みであること

作成するリソース一覧

リソース種別リソース名用途
Lambda 関数dva-layer-fnrequests を使う関数
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 が入っておらず、デプロイパッケージにも入れていないためです。

テストの実行結果(Runtime.ImportModuleError と No module named 'requests')
テストの実行結果(Runtime.ImportModuleError と No module named ‘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 8

unzip -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 が表示されます。

Lambda のレイヤーの一覧(dva-requests のバージョン 1)
Lambda のレイヤーの一覧(dva-requests のバージョン 1)

手順 5: 関数にレイヤーをアタッチする

dva-layer-fn の画面に戻り、「コード」タブの一番下の「レイヤー」セクションで「編集」をクリックします。「レイヤーを編集」の画面で「レイヤーの追加」をクリックすると、「レイヤーを選択」のダイアログが開きます。以下を選択して「追加」をクリックします。

  • レイヤーソース: 「カスタムレイヤー」
  • カスタムレイヤー: dva-requests
  • バージョン: 1
レイヤーを選択のダイアログ(カスタムレイヤー dva-requests のバージョン 1)
レイヤーを選択のダイアログ(カスタムレイヤー dva-requests のバージョン 1)

「レイヤーを編集」の画面に戻るので、「保存」をクリックします。

手順 6: もう一度テストする

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

テストの実行結果(requests のバージョンと status_code 200 が返る)
テストの実行結果(requests のバージョンと status_code 200 が返る)

関数のデプロイパッケージは数百バイトのままです。ライブラリを入れ替えたいときは、新しいレイヤーのバージョンを発行して、関数にアタッチするバージョンを変えます。

まとめ

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

参照先

キャリア相談
クラウドおさる

独学の先に、AWS の仕事がある ── ジュニアおさるのキャリア相談

CLF には受かったけれど、次に何をすればいいのか分からない。独学で AWS を学ぶジュニアおさるの相談に、株式会社ジェニュイン代表の土肥が答えています。

  • ✓資格はゴールか、CLF の次に何から手を付けるか
  • ✓EC2 の次に作ってみる構成と、IaC・コスト管理
  • ✓実務経験ゼロでも、面接で話せる「これまで」の作り方
新卒・未経験の方へ
クラウドおさる

AWS を仕事にしたい方へ ── 新卒・未経験から

この記事を書いているのは、AWS の設計・構築を仕事にしている現役エンジニアです。運営元の株式会社ジェニュインでは、新卒・未経験の方も募集しています。異業種や文系からインフラエンジニアになり活躍しているメンバーもいます。

  • ✓システム開発・WEB エンジニア / インフラ構築などの職種で、未経験から応募できます
  • ✓未経験から入社した社員の声を含む社員アンケートを掲載
  • ✓応募フォームから 1 分で応募できます

この記事が気に入ったら
フォローしてね!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

株式会社ジェニュイン(https://genuine-pt.jp)の代表取締役.

2018年〜インフラエンジニアとしてキャリアをスタートし、オンプレミスのネットワーク・サーバ環境で3年半、クラウド環境で4年半の8年間エンジニアとして従事。
2021年に佐藤氏の創業した株式会社Luxyを引き継ぎ、代表に就任。
その後アガルートグループ内の株式会社ジェニュインと合併。
合併後同社代表に就任。

目次