Flask is a Python web micro-framework. I’ll not go into details too much, you can read more in the docs if you are interested. What I’m going to use it for is to make a small Flask-based (and thus Python-based) test bed.
Here are the steps needed to get Flask up and running on Xubuntu 12.04. It should be straightforward on other systems, too, but bear in mind there might be differences.
On Xubuntu systems, Python is installed by default – on 12.04 you have this version:
If you don’t have it for some reason:
sudo apt-get install python
$ sudo apt-get install virtualenvwrapper
You’ll notice when you run the above that you also install some other packages:
The following extra packages will be installed: libjs-sphinxdoc libjs-underscore python-pip python-setuptools python-virtualenv
The important ones for our story are the python-* ones:
- pip is a python package manager
- setuptools is used by pip
- virtualenv is of course the virtual environment manager I mentioned above
Now, one note for Xubuntu. When you install virtualenvwrapper package, it installs some docs here:
which is similar to this online doc page. In both of these, it says that you should do something like this:
$ pip install virtualenvwrapper ... $ export WORKON_HOME=~/Envs $ mkdir -p $WORKON_HOME $ source /usr/local/bin/virtualenvwrapper.sh $ mkvirtualenv env1 Installing ...
This actually will not work in Xubuntu, at least in the current version that I have installed, which is 2.11.1-2. The thing is, there’s a small file here:
that explains Debian differences. What is happening here is that Debian package actually puts the file in this location:
and is intended to be used with bash-completion package. This works fine for me, but if it doesn’t work for you, you can always manually source this file as explained in the docs (and also the above README.Debian file):
$ source /etc/bash_completion.d/virtualenvwrapper
and you are all set to go.
Note that in either case you need to set up virtualenvwrapper – this is explained in /etc/bash_completion.d/virtualenvwrapper, here’s how I have it configured:
$ mkdir $HOME/.virtualenvs $ echo 'export WORKON_HOME=$HOME/.virtualenvs' >> ~/.bashrc
You need to start a new bash session to pick up the .bashrc config – it will do something like this:
$ bash virtualenvwrapper.user_scripts creating /home/icyrock.com/.virtualenvs/initialize virtualenvwrapper.user_scripts creating /home/icyrock.com/.virtualenvs/premkvirtualenv virtualenvwrapper.user_scripts creating /home/icyrock.com/.virtualenvs/postmkvirtualenv virtualenvwrapper.user_scripts creating /home/icyrock.com/.virtualenvs/prermvirtualenv virtualenvwrapper.user_scripts creating /home/icyrock.com/.virtualenvs/postrmvirtualenv virtualenvwrapper.user_scripts creating /home/icyrock.com/.virtualenvs/predeactivate virtualenvwrapper.user_scripts creating /home/icyrock.com/.virtualenvs/postdeactivate virtualenvwrapper.user_scripts creating /home/icyrock.com/.virtualenvs/preactivate virtualenvwrapper.user_scripts creating /home/icyrock.com/.virtualenvs/postactivate virtualenvwrapper.user_scripts creating /home/icyrock.com/.virtualenvs/get_env_details virtualenvwrapper.user_scripts creating /home/icyrock.com/.virtualenvs/premkproject virtualenvwrapper.user_scripts creating /home/icyrock.com/.virtualenvs/postmkproject virtualenvwrapper.user_scripts creating /home/icyrock.com/.virtualenvs/prermproject virtualenvwrapper.user_scripts creating /home/icyrock.com/.virtualenvs/postrmproject
and you should be good to go.
Let’s start a sample python environment:
$ mkvirtualenv default New python executable in default/bin/python Installing distribute.............................................................................................................................................................................................done. Installing pip...............done. virtualenvwrapper.user_scripts creating /home/icyrock.com/.virtualenvs/default/bin/predeactivate virtualenvwrapper.user_scripts creating /home/icyrock.com/.virtualenvs/default/bin/postdeactivate virtualenvwrapper.user_scripts creating /home/icyrock.com/.virtualenvs/default/bin/preactivate virtualenvwrapper.user_scripts creating /home/icyrock.com/.virtualenvs/default/bin/postactivate virtualenvwrapper.user_scripts creating /home/icyrock.com/.virtualenvs/default/bin/get_env_details
Note that whenever you have a fresh bash run, no virtualenv will be initialized. Do this if so:
$ workon default
This will initialized the virtualenv we aptly named “default”.
Finally, let’s install Flask and its dependencies:
$ pip install flask Downloading/unpacking flask Downloading Flask-0.9.tar.gz (481Kb): 481Kb downloaded Running setup.py egg_info for package flask ... Cleaning up...
Let’s do a quick test that all is OK. Following [this guide] from Flask itself, do this:
$ cat >flask_hello_world.py from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello World!' if __name__ == '__main__': app.run() ^D $ python flask_hello_world.py * Running on http://127.0.0.1:5000/
(Note for those not familiar – ^D above means press Ctrl+D to send EOF to cat). Browse to http://localhost:5000 and you should see “Hello World!” written to confirm Flask is installed and all OK.
Flask + gevent + gunicorn
Whenever you restart the machine you are working on, to run a Flask application, you need to:
- Start your terminal (usually a bash session these days)
- Initialize a Python virtualenv (“default” in our case) by running “workon default”
- Run “python application-name.py”
This can be scripted, but you at least need to do something manually. If you are developing an application that should be running all the time, you obviously don’t want to have manual steps. This can be solved in different ways, such as:
- Run the server on startup (e.g. using upstart)
- Run it when the user logs in (e.g. Xubuntu has a convenient Session and Startup app in Session Manager)
- Use xinetd
- Use a standalone server, such as the ones described in Flask’s deployment options page
I opted for the last one. It requires some sort of communication between the server and the Flask application. When it’s for Python applications, WSGI is typically used for this purpose. I found this page describing different WSGI servers and measuring their performance in depth. It’s a couple of years old, but it’s a great write up nonetheless. The conclusion was that gevent is the best option. Obviously, far from that I need it in this case, but it’s a good exercise for the future.
Flask’s deployment page also has a gevent setup part. To make a gevent front-end for our hello world application, put this in a file called flask_hello_world_gevent.py in the same folder where flask_hello_world.py lies:
from gevent.wsgi import WSGIServer from flask_hello_world import app http_server = WSGIServer(('', 5000), app) http_server.serve_forever()
For the above to work, you’ll have to install libevent-dev and gevent:
$ sudo apt-get install libevent-dev ... $ pip install gevent ...
Now you can run it:
$ python flask_hello_world_gevent.py 127.0.0.1 - - [2012-07-14 16:41:40] "GET / HTTP/1.1" 200 12 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:13.0) Gecko/20100101 Firefox/13.0.1"
The above shows the first request, too. You should get the familiar “Hello World!” text at this point by going to http://localhost:5000/.
The problem with the above is that it’s not really a server – you still need to run it via python. Not that this cannot be scripted, but there’s a better option – use Gunicorn with gevent. So install it:
$ pip install gunicorn
and start it:
$ gunicorn flask_hello_world:app -kgevent 2012-07-14 17:46:12  [INFO] Starting gunicorn 0.14.5 2012-07-14 17:46:12  [INFO] Listening at: http://127.0.0.1:8000 (17983) 2012-07-14 17:46:12  [INFO] Using worker: gevent 2012-07-14 17:46:12  [INFO] Booting worker with pid: 17986
Again, you should get the overly familiar “Hello World!” text, only now by going to http://localhost:8000/. Type “gunicorn -h” for a full page of options gunicorn supports, some of which can be used for controlling things such as the number of worker processes, bind address / port, logging files, etc.
Starting up applications with upstart
Upstart is useful when you have a need to manage process lifecycle, i.e. start / stop on boot or some events. It’s a good solution for having our Gunicorn server auto-started when the machine starts. As of version 1.3, Upstart supports user-level services and as of version 1.4 it supports setuid / setgid, which will be useful here to set the user / group. Let’s create a service called gunicorn:
$ cat|sudo tee /etc/init/gunicorn.conf setuid icyrock.com setgid icyrock.com script export HOME=/home/icyrock.com . $HOME/.virtualenvs/default/bin/activate cd $HOME/web/flask gunicorn flask_hello_world:app -kgevent end script ^D
Now we can start and stop the server easily with start / stop commands and get its status with status:
$ sudo start gunicorn gunicorn start/running, process 18790 $ status gunicorn gunicorn start/running, process 18790 $ sudo stop gunicorn gunicorn stop/waiting $ status gunicorn gunicorn stop/waiting
After “start unicorn”, you should get now annoyingly familiar “Hello World!” text at http://localhost:8000/.