I have a weird problem with a simple tcp-connection to a server running in docker, which I don't understand and don't know how to proceed.
I run on Windows and am using Docker-Desktop.
The server is a simple python script, which starts like this:
import socket
from datetime import datetime, timedelta
# listen to tcp on port 27279
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('localhost', 27279))
s.listen(1)
print("Server is listening")
conn, addr = s.accept()
print(f"Connection from {addr} has been established")
the script runs in a docker container which is started with
docker run -p 27180:27279 -it my-image
in the container log is says Server is listening
and netstat confirms it is indeed listening on that port.
# netstat -plant
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.1:27279 0.0.0.0:* LISTEN 1/python
when I try connect to it from my local machine using PS C:\WINDOWS\system32> ncat localhost 27180
nothing happens.
In wireshark (edgeshark) I can see the following lines
Source | Destination | Protocol | Info |
---|---|---|---|
172.17.0.1 | 172.17.0.2 | TCP | 60394 -> 27279 [SYN] |
172.17.0.2 | 172.17.0.1 | TCP | 27279 -> 60394 [RST, ACK] |
I interpret that as the port mapping is correct, but the connection is denied.
I am also very happy about any hints on what to test/check or any resources to read to get a better understanding of the problem.
When I run the same script outside a container in my local machine it works without problems: ncat localhost 27279
(note the different port, as there is obviously no port-mapping in this case) it works fine, resulting in these logs:
Server is listening
Connection from ('127.0.0.1', 18562) has been established
I would expect the same behaviour inside docker.
I thought my setup is very similar to the solution proposed in Docker how can I Establish TCP connection to container on a specific port(8080)?, except that it does not work.
I have tried restarting the container and the computer in case there are any dangling connections, but with no effect.
I also considered the solution provided in Connection closed by foreign host when connecting to docker container via tcp. Now the IP-Adress shown in wireshark is 127.26.9.1
, the problem persists.
This Unable to remotely connect to docker container question also looks a bit similar, but I think it does not apply since I operate on my local machine. so firewalls should not be an issue (correct me, if I'm wrong)
The default networking mode of Docker is the bridge mode. Which creates a "software bridge" according to their documentation.
What it also does is that it creates a network namespace and a virtual interface that's connected to this bridge for each container. Each container has a separate loopback (localhost) which is not routed even with the -p
option.
You'd have to change your script to listen on all interfaces by changing the bind call to s.bind(('0.0.0.0', 27279))
. This will make the script listen on it's own virtual interface, which is where the Docker routes published ports to.
Changing the networking mode to host mode using the --net=host
flag would disable the network namespace isolation entirely, which would make the container share the same loopback interface as the host.
This solution won't work on Docker for Windows or Docker for Mac because they both run the containers inside a VM, unlike when running Docker natively on Linux. This means that even with the host networking mode, they won't share the same loopback device as the host.