Fast Python deployments on Elastic Beanstalk

Patryk Zawadzki
Blog | Mirumee
Published in
3 min readJun 20, 2016

It should come as no surprise that some of the applications we build at Mirumee are hosted on Amazon’s architecture. Elastic Beanstalk gives us the ability to deploy and scale easily but the machines we choose for web workers are not necessarily great at compiling binaries. And compiling binaries is what inevitably happens when you install project’s dependencies from PyPI.

In our case it took 8 minutes to add a new machine to the load balancing pool. Here’s what we did to bring it down to 2 which is little more than it takes the machine to boot for the first time.

While compilation is necessary it’s only necessary once. Doing it on every machine and for every deployment not only wastes precious time but also introduces risk of two machines running slightly different code. We can do better than that. We can do all of this work ahead of the time and just unpack pre-built packages at install time. And it’s not that hard.

Get a custom package index

The first tool we need is a custom Python package server for pip to use. In our case we went with devpi but there are others. Whichever tool you choose, create a tiny EC2 instance and use the docs to get your server working there.

Note: while this solution requires you to maintain an extra machine, even a t2.nano instance should be enough to satisfy the requirements. In an auto-scaling environment the cost of such machine is nothing compared to other resources.

Build wheels of your dependencies

The second tool is the wheel package. It will allow pip to build wheels —pre-compiled versions of Python eggs. Make sure you’re using at least pip version 8 and have the latest versions of wheel and setuptools packages:

$ pip install --upgrade pip setuptools wheel

Once you have the packages installed, you can ask pip to build a package and all of its dependencies:

$ pip wheel pycrypto

For custom packages that contain a setup.py it’s equally easy:

$ python setup.py bdist_wheel

It’s up to you to choose whether you want to build all of your project’s dependencies (which may be a good idea for repeatable installs) or just the ones that contain compiled code (Pillow is a common first candidate).

Note: wheels are architecture-specific so make sure your development machine is running a binary-compatible version of Linux. Use Docker if you’re on a different operating system.

Upload wheel files to your package index

When you’re done building packages upload them to your package index. In case of devpi it’s as simple as:

$ devpi upload wheelhouse/*.whl

Make sure your machines have recent pip

At the time of this writing Elastic Beanstalk’s Python images come with pip version 7 which is not recent enough. Luckily this is quite easy to fix using .ebextensions:

commands:
update_pip:
command: "/opt/python/run/venv/bin/pip install --upgrade pip"

Have your machines use your package index

This one requires dropping in a pip configuration file to the root’s home directory and is also achieved using .ebextensions:

files:
"/root/.pip/pip.conf":
mode: "000744"
owner: root
group: root
content: |
[global]
index-url = http://example.com/your/package/index/
trusted_host = example.com

Note: remember to replace host names and paths with proper values.

Test it

Deploy the updated version of the app and tell Elastic Beanstalk to provision a new machine. You can use Elastic Beanstalk’s log viewer to confirm that the installation process is using your local package index and that it’s lightning-fast.

--

--