Tilegarden: Serverless Tile Rendering with AWS Lambda

By Matt Delsordo on September 6th, 2018

This summer, Matt joined the team as an Azavea Open Source Fellow to work on Tilegarden, which enables serverless Lambda-based raster and vector tile generation from PostGIS data. The Azavea Open Source Fellowship program is a 12-week professional training fellowship that matches software engineering fellows with open source projects at Azavea.


“Serverless” has been a popular computing buzzword ever since its inception several years ago. For those out of the loop, serverless computing refers to a kind of cloud-computing service where the service provider (Amazon Web Services, Microsoft Azure, etc.) manages computing infrastructure on the customer’s behalf. The developer writes their code and uploads it into an environment where it can be executed on demand, without having to deal with the often tedious process of server management. Tilegarden, the project I worked on this summer as part of Azavea’s 2018 Open Source Fellowship, is a tool that takes this paradigm and applies it to rendering map tiles. The project is a expansion on Azavean Matt McFarland’s proof of concept project Lambnik, written in Node.js to take advantage of more robust map tiling libraries.

But why?

At its core, Tilegarden is a single, stateless function that accepts parameters like coordinates, database credentials, and stylesheets and returns a map tile. Each time a deployed instance of Tilegarden receives a tile request, AWS Lambda creates a temporary environment in which to run that function, does the required computing, and then recycles the environment’s resources for use elsewhere in their ecosystem. This means that:

  • You don’t have to worry about provisioning server resources, since AWS handles it automatically.
  • You don’t have to worry about scaling, since as many execution contexts as necessary are created in order to handle all of your requests.
  • You only pay for the actual tile rendering that gets done, rather than a flat rate for server uptime.

These features are really useful in general but fit particularly well with map rendering needs, especially those of projects with relatively niche target demographics or moderate usage expectations. At Azavea, for example, many projects are very Philadelphia-centric, which means that most visitors will be visiting from the Philadelphia region during the day. At night, it stands to reason that traffic will slow down as the target demographic goes to sleep, but you tend to need to pay for your tile server to be up and running 24/7 on the off chance some Australian visits your project at 3:26 in the morning. Using Tilegarden, this stops being a worry, since you’re paying for “delivery of content”, not “availability to deliver content”.

Another benefit of serverless computing is its resilience to sudden traffic spikes. Say the previous example’s project suddenly goes viral for whatever reason (maybe that Australian happened to be impressed and made a popular tweet). Many a server has succumbed to the internet’s “hug of death” – when a server goes down due to a lack of resources to support the number of requests for content it receives. Going serverless with your tile rendering reduces this risk since computing power is allocated as-needed.

Tilegarden doesn’t just have to be used to serve tiles for a web map. You could use Tilegarden as a means to populate an S3 cache of map tiles and take advantage of the pay-per-use billing model to cheaply generate tiles that have been requested but are missing from the bucket. You could also use Tilegarden as a single-instance bulk tile rendering tool, taking advantage of the parallelism that Lambda affords by rendering far more tiles at once than an equally-priced tile server could handle.

What can it do?

A target use case for this project was for it to act as a serverless alternative to Windshaft, a tile server project managed by Carto that is also open source on GitHub and written in the same language with the same mapping libraries. I’ve compared Tilegarden’s functionality at the time of writing to that which Windshaft advertises on their GitHub page:

Windshaft vs Tilegarden

The goal was pretty well met: Tilegarden supports styling with CartoCSS (in addition to default Mapnik XML), supports run-time reprojections, can render both raster tiles…

Tilegarden raster tile support

…and Mapbox vector tiles…

Tilegarden Mapbox vector tile support

…and can serve UTF grids to facilitate interactivity.

Tilegarden UTF grid support

There’s also built-in support for filtering map data by pre-specified map layer, with client specified filtering on its way imminently.

Tilegarden map layer filteringTilegarden’s main draw over something like Windshaft is it’s serverless-ness, which besides the aforementioned scaling and pricing benefits allows for a greater degree of configuration at tile-render-time, since unique configurations can be specified for each API call, rather than once at the point where you start the server. In fact, you could hypothetically use one deployed instance of Tilegarden to serve a large number of projects at once, simply by adding customized map configuration files to an S3 bucket and pointing Tilegarden at them through the API.

