docker 网络之端口映射不完全探索

这篇文章实在团队内部的分享,如下:

今天的分享不包含故事背景、docker的发展与应用...

分享源于对问题 '我已经在 Dockerfile 中通过 EXPOSE 指定了端口为何任然无法访问?' 深入探索。其实今天的分享也可以视为对上一期峰哥分享的一个补充。

Docker容器的端口映射

容器的服务端口绑定到宿主机的端口上。效果就是:外部程序通过宿主机的P端口访问,就像直接访问 Docker 容器网络内部容器提供的服务一样。

今天的分享主要涉及到的 Docker run 命令,参数如下:

  • -p/-P
  • --expose

expose

expose 参数有两种使用形式:

  • docker run 命令时指定 --expose 参数, 如 --expose=8080
  • 在 Dockerfile 中,通过 EXPOSE 关键字

作用

EXPOSE 指令是声明运行时容器提供服务端口。

注意

仅仅是申明。并不是说你声明了这个端口,在运行容器的时候就会自动的暴露这个端口。使用时,还要依赖于容器的操作人员进一步指定网络规则。

本质上来说, EXPOSE 或者 --expose 只是为其他命令提供所需信息的元数据,或者只是告诉容器操作人员有哪些已知选择。

验证: 通过 docker run nginx 启动一个容器, 然后通过 `docker inspect id` 查看

"HostConfig": {
    "PortBindings": {
        "443/tcp": null,
        "80/tcp": null
    },
}

可以看到端口被标示成已暴露,但是没有定义任何与主机的端口映射。

-p/-P

-p-P 参数的完整形式:

  -p, --publish list 
  -P, --publish-all 

这两个参数都是发布端口到宿主主机。但用法上存在一点区别。

-p 显式将一个或者一组端口从容器里绑定到宿主机上。
-P 自动的将EXPOSE指令相关的每个端口映射到宿主机的端口上。

-p 参数常见的用法是: -p 宿主主机端口:容器端口。 如果使用 `docker run -p 8080:80 nginx` 命令启动nginx 容器,那么容器中的 80端口会绑定到主机的8080端口。

这是,我们再通过 docker inspect id 来查看,将会看到下面的绑定关系:

"HostConfig": {
    "PortBindings": {
        "443/tcp": null,
        "80/tcp": [
                    {
                        "HostIp": "0.0.0.0",
                        "HostPort": "8080"
                    }
                ]

    },
}

因为没有指定443端口的绑定,所以能看到仅有 80端口和宿主机存在端口映射关系。

另外,在使用-p参数是,我们可以忽略指定宿主主机端口。这是,docker会帮助我们自动的选择一个合适端口和容器端口进行绑定。这样做的好处是在启动多个容器时可以避免端口冲突。

例如上面的启动命令可以改为 docker run -p 80 nginx, 这时如果我们本机的80端口被占用,docker就会自动的选择一个其他端口。我们可以通过 docker psdocker inspect 命令来查看端口的映射关系。

-P 参数用法: docker run -P nginx.
-P 参数须配合 Dockerfile 一起使用, 能够将 Expose 声明的端口映射到宿主主机。

此时,使用 docker inspect命令,可以看到 dockerfile 中申明的端口都已经绑定到了主机上。

"HostConfig": {
    "PortBindings": {
        "443/tcp": [
                    {
                        "HostIp": "0.0.0.0",
                        "HostPort": "443"
                    }
                ],
        "80/tcp": [
                    {
                        "HostIp": "0.0.0.0",
                        "HostPort": "80"
                    }
                ]

    },
}

-expose 和 -p 功能对比

我们可以通过如下的实验来更好的理解两参数之间的区别。

  • 不在 DockerfileEXPOSE,也不通过 -p 参数指定
  • DockerfileEXPOSE,但不使用 -p 参数
  • DockerfileEXPOSE,也使用 -p 参数
  • DockerfileEXPOSE,也使用 -P 参数
  • 只使用 -p 参数

结果

  • 第一中情况: 不能在外网访问,也不能被 link 的 container 访问
  • 第二种情况: 不能被外网访问,但是能被 link 的 container 访问
  • 第三种情况: 能被外网访问,也能被 link 容器访问iw
  • 第四种情况: 和第三种情况一样
  • 第五种情况: 和第三种情况一样

Docker 端口映射原理

备注: 这一块挺乱的,我也没弄得很清楚,权当抛砖引玉了。

原本我理解端口映射是这样的一个通信过程:

  • Docker进程启动的时候,会在宿主主机创建路由,同时创建docker0网桥
  • 容器启动的时候创建 vethxx 的网卡,同时链接到网桥
  • 通过 -p 参数指定端口映射后, 创建iptables规则
  • 当有流量通过宿主主机端口进来用,通过iptables 匹配到规则后,转换为容器对应的子网ip
  • 主机的路由指定了 172.xx 网段的ip由 docker0 处理
  • docker0再将请求转发到子网中容器

