👻

AWS Lambda の 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