Okay, networking is hard. Docker networking isn’t much harder but it surely adds a layer of indirection. Here’s a good start if you want to dive in, but I’m gonna talk about two interesting cases. While not terribly difficult they’re not necessarily obvious.
Running on localhost
You might encounter this one when you pack your dev environment into a docker container — how should you run your dev server?
The obvious answer is just start it up as the tutorial says and publish the container port.
FLASK_APP=appname FLASK_ENV=development flask run
...
docker run -it --rm -p 5678:5000 --name <container-name> <image>
What’s gonna happen when you check your localhost:5678
? Probably nothing — this won’t work. The exact reason why will boil down to how the docker network is implemented on your platform, but in short:
- your development server probably listens on
127.0.0.1
; - the container gets its own IP (say,
172.17.0.2
) thus the networking layer simply routeslocalhost:5678
to172.17.0.2:5000
.
And no one is listening there.
Your production servers like Unicorn listen on 0.0.0.0
by default. To use a development server in a container make sure to tell it to listen on all the network interfaces.
FLASK_APP=appname FLASK_ENV=development flask run` --host=0.0.0.0 --port=5000
Opening ports to the world
Okay, we all know how to publish a port, do we?
docker run -it --rm -p 5433:5432 --name <container-name> <image>
I don’t know about you but I have an Nginx serving all HTTP(S) traffic and acting as a reverse proxy. And I rely on iptables to drop everything else because I just don’t want to think about this stuff.
But let’s take a look at what just happened to iptables as soon as we’ve published a port.
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 5433 -j DNAT --to-destination 172.17.0.3:5432
Shit! Publishing ports opens them to the world!
Now someone old and wise could say, ‘Paul, you’re an idiot for mixing access control and IP routing’. To which I respond, ‘True, but that is how it’s done virtually everywhere’. So I think this is a valuable lesson.
What you want instead is to publish the container port for localhost only.
-p 127.0.0.1:5433:5432
Here’s what you’ll see in iptables -L
output.
-A DOCKER -d 127.0.0.1/32 ! -i docker0 -p tcp -m tcp --dport 5433 -j DNAT --to-destination 172.17.0.3:5432
Ooof, at least your :5433
isn’t open now. Feels better?