后面和朋友了解了,发现还有 docker-proxy 的存在,于是,上面的理解是其实就片面的,不完善的。


到现在,关于 Docker 端口映射的实现一共有2方案.

  • 1.7版本之前 docker-proxy + iptables DNAT 的方式
    即,内网访问通过 iptables
    外网访问通过 proxy
  • 1.7版本之后的 iptables DNAT
    完全由 iptables 实现

Docker 1.7版本起,Docker提供了一个配置项: -userland-proxy,以让 Docker 用户决定是否启用 docker-proxy,默认为true,即启用docker-proxy。
现在的 Docker 环境默认的是: -userland-proxy=true。iptables 和 docker-proxy 都会起作用。

-userland-proxy=true的情况下

在启用 docker-proxy 的情况下。每设置一对端口映射就会启动一个 docker-proxy 进程。

可以通过 ps 命令查看 docker-proxy 进程信息

ps -ef | grep docker-proxy

root      5532 19713  0 2月20 ?       00:00:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 443 -container-ip 172.19.0.8 -container-port 443
root      5546 19713  0 2月20 ?       00:00:01 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 80 -container-ip 172.19.0.8 -container-port 80

通过 sudo netstat -nltpu 查看本机的端口监听情况:

tcp6       0      0 :::80                   :::*                    LISTEN      5546/docker-proxy          
tcp6       0      0 :::443                  :::*                    LISTEN      5532/docker-proxy

通过上面的命令,会发现,我们映射到宿主机的端口被 docker-proxy 进程监听了。

别急,我们再看一下iptables,发现其中增加了对应的规则:

 sudo iptables-save -t nat                 
# Generated by iptables-save v1.6.1 on Fri Feb 22 15:06:17 2019
*nat
:PREROUTING ACCEPT [55751:14743102]
:INPUT ACCEPT [55615:14734886]
:OUTPUT ACCEPT [260072:22717364]
:POSTROUTING ACCEPT [260200:22725044]
:DOCKER - [0:0]
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
-A POSTROUTING -s 172.20.0.0/16 ! -o br-bf4c59b26d33 -j MASQUERADE
-A POSTROUTING -s 172.19.0.0/16 ! -o br-e2ab1d51063d -j MASQUERADE
-A POSTROUTING -s 172.18.0.0/16 ! -o br-c9af812dc067 -j MASQUERADE
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
-A POSTROUTING -s 172.19.0.2/32 -d 172.19.0.2/32 -p tcp -m tcp --dport 6379 -j MASQUERADE
-A POSTROUTING -s 172.19.0.3/32 -d 172.19.0.3/32 -p tcp -m tcp --dport 27017 -j MASQUERADE
-A POSTROUTING -s 172.19.0.5/32 -d 172.19.0.5/32 -p tcp -m tcp --dport 3306 -j MASQUERADE
-A POSTROUTING -s 172.19.0.6/32 -d 172.19.0.6/32 -p tcp -m tcp --dport 9501 -j MASQUERADE
-A POSTROUTING -s 172.19.0.6/32 -d 172.19.0.6/32 -p tcp sudo iptables -t filter -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain FORWARD (policy DROP)
target     prot opt source               destination         
DOCKER-USER  all  --  anywhere             anywhere            
DOCKER-ISOLATION-STAGE-1  all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere             ctstate RELATED,ESTABLISHED
DOCKER     all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere             ctstate RELATED,ESTABLISHED
DOCKER     all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere             ctstate RELATED,ESTABLISHED
DOCKER     all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere             ctstate RELATED,ESTABLISHED
DOCKER     all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere            

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         

Chain DOCKER (4 references)
target     prot opt source               destination         
ACCEPT     tcp  --  anywhere             172.19.0.2           tcp dpt:6379
ACCEPT     tcp  --  anywhere             172.19.0.3           tcp dpt:27017
ACCEPT     tcp  --  anywhere             172.19.0.5           tcp dpt:mysql
ACCEPT     tcp  --  anywhere             172.19.0.6           tcp dpt:9501
ACCEPT     tcp  --  anywhere             172.19.0.6           tcp dpt:ssh
ACCEPT     tcp  --  anywhere             172.19.0.8           tcp dpt:https
ACCEPT     tcp  --  anywhere             172.19.0.8           tcp dpt:http

Chain DOCKER-ISOLATION-STAGE-1 (1 references)
target     prot opt source               destination         
DOCKER-ISOLATION-STAGE-2  all  --  anywhere             anywhere            
DOCKER-ISOLATION-STAGE-2  all  --  anywhere             anywhere            
DOCKER-ISOLATION-STAGE-2  all  --  anywhere             anywhere            
DOCKER-ISOLATION-STAGE-2  all  --  anywhere             anywhere            
RETURN     all  --  anywhere             anywhere            

Chain DOCKER-ISOLATION-STAGE-2 (4 references)
target     prot opt source               destination         
DROP       all  --  anywhere             anywhere            
DROP       all  --  anywhere             anywhere            
DROP       all  --  anywhere             anywhere            
DROP       all  --  anywhere             anywhere            
RETURN     all  --  anywhere             anywhere            

