Update: After receiving some feedback, that it is not obvious if you need to use Python 2 or 3 for this article, I decided, that I want to update it all the way for Python 3. AWS Lambda supports it, Zappa supports it and we all want Python 3!
I wanted to try out AWS Lambda for a while now. But it took some time to find a reasonable development task that fits this infrastructure. Serverless deployment is very intriguing, escpecially for someone like me that doesn’t enjoy to fiddle with servers, operating systems and stuff.
No, I won’t reveal any detals about my project yet. But I want to share my experiences building a simple webapp – the good old hello world example – and deploy it on AWS Lambda.
Lambda functions can be created using Python, node, Java and C# at the moment. (Take a look at the document about the supported execution environments.) Obviously my choice is Python.
And as I plan to deploy a whole web application and not just a simple function, I need some more incredients to build it.
- Zappa – A framework to easily build and deploy serverless web services with Python.
- Flask – The other awesome web framework for Python besides Django. For the project I have in mind, it seems to be the better choice. And I wanted to build something with Flask anyway.
- awscli – The AWS Command Line Interface (CLI) is a unified tool to manage your AWS services.
Step 1: Set up the Python environment
I assume, that you are using macOS and Homebrew is installed on your development machine. Currently I am a big fan of pipenv to manage project dependencies and the virtual environment for projects.
Open a terminal and execute the following commands. Afterwards we have a nice and clean Python installation along with some essential tools.
# install python brew update brew install python3 # install and upgrade some essential packages pip install --upgrade pip setuptools pipenv # optionally install some handy development tools pip install flake8 autopep8 ipython cookiecutter
If you are running Windows or Linux, you need to configure the Python environment on your own.
Step 2: Setup the project and create the web application
In this step we will setup our new project and create the trivial web application. First, we have to execute some shell commands to create our new project.
# create an empty directory for our project mkdir lambda-hello-world cd lambda-hello-world # Init the Pipfile and a virtual environment for the project. pipenv --three # activate the virtual environment pipenv shell # install Zappa and Flask along with the AWS command line tools pipenv install zappa flask pipenv install --dev awscli # optionally install some handy development tools inside the virtual environment pipenv install --dev flake8 autopep8 ipython
Next we create a file named hello_world.py
inside the directory lambda-hello-world
with the following content. Nothing tricky, just a trivial Flask web application to return the string “Hello, world!”.
from flask import Flask app = Flask(__name__) @app.route('/') def index(): return "Hello, world!", 200 # We only need this for local development. if __name__ == '__main__': app.run()
Finally, we can fire up our local debug server and test our simple web application.
export FLASK_APP=hello_world.py flask run
You can reach it by opening the url http://127.0.0.1:5000/ in your browser.
Step 3: Configure your AWS credentials and default region
Before we deploy our web application, we have to make sure we have a valid AWS account and our AWS credentials and default region is properly configured on the development machine.
All this can be done with the AWS Command Line Tools as described in the document Configure the AWS command line interface. Basically we just have to run the command aws configure
in our project directory and fill in the access key id, the secret access key and the default region.
Now our AWS account is setup on our development machine and we can start to configure Zappa and deploy our application to AW.
Step 4: Configure Zappa for our project
So far we have a setup our project, added a simple web application and configured our AWS account. Now, we can prepare our project to be managed and deployed by Zappa. In order to do this, we just have to run the following command inside the project directory.
zappa init
It creates a file named zappa_settings.json
inside your project directory with the following content. That is all we need to deploy our app.
{ "dev": { "app_function": "hello_world.app", "s3_bucket": "zappa-some-random-id", "runtime": "python3.6" } }
Of course Zappa offers a lot more configuration options for bigger projects.
Step 5: Deploy the web application
Last but not least, we can deploy the app and enjoy our simple, (extremly :))scalable “Hello , World!” application.
zappa deploy
What’s next?
Well, now it is about time to built something useful with this awesome stack. I really like how you can built fast and scalable web apps which access a lot of other AWS services like Rekognition, DynamoDB and so on. You can expect some more blog posts out my adventures in Lambda land in the near future.
Image credit: Qfamily. Shared under the Creative Commons Attribution 2.0 Generic license.
Thanks for this post, I found it helpful.
One remark: maybe you could make it explicit that the project HAS to be setup using python 2.7… I set it up using 3.5 first, because from the way you wrote it it did not seem mandatory to use 2.7.
Thanks for the feedback. I will update the post later today.
Do you know if Zappa handles flask-wtf, flask-bootstrap and/ or flast-restful?
I have successfully used flask-restful and flask-wtf.
Is it possible to use environment variables in the flask app with this set up?
Yes, Zappa supports this out of the box. https://github.com/Miserlou/Zappa#setting-environment-variables
Sorry for long post, but am banging my head against this, so any help much appreciated. Error at the bottom – top shows system details in case helpful. Any pointers at all would be well received!
(venv) root@osmc:/opt/alexa-kodi/kodi-alexa-master/venv/bin# uname -a
Linux osmc 4.4.27-9-osmc #1 SMP PREEMPT Tue Mar 14 20:54:19 UTC 2017 armv7l GNU/Linux
(venv) root@osmc:/opt/alexa-kodi/kodi-alexa-master/venv/bin# which python
/opt/alexa-kodi/kodi-alexa-master/venv/bin/python
(venv) root@osmc:/opt/alexa-kodi/kodi-alexa-master/venv/bin# /opt/alexa-kodi/kodi-alexa-master/venv/bin/python -V
Python 3.4.2
(venv) root@osmc:/opt/alexa-kodi/kodi-alexa-master/venv/bin# which zappa
/opt/alexa-kodi/kodi-alexa-master/venv/bin/zappa
(venv) root@osmc:/opt/alexa-kodi/kodi-alexa-master/venv/bin# zappa -v
0.42.0
(venv) root@osmc:/opt/alexa-kodi/kodi-alexa-master/venv/bin# more zappa_settings.json
{
“dev”: {
“app_function”: “alexa.app”,
“aws_region”: “eu-west-1”,
“profile_name”: “default”,
“s3_bucket”: “BUCKET_ID_REMOVED ”
}
}
(venv) root@osmc:/opt/alexa-kodi/kodi-alexa-master/venv/bin# zappa deploy
Calling deploy for stage dev..
Oh no! An error occurred! 😦
==============
Traceback (most recent call last):
File “/usr/lib/python3.4/distutils/dir_util.py”, line 70, in mkpath
os.mkdir(head, mode)
FileExistsError: [Errno 17] File exists: ‘/tmp/1495702120/gunicorn’
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File “/opt/alexa-kodi/kodi-alexa-master/venv/lib/python3.4/site-packages/zappa/cli.py”, line 2353, in handle
sys.exit(cli.handle())
File “/opt/alexa-kodi/kodi-alexa-master/venv/lib/python3.4/site-packages/zappa/cli.py”, line 456, in handle
self.dispatch_command(self.command, stage)
File “/opt/alexa-kodi/kodi-alexa-master/venv/lib/python3.4/site-packages/zappa/cli.py”, line 490, in dispatch_command
self.deploy()
File “/opt/alexa-kodi/kodi-alexa-master/venv/lib/python3.4/site-packages/zappa/cli.py”, line 650, in deploy
self.create_package()
File “/opt/alexa-kodi/kodi-alexa-master/venv/lib/python3.4/site-packages/zappa/cli.py”, line 1945, in create_package
output=output
File “/opt/alexa-kodi/kodi-alexa-master/venv/lib/python3.4/site-packages/zappa/core.py”, line 480, in create_lambda_zip
copy_tree(temp_package_path, temp_project_path, update=True)
File “/usr/lib/python3.4/distutils/dir_util.py”, line 168, in copy_tree
verbose=verbose, dry_run=dry_run))
File “/usr/lib/python3.4/distutils/dir_util.py”, line 141, in copy_tree
mkpath(dst, verbose=verbose)
File “/usr/lib/python3.4/distutils/dir_util.py”, line 74, in mkpath
“could not create ‘%s’: %s” % (head, exc.args[-1]))
distutils.errors.DistutilsFileError: could not create ‘/tmp/1495702120/gunicorn’: File exists
==============
Need help? Found a bug? Let us know! 😀
File bug reports on GitHub here: https://github.com/Miserlou/Zappa
And join our Slack channel here: https://slack.zappa.io
Love!,
~ Team Zappa!
(the file does exist, but each time I run this, it creates a new one, owned by root, e.g.
-rwxr-xr-x 1 root root 257 May 25 08:28 /tmp/1495702120/gunicorn
Having spent hours on this, I think I spotted the mistake within seconds of posting here. I’m running as root. Something seems unhappy with this, as I’m getting more useful errors when running as a less-privileged user. Sorry for the bother, I hope this helps someone else
Hey Oliver, I have browsing for a long time for easy to understand Zappa intro tutorial for a python beginner like me and your blog has been the most helpful. I loved the way you simplified the learning. Thanks a ton!
Hi there. Thanks for the tutorial, I found it useful. Just thought I’d mention that using Homebrew 1.2.4 on macOS Sierra 10.12.5, python3 gets installed as…. python3, with pip installed as pip3.
Thus if I were to copy and paste your example commands it would all be using python 2.7, which I believe is not what you want here. (I will probably add an alias as I don’t think I really need python 2.7 for anything, I’m just building Lambda apps.)
Thank you! Followed the post and it worked well for me.
This is what i am getting while trying to deploy, it’s the same with django.