In which I compare deploying Flask Apps to AWS Lambda with Zappa and Serverless Framework
divporter / flask-lambda
Deploying a Flask App to AWS Lambda
One of the first programming languages I learned was Python. The first version (v0) of my hobby site VeganFeeds however was written in PHP with an Apache server connected to a PostgreSQL database serving up dynamically rendered HTML. In the second version, confusingly named v1, I moved from the traditional LAMP stack (or LAPP in my case) to the JAM stack. Being familiar with Python, I decided that Flask would be the 'A' in my JAM stack.
After having built the Flask app, instead of continuing with a traditional web server I decided to give the serverless approach a try. Now that I've finished my trip down memory lane, let's skip ahead to deploying a Flask app to AWS Lambda.
While SAM CLI can deploy Python Lambdas, you can't deploy a Flask app straight out of the box. Although you can with a little help from flask-serverless. I have not used it personally so I won't review it here.
Zappa
Given an existing Flask app, Zappa is the easiest way to get your Flask app up there. And when I first discovered Zappa I knew next to nothing about the AWS ecosystem and so it was a friendly resource to enable me to get my Flask app out into the wild.
It is as simple as:
$ pip install zappa
$ zappa init
$ zappa deploy
Unfortunately though it's never that easy. The above assumes that you have installed your dependencies in a virtual environment within the project directory. So if you haven't done that you will get an error like below:
Error: Zappa requires an active virtual environment!
So let's remedy that by setting up a virtual environment and installing our dependencies into it.
$ python3 -m venv venv
$ source venv/bin/activate
$ pip install zappa flask
OK, so now we're ready to deploy. If we run zappa deploy
again we now face a new error.
Error: This application is already deployed - did you mean to call update?
As featured as Zappa is, I find having separate commands for an initial deploy and updating an existing deployments annoying. Anyway, let's do it. Let's call zappa update
. And hooray it works.
Your updated Zappa deployment is live!: https://051zc5ynck.execute-api.ap-southeast-2.amazonaws.com/dev
Let's have a look in the browser.
Chances are though, if you're building an app beyond "hello world" you may end up adding a dependency that works fine on your development OS but is incompatible in the Lambda environment. Then you're going to need to mimic the Lambda environment when installing your dependencies with docker using the very handy Lambda Docker images built by the lambci team. So you could do something like:
$ pip freeze > requirements.txt
$ docker run -ti -v $(pwd):/var/task --rm lambci/lambda:build-python3.6 bash -c "python3 -m venv zappa_venv && source zappa_venv/bin/activate && pip install -r requirements.txt"
Now the dependencies are built in the correct environment and when you run zappa deploy
your Lambda should now work again. But that's a great big ugly command. Sure you use a Dockerfile, but if you're unfamiliar with Docker then that's going to make things harder.
Serverless Framework
Now despite all that Zappa bashing it was around when there was little else and really stretched the capabilities of CloudFormation and did a lot of heavy lifting behind the scenes before native support was rolled out as standard. It also comes with a lot of handy features such as the keep_warm
feature to get around the cold start issue.
The Serverless Framework is a more generic tool. Not only covering languages other than Python, such as Node.js and Ruby, but also other Function-as-a-Service (FaaS) providers such as Google Cloud and Microsoft Azure.
Installation-wise the Serverless Framework is not as friendly to set up the first time as Zappa. Obviously aside from a Python installation you will also need Node.js installed as a prerequisite.
Assuming you have node next is to install serverless via npm which ships with Node.js
$ npm install -g serverless
The -g
flag means it is now available globally and you need not install it again for future projects. OK, so now what?
$ serverless create --template aws-python3
OK cool, so we have a few files now. handler.py
,.gitignore
and serverless.yml
Focusing on handler.py
, this file reveals the bare bones of a Python Lambda handler. So how do we squeeze our Flask app into this? Well we're actually not going to use it at all. We're going to use a plugin. The Serverless community is very active with a slew of plugins to keep things nice and simple. The serverless-wsgi plugin like Zappa is designed for deploying Python WSGI apps to Lambda and abstracting away the handler protocols. You'll notice at the bottom of the link that the plugin draws inspiration from Zappa. To add the plugin you'll need to run
$ sls plugin install -n serverless-wsgi
Note that sls
is a handy alias for serverless
.
Next we need to add a few lines to the bottom of our serverless.yml
file.
custom:
wsgi:
app: app.app
Next we need to modify our serverless.yml
in the functions section so that it looks like this
functions:
hello:
handler: handler.hello
api:
handler: wsgi_handler.handler
events:
- http: ANY /
- http: ANY {proxy+}
Unlike Zappa there is no dependency on running a virtual environment, however serverless-wsgi
relies on the virtualenv
module. In case you don't have it installed simply run
$ pip install virtualenv
followed by
$ sls deploy
With any luck the output will now show the url for our newly created api
And if we open in a browser...
It works great. Now what if we want to add a second exclamation and say 'Hello, World!!'. Unlike Zappa we can just run sls deploy
again. The deploy
command handles both creation and updates. Lovely!
OK, but what about my dependencies that I need docker for? Well thankfully there's a plugin for that. serverless-python-requirements will do the Docker/lambci stuff for you. It does other nifty stuff too, like zipping them up to reduce their size or even throwing them in a layer for you!
What about Chalice?
Chalice is the offical AWS tool for deploying Python Lambdas. It has a similar syntax to Flask (and other Python WSGI frameworks), so for Flask users it will feel comfortable. But unlike Zappa or Serverless you can't use Chalice to deploy an existing Flask app. You must refactor it first.
Chalice is not as configurable as Zappa and Serverless, so you will not get to enjoy all the features of a Flask App along with the flexibility of Zappa or Serverless. For starting a simple project Chalice will help you get up and running quickly. Then if you find it doesn't meet your needs then you can look to Zappa and Serverless.
Top comments (0)