これは何?
今までインフラエンジニアで開発経験など無かったのですが, 趣味やサーバレスの検証も兼ねてLambda Layerを初めて自分で作成し実装した際にライブラリが異なることでinvalid ELF header
エラーが発生した際の対処法になります.
調査
Pythonで、momentoライブラリを導入してLambda Layerにアップロードした際に、 invalid ELF header
エラーが発生しました.
(momentoについて詳しくはこちら)
これは、ローカルで実行しているPythonライブラリとLambdaで実行しているPythonライブラリが異なるため、ELF(Executable and Linking Format)ヘッダーの規定が異なりインポートエラーが発生しているものとなります.
{
"errorMessage": "Unable to import module 'lambda_function': /opt/python/cryptography/hazmat/bindings/_rust.abi3.so: invalid ELF header",
"errorType": "Runtime.ImportModuleError",
"requestId": "xXxXxX-xXxXxX ...",
"stackTrace": []
}
そのため, Lambdaで使用しているPythonのコンテナイメージを取得し、ローカル上でパッケージインストールする方法を後述で解説します.
imageについて
Lambdaのランタイムバージョンのイメージについてはこちらを参考にdocker pullを実行します.
実行するランタイムの最新バージョンを以下の赤線のように指定し, コピーしました.
手順
フォルダの構成は以下の通りとなります.
./path/to/dir
├── layer.zip
├── python
│ ├── momento
│ ├── momento-1.19.1.dist-info
│ │ :
│ │ :
│ │
│ ├── requests
│ └── requests-2.31.0.dist-info
└── requirements.txt
ローカルフォルダ./pythonにパッケージをインストールします.
重要なのはフォルダ名を ./python として、下にパッケージをインストールします.
これはLambdaレイヤーで使用するライブラリはpythonフォルダ上である必要があるためです.
https://repost.aws/ja/knowledge-center/lambda-import-module-error-python
重要: Python 用にインポートするライブラリを /python フォルダ内に配置します.
cd ./path/to/dir
docker run --rm -v ./:/var/task public.ecr.aws/sam/build-python3.10:1.110.0-20240222205900 \
pip install -r requirements.txt -t ./python
zipコマンドでパッケージを圧縮.
zip -r layer.zip ./python
圧縮できたら, あとはLambda Layerに圧縮ファイルをアップロードすれば準備完了です.
テスト
Lambda Layerのバージョンを更新した後, 以下のLambda関数を実行します.
MOMENTO_API_KEY は Momento Cache でRead/Write権限を持ったAPIキー.
MOMENTO_CACHE_NAME は Momento Cacheのキャッシュ名を環境変数として指定する. かもしくは)
import json
import os
from datetime import timedelta
import logging
from momento import CacheClient, Configurations, CredentialProvider
from momento.responses import CacheGet, CacheSet, CreateCache, ListCaches, CacheIncrement
import requests
MOMENTO_API_KEY = os.getenv('MOMENTO_API_KEY', 'xXxXxXxXxXxXxXxXxX')
MOMENTO_CACHE_NAME = os.getenv('MOMENTO_CACHE_NAME', 'test-cache-name')
logger = logging.getLogger()
logger.setLevel("INFO")
def lambda_handler(event, context):
client = create_cache_client()
client.set(MOMENTO_CACHE_NAME, "test-key", "test-value")
res = client.get(MOMENTO_CACHE_NAME, "test-key")
logger.info("Result: {}".format(res))
return {
'statusCode': 200,
'body': json.dumps(res)
}
def create_cache_client():
cache_client = CacheClient(
configuration=Configurations.Laptop.v1(),
credential_provider=CredentialProvider.from_environment_variable("MOMENTO_API_KEY"),
default_ttl=timedelta(seconds=600),
)
return cache_client
これでエラーが出力されなければOKになります.