のべラボ.blog

謙虚に、臆せず、さぼらずにブログを書く

AWS SAM における Lambda 関数のエイリアスとバージョンの重みづけについて

以前から、AWS SAM を使用して AWS Lambda 関数のエイリアスとバージョンの重みづけを設定したいなーと思ってました。

ただ、結論から言うと、AWS SAM 仕様では重みづけの設定する方法は無く、AWS CloudFormation のプロパティとして設定する必要があることがわかりました。


エイリアスやバージョンの発行、という観点だと、AWS SAM では、AutoPublishAlias というプロパティを使用できます。

docs.aws.amazon.com

このプロパティの指定だけで、AWS Lambda 関数のエイリアスと、それに関連付けられた AWS Lambda 関数のバージョンが自動的に発行されます。

AutoPublishAlias を指定している場合、SAM でスタックが作成、更新される都度に新しいバージョンが発行され、エイリアスの重みづけは常に 100% に設定されます。

AutoPublishAlias を指定したうえで、さらに DeploymentPreference というプロパティを指定すると、 新バージョンへの段階的な移行が可能になり、例えば Canary や Linear などの手法で、時間の経過とともに新バージョンに振り分けるトラフィック量を自動的に変更したり、CloudWatch Alarm と連動させて、デプロイをロールバックすることもできます。 docs.aws.amazon.com

この AutoPublishAliasDeploymentPreference は、非常に強力かつ便利ではあるのですが、意外に単純なことができないことがわかりました。

それは、Canary や Linear などの手法ではなく、単に エイリアスと複数バージョンの重みづけを設定すること です。これができないのです。

例えば、次のような手順を AWS SAM AutoPublishAliasDeploymentPreference を指定して 行うことはできません。

  1. AWS Lambda関数の バージョン 1を発行する。エイリアスと関連付けて重みづけは 100% とする。
  2. AWS Lambda関数のコードを更新して、バージョン 2を発行する。エイリアスで、バージョン 1 は90%、バージョン 2は 10%の重みづけにする。
  3. AWS Lambda関数の バージョン 2の、エイリアスて重みづけを 100% とする。

DeploymentPreference で Canary や Linear を指定するのも便利ですが、その場合は、あくまであらかじめ用意されている、Canary10Percent15Minutes や Linear10PercentEvery10Minutes というタイプから選ぶしかありません。

トラフィックを移行する割合を手動で指定したい、自分でトラフィックを切り替えるタイミングを制御したいという場合は、AWS SAM 仕様のプロパティでは対応するものが無いのです。


では、どうすればいいかというと、AWS CloudFormation のプロパティで指定する、という方法があります。

まず、AWS Lambda 関数の バージョン 1を発行し、エイリアスと関連付けて重みづけは 100% とするための SAM テンプレートの例をみてみましょう。

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Globals:
  Function:
    Timeout: 3
Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function 
    Properties:
      FunctionName: HelloSAMAliasVersion
      CodeUri: hello_world/
      Handler: app.lambda_handler
      Runtime: python3.7
  HelloWorldFunctionVersion1:
      Type: AWS::Lambda::Version
      Properties:
        FunctionName: !Ref HelloWorldFunction
        Description: v1
  HelloWorldFunctionAlias:
      Type: AWS::Lambda::Alias
      Properties:
        FunctionName: !Ref HelloWorldFunction
        FunctionVersion: !GetAtt HelloWorldFunctionVersion1.Version
        Name: live

SAM テンプレートとして記載していますが、AWS Lambda 関数のエイリアスやバージョンを発行する指定は、Type: AWS::Lambda::VersionType: AWS::Lambda::Alias と、AWS CloudFormation のプロパティを指定しています。

このSAM テンプレートを使用して デプロイし、スタックを作成すると、エイリアス名 live で、最初のバージョンが重みづけ 100% で設定された状態になります。

https://cdn-ak.f.st-hatena.com/images/fotolife/n/neob/20220821/20220821114557.png


次に、AWS Lambda 関数のコードを更新し、新しいバージョン 2として、エイリアス live に 10% の重みづけでトラフィックを振り分けたいとします。

その場合は、SAM テンプレートを次のように変更してデプロイします。

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Globals:
  Function:
    Timeout: 3
Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function 
    Properties:
      FunctionName: HelloSAMAliasVersion
      CodeUri: hello_world/
      Handler: app.lambda_handler
      Runtime: python3.7

  HelloWorldFunctionVersion1:
      Type: AWS::Lambda::Version
      Properties:
        FunctionName: !Ref HelloWorldFunction
        Description: v1
  HelloWorldFunctionVersion2:
      Type: AWS::Lambda::Version
      Properties:
        FunctionName: !Ref HelloWorldFunction
        Description: v2
  HelloWorldFunctionAlias:
      Type: AWS::Lambda::Alias
      Properties:
        FunctionName: !Ref HelloWorldFunction
        FunctionVersion: !GetAtt HelloWorldFunctionVersion1.Version
        Name: live
        RoutingConfig:
          AdditionalVersionWeights:
            - FunctionVersion: !GetAtt HelloWorldFunctionVersion2.Version
              FunctionWeight: 0.1

デプロイが完了すると、バージョン 1が 90%、バージョン 2が 10% になったことを確認できます。

https://cdn-ak.f.st-hatena.com/images/fotolife/n/neob/20220821/20220821114603.png


この後、バージョン 2 へ完全に切り替えたい場合は、このテンプレートの FunctionWeight: で指定する値を 1.0 変更してデプロイします。

https://cdn-ak.f.st-hatena.com/images/fotolife/n/neob/20220821/20220821114608.png


これらの プロパティについては、次のドキュメントも参考にしてください。

docs.amazonaws.cn


AWS CloudFormation のプロパティを使うと、自分で重みづけや、切り替えタイミングを制御できるのはいいのですが、プロパティの記載量はどうしても増えます。

SAM であれば、次のように、AWS Lambda 関数 で AutoPublishAlias を指定しつつ、さらに API GatewayAPIとの統合もシンプルに指定できます。

 HelloWorldFunction:
    Type: AWS::Serverless::Function 
    Properties:
      CodeUri: hello_world/
      Handler: app.lambda_handler
      Runtime: python3.7
      AutoPublishAlias: live
      Events:
        HelloWorld:
          Type: Api 
          Properties:
            Path: /hello
            Method: get

ただし、AWS CloudFormation のプロパティでエイリアスやバージョンを手動指定すると、このような書き方はできません。

なので、AutoPublishAlias で、自分でバージョンの重みづけができればいいのですが、それは現状ではサポートされていないようです。

(次の issue にも Feature request として挙げられていましたが、DeploymentPreferences を使いなさい、と close されてました。)

github.com

ちょっと残念ですね。