20.4 映射容器端口到宿主主机的实现
默认情况下,容器可以主动访问到外部网络的连接,但是外部网络无法访问到容器。
1.容器访问外部实现
假设容器内部的网络地址为172.17.0.2,本地网络地址为10.0.2.2。容器要能访问外部网络,源地址不能为172.17.0.2,需要进行源地址映射(Source NAT,SNAT),修改为本地系统的IP地址10.0.2.2。
映射是通过iptables的源地址伪装操作实现的。
查看主机nat表上POSTROUTING链的规则。该链负责网包要离开主机前,改写其源地址。
- $ sudo iptables -t nat -nvL POSTROUTING
- Chain POSTROUTING (policy ACCEPT 12 packets, 738 bytes)
- pkts bytes target prot opt in out source destination
- ...
- 0 0 MASQUERADE all -- * !docker0 172.17.0.0/16 0.0.0.0/0
- ...
其中,上述规则将所有源地址在172.17.0.0/16网段,且不是从docker0接口发出的流量(即从容器中出来的流量),动态伪装为从系统网卡发出。MASQUERADE行动跟传统SNAT行动相比,好处是它能从网卡动态获取地址。
2.外部访问容器实现
容器允许外部访问,可以在docker run时候通过-p或-P参数来启用。
不管用那种办法,其实也是在本地的iptable的nat表中添加相应的规则,将访问外部IP地址的网包进行目标地址DNAT,将目标地址修改为容器的IP地址。
以一个开放80端口的Web容器为例,使用-P时,会自动映射本地49000~49900范文内的端口随机端口到容器的80端口:
- $ iptables -t nat -nvL
- ...
- Chain PREROUTING (policy ACCEPT 236 packets, 33317 bytes)
- pkts bytes target prot opt in out source destination
- 567 30236 DOCKER all -- * * 0.0.0.0/0 0.0.0.0/0
- ADDRTYPE match dst-type LOCAL
- Chain DOCKER (2 references)
- pkts bytes target prot opt in out source destination
- 0 0 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0
- tcp dpt:49153 to:172.17.0.2:80
- ...
可以看到,nat表中涉及两条链,PREROUTING链负责包到达网络接口时,改写其目的地址。其中规则将所有流量都扔到DOCKER链。而DOCKER链中将所有不是从docker0进来的网包(意味着不是本地主机产生),将目标端口为49153的,修改目标地址为172.17.0.2,目标端口修改为80。
使用-p 80:80时,与上面类似,只是本地端口也为80:
- $ iptables -t nat -nvL
- ...
- Chain PREROUTING (policy ACCEPT 236 packets, 33317 bytes)
- pkts bytes target prot opt in out source destination
- 567 30236 DOCKER all -- * * 0.0.0.0/0 0.0.0.0/0
- ADDRTYPE match dst-type LOCAL
- Chain DOCKER (2 references)
- pkts bytes target prot opt in out source destination
- 0 0 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0
- tcp dpt:80 to:172.17.0.2:80
- ...
有两点需要注意:
·这里的规则映射了0.0.0.0,意味着将接受主机来自所有网络接口上的流量。用户可以通过-p IP:host_port:container_port或-p IP::port来指定绑定的外部网络接口,以制定更严格的访问规则;
·如果希望映射永久绑定到某个固定的IP地址,可以在Docker配置文件/etc/default/docker中指定DOCKER_OPTS="—ip=IP_ADDRESS",之后重启Docker服务即可生效。
