There are a truly absurd number of ways to deploy applications on AWS. Corey Quinn wrote two separate articles wherein he described 17 ways to deploy containers on AWS -- that's 34 different ways just to deploy containers (granted, some of the listed ways to deploy are farcical).
All of these options inevitably spawn fights about the "right" way to deploy on AWS. I'm now going to explain my preferred way to deploy a serverless API, and then you can fight me.
- Write your API business logic code in a way that can be run on AWS Lambda.
- Write a CDK template in the same language as your Lambda function code and store along with your Lambda function code in source control (I'm going to assume you're using git here, but use whatever source control you want).
- Deploy your serverless API from your git commit as a self-contained unit.
There's a lot to unpack here, but the main thing I want convey here is that it is awesome to write your executable code and your IaC templates in the same language.
Your CDK template
In the case of a serverless API, these are the main things you'll want in your CDK code:
- IAM policies that limit your lambda permissions using the principle of least privilege.
- A Lambda resource that you'll deploy your code to.
- An API Gateway resource (RestApi) that you'll point at your Lambda.
There are a bunch of other services you could include here (RDS database, dynamoDB, etc etc) if you need them, but that's the beauty of this system -- you're not limited by a specific serverless framework, you can just add arbitrary services to your CDK code as you need them.
from aws_cdk import ( aws_lambda as lambda_, aws_apigateway as apigw, aws_iam as iam, aws_ecr as ecr, Stack) from constructs import Construct import os class SomeStackName(Stack): def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: super().__init__(scope, construct_id, **kwargs) # Create the Lambda execution role lambda_role = iam.Role(self, id="somestack-lambda", role_name='SomestackManagementRole', assumed_by=iam.ServicePrincipal("lambda.amazonaws.com"), managed_policies= [ iam.ManagedPolicy.from_aws_managed_policy_name("service-role/AWSLambdaVPCAccessExecutionRole"), iam.ManagedPolicy.from_aws_managed_policy_name("service-role/AWSLambdaBasicExecutionRole") ] ) # Get an existing ECR repo that has a docker image you want to deploy to Lambda repo = ecr.Repository.from_repository_name(self, "SomestackRepo", "somestack_ecr_repo") # Create the Lambda function somestack_lambda = lambda_.DockerImageFunction(self, "SomestackLambda", code=lambda_.DockerImageCode.from_ecr( repository=repo, tag=some_image_tag_name ), role=lambda_role ) # Create the API Gateway api = apigw.LambdaRestApi(self, "somestack-endpoint", handler=somestack_lambda, default_cors_preflight_options=apigw.CorsOptions(allow_origins=["*"]) )
That's all the Infrastructure as Code that you need to create a serverless API! You can learn more from the official CDK developer guide.
So now you have your CDK IaC written and you're ready to start incorporating it into a real devops pipeline. What does the day-to-day development process look like?
- Make a branch.
- Make some Lambda code changes, some CDK changes, whatever, and push your branch.
- A git server webhook should trigger a CDK build to test your Infrastructure as Code (IaC). This webhook also runs some tests on your temporarily built environment to make sure you have no regressions.
- Merge your branch into main (master, whatever you call it).
- Another git server webhook that only triggers on main branch commits deploys your code to your dev environment (which can then be promoted to stage/prod as necessary)
Now you're ready to build serverless, production-grade APIs with incredible speed and very little risk! You don't have to have a team of engineers doing server maintenance, and you can spin up as many independent microservices as you need to without worrying about autoscaling policies or cluster management. It's an incredibly powerful pattern for certain types of software.