Running Containers on AWS Lambda
earthly.dev
earthly.dev
I have a follow up coming where I use go in a container, and the request speed got a lot better.
The service in this article is my html to text convert, so having a container where I could install OS-dependencies was crucial to getting this working. It's covered here and here:
My team transitioned a lightweight python based lambda from zip to container based and we also saw a small (but definitely real) speedup in request time. I'm not the one who did the benchmarking, but iirc it was about 10ms faster. From ~50ms to ~40ms or so.
edit: originally phrased it backwards to seem like container method was slower.
Specifically regarding cold starts of lambda instances, I recall this excellent article from Jan 2021 which measures cold start latencies for a variety of AWS lambda runtimes:
https://mikhail.io/serverless/coldstarts/aws/languages/
I would love to see a 2022 update.
I recently made a project for the company and didn't even consider containers (which I love btw) because I though I could save some ms without container.
Do you know if lambda containers performance is also good in nodejs? I need to have python and nodejs lambdas.
Sorry don't know about nodejs yet. This was for a new lambda that wasn't in production yet, so it made sense to experiment.
The increase in speed was unexpected but nice. But we're also assuming this increase is a static ~10ms speedup, and not a 20% speedup, but we only tested one lambda. I'm interested to see Adam's followup post and see if I'm wrong about that.
Oh also just want to say, since the workings of lambda are hidden, it could be some totally different reason why containers perform better. Since its a newer feature, maybe it's running a newer version of the backend that the zip file backend will be moved to. Maybe container-based lambdas are allocated on newer hardware. Maybe containers are actually just faster. Can't ever really know without getting hired by Amazon and signing an NDA, lol.
I was interested on the cold starts though. I will have to experiment in the future.
> When the Lambda service receives a request to run a function via the Lambda API, the service first prepares an execution environment. During this step, the service downloads the code for the function, which is stored in an internal Amazon S3 bucket (or in Amazon Elastic Container Registry if the function uses container packaging). It then creates an environment with the memory, runtime, and configuration specified. Once complete, Lambda runs any initialization code outside of the event handler before finally running the handler code.
https://aws.amazon.com/blogs/compute/operating-lambda-perfor...
But your broader point is correct. Cold starts are a challenge, but they're one that the team is constantly working on and improving. You can also help reduce cold-start time by picking languages without heavy VMs (Go, Rust, etc), but reducing work done in 'static' code, and by minimizing the size of your container image. All those things will get less important over time, but they all can have a huge impact on cold-starts now.
Another option is Lambda Provisioned concurrency, which allows you to pay a small amount to control how many sandboxes Lambda keeps warm on your behalf: https://docs.aws.amazon.com/lambda/latest/dg/provisioned-con...
If I had a Spring Java (well, Kotlin) app that processes stuff off SQS (large startup time but potentially very high parallelism), would you recommend running ECS containers and scale them up based on SQS back-pressure? Or would you package them up as Lambdas with provisioned capacity? Throughput will be fairly consistent (never zero) and occasionally bursty.
"Lambda containers" is a bit of a misnomer, as you can have multiple instances of a function run on the same container, it's just that initial startup time once the container shuts down that is slow (which can be somewhat avoided by a "warming" function set to trigger on a cron).
I would definitely go with containers if your intention is to use Spring. ECS containers can autoscale just the same as lambdas.
There's some work being done to package Java code to run more efficiently in serverless computing environments, but IIRC, it's not there yet.
When I looked the Lambda API looked uncomplicated to implement (I saw an example somewhere) and it felt like you could just write a few controllers and gain the ability to run a subset of functionality in Lambda, especially if your app could be kept warm.
(to your cron comment, I thought that the reserved capacity would mean the container would be forcibly kept warm?)
> By default, Lambda polls up to 10 messages in your queue at once and sends that batch to your function.
From https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html
Accelerating cold starts with checkpoint and restore is a good idea. There's been a lot of research in academia around it, and some progress in industry too. It's one of those things, though, that works really well for specific use-cases or at small scale, but take a lot of work to generalize and scale up.
For example, one challenge is making sure that random number generators (RNGs) don't ever return the same values ever after cloning (because that completely breaks GCM mode, for example). More details here: https://arxiv.org/abs/2102.12892
As for CRIU specifically, it turned out not to be the right fit for Lambda, because Lambda lets you create multiple processes, interact with the OS in various ways, store local state, and other things that CRIU doesn't model in the way we needed. It's cool stuff, though, and likely a good fit for other use-cases.
I know it defeats the conceptual purpose of serverless, but it's a nice workaround while cloud platforms work on mitigating the cold start problem.
Using containers for lambda is generally a bad idea for anything TypeScript/JavaScript that handles a realtime request - you just can't beat the speed of a single JavaScript file (compiled, in the case of TS). AWS CDK now ships with the NodeJSFunction as well, which makes generating those a breeze with ESBuild.
That said, the $2-5/month is fine as well for some cases.
Just doing a warming job with no other usage falls well within free tier usage, I can confirm.
There's a good argument to go with packaged code instead of containers if you can manage the development complication and versioning (cold starts measured in milliseconds).
But Lambda also does things that CGI didn't do: dynamic auto-scaling, integration with queues and streams for async processing, strong security isolation with fine-grained authorization, host failure detection and recovery, datacenter failure resilience, and many other things. The interface being familiar and relatively analogous to existing things is intentional. The point is to be different where being different helps, and familiar where being different doesn't help.
They are scalable (including, importantly, down to zero!), have their own dedicated resources for each process, and are pretty efficient at being put to sleep and woken up. Plus the code is immutable by default and you get a lot out of the box like logging and setting limits.
I wouldn't start a new project with CGI at all right now but I use Lambda constantly.
Lambda is the control plane running inside an "AWS OS" context, that means it has access to internal apis with scoped permissions. Most commonly people discuss user facing lambdas on the edge, however you are not obligated to expose it to the world.
If you do choose to go the cloud route understand that your account activities generate quite a lot of data. Simplest example would be custom CloudWatch events generated from say autoscaling groups i.e. "Server 23 has ram usage above some moving average threshold" => kick off lambda => custom logic (send email/slack, reboot server, spin up more instances, whatever).
People who like middle brow dismissals would say "what does it matter where the script runs, it could just as easily be running on the instance itself" - to them I say, pain is the best teacher. :)
[1] https://www.youtube.com/watch?v=A-7j0QlGwFk&ab_channel=Insid...
And now Cloud Functions gen 2 as well…?
We're working on some more formal documentation of this, both to help customers understand how to optimize things, and to join the research conversation about container loading. Our solution combines deterministic flattening, demand loading, erasure coding, convergent encryption, and a couple of other cool technologies to both accelerate the average case and drive down tail latency.
Returning a 500 would look something like this:
{
"statusCode" : 500,
"headers" : {
"content-type": "text/plain; charset=utf-8"
},
"body" : "Some error fetching the content"
}Simply put: Fargate/ECS/EC2+EB = long running tasks
Lambda = Short burst tasks with a max life of 15 minutes
Running a lambda 24/7 will nuke your credit card. Using Fargate for a scheduler/cron job that only runs 4 times a day will nuke your credit card. Use the right tool for the right job.