这篇文章实在团队内部的分享,如下:
今天的分享不包含故事背景、docker的发展与应用…
分享源于对问题 ‘我已经在 Dockerfile 中通过 EXPOSE 指定了端口为何任然无法访问?’ 深入探索。其实今天的分享也可以视为对上一期峰哥分享的一个补充。
Docker容器的端口映射
容器的服务端口绑定到宿主机的端口上。效果就是:外部程序通过宿主机的P端口访问,就像直接访问 Docker 容器网络内部容器提供的服务一样。
 
今天的分享主要涉及到的 Docker run 命令,参数如下:
expose expose 参数有两种使用形式:
在 docker run 命令时指定 --expose 参数, 如 –expose=8080 
在 Dockerfile 中,通过 EXPOSE 关键字 
 
作用 
EXPOSE 指令是声明运行时容器提供服务端口。
 
注意 :
仅仅是申明。并不是说你声明了这个端口,在运行容器的时候就会自动的暴露这个端口。使用时,还要依赖于容器的操作人员进一步指定网络规则。
本质上来说, EXPOSE 或者 --expose 只是为其他命令提供所需信息的元数据,或者只是告诉容器操作人员有哪些已知选择。
验证: 通过 docker run nginx 启动一个容器, 然后通过 `docker inspect id` 查看
1 2 3 4 5 6 7 "HostConfig" : {    "PortBindings" : {         "443/tcp" : null,         "80/tcp" : null     }, } 
可以看到端口被标示成已暴露,但是没有定义任何与主机的端口映射。
-p/-P -p 与 -P 参数的完整形式:
1 2 -p, --publish list  -P, --publish-all  
这两个参数都是发布端口到宿主主机。但用法上存在一点区别。
-p 显式将一个或者一组端口从容器里绑定到宿主机上。-P 自动的将EXPOSE指令相关的每个端口映射到宿主机的端口上。
-p 参数常见的用法是: -p 宿主主机端口:容器端口。 如果使用 `docker run -p 8080:80 nginx` 命令启动nginx 容器,那么容器中的 80端口会绑定到主机的8080端口。
这是,我们再通过 docker inspect id 来查看,将会看到下面的绑定关系:
1 2 3 4 5 6 7 8 9 10 11 12 13 "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 ps 或 docker inspect 命令来查看端口的映射关系。
-P 参数用法: docker run -P nginx.
此时,使用 docker inspect 命令,可以看到 dockerfile 中申明的端口都已经绑定到了主机上。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 "HostConfig" : {    "PortBindings" : {         "443/tcp" : [                     {                         "HostIp" : "0.0.0.0" ,                         "HostPort" : "443"                      }                 ],         "80/tcp" : [                     {                         "HostIp" : "0.0.0.0" ,                         "HostPort" : "80"                      }                 ]     }, } 
-expose 和 -p 功能对比 我们可以通过如下的实验来更好的理解两参数之间的区别。
不在 Dockerfile 里 EXPOSE,也不通过 -p 参数指定 
在 Dockerfile 里 EXPOSE,但不使用 -p 参数 
在 Dockerfile 里 EXPOSE,也使用 -p 参数 
在 Dockerfile 里 EXPOSE,也使用 -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 的方式 
1.7版本之后的 iptables DNAT 
 
Docker 1.7版本起,Docker提供了一个配置项: -userland-proxy,以让 Docker 用户决定是否启用 docker-proxy,默认为true,即启用docker-proxy。
-userland-proxy=true的情况下 
在启用 docker-proxy 的情况下。每设置一对端口映射就会启动一个 docker-proxy 进程。
可以通过 ps 命令查看 docker-proxy 进程信息
1 2 3 4 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 查看本机的端口监听情况:
1 2 tcp6       0      0 :::80                   :::*                    LISTEN      5546/docker-proxy           tcp6       0      0 :::443                  :::*                    LISTEN      5532/docker-proxy 
通过上面的命令,会发现,我们映射到宿主机的端口被 docker-proxy 进程监听了。
别急,我们再看一下iptables,发现其中增加了对应的规则:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88  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 查看到:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 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 规则