There is something that I don't understand about the process of IPs allocation in the docker network mechanism.
Suppose I run a few containers. Each gets its own IP address - from where these IP addresses came from?
If one of the container listen to a port, and I go to the browser and write the <IP>:<PORT>
and see the webpage - How does my computer know to resolve that (That this is a local IP address)?
问题:
回答1:
A full discussion of Docker networking is out of scope here, so I'll just assume from your question you're talking about bridge
networking (which is the default).
When you start the Docker daemon (dockerd
) it creates a ethernet bridge network interface on your local machine called docker0
.
~ > ifconfig
docker0 Link encap:Ethernet HWaddr 12:42:09:64:a9:dd
inet addr:172.17.0.1 Bcast:172.17.255.255 Mask:255.255.0.0
inet6 addr: fe80::42:9ff:fe64:a9da/64 Scope:Link
UP BROADCAST MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:86 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 B) TX bytes:11923 (11.9 KB)
This can be customized if neccessary but usually defaults are fine.
This is represented in Docker as a network called bridge
:
~ > docker network inspect bridge
[
{
"Name": "bridge",
"Id": "25191b73563206a321498c0fac55a897de6ba0333d19f0bdc32c78d290b9fedc",
"Created": "2018-05-07T18:31:08.680222396-07:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16",
"Gateway": "172.17.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {
"com.docker.network.bridge.default_bridge": "true",
"com.docker.network.bridge.enable_icc": "true",
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
"com.docker.network.bridge.name": "docker0",
"com.docker.network.driver.mtu": "1500"
},
"Labels": {}
}
]
Notice the inet addr
/ Gateway
are the same. Also notice:
"com.docker.network.bridge.name": "docker0"
To answer one part of your question, your container IP addresses are allocated from this subnet (172.17.0.0/16
) that Docker creates. Now we know some networking stuff has been setup, let's run a container and see what happens.
~ > docker run -d -p 7000:5000 johnharris85/simple-hostname-reporter:2
eeba0f9d23bbd3c10ddf61120ce5d7d1ded6db1515fc37725b68eae12ab6c9b5
We can see that this container has an ip address on my bridge network:
~ > docker container inspect eeb --format "{{ .NetworkSettings.Networks.bridge.IPAddress }}"
172.17.0.2
In fact, I can use that IP address and the container port to hit my app (try 172.17.0.2:5000
in your browser!). However this is not very scalable / dynamic as this IP address could change when my container is restarted. Also I had to do a bunch of stuff to find it.
Instead of having to do that, I am mapping port 7000 on my host machine to port 5000 in my container (this is the port my application is listening on) so I can visit localhost:7000
in my browser and hit my app (try that too!).
OK great, so what makes the traffic to port 7000 on my machine magically route to port 5000 in my container?
Let's take a look at iptables
!:
~ > sudo iptables -t nat -S
# ... some stuff
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 7000 -j DNAT --to-destination 172.17.0.2:5000
The (most) important line as far as we're concerned here is the one I've left in above. It says "for all traffic not coming from the docker0 interface (! -i docker0
), using the TCP protocol (-p tcp
), destined for port 7000 (--dport 7000
) , actually route it to 172.17.0.2:5000 (--to-destination 172.17.0.2:5000
)". That is a little simplified of course, but essentially what's going on.
Now if you start another container (this time let's bind to host port 9999):
~ > docker run -d -p 9999:5000 johnharris85/simple-hostname-reporter:2
ac4df2bd0a961bfa08735d64fa7f6e69f171e7e499fb86d2ecac6cfba350a5d4
And do a quick check of it's IP:
~ > docker container inspect ac4 --format "{{ .NetworkSettings.Networks.bridge.IPAddress }}"
172.17.0.3
Now iptables
again:
~ > sudo iptables -t nat -S
# ... some stuff
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 7000 -j DNAT --to-destination 172.17.0.2:5000
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 9999 -j DNAT --to-destination 172.17.0.3:5000
Notice we now have another rule, same structure, this time saying for all traffic to port 9999, send to our new container IP (--to-destination 172.17.0.3:5000
).
Stop the containers and you'll notice these rules disappear!
IANA networking expert, so some stuff might be a little simplified but hope it helps!