👻

AWS Lambda PythonのLayer定義(マージ順序)には気をつけよう👻

に公開

まとめ🚀

  • AWS Lambdaにはライブラリを様々な手段で追加可能
  • 別の手段で異なるversionの同一ライブラリを複数追加した時にはロード順に注意が必要
  • Lambda Layerにはマージ順序というものが存在する
  • Layerのマージ順序によりライブラリは後勝ちで追加される

Lambda Layerにはマージ順序という概念がある💭

最初にネタバラシと伝えたいことから。マージ順序という概念があります。
これは数字が大きい方へ順にライブラリがデプロイパッケージに追加・反映されます。

もし同一ライブラリの異なるバージョンを異なるLayerから追加する状況があった場合、
Lambdaからimportなどでロードすると後勝ち的にマージ順序が後のLayerに含まれるライブラリのversionを利用します。
マージ順序とは

補足)Layer以外のライブラリのロードについて💿

色々考えるにあたって1つ目に留まった記事があります。
https://dev.classmethod.jp/articles/lambda-python-libary-order/

こちらの記事より引用すると、
LambdaにおけるLayerも含めたPythonのライブラリのロード順序は次のとおりです。

  1. デプロイパッケージ
  2. Layerのpython/lib/python3.8/site-packages/
  3. Layerのpython
  4. デフォルトで入っているライブラリ

「LambdaにおけるPythonライブラリのロード順序を調べてみる」classmetod 夏目祐樹 より

なかなか無い着眼ポイントでしたので大変ためになりました。

事例)Pydantic v2を導入しようとしてもとにかくv1になる📔

ここからは日記のようなものです。

序章: Pydantic v2を使うぞ

Lambda Layerは自作はもちろん、公開されているものもいくつかあります。
私の職場でメジャーなものは AWS Lambda Powertools for Python です。
こちらは近年Version.3が公開されました。

Lambda PowertoolsではAPIエンドポイントのパスルーティングやValidation、
Parserなどの機能を有しており、Pydantic等が依存関係としてインストールされるます。
Lambda PowertoolsのバージョンとPydanticについては次の関係がありました。

Lambda Powertools v2

  • Pydantic v1が標準の依存関係でインストールされる (v1 with Pydantic v2)
  • Pydantic v2も別途インストールすればoptionalに利用可能 (v2 with Pydantic v2)

Lambda Powertools v3

  • Pydantic V2が標準の依存関係でインストールされる (v3 with pydantic v2)

当時のプロジェクトではまだLambda Powertools V2を採用していました。
後々を考えてPydantic V2を利用するべきということでPowertools v3 with Pydantic v2の組み合わせにすべくに自作のLambda Layerを作成し、そちらにPydantic v2を追加するアプローチを取ることにしました。

本章: なんかずっとPydantic v1の書き方しかできない

AWS Lambda Powertools for Python のLayerはARNが公開されておりそのまま追加できます。
これと別に仮名ですがPydanticV2Layerというのを作成しました。

IaCではAWS SAM templateを利用して次のように記載しています。
一部抜粋です。

  SampleFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: !Sub "${Environment}-sample-function"
      PackageType: Zip
      CodeUri: src/
      Handler: app.lambda_handler
      Role: !GetAtt LambdaExecutionRole.Arn
      Timeout: 45
      MemorySize: 512
      Runtime: python3.12
      VpcConfig:
        SecurityGroupIds:
         -SubnetIds:
         -Layers:
        - !Ref PydanticV2Layer
        - !Sub "arn:aws:lambda:${AWS::Region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:77"
      Environment:
        Variables:
          ENVIRONMENT: !Ref Environment

さて、これでdeployすると、Pydanticのimportはできるのですが「どうもv1を使ってるな」に陥りました。

本記事の最初に書いた通りLayersプロパティの順番が悪さしているんですよね。

pydantic v2を反映 --> Lambda Powertools(内部的にはpydantic v1)を反映

ということが起きてます。

終章: めっちゃ初歩なやつか、これ

不思議だなぁと思って原因を探っていたのですがマネジメントコンソールも覗いてみることにしました。
それで「マージ順序」というキーワードが目につきます。
IaCに気持ちが寄りすぎて知識から抜け落ちてたんですよね。

よってSAM templateは次のように直しました。

  SampleFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: !Sub "${Environment}-sample-function"
      PackageType: Zip
      ~(略)~
      VpcConfig:
        SecurityGroupIds:
         -SubnetIds:
         -Layers:
        - !Sub "arn:aws:lambda:${AWS::Region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:77"
        - !Ref PydanticV2Layer  # ⭐️後勝ちの関係を意識して後ろにした
      Environment:
        Variables:
          ENVIRONMENT: !Ref Environment

ただし、このままではsamは差分の変更として扱わず更新が行われませんでした。
明確に差分として更新させるために、MemorySizeの変更などで対処しました。

慢心せず変更が入ったことまで確認は大事ですね。

Discussion