The Evolution of Maintainable Lambda Development Pt 2
Something that may not immediately be clear when introducing yourself to the idea of serverless development in AWS lambda, is that any program is written in a compatible language that fits within...
Where We are Now
As an Advanced AWS Consulting Partner, we have experienced first-hand the success AWS Lambda has offered our clients. To view part one of this three-part series, click here.
Code Management
Something that may not immediately be clear when introducing yourself to the idea of serverless development in AWS lambda, is that any program is written in a compatible language that fits within the spatial memory and time constraints of the execution environment can be lifted into Lambda. This includes applications of significant complexity, such as dynamic websites built in a robust web framework like Django or Spring. Lambda is not just viable for highly distributed microservices with separate deployments for small discrete functions - though this may be a valuable architecture for various applications - a single lambda deployment can serve an entire backend web stack for more traditional SSR or acting as an API for SPAs or other consumers.
It's a cornerstone of IaaC that the resources that must exist for an application to function be defined in such a way that the environment can be reproduced with minimal effort. In addition, changes to this infrastructure should be tracked and applied automatically or with minimal effort. In the simplest way, such a goal might be achieved by some bash scripts or makefiles. For greater consistency and easier declarative configurations applications such as Ansible or Chef grew in popularity. We'll briefly explore some alternative for managing the state of our serverless infrastructure along with our program logic by looking at two popular serverless frameworks Zappa and Serverless, and also
Terraform - a tool we've looked at before in our blog. At JBS we've utilized these three tools to a significant extend, relying on them to manage, and deploy many production projects.
An example of a serverless architecture with various AWS services vs. a traditional single server (which in this example is connected securely). We will not delve into specific serverless architectures in this article, but focus on some tools that we can use to manage architectures like this.
Zappa
Zappa is a serverless framework written in python that we have had great success with at JBS. The most common and quickly deployable configuration will provision an AWS stack using API Gateway (APIG) that will handle web requests to an application packaged up for Lambda that will perform processing and return a response to APIG for the client. Zappa manages all of these resources and integrations for you. Zappa works with any WSGI-compatible application you are interested in, as it handles the translation of an APIG payload to WSGI so that the request is ready for the application.
Pros
- Zappa is easy to configure and get running quickly, especially when you are using a Python framework and are already familiar with the language
- Zappa's asynchronous task execution is an easy-to-use way to scale operations within lambda by dispatching additional executions leveraging easy-to-use decorator functions
Cons
- Zappa's design is deeply entrenched in the concept of deploying a serverless python HTTP web application. While it provides other helpful abstractions as described above, once you move beyond interaction with that application, Zappa often requires customization or cannot fulfill
- Zappa's stewardship is in question. There was only one release after March in the year 2020, on October 7 with 0.52.0.
Serverless Framework
Much like Zappa, serverless allows us to quickly deploy an APIG --> Lambda serverless website. Serverless framework is written in Javascript and offers a wide array of plugins that are responsible for much of its functionality such as more easily managing different environments on domains, packing up python requirements, managing your environment with .env files, providing a dynamoDB local proxy for development and so on. Serverless framework itself is not very opinionated about its behavior and offers a core set of configurable deployment options, and for an architecture of much complexity and to DRY out more specific tasks, a decent number of plugins are necessary.
Pros
- Serverless framework and its plugins are [open source](https://github.com/serverless/serverless), but it benefits from providing services for larger teams via a [paid model](https://www.serverless.com/pricing/) which may foster adoption in organizations that need better support of their serverless deployment software
- Serverless Framework's platform-agnostic nature means it is not limited to just Javascript language projects and its wide assortment of plugins mean it can address a lot of concerns other than just packaging and deploying applications
Cons
- Serverless Framework's plugins are largely community created and supported, which means you will need to review documentation and implementation of various levels of maturity to determine which plugin is right for your use. It often requires a bit of searching and tweaking to determine the right plugin for the right job, as plugins do not necessarily follow a specific general design of a more opinionated IaaC system such as terraform.
Terraform
Terraform's role is not to abstract away the necessary AWS (or other cloud) components to run a serverless application but rather a complete cloud infrastructure state management.
Pros
- Terraform is very popular and much like Serverless Framework, it is open source but its continued development is supported by paid offerings such as Terraform Enterprise which indicate it should have a long life
- Terraform can generally be used to do anything you can imagine with cloud deployments including remote executions, very complex state and environment management, and it provides plugins to abstract a nearly endless amount of various cloud provider resources and integrations
Cons
- While popular in the IaaC space and adopted by many developers and organizations terraform is not a "ready to go" platform for a serverless web application deployment or any specific application architecture. Using terraform requires an investment in reading documentation and testing deploys to familiarize yourself with its complexities.
- Terraform is written in Go and requires the use of Haschicorp Configuration Language HCL in userland. None of the most popular web frameworks of 2020 are written in Go and HCL is a terraform-specific language. This means that investment in implementing and contributing to terraform is less likely to overlap with the existing language expertise of most web engineers.
Additional Infrastructure
As mentioned in the first part of this series, most problems we want to solve are not going to be conquered with Lambda alone. We probably need a place to store data persistently, maybe we need an object cache to speed up processing between multiple lambda invocations, or maybe we require further assistance in preparing and processing data for more intensive applications such as search. We saw in the previous section how APIG can fill the role of a webserver as we disseminate the pieces of a traditional web server into their cloud components, so let's take a look at some more AWS services we can utilize, and some ways we may want to manage this infrastructure.
Zappa
Zappa does not provide a means to define additional resources as part of the overall infrastructure. It is also somewhat rigid in how it defines certain resources which can lead to friction when incorporating Zappa within organizations with more rigid requirements on cloud resource management (e.g. this GH issue). With Zappa, you are better off allowing it to manage all the pieces needed for your web application on its own and manage other resources with a separate tool such as stacker or Terraform (which we will discuss more in a following section). If you are not alright with Zappa making certain decisions about your resources, or your organization does not permit the level of access that Zappa needs to create and manage all the resources in its stack (often the case for IAM resources) you can still use Zappa's package command to create an archive that is ready for upload to lambda and utilize the other helpful functions the project provides for use after code is deployed.
Serverless Framework
Serverless framework, like Terraform, can integrate with AWS, Microsoft Azure, Google Cloud Platform, and many others. One major distinction between Serverless Framework and Terraform when it comes to the AWS provider is that serverless.js uses CloudFormation directly. Additional `resources` will be tracked in a CF stack and update state and progress monitored via the AWS API. Terraform maintains its own state files, and you will not have the same visibility into issues in the AWS console that CF would provide.
Terraform
Terraform is a fully-featured IaaC software tool that provides its own language (HCL) and project structure that can manage not just in AWS but Microsoft Azure, Google Cloud Platform, and many others. As mentioned previously, terraform manages its own state rather than utilizing CF like Zappa and serverless.
Project Management
We have set the groundwork for how to address project management issues and scaling team sizes when working with lambda because with the prior considerations in place, we now have all the necessary tooling to follow agile best practices as we would with any technology with teams small to large. Our developers can implement and test locally, we can create multiple environments for additional development and thorough testing, and we continue to utilize the kind of release management and code review policies we would in any mature and maintainable project.
Zappa
Zappa uses a single configuration file that will reside at the root of your project. When Zappa is installed, the parsing of this file is what allows `Zappa` functions to work properly when Zappa is installed in your python environment.
# Example of a simple django zappa_settings.json
{
"dev": { // The name of your stage
"s3_bucket": "lambda", // The name of your S3 bucket
"django_settings": "your_project.settings" // The python path to your Django settings.
}
}
Serverless Framework
Serverless Framework also uses a configuration file alongside your project to define deployment variables, etc.
# Example of a simple django serverless.yml
service: users
functions: # Your "Functions"
usersCreate:
events: # The "Events" that trigger this function
- http: post users/create
usersDelete:
events:
- http: delete users/delete
resources: # The "Resources" your "Functions" use. Raw AWS CloudFormation goes in here.
Terraform
Terraform modules typically need to exist in their own folder at the root of your project to be maintainable. There is typically much more than one file in a terraform deployment, usually at a minimum the main file and a variable declaration file, but typically many more files for each resource grouping. We'll see a minimal example of using TF to provide a packaged lambda deploy archive in the following section.
CI
The real benefit of incorporating IaaC within your project or application is that you can script environment changes with other aspects of your application deployment. Using either of the three options we've investigated in this article, you can ensure that your application is kept in step with your necessary cloud resource changes. Below are a couple (incomplete) examples of what this may look like using Gitlab CI and a Django project
Zappa
# .gitlab-ci.yml
#
# Example of using Zappa alone to deploy the application
#
# Deploy static files to an S3 bucket configured in our project elsewhere
- python manage.py collectstatic_project --noinput
# Deploy our project to the ENV we defined in our gitlab yml
- zappa update $ENV
# Run migrations to make sure our DB is in the right state for the application
- zappa manage $ENV migrate
Serverless Framework
#.gitlab-ci.yml
# All interesting things are configured in our serverless.yml file
./node_modules/serverless/bin/serverless.js deploy -s ${ENV}
Terraform
Example of using Zappa package
to deploy the application with terraform
# .tf files
#
# Must define the lambda resource in our TF
#
resource "aws_lambda_function" "this" {
s3_bucket = aws_s3_bucket.zappa_files.bucket
s3_key = aws_s3_bucket_object.zappa_zip.key
source_code_hash = filebase64sha256(local.package_file_path)
publish = true
# Ensure the zappa package gets to S3 to be loaded into lambda
resource "aws_s3_bucket_object" "zappa_zip" {
bucket = aws_s3_bucket.zappa_files.id
acl = "private"
key = local.package_file_name
source = local.package_file_path
etag = filemd5(local.package_file_path)
}
# .gitlab-ci.yml
#
# Use zappa to package our application and make it available to our TF
# Package our application for the ENV we defined in our GitLab yml and our Zappa settings
- zappa package ${ENV} -o package-project-${ENV}.zip
- mv package-timeout-${ENV}.zip ../terraform
- cd ../terraform
- terraform apply -auto-approve
Conclusion
There are so many options for engineers when it comes to IaaC for serverless architectures that we've just barely scratched the surface of what these three tools can do - and there are many other tools we will not comment on in this article. Making the appropriate decision for your project and/or organization early is important so that you do not have to re-engineer your IaaC (maybe ever!) which would subtract velocity from feature development. There is not a good reason to start a serverless project without one of these solutions in place - even for those new to serverless development, there are tools (such as Zappa) that can get you up and running quickly with a fully version controlled and reliable architecture. Just as it is best practice to version control all application code, in the current cloud-powered ecosystems with the vast array or tools at our disposal, it is expected to define architecture to support a deployment upfront - no matter how minimal or complex.
Something we haven't discussed much is the difference between local development and a serverless deployment. Clearly, engineers are not working on their local machines in a true lambda deployment - nor would it be feasible to actually iterate on code in a deployment - it would be prohibitively expensive and slow. In the third installment of this series, we will look at a couple of ways we can get close to parity between local and deployed environments, and how the introduction of containers in lambda offers further confidence in bridging local and deployed applications.
Stay tuned for part 3 in this series coming soon.
The JBS Quick Launch Lab
Free Qualified Assessment
Quantify what it will take to implement your next big idea!
Our assessment session will deliver tangible timelines, costs, high-level requirements, and recommend architectures that will work best. Let JBS prove to you and your team why over 24 years of experience matters.