How much cheaper is it, actually?

The answer is: it depends on what your goals are. For comparison, the following graphs compare pricing for AWS Lambda to that for certain EC2 instances, per month:

Chart of tiles served vs hosting cost

The graph says that, if your tiles take less than 128MB of memory and one second to render each, you can render about 5.8 million tiles a month for the same price as hosting Windshaft on one of EC2’s T2 Medium instances. If your performance needs are more in line with a T2 Large, that will get you around twice as many tiles per month at 11.7 million.

AWS Lambda charges per execution at a rate relative to the time a function takes to execute (how long it takes a tile to render) and how much memory you’ve allocated to the function (not how much it actually uses). That last bit is the biggest influence on the price – allocating 384MB for a complex tile decreases your potential output to only about half a million tiles – but the good news is that performance can be improved by intelligently configuring and requesting your tiles. Even then, half a million tiles per month is nothing to scoff at, and keep in mind that AWS Lambda’s resource isolation would allow it to bear the load of all half-million requests simultaneously by queuing requests that surpass its concurrency limits.

Trade-offs

Serverless tile rendering is not a good fit for every project. The key down side is performance optimization – since functions are stateless and executed in isolation, they are unable to share resources and have to perform tasks like parsing configuration files, loading external style resources, and establishing database connections on a per-API-call basis. Tilegarden is also impacted by AWS Lambda’s cold-start time – the time it takes to provision a new environment for a function that hasn’t been run in a while. This cold-start time scales with the size of the uploaded code bundle, which in Tilegarden’s case is nearing AWS Lambda’s size cap (Mapnik as a dependency is gigantic!). Overall, even though using AWS Lambda gives you potentially higher throughput, per-tile performance can be worse than a stateful, persistent tile server.

Development Process

Tilegarden is, in large part, a wiring of Mapnik’s (a tile rendering library) Node bindings to a serverless API. Ignoring dependencies, the actual codebase is pretty small; AWS and Mapnik do all the heavy lifting. Mapbox’s utility carto was used to streamline the specification of map configurations, and we aimed to lean on it in order to let users do anything with Tilegarden that you can tell Mapnik to do with carto. ClaudiaJS was our serverless deployment utility/framework of choice, and was chosen due to its easy integration into an already NPM (or yarn) based project.

On the AWS end, Lambda is obviously the core of the project. API Gateway is the service used   for defining an API for Tilegarden’s Lambda function, something ClaudiaJS has built-in, express-like support for. One of the project’s biggest roadblocks was the way API Gateway currently handles binary response data, which prevents requested images from loading properly inside of <img /> tags. This breaks web mapping tools like Leaflet, but we managed to circumvent the problem by including an AWS CloudFront deployment that adds proper Accept headers to requests to the API.

One other dependency of note was claudia-local-api, which plugs ClaudiaJS’s claudia-api-builder utilities into express in order to locally mock a serverless deployment. Usually, when you work with serverless projects, you test your code by essentially unit testing your single stateless function. You can’t really do that, though, if your function’s output is primarily binary content, so we used claudia-local-api to run code locally and visually check tiles. claudia-local-api is a community project rather than supported by the ClaudiaJS developers, so I ended up creating a fork in order to add support for some Tilegarden use-cases that the tool’s creator didn’t account for. Thank goodness for open source software.

Conclusion

Tilegarden has shaped up to be a really promising tool for a variety of GIS hosting applications. If you want to test out some of the demos I’ve shown off in this blog post, check out the project’s webpage at azavea.github.io/tilegarden! If you’re interested in using a tool like this in your own project, check it out on GitHub (github.com/azavea/tilegarden) where its available for download and where you can post issues and make contributions.

I want to give a big Thank You to my mentors Klaas Hoekema and Matt McFarland for all of their guidance over the course of this project’s development, and also to everyone at Azavea for the opportunity to be a part of this year’s Open Source Fellowship, and for making being there a really great time!