1. 程式人生 > >容器埠對映到主機埠探究

容器埠對映到主機埠探究

容器的網路

在說埠之前,先明確下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>

歡迎到微信裡去當吃瓜群眾