Serverless Framework, Build Immutable Package for CI/CD Pipeline
Serverless Framework is open source and makes deploying serverless applications to cloud providers a breeze. Getting started takes less than a minute and you can have an endpoint accessible in less than 10 minutes. Getting Started with the Serverless framework and CI/CD workflow for Serverless framework is well documented in the official website and this post for CI/CD workflow. According to the official documentation, using Serverless Framework, applications can be packaged once and deployed multiple times using the package command. For example:
# create serverless package$ sls package --package <path_to_package>
# deploy the same package to multiple regions
$ sls deploy --package <path_to_package> --stage qa \
--region us-east-1
$ sls deploy --package <path_to_package> --stage qa \
--region eu-central-1
But, you will soon realize that it is not possible to deploy a packaged service to different stages, the stage used for a build has to be used for deployment as well. The problem with the package command is that the variables are already substituted and CloudFormation template that is transferred to the deploy stage already contains the resolved variables, for that environment or stage.
The solution will be to build for each environment independently in build server and store them separately or build each time you deploy to any environment, which is against the principle of building an immutable deployment package and hence you can never be certain that the package generated in each build is exactly the same as the one that was generated in the previous build.
As always, there is a workaround for it so that you do not have to build the package each time you deploy. This is how I package my services:
In serverless.yml define package.artifact as an option:
package:
artifact: ${opt:artifact, ""}
The conditional use of package.artifact allows us to do an initial build without an artifact but then when we provide an already existing .zip package as an artifact using — artifact option, it will use the provided .zip file as an artifact, hence it will not create a new package for the lambda service.
When we run the serverless “deploy” command it will generate the cloudformation template and a state file for each deployment which is fine because the variable in cloudformation and state does change by environment and it has to be regenerated, but the package .zip does not change.
Use serverless package command for dev stage to generate the deployment service zip and other files.
$ serverless package --stage dev --package ./target
Remove all other files except the generate .zip package
$ find ./target ! -name '*.zip' -delete
Copy serverless.yml and package.json that has serverless plugins and dependencies which will be required for deploying the created package at any time in future.
$ cp serverless.yml ./target/
$ cp package.json ./target/
Finally, create the immutable package for the serverless application
$ cd ./target && zip -r service-name-package.zip .
The created service-name-package.zip, which includes the service package itself as well as additional files required for deployment. It can now be stored in any binary repository and used during deployment.
For deployment using the created zip package, extract the zip file and run npm install to install serverless and serverless dependencies.
$ unzip service-name-package.zip && cd service-name-package$ npm install
Run Serverless deploy,
$ serverless deploy --artifact service-name.zip --stage qa
This will generate .serverless directory and the cloudformation template as well as state file but will use the provided artifact instead of creating a new zip for the service.
Update:
If copying files to the target directory are not desirable, keeping everything in the same location but just including the created target/*.zip works better, as you do not have to pick and choose files to package but rather take everything in addition to the zip file then run:
$ serverless deploy --artifact target/service-name.zip --stage qa
Please do let me know if you have any questions or have come across different approaches to package serverless applications.