This repository has been archived by the owner on Mar 10, 2023. It is now read-only.
forked from davidgf/serverless-plugin-canary-deployments
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Enable canary deployment capabilities for Lambda functions triggered …
…by API Gateway
- Loading branch information
Showing
19 changed files
with
2,342 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
root = true | ||
|
||
[*] | ||
indent_style = space | ||
indent_size = 2 | ||
end_of_line = lf | ||
charset = utf-8 | ||
trim_trailing_whitespace = true | ||
insert_final_newline = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
{ | ||
"env": { | ||
"mocha": true | ||
}, | ||
"extends": "airbnb", | ||
"rules": { | ||
"comma-dangle": [ | ||
2, | ||
"never" | ||
], | ||
"array-bracket-spacing": 0, | ||
"object-curly-spacing": 0, | ||
"object-curly-newline": 0 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
node_modules |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
ISC License | ||
|
||
Copyright (c) 2018, David García Fernández | ||
|
||
Permission to use, copy, modify, and/or distribute this software for any | ||
purpose with or without fee is hereby granted, provided that the above | ||
copyright notice and this permission notice appear in all copies. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
# Serverless Plugin Canary Deployments | ||
|
||
A Serverless plugin to implement canary deployments of Lambda functions, making use of the [traffic shifting feature](https://docs.aws.amazon.com/lambda/latest/dg/lambda-traffic-shifting-using-aliases.html) in combination with [AWS CodeDeploy](https://docs.aws.amazon.com/lambda/latest/dg/automating-updates-to-serverless-apps.html) | ||
|
||
## Installation | ||
|
||
`npm i --save-dev serverless-plugin-canary-deployments` | ||
|
||
## Usage | ||
|
||
To enable gradual deployments for Lambda functions, your `serverless.yml` should look like this: | ||
|
||
```yaml | ||
service: canary-deployments | ||
provider: | ||
name: aws | ||
runtime: nodejs6.10 | ||
iamRoleStatements: | ||
- Effect: Allow | ||
Action: | ||
- codedeploy:* | ||
Resource: | ||
- "*" | ||
|
||
plugins: | ||
- serverless-plugin-canary-deployments | ||
|
||
functions: | ||
hello: | ||
handler: handler.hello | ||
events: | ||
- http: GET hello | ||
deploymentSettings: | ||
type: Linear10PercentEvery1Minute | ||
alias: Live | ||
preTrafficHook: preHook | ||
postTrafficHook: postHook | ||
alarms: | ||
- FooAlarm | ||
- BarAlarm | ||
preHook: | ||
handler: hooks.pre | ||
postHook: | ||
handler: hooks.post | ||
``` | ||
|
||
## Configuration | ||
|
||
* `type`: (required) defines how the traffic will be shifted between Lambda function versions. It must be one of the following: | ||
- `Canary10Percent5Minutes`: shifts 10 percent of traffic in the first increment. The remaining 90 percent is deployed five minutes later. | ||
- `Canary10Percent10Minutes`: shifts 10 percent of traffic in the first increment. The remaining 90 percent is deployed 10 minutes later. | ||
- `Canary10Percent15Minutes`: shifts 10 percent of traffic in the first increment. The remaining 90 percent is deployed 15 minutes later. | ||
- `Canary10Percent30Minutes`: shifts 10 percent of traffic in the first increment. The remaining 90 percent is deployed 30 minutes later. | ||
- `Linear10PercentEvery1Minute`: shifts 10 percent of traffic every minute until all traffic is shifted. | ||
- `Linear10PercentEvery2Minutes`: shifts 10 percent of traffic every two minutes until all traffic is shifted. | ||
- `Linear10PercentEvery3Minutes`: shifts 10 percent of traffic every three minutes until all traffic is shifted. | ||
- `Linear10PercentEvery10Minutes`: shifts 10 percent of traffic every 30 minutes until all traffic is shifted. | ||
* `alias`: (required) name that will be used to create the Lambda function alias. | ||
* `preTrafficHook`: (optional) validation Lambda function that runs before traffic shifting. It must use te CodeDeploy SDK to notify about this step's success or failure (more info [here](https://docs.aws.amazon.com/codedeploy/latest/userguide/reference-appspec-file-structure-hooks.html)). | ||
* `postTrafficHook`: (optional) validation Lambda function that runs after traffic shifting. It must use te CodeDeploy SDK to notify about this step's success or failure (more info [here](https://docs.aws.amazon.com/codedeploy/latest/userguide/reference-appspec-file-structure-hooks.html)) | ||
* `alarms`: (optional) list of CloudWatch alarms. If any of them is triggered duringt the deployment, the associated Lambda function will automatically roll back to the previous version. | ||
|
||
## How it works | ||
|
||
The plugin relies on the [AWS Lambda traffic shifting feature](https://docs.aws.amazon.com/lambda/latest/dg/lambda-traffic-shifting-using-aliases.html) to balance traffic between versions and [AWS CodeDeploy](https://docs.aws.amazon.com/lambda/latest/dg/automating-updates-to-serverless-apps.html) to automatically update its weight. It modifies the `CloudFormation` template generated by [Serverless](https://github.com/serverless/serverless), so that: | ||
|
||
1. It creates a Lambda function Alias for each function with deployment settings. | ||
2. It creates a CodeDeploy Application and adds a [CodeDeploy DeploymentGroup](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-codedeploy-deploymentgroup.html) per Lambda function, according to the specified settings. | ||
3. It modifies events that trigger Lambda functions, so that they invoke the newly created alias. | ||
|
||
## Limitations | ||
|
||
For now, the plugin only works with Lambda functions invoked by API Gateway. More events will be added soon. | ||
|
||
## License | ||
|
||
ISC © [David García](https://github.com/davidgf) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,268 @@ | ||
{ | ||
"AWSTemplateFormatVersion": "2010-09-09", | ||
"Resources": { | ||
"MyFunction": { | ||
"Type": "AWS::Lambda::Function", | ||
"Properties": { | ||
"Code": { | ||
"S3Bucket": "aws-nodejs-dev-serverlessdeploymentbucket-14iyutt06erm7", | ||
"S3Key": "serverless/aws-nodejs/dev/1515768309333-2018-01-12T14:45:09.333Z/aws-nodejs.zip" | ||
}, | ||
"Handler": "handler.hello", | ||
"Role": { | ||
"Fn::GetAtt": [ | ||
"MyFunctionRole", | ||
"Arn" | ||
] | ||
}, | ||
"Runtime": "nodejs6.10", | ||
"Tags": [ | ||
{ | ||
"Value": "SAM", | ||
"Key": "lambda:createdBy" | ||
} | ||
] | ||
} | ||
}, | ||
"CodeDeployServiceRole": { | ||
"Type": "AWS::IAM::Role", | ||
"Properties": { | ||
"ManagedPolicyArns": [ | ||
"arn:aws:iam::aws:policy/service-role/AWSCodeDeployRoleForLambda" | ||
], | ||
"AssumeRolePolicyDocument": { | ||
"Version": "2012-10-17", | ||
"Statement": [ | ||
{ | ||
"Action": [ | ||
"sts:AssumeRole" | ||
], | ||
"Effect": "Allow", | ||
"Principal": { | ||
"Service": [ | ||
"codedeploy.amazonaws.com" | ||
] | ||
} | ||
} | ||
] | ||
} | ||
} | ||
}, | ||
"MyFunctionRole": { | ||
"Type": "AWS::IAM::Role", | ||
"Properties": { | ||
"ManagedPolicyArns": [ | ||
"arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" | ||
], | ||
"AssumeRolePolicyDocument": { | ||
"Version": "2012-10-17", | ||
"Statement": [ | ||
{ | ||
"Action": [ | ||
"sts:AssumeRole" | ||
], | ||
"Effect": "Allow", | ||
"Principal": { | ||
"Service": [ | ||
"lambda.amazonaws.com" | ||
] | ||
} | ||
} | ||
] | ||
} | ||
} | ||
}, | ||
"MyFunctionVersione57c0000de": { | ||
"DeletionPolicy": "Retain", | ||
"Type": "AWS::Lambda::Version", | ||
"Properties": { | ||
"FunctionName": { | ||
"Ref": "MyFunction" | ||
} | ||
} | ||
}, | ||
"MyOtherFunctionVersione57c0000de": { | ||
"DeletionPolicy": "Retain", | ||
"Type": "AWS::Lambda::Version", | ||
"Properties": { | ||
"FunctionName": { | ||
"Ref": "MyOtherFunction" | ||
} | ||
} | ||
}, | ||
"MyFunctionDeploymentGroup": { | ||
"Type": "AWS::CodeDeploy::DeploymentGroup", | ||
"Properties": { | ||
"ApplicationName": { | ||
"Ref": "ServerlessDeploymentApplication" | ||
}, | ||
"AutoRollbackConfiguration": { | ||
"Enabled": true, | ||
"Events": [ | ||
"DEPLOYMENT_FAILURE", | ||
"DEPLOYMENT_STOP_ON_ALARM", | ||
"DEPLOYMENT_STOP_ON_REQUEST" | ||
] | ||
}, | ||
"ServiceRoleArn": { | ||
"Fn::GetAtt": [ | ||
"CodeDeployServiceRole", | ||
"Arn" | ||
] | ||
}, | ||
"DeploymentConfigName": { | ||
"Fn::Sub": [ | ||
"CodeDeployDefault.Lambda${ConfigName}", | ||
{ | ||
"ConfigName": "Linear10PercentEvery1Minute" | ||
} | ||
] | ||
}, | ||
"DeploymentStyle": { | ||
"DeploymentType": "BLUE_GREEN", | ||
"DeploymentOption": "WITH_TRAFFIC_CONTROL" | ||
} | ||
} | ||
}, | ||
"MyFunctionAliaslive": { | ||
"Type": "AWS::Lambda::Alias", | ||
"UpdatePolicy": { | ||
"CodeDeployLambdaAliasUpdate": { | ||
"ApplicationName": { | ||
"Ref": "ServerlessDeploymentApplication" | ||
}, | ||
"AfterAllowTrafficHook": { | ||
"Ref": "MyOtherFunction" | ||
}, | ||
"DeploymentGroupName": { | ||
"Ref": "MyFunctionDeploymentGroup" | ||
} | ||
} | ||
}, | ||
"Properties": { | ||
"FunctionVersion": { | ||
"Fn::GetAtt": [ | ||
"MyFunctionVersione57c0000de", | ||
"Version" | ||
] | ||
}, | ||
"FunctionName": { | ||
"Ref": "MyFunction" | ||
}, | ||
"Name": "live" | ||
} | ||
}, | ||
"MyOtherFunctionDeploymentGroup": { | ||
"Type": "AWS::CodeDeploy::DeploymentGroup", | ||
"Properties": { | ||
"ApplicationName": { | ||
"Ref": "ServerlessDeploymentApplication" | ||
}, | ||
"AutoRollbackConfiguration": { | ||
"Enabled": true, | ||
"Events": [ | ||
"DEPLOYMENT_FAILURE", | ||
"DEPLOYMENT_STOP_ON_ALARM", | ||
"DEPLOYMENT_STOP_ON_REQUEST" | ||
] | ||
}, | ||
"ServiceRoleArn": { | ||
"Fn::GetAtt": [ | ||
"CodeDeployServiceRole", | ||
"Arn" | ||
] | ||
}, | ||
"DeploymentConfigName": { | ||
"Fn::Sub": [ | ||
"CodeDeployDefault.Lambda${ConfigName}", | ||
{ | ||
"ConfigName": "Canary10Percent30Minutes" | ||
} | ||
] | ||
}, | ||
"DeploymentStyle": { | ||
"DeploymentType": "BLUE_GREEN", | ||
"DeploymentOption": "WITH_TRAFFIC_CONTROL" | ||
} | ||
} | ||
}, | ||
"MyOtherFunctionAliaspublic": { | ||
"Type": "AWS::Lambda::Alias", | ||
"UpdatePolicy": { | ||
"CodeDeployLambdaAliasUpdate": { | ||
"ApplicationName": { | ||
"Ref": "ServerlessDeploymentApplication" | ||
}, | ||
"DeploymentGroupName": { | ||
"Ref": "MyOtherFunctionDeploymentGroup" | ||
} | ||
} | ||
}, | ||
"Properties": { | ||
"FunctionVersion": { | ||
"Fn::GetAtt": [ | ||
"MyOtherFunctionVersione57c0000de", | ||
"Version" | ||
] | ||
}, | ||
"FunctionName": { | ||
"Ref": "MyOtherFunction" | ||
}, | ||
"Name": "public" | ||
} | ||
}, | ||
"MyOtherFunctionRole": { | ||
"Type": "AWS::IAM::Role", | ||
"Properties": { | ||
"ManagedPolicyArns": [ | ||
"arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" | ||
], | ||
"AssumeRolePolicyDocument": { | ||
"Version": "2012-10-17", | ||
"Statement": [ | ||
{ | ||
"Action": [ | ||
"sts:AssumeRole" | ||
], | ||
"Effect": "Allow", | ||
"Principal": { | ||
"Service": [ | ||
"lambda.amazonaws.com" | ||
] | ||
} | ||
} | ||
] | ||
} | ||
} | ||
}, | ||
"ServerlessDeploymentApplication": { | ||
"Type": "AWS::CodeDeploy::Application", | ||
"Properties": { | ||
"ComputePlatform": "Lambda" | ||
} | ||
}, | ||
"MyOtherFunction": { | ||
"Type": "AWS::Lambda::Function", | ||
"Properties": { | ||
"Code": { | ||
"S3Bucket": "aws-nodejs-dev-serverlessdeploymentbucket-14iyutt06erm7", | ||
"S3Key": "serverless/aws-nodejs/dev/1515768309333-2018-01-12T14:45:09.333Z/aws-nodejs.zip" | ||
}, | ||
"Handler": "handler.hello", | ||
"Role": { | ||
"Fn::GetAtt": [ | ||
"MyOtherFunctionRole", | ||
"Arn" | ||
] | ||
}, | ||
"Runtime": "nodejs6.10", | ||
"Tags": [ | ||
{ | ||
"Value": "SAM", | ||
"Key": "lambda:createdBy" | ||
} | ||
] | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.