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.
The best way to deploy your serverless API on AWS:
- 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.
The actual development workflow
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.
Oldest comments (3)
One of the biggest problems for noob / beginner-intermediate developers is the paradox of choice in the AWS universe. Slows down learning tremendously getting a 10K view of the landscape
100% true. That's one reason I present things in an opinionated way -- I've evaluated dozens of deployment options, and this is the one that balances short-term and long-term developer friction the best for cloud-based APIs that don't require highly optimized response times. There are lots of options that are quicker to initially deploy with (including just clicking around in the AWS console!), but they immediately become productivity destroyers when multiple developers start adding features to a production product. This pattern accounts for that.
Very well done. Thanks.