容器的网络
在说端口之前,先明确下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>