Deploying a Python Flask web app on AWS Lambda

The best server is no server?

OK, servers are still involved with “serverless computing“, but not ones that you and I need to worry about maintaining and scaling. While serverless platforms like Amazon’s AWS Lambda, Google’s Cloud Functions, IBM’s OpenWhisk, Microsoft’s Azure Functions, and others aren’t the right fit for every need, I’m beginning to think they’re applicable to more apps and services than not.

I’ve been playing around with AWS Lambda for a little while now. After getting a number of functions up and running, I started to think about how this platform might be applicable for web apps. While I’ve built a Slack app using Node.js, I have to admit I missed the elegance of Python.

I settled on getting a little web app based on a Python web framework up and running on Lambda. I wanted the deployment to be as painless as possible (i.e. easily repeatable once configured). For this exercise I didn’t care about integrating with outside services. I was fine with running against a local SQLite database, knowing I could switch over to RDS or another database at another time. Here is what I ended up selecting for this task:

I chose Flask because it’s well regarded and simple to get running. I chose the Serverless framework (versus something like Zappa) because Serverless provides the ability to deploy Python and non-Python based projects quickly while also allowing you to deploy to other cloud providers like Google, Microsoft, etc. I chose the Flaskr app because it’s simple but not too simple.

Before we get started

There’s quite a bit that needs to be setup and running before my howto below will work for you. Here is what I expect to be running:

Also, my instructions assume you’re on Linux or Mac. If you’re on Windows, you’ll need to adjust the commands for your environment.

How to get it all running

  1. Setup Serverless and the Flaskr app locally
  2. Modify SQLite code to run in Lambda (and locally)
  3. Configure the Serverless deployment
  4. Deploy to AWS
  5. Remove from AWS
1. Setup Serverless and the Flaskr app locally

In a terminal session run the following:

# Install Serverless globally
npm install -g serverless

# Create a flaskr in our home directory and clone the flaskr project
mkdir ~/flaskr
cd ~/flaskr
git init
git remote add -f origin https://github.com/pallets/flask.git
git config core.sparseCheckout true
echo "examples/flaskr/" >> .git/info/sparse-checkout
git pull origin 0.12-maintenance

# Move the flaskr project out of the examples dir & get rid of the examples dir
mv examples/flaskr .
rm -r examples
cd flaskr

# Install the Serverless WSGI plugin locally
npm install --save serverless-wsgi

# Create the Serverless AWS Python template
serverless create --template aws-python

# Add Flask 0.12.2 as a requirement
echo "Flask==0.12.2" >> requirements.txt

# Make flaskr runnable
export FLASK_APP=flaskr
sudo -H pip install --editable .

# Initialize the database
flask initdb

These commands install the Serverless framework, downloads the Flaskr app from Github, sets up the app to run with Flask, and initializes a local SQLite database with the necessary table. If all goes well, you should be able to start up Flaskr locally:

flask run

In this case I can access http://127.0.0.1:5000/ in my browser and see something like this:

2. Modify SQLite code to run in Lambda (and locally)

We need to modify the flaskr/flaskr.py file in order to get SQLite to work in Lambda. In case it wasn’t already obvious, this setup is not meant for production. With that out of the way…

Modify the replace the app.config.update section with the following to configure the SQLite database file to be created in /tmp:

flaskr/flaskr.py

app.config.update(dict(
    DATABASE='/tmp/flaskr.db',
    DEBUG=True,
    SECRET_KEY='development key',
    USERNAME='admin',
    PASSWORD='default'
))

Then look further down in the file and modify the @app.route(‘/’) section with the following:

@app.route('/')
def show_entries():
    db = get_db()
    entries = {}
    try:
        cur = db.execute('select title, text from entries order by id desc')
        entries = cur.fetchall()
    except:
        init_db()
        entries = {}
    finally:
        return render_template('show_entries.html', entries=entries)

The above change tries to query the database and if there’s an exception, will attempt to call our init_db() function to create the SQLite database and set an empty entries variable.

3. Configure the Serverless deployment

Now we need to setup our Serverless deployment by setting the serverless.yml file to:

serverless.yml

service: test

provider:
  name: aws
  runtime: python2.7

exclude:
    - node_modules/**

plugins:
  - serverless-wsgi

functions:
  flaskr:
    handler: wsgi.handler
    events:
      - http: ANY {proxy+}
      - http: ANY /

custom:
  wsgi:
    app: flaskr.app

These settings tell AWS we have a Python based function to run, we’ll use the serverless-wsgi plugin, all http requests are to be handled by the WSGI handler to run our flaskr.app. Note we need to explicitly set events to respond for “/” even though we have the catch all entry of {proxy+} set. If we don’t, AWS API Gateway will return an error of Missing Authentication Token.

Let’s test this locally one last time before we deploy to AWS:

sls wsgi serve

You should get a message telling you where the Flask app is running (e.g. http://localhost:5000) to the one above, complete with a locally running instance of the flaskr app.

4. Deploy to AWS

Now we’re ready to deploy to AWS:

sis deploy -v

This can take a little bit to run, as this single command takes care of all the AWS setup for us to access our flaskr app via Lambda. When successful, you should see output towards the bottom similar to the following:

Stack Outputs
FlaskrLambdaFunctionQualifiedArn: arn:aws:lambda:us-east-1:770800358818:function:test-dev-flaskr:31
ServiceEndpoint: https://zwe667htwd.execute-api.us-east-1.amazonaws.com/dev
ServerlessDeploymentBucketName: test-dev-serverlessdeploymentbucket-s03hsyt0f0oz

Copy the URL from the ServiceEndpoint and access it in your web browser. You should see something familiar:

If you don’t get a response or see an error of some sort, it’s time to look at the logs on AWS:

sls logs -f flaskr -t

If you want to make changes to the app (e.g. change templates, modify routes in flaskr/flaskr.py, etc.) and don’t need to make changes to your serverless.yml file, you can do a much quicker re-deploy of the code (i.e. no AWS configuration):

sls deploy function --function flaskr
5. Remove from AWS

Once you’re done with these steps, it’s a good idea to remove your function so that you don’t get unexpectedly charged for anything related to this deployment. Don’t fret, it’s only one line to get it all running again. 🙂

sls remove

Where to from here?

I may get this example working with a proper database. If I do, I’ll be sure to add a new post explaining how I did that.

Join the Conversation

3 Comments

  1. Hi, thanks for the tutorial. I am getting an error when trying to run the app using the sls wsgi serve command. I get AttributeError: module 'flaskr' has no attribute 'app'. Am I missing a step here?

Leave a comment