I have an issue with port forwarding that I can't resolve. I'm running Linux in VM and I'm using docker from that VM. When I try to set port forwarding from docker compose for example:
ports:
- "3080:3080"
It works only in case the application running in container is listening to 0.0.0.0:3080. Problem is that, most of the applications I'm dockerizing are listening to localhost. Any interface other then 0.0.0.0 causes port forwarding not to work. Do you have an idea why this happens or how to solve this?
I'm running:
Docker version 17.05.0-ce, build 89658be
docker-compose version 1.17.1, build unknown
Thanks
P.S. I found a temporary workaround. I specify network mode "host" to containers, forcing the container to use host OS network, but this approach doesn't work on MacOS.
"How to solve this" is to set the applications to listen to 0.0.0.0. For short scripts you'll often see this hard-coded in a main function (or even implied by a library) but it's an extremely common option for "real" servers, and the sort of thing you might expose via a command-line option or environment variable.
In terms of "why": inside Docker, each container runs in an isolated network namespace. For example, if you try:
$ docker run --rm busybox ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:02
inet addr:172.17.0.2 Bcast:172.17.255.255 Mask:255.255.0.0
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
The important thing here is that each container has its own localhost
, different from every other container's localhost
and the host's localhost
. So if you set a container to bind(2) to 127.0.0.1, it will only accept connections from 127.0.0.1 within the same container.
Meanwhile, Docker runs a network address translation (NAT) layer for you. If you run docker run -p 3080:3080
as you show, and then (from the host) run iptables -vL
, one of the things you will find digging around is a port-forwarding rule that routes inbound requests to port 3080 on the host, to the container IP address (in my example 172.17.0.2), over device docker0
, to port 3080. In the container's network address space, it will receive the inbound connection on the artificial container-local eth0
interface; if you were to call getsockname(2) on the socket you'd see the 172.17.0.2 address. Your process must accept connections on the container-local eth0
interface, or all interfaces, to be reachable from outside the container.
All of this is implementation details; you'll almost never need to actually worry about any of it. For instance, since the 172.17.0.0/16 address are artificially managed by Docker, you can't reach them from off-host, and they'll change between different docker run
s; for communicating between containers (on the same Docker-internal network) you do indirectly use them, but usually via the DNS service Docker provides (so connect to other-container-name
as a host name, which will happen to get resolved to 172.17.0.3). If you look at the detailed output of some particularly involved servers' startup sequences you will see them iterate through interfaces and bind to all of them explicitly; but for most applications the right answer in Docker space is just to always bind to 0.0.0.0.
I suggest network mode bridge in docker-compose file
driver: bridge
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With