by Valts Ausmanis · January 16, 2024
Nowadays when we talk about building and deploying cloud applications - Infrastructure as code (IaC) term is no stranger to us. Most of the cloud applications are being managed and deployed through the “code” instead of manual changes. In this article we will take a look on two widely used IaC tools: Serverless Framework and Cloud Development Kit (CDK). Both tools are open-source and built with focus to deploy applications on AWS cloud. Based on the Datadog “State of serverless” report – Serverless Framework is the most popular IaC tool used for managing AWS Lambda functions among Datadog customers. But let’s take a step back because most popular doesn’t always mean the best fit for everything and take a look on what it takes to build and deploy serverless application using both of these tools.
For several years Serverless Framework (starting from 2016) was the go-to tool for building and deploying serverless (mainly AWS Lambda based) applications on AWS cloud. Yes, there was AWS SAM (available from 2016) but due to some missing functionality (like custom authorizers, no real CLI support at that time etc.), being more like AWS CloudFormation extension not a real IaC tool and of course Serverless Framework awesome community at that time it wasn’t the first choice.
But then in 2019 AWS CDK was released which was developed by AWS - finally the first real Infrastructure as Code tool where you could use your preferred programming language (like Typescript, Python) for building and deploying cloud applications to AWS.
The best way to compare these two tools is to have real hands-on comparison and take a simple serverless application use case like “Newsletter back-end service” with following resources to be built and deployed on AWS cloud:
POST /subscriber
DELETE /subscriber/{subscriberId}
Additionally, we will use code bundler to bundle and minify our Lambda JavaScript code. In this case we will stick with esbuild which have proved itself as pretty fast one.
This way we will be able to see what it takes to build and deploy this application to AWS from both sides.
Serverless Framework is an open-source Infrastructure as Code (IaC) tool for building, packaging and deploying serverless application to major public clouds like AWS, Microsoft Azure, Google Cloud Platform and more.
Serverless applications and related resources (like functions, events, roles etc.) are being configured via YAML file serverless.yml
:
service: newsletter
functions: # Your "Functions"
subscribe:
handler: src/subscribe.handler
events: # The "Events" that trigger this function
- httpApi: 'POST /subscriber'
unsubscribe:
handler: src/unsubscribe.handler
events:
- httpApi: 'DELETE /subscriber/{subscriberId}'
We have now defined Lambda functions and added API Gateway as event source to trigger Lambdas but we still have to add DynamoDB table and give Lambdas access rights to update DB table:
custom:
SUBSCRIBERS_TABLE: ${self:service}-${self:provider.stage}-subscribers
provider:
iam:
role:
statements:
- Effect: Allow
Action:
- dynamodb:UpdateItem
- dynamodb:DeleteItem
Resource:
- Fn::GetAtt: [SubscribersTable, Arn]
# The "Resources" your "Functions" use. Raw AWS CloudFormation goes in here.
resources:
Resources:
SubscribersTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: ${self:custom.SUBSCRIBERS_TABLE}
AttributeDefinitions:
- AttributeName: subscriberId
AttributeType: S
KeySchema:
- AttributeName: subscriberId
KeyType: HASH
BillingMode: PAY_PER_REQUEST
If due to some reason YAML format doesn’t work for you it’s possible to use alternative configuration formats like JSON, JavaScript or TypeScript. But don’t get too excited about JS or TypeScript usage – at the end it’s still the JS object that should be exported.
Before we can package our serverless framework service we have to set provider values where to deploy (= AWS), Lambda runtime, default stage and region in serverless.yml
:
provider:
name: aws
runtime: nodejs18.x
stage: ${opt:stage, 'dev'}
region: ${opt:region, 'eu-west-1'}
If necessary your can check out serveless.yml
reference with all the available properties when provider is set to aws
.
To decrease the Lambda code size (and to improve AWS lambda cold starts) we will package our functions separately and use esbuild bundler by adding following lines in the serverless.yml:
custom:
esbuild:
exclude: ['@aws-sdk'] # Exclude the @aws-sdk from the build as it is already available in the Lambda environment
minify: true # Minify the code
package:
individually: true # Package individually per functions
You can find complete serverless.yml
code for our newsletter serverless application in our github.com/cloudviz-io/cloudviz-blog-samples repository.
By using Serverless CLI, it’s possible to package your serverless project without deploying it to AWS. Running the serverless package
command Serverless Framework will build the deployment artifacts (for default “dev” stage) in .serverless
directory:
Artifacts folder contains:
serverless.yml
servereless-state.json
fileYou can package and deploy multiple similar serverless applications by just using the --stage <stage-name>
parameter. This is pretty useful in CI/CD workflows when deploying your application to dev -> qa -> prod
environments. By using different stage names for your environments, the underlying serverless application resources like Lambda functions, API gateway endpoints, Cloudwatch log groups etc. are auto-named in a nice, clear way. Example for Lambda functions: <service-name>-<stage>-<function-name> -> "newsletter-dev-subscribe"
To deploy serverless application just use simple CLI command serverless deploy
, which will deploy application to AWS cloud by using configured default AWS credentials:
We have successfully deployed serverless application with two API Gateway endpoints, Lambdas and DynamoDB table to AWS cloud in less than 2 minutes. Additionally, S3 bucket was created where the Serverless Framework uploads application artifacts (zipped Lambda code, CloudFormation templates) and uses these for deployment process. Now to see the overview of our newsletter service let's visualize deployed serverless architecture with Cloudviz.io AWS diagram generator:
You can find all the IaC and Lambda related code for building newsletter service using Serverless Framework in our GitHub repository.
AWS CDK is an open-source Infrastructure as Code (IaC) tool for building, packaging and deploying cloud applications to AWS using your preferred programming language like TypeScript, JavaScript, Python, Java etc.
In order to define AWS infrastructure for serverless application we have to create AWS CDK app which is written in your preferred programming language (ex. TypeScript, JavaScript, Python etc.) that uses the AWS CDK library.
Each CDK app consists of:
App
represents root object and can contain multiple stacks. An app represents all the stacks that application requires to be fully functional.Stack
represents an AWS CloudFormation template with all the related AWS resources. Stacks can be deployed separately.Constructs
represents AWS resources that we will create (like. Lambda, API Gateway, DynamoDB etc.).AWS suggested way of defining CDK app is to have:
bin
folder that contains file which is main entry point for our application (newsletter.js
).lib
folder that contains all code for CDK stacks and constructs to define AWS resources that should be deployed (newsletter-stack.js
).bin/newsletter.js
- JavaScript code that defines main entry point for cloud application:
import { App } from 'aws-cdk-lib';
import { NewsletterCdkStack } from '../lib/newsletter-stack.js';
import { resourceName } from './app-config.js';
const app = new App();
// See more about defining environments: https://docs.aws.amazon.com/cdk/v2/guide/environments.html
new NewsletterCdkStack(app, resourceName('demo'), { stage: 'demo', env: { region: 'eu-west-1' } });
lib/newsletter-stack.js
- JavaScript code that defines AWS resources for our newsletter serverless application:
import { Stack } from 'aws-cdk-lib';
import { resourceName } from '../bin/app-config.js';
import { Runtime } from 'aws-cdk-lib/aws-lambda';
import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs';
import { LambdaIntegration, RestApi } from 'aws-cdk-lib/aws-apigateway';
import { Effect, PolicyStatement } from 'aws-cdk-lib/aws-iam';
import { Table, AttributeType } from 'aws-cdk-lib/aws-dynamodb';
export class NewsletterCdkStack extends Stack {
constructor(scope, id, props) {
super(scope, id, props);
// create subscriber DynamoDB table
const subscriberTable = new Table(this, 'SubscriberTable', {
tableName: resourceName(`${props.stage}-subscribers`),
partitionKey: {
name: 'subscriberId',
type: AttributeType.STRING,
},
});
// set default Lambda parameters
const nodeJsFunctionProps = {
bundling: {
externalModules: [
'@aws-sdk', // Use the '@aws-sdk' available in the Lambda runtime
],
minify: true,
},
runtime: Runtime.NODEJS_18_X,
initialPolicy: [
new PolicyStatement({
actions: ['dynamodb:DeleteItem', 'dynamodb:UpdateItem'],
effect: Effect.ALLOW,
resources: [subscriberTable.tableArn],
}),
],
environment: {
SUBSCRIBERS_TABLE: subscriberTable.tableName,
},
};
// create Lambda functions
const subscribeLambda = new NodejsFunction(this, 'SubscribeHandler', {
functionName: resourceName(`${props.stage}-subscribe`),
entry: 'src/subscribe.js',
...nodeJsFunctionProps,
});
const unsubscribeLambda = new NodejsFunction(this, 'UnsubscribeHandler', {
functionName: resourceName(`${props.stage}-unsubscribe`),
entry: 'src/unsubscribe.js',
...nodeJsFunctionProps,
});
// create API Gateway endpoints
const api = new RestApi(this, 'NewsletterApi', {
restApiName: resourceName(`${props.stage}-api`),
endpointExportName: resourceName(`${props.stage}-ApiGwUrl`),
description: 'Newsletter API',
deployOptions: { stageName: props.stage },
});
// attach Lambda functions to API Gateway endpoints
const subscriberEndpoint = api.root.addResource('subscriber');
subscriberEndpoint.addMethod('POST', new LambdaIntegration(subscribeLambda));
const unsubscribeEndpoint = subscriberEndpoint.addResource('{subscriberId}');
unsubscribeEndpoint.addMethod('DELETE', new LambdaIntegration(unsubscribeLambda));
}
}
By using CDK CLI, it’s possible to package your serverless project without deploying it to AWS. This can be useful in CI/CD workflows to verify that your IaC code can successfully generate the build and use the same build artifacts (like Lambda code, templates) when deploying across all the environments (ex. dev -> qa -> prod
). Running the cdk synth
command CDK will build the deployment artifacts (for default “demo” stage) in cdk.out directory:
Artifacts folder contains:
newesletter-demo.template.json
) that describes AWS resources defined in CDK stacktree.json
file which is used by AWS CDK Explorer to display your resources in a tree viewmanifest.json
file that defines the set of instructions that are needed in order to deploy the assembly directoryLambda code was bundled using esbuild.
Before we can deploy our CDK app we have to first bootstrap (with command cdk bootstrap
) specific AWS account and region. The main purpose is to provision set of resources (like S3 bucket, IAM roles) that are required to support deployment of CDK applications.
To deploy serverless application just use simple CLI command cdk deploy
, which will deploy application to AWS cloud by using configured default AWS credentials:
You can find all the IaC and Lambda related code for newsletter serverless application using AWS CDK in our GitHub repository.
So we have built and deployed two similar serverless applications using two different IaC tools: Serverless Framework and AWS CDK. As you can already see in previous Lets build serverless application section that there are differences how you build, define, package and deploy serverless applications to AWS cloud. Now let’s dive into the more specific comparison.
Serverless Framework lets you define your serverless application by using templates (YAML or JavaScript object). AWS CDK lets you use your preferred programming language (TypeScript, JavaScript, Python, Java, C# or Go) to define your next cloud application.
Some will say that they like simplicity of Serverless Framework and I fully agree with them until they start to grow their application and add AWS resources that are not part of default serverless.yml
template or available as plugins. When you start to add custom CloudFormation templates to your project the complexity of serverless application starts to grow and you have to get your hands dirty by using purely CloudFormation templates, keep the common AWS resource naming, properly tag your resources, and add different IAM access rights to your Lambdas or other resources that are involved. However, by using CDK and its constructs you have most of the AWS resources already defined as part of the AWS CDK library and ready to be used in your cloud application.
When we compare the popularity of these two tools in open-source community there is clear winner – Serverless Framework with ~46k stars comparing to CDK ~11k stars.
But it’s not a big surprise as Serverless Framework was released 3 years earlier than CDK and many organizations are still using it because at that time there wasn’t any good alternative and it still works really well, being well maintained and there is almost 99% chance that when you will hit any issue there will be already answer for that because someone had the same challenge as you before.
You can use Serverless Framework for not only to deploy your serverless application to AWS but to other major public clouds like Microsoft Azure or Google Cloud Platform.
This is really nice but you should ask a question to yourself: “Will I really deploy my serverless application to multiple cloud providers?”. In most scenarios your answer will be “No”. If the answer due to some specific requirements of your use case is “Yes”, then it’s clear that the complexity of your serverless application (like traffic routing, load balancing etc.) will grow and as we talked before the main advantage of Serverless Framework is its simplicity. Thus, for multi-cloud deployments I would consider different IaC tools like Terraform or even Pulumi that will let you to go way above the simple serverless application requirements and provision any additional resources (like DNS stuff, Load balancers etc.) needed to run your cloud application.
For serverless you basically have one definition serverless.yml
template which you use to define your serverless application and additional CloudFormation templates for the AWS resources that can’t be defined using default Serverless Framework parameters or available as plugins.
For CDK you could have one input file too but that brakes some of the advantages of using your preferred programming language to structure your IaC code properly. That’s why I would suggest that you first go through some CDK “hello world” example docs to better understand how to structure and define your CDK application and how to correctly create the stacks and constructs. This adds a bit more learning curve to CDK when compared with the Serverless Framework.
Based on the Datadog “State of serverless” report – 65% of organizations have connected AWS Lambda functions to a VPC. When you connect your Lambdas to VPC you improve security for your serverless application so that Lambdas can “talk” to other AWS services like S3, DynamoDB, RDS without requests actually leaving the AWS network (ex. by using VPC endpoints or proper security group configurations). But for many organizations it’s not only about additional security but the fact that they have already solid compute and database workloads running in the VPCs and they connect Lambdas to VPC in order to integrate with existing databases, cache instances or even internal services.
For serverless framework you can use serverless VPC plugin to automatically create new VPC related resources based on your provided configuration in serverless.yaml
file. Or use serverless VPC discovery plugin to setup the existing vpc configuration for Lambdas.
For CDK you can use VPC construct to define your new VPC resources or use VPC lookup function to get the details of existing VPC and use it when defining your Lambdas.
Both tools provide several default values for new VPC creation that you “kinda don’t have to think about” and would work for common use-cases. But to be honest I would not use Serverless Framework to deploy completely new VPC. Why? You can ask yourself a question – “Why am I deploying new VPC? Will I use additional VPC based services like RDS database, cache instances or maybe I will add VPC peering to existing VPC to connect to existing databases?”. If you answer to second question is “Yes” then most likely you will have to use custom CloudFormation templates to make this work with Serverless Framework and then at the end you will use serverless.yml + serverless vpc plugin + CloudFormation templates
which is far from term “simplicity”.
There are many Serverless Framework plugins that can extend or overwrite frameworks default functionality. Most of the plugins are maintained by different community members.
For CDK the most similar thing to Serverless Framework plugins could be CDK construct library. L2 constructs represent specific AWS resources and L3 constructs represent patterns to help you complete common tasks in AWS, often involving multiple kinds of resources (ex. API Gateway REST API with AWS Lambda proxy integration).
The main differences between Serverless Framework plugins and AWS CDK Constructs:
Serverless Framework CLI includes commands to test and invoke your functions locally by using command like serverless invoke local --function functionName
.
AWS CDK CLI doesn’t have this functionality included but it’s possible to use SAM CLI to locally invoke the CDK app functions, start APIs and Lambda endpoints:
# Invoke the function FUNCTION_IDENTIFIER declared in the stack STACK_NAME
sam local invoke [OPTIONS] [STACK_NAME/FUNCTION_IDENTIFIER]
# Start all APIs declared in the AWS CDK application
sam local start-api -t ./cdk.out/CdkSamExampleStack.template.json [OPTIONS]
# Start a local endpoint that emulates AWS Lambda
sam local start-lambda -t ./cdk.out/CdkSamExampleStack.template.json [OPTIONS]
Even Serverless Framework has multiple cloud providers which you can use to deploy your serverless applications – AWS is still default cloud provider used by Serverless Framework and therefore all the Serverless Framework documentation applies to AWS by default.
AWS CDK was developed by AWS and is meant to be used to deploy your cloud applications to AWS cloud only.
Comparing with other IaC tools like Terraform which uses cloud provider API calls and manages the state of the application by itself (which sometimes causes a lot of pain for developers) to deploy cloud applications to AWS – both Serverless Framework and AWS CDK uses AWS IaC service called AWS CloudFormation.
In the Lets build serverless application section we generated serverless application deployment assets (CloudFormation templates, bundled Lambda code) by using both of the tools. To be correct we used the same code bundler esbuild
for both tools. The build results were basically the same.
Serverless packaging:
CDK packaging:
As you can see Serverless Framework packaged serverless application in 0s and CDK in 11ms (0.011s). As most likely Serverless Framework rounded the ms to whole seconds the result was the same.
Both tools under the hood use AWS CloudFormation to deploy cloud applications to AWS. So, deployment speed per serverless application deployment (= one CloudFormation stack) should be similar.
In previous sections when we built and deployed our newsletter serverless application by using both of the tools deployment speeds bit differed (~20 seconds difference).
Serverless Framework deployment:
CDK deployment:
One assumption for this difference could be because underlying Lambda, API Gateway, DynamoDB and IAM services provided different response times for similar requests during new AWS resource creation.
I would suggest to choose the IaC tool for your next project based on the complexity of your infrastructure and already existing knowledge inside the team. For example, if your team has already solid Terraform experience and if you are successfully using it for your existing cloud applications then in most scenarios it wouldn’t make sense to start to use CDK or Serverless Framework at all.
If your team wants to explore new IaC tools for the next project then analyze complexity of the cloud application that you will build. I have mentioned and showed in this article Serverless Framework simplicity when it comes to quickly build simple serverless applications with Lambdas and different event sources. If you see that you will not use additional VPC based services like RDS databases, cache instances, VPC endpoints, VPC peering etc. and just use Lambdas with few other AWS resources like API GW, S3 or DynamoDB then you should be good by choosing Serverless Framework.
Before AWS CDK was released, I was all-in Serverless Framework advocate for building and deploying serverless applications to AWS cloud. At first, I didn’t welcome this new tool with open arms. I was so used to Serverless Framework simplicity and community support that I unintentionally ignored use cases (like Lambdas connected to custom VPCs and accessing dedicated RDS databases and other VPC related services) where it could make sense to use the AWS CDK fully. But instead, we used Serverless Framework for purely serverless based services containing Lambdas, API GW, SNS, DynamoDB etc. and CDK for VPC related resources like custom VPCs, security groups, RDS databases, VPC peering, NAT GWs, VPC endpoints etc. After some time, I and our team got used to AWS CDK and for next projects we started to use this tool fully.
Now few years later I still think this was good decision! You use one IaC tool and have all your infrastructure code (no custom CloudFormation templates + plugins + serverless.yml combination) and your Lambdas code written in one language (like JavaScript, TypeScript, Python etc.). Yes, there will be small learning curve comparing to Serverless Framework but If you and your team already use one of the CDK supported languages (and most likely you do) then your first CDK application will be up and running in no time.
Tired of browsing through the AWS console?
Try out Cloudviz.io and visualize your AWS cloud environment in seconds
As experienced AWS architects and developers, our goal is to provide users an easy way to create stunning AWS architecture diagrams and detailed technical documentation. Join us to simplify your diagramming process and unleash the beauty of your cloud infrastructure
Support
Contact
Copyright © 2019 - 2024 Cloudviz Solutions SIA