SAMを使ってLambdaをコンテナイメージでデプロイした時に困ったこと

lambda_deploy_container_imageAWS

Lambdaをコンテナイメージで実行するメリット

従来のLambdaではZIPファイルをアップロードする形でZIPパッケージは解凍後に250Mが上限というような制限がありました。

コンテナイメージを使えるようになったことで、Lambda関数を最大10GBコンテナイメージとしてパッケージ化し、デプロイできるようになりました。

またローカルで動かしたものをそのままLambda上で動かせるというのもメリットだと思います。

事前に大容量ファイルをコンテナイメージ内に含めることができるというのも良い点です。

私が今回コンテナイメージのLambdaを使ったのもこれが実現できることが理由でした。

詰まったポイント

コンテナイメージでLayer(Extension)を使う

通常LambdaではLayerを使用する際にコンソール画面から選択したりすることで簡単に指定することができましたが、コンテナイメージを使った場合にはこのような方法では実現できません。

使用するLayerのバイナリをコンテナイメージの/opt/extensions配下に含めることで実現できます。

Lambda は /opt/extensions ディレクトリを検索し、見つかった全ての拡張機能の初期化を開始します。拡張機能は、バイナリまたはスクリプトとして実行可能である必要があります。関数コードディレクトリは読み取り専用であるため、拡張機能は関数コードを変更できません。

コンテナイメージ内でLambda レイヤーと拡張機能を動作させる

Parameter and Secret Lambda Extensionを使う場合を考えます。

Parameter and Secret Lambda ExtensionはLambdaから直接Parameter StoreやSecrets Managerの値を取得することができる拡張機能です。
以前はSDK経由で直接APIを叩いていましたが、今ではキャッシュなどもよしなにしてくれる便利なものです。

まずはExtensionのバイナリを取得します。

Parameter and Secret Lambda ExtensionのARNは公式ドキュメントに記載されており、東京リージョンは以下になります。

arn:aws:lambda:ap-northeast-1:133490724326:layer:AWS-Parameters-and-Secrets-Lambda-Extension:4
arn:aws:lambda:ap-northeast-1:133490724326:layer:AWS-Parameters-and-Secrets-Lambda-Extension-Arm64:4

以下のコマンドでzip形式のファイルをダウンロードします。
今回はarm64のLambdaを使うためArm64の方をダウンロードします。

curl $(aws lambda get-layer-version-by-arn --arn arn:aws:lambda:ap-northeast-1:133490724326:layer:AWS-Parameters-and-Secrets-Lambda-Extension-Arm64:4 --query 'Content.Location' --output text) --output layer.zip 

ダウンロードしたzipを使ってDockerfileでコンテナイメージを作成します。

FROM public.ecr.aws/lambda/python:3.9 as layer
RUN yum install -y unzip
RUN mkdir -p /opt
ADD layer.zip ./
RUN unzip layer.zip -d /opt

###############################################
FROM public.ecr.aws/lambda/python:3.9
COPY ./requirements.txt ${LAMBDA_TASK_ROOT}
ENV HNSWLIB_NO_NATIVE=1
RUN pip install -r requirements.txt
WORKDIR /opt/extensions
COPY --from=layer /opt/extensions .
WORKDIR ${LAMBDA_TASK_ROOT}
COPY app.py ${LAMBDA_TASK_ROOT}
CMD ["app.lambda_handler"]

まずLayer用のステージでダウンロードしたParameter and Secret Lambda Extensionのバイナリのzipを/optに展開します。

そして最終的に作成されるイメージの/opt/extensionsにlayerステージで展開した/opt/extensionsをコピーします。

これでParameter and Secret Lambda Extensionが使えるようになったのであとはLambda関数の中で実際にSecret Managerの値を取得します。

サンプルコード

def get_secrets():
    end_point = 'http://localhost:2773/secretsmanager/get'
    secret_key = os.environ['SECRET_KEY']
    url = f'{end_point}?secretId={secret_key}'
    headers = {
        'X-Aws-Parameters-Secrets-Token': os.environ['AWS_SESSION_TOKEN']
    }
    
    res = requests.get(url, headers=headers)
    res.raise_for_status()
    secret_string = json.loads(res.text)['SecretString']

SAMで複数関数のデプロイ

SAMで複数のLambda関数をデプロイする際に、テンプレートでImageUriを指定しているのに1つのリポジトリ(ECR)にプッシュされてしまいました。

原因としてはSAMの理解が浅かったというところになりますが、samconfig.tomlimage_repositoriesに各関数のプッシュ先のリポジトリを指定してあげることで解決しました。

samconfig.tomlとはAWS SAM CLIの設定ファイルであり、samコマンドを使用する際の設定が定義されたファイルです。

sam deploy --guided コマンドを使用した際にプロジェクトのルートディレクトリに自動生成されます。

以下のようにどのような値を設定するかを聞いてくるので適切な値を設定します。

以下のような内容のファイルが生成されます。

version = 0.1

[default.deploy]
[default.deploy.parameters]
stack_name = "lambda-default"
s3_bucket = "aws-sam-cli-managed-bucket"
s3_prefix = "lambda-default"
region = "ap-northeast-1"
capabilities = "CAPABILITY_NAMED_IAM"
image_repositories = []

ここのimage_repositoriesに各関数名と格納するリポジトリを紐づけてあげます。

image_repositories = [
    "Lambda1={accountId}.dkr.ecr.ap-northeast-1.amazonaws.com/{repositoryName1}",
    "Lambda2={accountId}.dkr.ecr.ap-northeast-1.amazonaws.com/{repositoryName2}",
]

このように設定した上で改めてsam deployをするとsamconfig.tomlの内容を元にデプロイされるため、各リポジトリに対して関数がデプロイされます。

参考

コンテナイメージ内でLambda レイヤーと拡張機能を動作させる

コンテナイメージを使った Lambda 関数のあれこれ

AWS SAM CLI の設定ファイル

コメント

タイトルとURLをコピーしました