容器埠對映到主機埠探究
容器的網路
在說埠之前,先明確下docker 容器的網路,可以用過docker network
命令常看docker的網路:
# docker network ls
NETWORK ID NAME DRIVER
33b01b58a9a2 bridge bridge
ffe4299ed1a9 host host
fc93a0e85b75 none null
可以看到,docker中包含bridge,host,none三種網路,其實除了這三種,還有container第四種網路。
其中,bridge是預設的網路,也就是在不指定網路模式的時候docker run
使用的的網路模式,而none,正如其名,是不分配任何網路。host模式和container模式不是那麼常用,前者是使用宿主機的網路,而後者是和指定的容器共享網路。
而這所討論的容器埠與主機埠對映的原理,就是docker預設的bridge網路模式。
bridge網路
當docker run
一個容器,可以使用-p
引數指定一個開放埠,甚至可以把這個埠對映的主機的某一埠上。比如,可以通過以下命令將nginx容器的80埠對映到主機的8080埠上:
# docker run -d -p 8080:80 --name nginx daocloud.io/library/nginx 81ea762a6bb99c1ee8c72d0fcc91f5151f8f65cf7c26747dd1f64d260845e21c # docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 81ea762a6bb9 daocloud.io/library/nginx "nginx -g 'daemon off" 13 seconds ago Up 12 seconds 443/tcp, 0.0.0.0:8080->80/tcp nginx
可以嘗試訪問宿主機的8080埠,可以看到是一個執行的ngxin。
使用docker inspect
命令可以看到nginx容器網路相關的配置:
"NetworkSettings": { "Bridge": "", "SandboxID": "0af9e9bf575206c247c0c0e1915dfec533dc173ae55917662e8c08df19441fe3", "HairpinMode": false, "LinkLocalIPv6Address": "", "LinkLocalIPv6PrefixLen": 0, "Ports": { "443/tcp": null, "80/tcp": [ { "HostIp": "0.0.0.0", "HostPort": "8080" } ] }, "SandboxKey": "/var/run/docker/netns/0af9e9bf5752", "SecondaryIPAddresses": null, "SecondaryIPv6Addresses": null, "EndpointID": "16ab10c9a32ea277f3f78b589ceee0f8698feafdfe6061c9ca3ac9f9633d1af8", "Gateway": "172.17.0.1", "GlobalIPv6Address": "", "GlobalIPv6PrefixLen": 0, "IPAddress": "172.17.0.2", "IPPrefixLen": 16, "IPv6Gateway": "", "MacAddress": "02:42:ac:11:00:02", "Networks": { "bridge": { "IPAMConfig": null, "Links": null, "Aliases": null, "NetworkID": "33b01b58a9a2ac92011051f1f75aa94ec5eb29bdbac06580aef462d700e527ef", "EndpointID": "16ab10c9a32ea277f3f78b589ceee0f8698feafdfe6061c9ca3ac9f9633d1af8", "Gateway": "172.17.0.1", "IPAddress": "172.17.0.2", "IPPrefixLen": 16, "IPv6Gateway": "", "GlobalIPv6Address": "", "GlobalIPv6PrefixLen": 0, "MacAddress": "02:42:ac:11:00:02" } } }
docker也提供了直接對網路進行管理的docker network
命令,可以通過docker network inspect
命令檢視docker網路的詳細資訊:
# docker network inspect bridge
[
{
"Name": "bridge",
"Id": "33b01b58a9a2ac92011051f1f75aa94ec5eb29bdbac06580aef462d700e527ef",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16"
}
]
},
"Internal": false,
"Containers": {
"81ea762a6bb99c1ee8c72d0fcc91f5151f8f65cf7c26747dd1f64d260845e21c": {
"Name": "nginx",
"EndpointID": "16ab10c9a32ea277f3f78b589ceee0f8698feafdfe6061c9ca3ac9f9633d1af8",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
}
},
"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": {}
}
]
埠對映
通過上面可以發現,bridge只不過是ip域為172.17.0.0/16子網,通過ifconfig
命令也可以獲得一些資訊:
# ifconfig
docker0 Link encap:Ethernet HWaddr 02:42:72:b4:25:d9
inet addr:172.17.0.1 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::42:72ff:feb4:25d9/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:37 errors:0 dropped:0 overruns:0 frame:0
TX packets:39 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:3684 (3.6 KB) TX bytes:3399 (3.3 KB)
docker是通過iptables將流量打到docker0的子網上的,因此,可以嘗試找下iptables的配置:
# iptables-save |grep 172.17.0.*
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
-A POSTROUTING -s 172.17.0.2/32 -d 172.17.0.2/32 -p tcp -m tcp --dport 80 -j MASQUERADE
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 8080 -j DNAT --to-destination 172.17.0.2:80
-A DOCKER -d 172.17.0.2/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 80 -j ACCEPT
在上面已經得知nginx容器的ip正是172.17.0.2
,iptables會將來自8080埠的流量打在80埠上。
其實在宿主機直接訪問子網172.17.0.2
的80埠就可以獲得nginx正確的返回:
# curl 172.17.0.2:80
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
歡迎到微信裡去當吃瓜群眾