Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Docker port forwarding not working when container is listening on localhost interface

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.

like image 941
Davita Avatar asked Oct 13 '18 14:10

Davita


2 Answers

"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 runs; 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.

like image 156
David Maze Avatar answered Oct 10 '22 21:10

David Maze


I suggest network mode bridge in docker-compose file

driver: bridge
like image 1
Shiju Avatar answered Oct 10 '22 19:10

Shiju