In a previous post, I wrote about how I set up a simple dockerized flask app. I haven’t had to mess with it much since then; the last time I touched it was about a year ago. A year is basically eons in Docker time, so it wasn’t a surprise when an update to Docker all but rendered my setup obsolete. Specifically, container links are now marked deprecated with a warning that the feature may eventually be removed.
Enter User-Defined Networks
User-defined networks are virtual networks that you can create. I run my containers on a single physical host (for the purposes of this blog, my Macbook),
so I’ll create a
bridge network. All containers on the same
bridge network can talk to each other via either IPs or container names.
Also, a container can be part of multiple bridge networks (more on this later).
My Legacy Setup
I have a standard web application setup with 3 containers - an nginx reverse proxy, a web container running flask and a DB container running Postgres.
With my earlier setup, I used container links (
--link) to allow nginx to talk to the web container, and the web container to talk to the DB container. Links inject host and port information as environment variables in the container which is convenient. However, the drawback is that they’re fairly static and you can’t add/remove links without restarting the container. And of course, the fact that they’re deprecated.
For the purposes of migration, I could create a single bridge network, connect all 3 containers to this network and call it a day (recall that containers on the same bridge network can talk to each other by default). However, we don’t want the nginx container to be able to communicate with the DB container in accordance with the principle of least privilege. Not only is this a good security practice, but also forces you to think through dependencies between your application components.
To that end, we will create two separate bridge networks:
web_nw to connect the nginx and flask containers, and
db_nw to connect the flask and Postgres containers.
The setup is pictured below.
For any serious production deployment of a multi-container setup like this, you’ll want to use an orchestration tool like Docker Compose. In general, my philosophy is to learn how things work under the hood before reaching for tools that do the magic for you. As such, we’ll create the setup using only the base set of docker commands.
Create the two user-defined networks
Verify that the networks were created:
Run the DB container connected to the
db_nw bridge network
Run the web (flask) container connected to both the
Note that the
docker run command takes only a single network argument, so if you want to connect a container to multiple networks you need to
use a separate
docker network connect command after starting up the container.
Run the nginx container on the
Browse to localhost:8080 and voilà you should see your app’s home page.
Note that I needed zero configuration changes in my
nginx.conf. To revisit the config from my earlier post:
flaskapp resolves just fine since the
flaskapp container is running on the same bridge network as the nginx container.
Migrating to user-defined networks required a slight re-design and some extra work, but it makes my setup a lot more flexible and powerful. In my next post, I’ll show you how I can deploy a new version of my flask app in a separate container and dynamically reload the nginx configuration to point to the new container with zero downtime.