Chain DOCKER-USER (1 references)
target     prot opt source               destination         
RETURN     all  --  anywhere             anywhere -m tcp --dport 22 -j MASQUERADE
-A POSTROUTING -s 172.19.0.8/32 -d 172.19.0.8/32 -p tcp -m tcp --dport 443 -j MASQUERADE
-A POSTROUTING -s 172.19.0.8/32 -d 172.19.0.8/32 -p tcp -m tcp --dport 80 -j MASQUERADE
-A DOCKER -i br-bf4c59b26d33 -j RETURN
-A DOCKER -i br-e2ab1d51063d -j RETURN
-A DOCKER -i br-c9af812dc067 -j RETURN
-A DOCKER -i docker0 -j RETURN
-A DOCKER ! -i br-e2ab1d51063d -p tcp -m tcp --dport 6379 -j DNAT --to-destination 172.19.0.2:6379
-A DOCKER ! -i br-e2ab1d51063d -p tcp -m tcp --dport 27017 -j DNAT --to-destination 172.19.0.3:27017
-A DOCKER ! -i br-e2ab1d51063d -p tcp -m tcp --dport 3306 -j DNAT --to-destination 172.19.0.5:3306
-A DOCKER ! -i br-e2ab1d51063d -p tcp -m tcp --dport 9501 -j DNAT --to-destination 172.19.0.6:9501
-A DOCKER ! -i br-e2ab1d51063d -p tcp -m tcp --dport 2222 -j DNAT --to-destination 172.19.0.6:22
-A DOCKER ! -i br-e2ab1d51063d -p tcp -m tcp --dport 443 -j DNAT --to-destination 172.19.0.8:443
-A DOCKER ! -i br-e2ab1d51063d -p tcp -m tcp --dport 80 -j DNAT --to-destination 172.19.0.8:80

这里的 DOCKER 对应的是由 docker 自定义的一组过滤规则,可以通过 sudo iptables -t filter -L 查看到:

sudo iptables -t filter -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain FORWARD (policy DROP)
target     prot opt source               destination         
DOCKER-USER  all  --  anywhere             anywhere            
DOCKER-ISOLATION-STAGE-1  all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere             ctstate RELATED,ESTABLISHED
DOCKER     all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere             ctstate RELATED,ESTABLISHED
DOCKER     all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere             ctstate RELATED,ESTABLISHED
DOCKER     all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere             ctstate RELATED,ESTABLISHED
DOCKER     all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere            

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         

Chain DOCKER (4 references)
target     prot opt source               destination         
ACCEPT     tcp  --  anywhere             172.19.0.2           tcp dpt:6379
ACCEPT     tcp  --  anywhere             172.19.0.3           tcp dpt:27017
ACCEPT     tcp  --  anywhere             172.19.0.5           tcp dpt:mysql
ACCEPT     tcp  --  anywhere             172.19.0.6           tcp dpt:9501
ACCEPT     tcp  --  anywhere             172.19.0.6           tcp dpt:ssh
ACCEPT     tcp  --  anywhere             172.19.0.8           tcp dpt:https
ACCEPT     tcp  --  anywhere             172.19.0.8           tcp dpt:http

Chain DOCKER-ISOLATION-STAGE-1 (1 references)
target     prot opt source               destination         
DOCKER-ISOLATION-STAGE-2  all  --  anywhere             anywhere            
DOCKER-ISOLATION-STAGE-2  all  --  anywhere             anywhere            
DOCKER-ISOLATION-STAGE-2  all  --  anywhere             anywhere            
DOCKER-ISOLATION-STAGE-2  all  --  anywhere             anywhere            
RETURN     all  --  anywhere             anywhere            

Chain DOCKER-ISOLATION-STAGE-2 (4 references)
target     prot opt source               destination         
DROP       all  --  anywhere             anywhere            
DROP       all  --  anywhere             anywhere            
DROP       all  --  anywhere             anywhere            
DROP       all  --  anywhere             anywhere            
RETURN     all  --  anywhere             anywhere            

Chain DOCKER-USER (1 references)
target     prot opt source               destination         
RETURN     all  --  anywhere             anywhere 

-userland-proxy=false的情况下

待研究~

性能

docker-proxy 在网络上吐槽的比较多,因为每一对端口映射都会对一个 docker-proxy进程,如果端口较多,可能就会带来性能问题。且在单个 docker-proxy 的情况下,性能比 iptables 略差。

总结

- --link 能够访问 expose 声明的端口

  • -expose 仅声明端口,并不会自动映射到宿主主机
  • -p 指定端口映射关系
  • -P 将 expose 声明的端口发布到宿主主机
  • 在处理端口映射是,iptables 规则优先,如果没有匹配到iptables规则,则由 docker-proxy处理

待深入研究的问题:

  • container <-> container、host <-> container、 container <-> host 各自怎么选择策略的
  • iptables 规则
本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!