aws / serverless-application-model

The AWS Serverless Application Model (AWS SAM) transform is a AWS CloudFormation macro that transforms SAM templates into CloudFormation templates.
https://aws.amazon.com/serverless/sam
Apache License 2.0
9.36k stars 2.39k forks source link

Feature request - Lambda alias shifting #777

Open milancermak opened 5 years ago

milancermak commented 5 years ago

This is a feature request to "do something like AutoPublishAlias, but better." Basically, I would like SAM to support exactly the process that is described in #298 and #304.

Before I actually used AutoPublishAlias when deploying, my mental model of how it works was just as described in those issues. I did not expect deploying a new stage would remove the previous stage alias. I'm clearly not the only one who thought so. This is also the documented way on how to use aliases. Absolutely no document I read mentioned the best practice of separating stages to stacks. Not even in Safe Lambda deployments.

Now of course I read the detailed and helpful explanation by @jfuss on these best practices. I see the value in it. But I also think there's merit to the original, intended way of having multiple aliases pointing to different version in the same stack. @jimcatts has a pretty convincing use case. I have another one - I have a hobby Alexa skill. It's really an overkill and needlessly complex to have 2 stacks (beta & prod) for an Alexa skill. I'm never going to run into the issues @jfuss mentioned are inherent in using the same stack for multiple stages.

Also, what is then the use case for AutoPublishAlias when the alias is pretty much always pointing to $LATEST. Or am I missing something?

So I would ask the following: 1) Please update the documentation with best practices around multi-stage deployments, use of AutoPublishAlias where it's clearly mentioned it removes the "previous" alias, etc. 2) Please consider making the functionality mentioned above into a feature.

joshuacura commented 5 years ago

this is also useful for rolling back published versions (thru selecting just the alias) which I think is easier instead of repointing the alias to another version

jimcatts commented 5 years ago

In the meantime I solved this problem for our needs by post-processing the output of SAM. After we’ve run our templates through a cloudformation package we execute some custom code that goes through the template looking for SAM functions where AutoPublishAlias is true. When we find one we run this function to get the psuedo-random suffix that SAM appends to the end of the FunctionVersion that ultimately add into the template when it’s deployed:

function getHashSuffix(codeUri) {
    const match = codeUri.match(/s3:\/\/([.a-zA-Z0-9_-]+?)\/([.a-zA-Z0-9_/-]+)/);

    const codeObject = {
          "S3Bucket": match[1],
          "S3Key": match[2]
        };

    const code = utf8.encode(JSON.stringify(codeObject));

    return crypto.createHash('sha1').update(code).digest('hex').slice(0,10);
}

Once we have the suffix, we can create our own aliases pointing at that version like:

template.Resources[`${r}Alias${suffix}`] = {
                DeletionPolicy: 'Retain',
                Properties: {
                    FunctionName: {
                        Ref: r
                    },
                    FunctionVersion: {
                        'Fn::GetAtt': [
                            `${r}Version${suffix}`,
                            'Version'
                        ]
                    },
                    Name: packageData.deploy_version.replace(/\./g, '_')
                },
                Type: 'AWS::Lambda::Alias'
            }

The key element here is the DeletionPolicy: ‘Retain’ and the fact that the alias is using the semantic version of our package replacing ‘.’ with ‘_’. The next time the template is deployed the hash will change and the new alias with the new version number will be created and even though the old one won’t be in the template anymore, it won’t be deleted because of the DeletionPolicy.

So every time we deploy two aliases are created both pointing at the new version - the numeric one SAM uses internally and for traffic shifting, and ours which other microservices can then reference.

keetonian commented 5 years ago

@milancermak thank you for bringing this up. I agree that we should update the documentation to more clearly state these "best practice" decisions that SAM makes. Your other point is also a good one; I agree that we need to better support the "best practice" way of making Alexa Skills.

I haven't made an Alexa skill yet, could you post some of the thing you have found that are necessary when making an Alexa skill in SAM? I would like to be able to add features and documentation around this to make it easier for SAM users, as I have seen several people trying to do this. What are best practices that you have seen, and could we update or add examples that would make this process easier?

milancermak commented 5 years ago

I don't know of any Alexa specific best practices when it comes to SAM. A skill is "just another Lambda function." However having the aliases behaviour as I've described in the issue would help.

rinatio commented 5 years ago

There's an example in AWS blog: https://aws.amazon.com/blogs/compute/using-api-gateway-stage-variables-to-manage-lambda-functions/ which shows how to use stage variables to refer to Lambda alias. There're probably other examples and tools using this approach and it seems to make sense especially in small applications and APIs.

Also examples on AWS docs page for AWS::Lambda::Alias show that you can use aliases for PROD and TestingForMyApp.

rinatio commented 5 years ago

And seems like there's almost no difference between having

AutoPublishAlias: "staging"

and

  StagingAlias:
    Type: AWS::Lambda::Alias
    Properties:
      FunctionName: !Ref MyLambdaFunction
      FunctionVersion: $LATEST
      Name: "staging"

except first one points to a version which is $LATEST anyway.

tmiklu commented 3 years ago

@rinatio AutoPublishAlias should be used with DeploymentPreference for SAM and traffic shifting

CGarces commented 1 month ago

I'm trying to find a workaround

With the template from the HelloWorld sample I have added a AWS::Lambda::Version and AWS::Lambda::Alias resources

The internal version of my development is passed as parameter like sam deploy --parameter-overrides InternalVersion=v20241017

But for some reason not works... the lambda code is updated but the AWS::Lambda::Version is not triggered in the change set?? So the tag is incorrectly applied to another version

Any clue? I can't see the error in my workaround

AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: >
  sam-demo

  Sample SAM Template for sam-demo

Parameters:
  InternalVersion:
    Description: Internal version date
    Type: String
    Default: vYYYYMMDD

# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
  Function:
    Timeout: 3

Resources:
  LastVersion:
    Type: AWS::Lambda::Version
    DeletionPolicy: Retain
    UpdateReplacePolicy: Retain
    Properties:
      FunctionName: !Ref HelloWorldFunction

  ProdAlias:
    Type: AWS::Lambda::Alias
    DeletionPolicy: Retain
    UpdateReplacePolicy: Retain
    Properties:
      FunctionName: !Ref HelloWorldFunction
      FunctionVersion: !GetAtt LastVersion.Version
      Name: !Ref InternalVersion

  HelloWorldFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      CodeUri: hello_world/
      Handler: app.lambda_handler
      Runtime: python3.11
      Architectures:
        - x86_64
      Events:
        HelloWorld:
          Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
          Properties:
            Path: /hello
            Method: get