Local Development Environment: from Vagrant to Docker
When I first started at Shippo in 2020, we onboarded with a few other engineers that week and we quickly noticed that the dev box set-up could use some improvements. The process with Vagrant would take days on average to get a fully functional dev box running.
This was definitely a process that we wanted to improve from the very beginning.
Throughout this article, we will show how we managed to successfully dockerize a complex Django application, Celery workers, NSQ cluster, Redis, and a Postgres DB.
Every month, the engineering team observes what we call “Tech Wednesday”. It is a dedicated time to fix tech debt and work on projects that developers rarely have time to deal with during their day-to-day schedules. Think of it as a full day of Hackathon at your company — but with concrete outcomes.
That’s what got me started on this very exciting project. Even if my knowledge of Docker was built throughout my experience, we realized we could go a step further and embrace the new challenge.
Dockerfile to build our main App image
We have list of 41 dependencies we need to build the Django App docker image.
The challenge here was to get all the dependencies for our Python & DB versions. I must say it was quite a long list of deps to get everything right.
We also had to use a specific dependency for psycopg which required a specific version of libpq-dev. The most recent version that works comes with postgresql-server-dev (thanks to Arjaan Buijk for that)
The workers are based on the Dockerfile’s generated image.
One of the main challenges for the Workers to be able to run successfully with the Django App was to retrieve and set all the environment variables correctly: AWS, database, Elastic Search, Django environment variables to name a few (and that had to be done for more than 200 of them).
Redis & Postgres
We had to find the correct Redis image version for it to be compatible with our Django App and Workers. We also learned a lot about docker-compose configuration such as network_mode, depends_on, links, and exposing the right ports.
I also want to point out the use of bind mounts, to support easy development and hot reloading for development only.
When using multiple docker-compose configurations, one roadblock we had was with connection issues between containers across those docker-compose networks. Containers are supposed to be isolated, so how do they communicate with each other? To enable a network communication between containers across docker networks we are using Docker’s built-in bridge network.
We had to replace most instances of ‘localhost’ to an environment variable, here’s why: localhost is generally used, but docker.for.mac.localhost is needed when the app is running in a docker container.
Luckily enough, NSQ was actually, and I quote, the easiest part of the project, as we could find a docker-compose configuration example.
We were able to reduce the dev box set-up from a week to less than a day, tackling a challenge and accomplishing a goal to be very proud of.
Throughout this journey, we were able to overcome many obstacles that allowed me to deepen our knowledge about Docker containers Shippo’s stack and a lot more. Which describes very well one of Shippo values: Passion for Hard Challenges.
Using Docker removed 11 manual steps out of 15 that each developer had to go through for the Vagrant setup, which is about 75% more automated now.
We haven’t won yet: there are still some issues coming up with the setup, which we continue fixing from time to time. As Shippo, the team, is always committed to make constant improvements, we are grateful we could help everyone to make the dev experience better!
We’re hiring, join our team!