Migrating from legacy container links
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.
Proposed Setup
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.
Getting there
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.
Step 1
Create the two user-defined networks db_nw
and web_nw
Verify that the networks were created:
Step 2
Run the DB container connected to the db_nw
bridge network
Step 3
Run the web (flask) container connected to both the db_nw
and web_nw
networks
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.
Step 4
Run the nginx container on the web_nw
network
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:
The hostname flaskapp
resolves just fine since the flaskapp
container is running on the same bridge network as the nginx container.
Summary
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.