Previously, we left our app working with our local python+gunicorn+nginx installation. In order to get there we had to do quite a bit of configuration and if we wanted to deploy this in a server or send it to a friend, we would have to go through a very error-prone process subject to version changes and missing libraries. A potential nightmare if we contemplate switching from one operating system to another. Is there a way in which we could combine our code and configuration in a single easy to deploy multi-platform package?
One solution for this is to create a single Docker container that, when run, will create the environment and deploy our code in a controlled environment.
In the Docker hub you will find thousands of preconfigured containers. The best way to start is to find the closest one that would suit us and customize it. That way you avoid laying the ground work and just focus on the specifics of your application.
I tend to trust the containers built by larger vendors, organizations or open-source projects, because I find that they usually keep their containers up to date and –most importantly– they are heavily battle-tested in dev and production.
In this case, I chose a gunicorn container created by the Texas Tribune. To start, you download and install Docker, and then download your chosen container to your machine.
The way to customize a Docker container is to edit the Dockerfile. There you will specify commands to install, copy or run files specific to your project. In our case, I added an installation of python-dev, falcon and the google or-tools:
#install whats neccessary for or-tools RUN pip install --upgrade pip RUN pip install --upgrade wheel setuptools virtualenv RUN apt-get -y install flex bison RUN apt-get -y --fix-missing install autoconf libtool zlib1g-dev texinfo help2man gawk g++ curl texlive cmake subversion #install gunicorn and falcon for providing API endpoints RUN pip install gunicorn==19.6 RUN pip install falcon #install or-tools #https://github.com/google/or-tools/releases/download/v5.0/or-tools_python_examples_v5.0.3919.tar.gz ADD or-tools_python_examples_v5.0.3919.tar.gz /app RUN mv /app/ortools* /app/ortools && cd /app/ortools/ && python setup.py install --user
Then I created separate configuration files for gunicorn and nginx, and a couple of supervisor configurations. Supervisor will restart the services in case one of them goes down, which might happen if I introduce an unrecoverable error in the python script:
#copy configuration files ADD gunicorn_conf.py /app/ ADD gunicorn.supervisor.conf /etc/supervisor/conf.d/ ADD nginx.conf /app/ ADD nginx.supervisor.conf /etc/supervisor/conf.d/
After the initial configuration, we build using the docker build command:
docker build --no-cache -t danielpradilla/or-tools .
And then, we run the container as a daemon:
docker run -p 5000:80 -d --name or-tools-gunicorn danielpradilla/or-tools
The web server port is specified as a parameter. This maps port 5000 in localhost to port 80 in the container.
Now, time to install our code. You can copy your code to the Docker container, but what I prefer is to have my code in a local folder in my machine, outside##italics of the Docker container. That way, I don’t need to copy the code to the container every time I change it, and I keep a single unmistakable copy of the code.
To do this, you mount the local folder as an extra folder inside the container. Change the Dockerfile and add
VOLUME ["/app/logs","/app/www"]
And then when, you run the container, you specify the location of your local folder:
docker run -v :/app/www -p 5000:80 -d --name or-tools-gunicorn danielpradilla/or-tools
This will allow you to experiment with multiple versions of the code (production and development) with a simple parameter change. You can run two docker containers pointing to different folders and opening different ports, and then compare the results side by side!