背景
之前在《Docker 动态修改容器端口映射的方法》一文中,说明了如何使用修改配置和加防火墙规则实现动态端口映射。但是没有具体分享加防火墙实现动态端口映射的实际案例。今天就分享一下实际操作案例,供大家参考。
分析
动态端口映射的用途
容器端口的动态映射,是用于实现外部访问服务器上的容器内运行的服务的。
如果容器已经运行了,不想停止Docker服务,又想增加端口映射,以便让外部可以立即访问,那就需要使用iptables来实现端口的动态映射。如果可以重启Docker服务,那么采用前面的文章里的方法即可。
实践操作的环境
宿主机局域网IP:192.168.1.15
容器的IP:172.17.0.2
容器已映射端口:443
容器内有监听0.0.0.0的端口:8086
容器内是否开启防火墙:没有开启
宿主机是否正常访问容器的端口:是
宿主机是否开启IPv4转发:是
宿主机局域网IP查看:
ip addr
找到eth0即宿主机使用的网卡这一组,可以看到。
容器IP查看:
docker exec {容器名或容器ID} ip addr
找到eth0@ifxxx这样的一组,查看对应的IP。一般是172开头的,默认一般是172.17.0.2。
容器已有的端口映射查看:
docker ps
找到容器名那一行,找到PORTS那一列,就是已经做好的端口映射。例如:0.0.0.0:443->443/tcp 。
查看容器内已监听的端口:
docker exec {容器名或容器ID} netstat -ltnp|grep 0.0.0.0|grep -v 127.0.0.1
这里过滤出来监听0.0.0.0的端口并排除监听127.0.0.1的端口。选择一个没有做端口映射的来作为测试,我们这里选择的是8086。
查看容器是否开启防火墙:
docker exec {容器名或容器ID} service iptables status | grep Active
如果不是running状态,即没有运行。
因为我移除了iptbales服务配置文件,容器内的防火墙是没有起来的。
查看宿主机是否正常telnet访问容器的8086端口:
如上图所示,看到了Escape character is '^]'即表示端口是通的,可以正常访问,如果没有出现则表示不通。
查看宿主机是否开启IPv4转发:
sysctl net.ipv4.ip_forward
如果返回的值为1即表示开启了,值为0表示未开启。
可以看到,已经开启了IPv4的转发。
实践操作目标
我们使用iptables添加规则,实现在不重启Docker服务和容器的前提下,让外部可以访问容器里的8086服务,即可以telnet宿主机192.168.1.15的8086端口。
实践步骤
1.测试宿主机IP是否可以telnet 连接192.168.1.15 的8086
可以发现:
Trying 192.168.1.15...
telnet: connect to address 192.168.1.15: Connection refused
因为本机并没有监听8086端口,所以使用本机无法telnet8086端口。那从外部执行本机的8086就更不可能成功了。此时我们就是无法从外部访问容器的8086端口的。
2.设置根据端口的转发
只要宿主机IP接收到8086的端口的请求,就转发到172.17.0.2上的8086。前面我们已经确认了172.17.0.2的8086端口是可以通信的。所以请求只要转发到172.17.0.2的8086,请求就可以进入容器。
iptables -t nat -A DOCKER -p tcp --dport {容器外部端口} -j DNAT --to-dest {容器ip}:{容器内部端口}
-t nat:用于指定在nat表添加规则。NAT表用于做IP和端口转换的。
-p tcp:用于指定转换的协议为tcp,这个根据监听的协议来填写。
-A DOCKER:在DOCKER链追加一条规则。如果要把规则写到最开始即最优先匹配,可以把A换成I。A即add,追加到末尾,I即insert,插入到开头。
--dport即容器外部的端口,也即宿主机的端口。这里我们替换为8086。
-j DNAT:就是做DNAT的转换,把目的IP和端口进行转换。
--to-dest:将请求的目的IP和端口修改为什么。这里换为容器的IP和容器内监听的8086。
容器外部的端口和容器内部的端口可以不一样,但是容器内部的端口必须是内部已经监听的端口,而容器外部的端口就是外部拿来通信的端口。
最终的命令如下:
iptables -t nat -A DOCKER -p tcp --dport 8086 -j DNAT --to-dest 172.17.0.2:8086
执行后查看iptables的nat表:
iptables -t nat -nL DOCKER | grep 8086
在宿主机上试一下telnet就通了。
这样就搞定了吗?你从局域网的其他机器执行telnet 192.168.1.15 8086,发现还是不通。
到此,我们已经打通了宿主机可以通过宿主机IP对端口的直接访问,但是外部的还不行。如果你只是需要宿主机可以访问,那就到此结束了。如果你需要在局域网的其他机器也能访问,那还要继续。
3.添加允许外部IP访问8086端口的规则
iptables -A DOCKER -p tcp -s {外部IP} --dport {宿主机的端口} -j ACCEPT
默认不加-t参数,默认就是filter表。-s(源IP)参数用于指定外部特定IP允许访问宿主机。如果不加-s表示外部所有IP都可以访问。--dport则表示只能访问指定的端口。-j ACCEPT表示允许。
最终的命令如下:
iptables -A DOCKER -p tcp -s 外部IP --dport 8086 -j ACCEPT
不过你会发现,此时还是无法从外部IP访问8086。此时虽然外部能请求到宿主机的8086,但是宿主机将这个请求丢了。在宿主机上请求,因为都是内部IP,不管是用宿主机的局域网IP还是172的,都是内部流量,所以会通过容器的网桥进行转发,所以宿主机上可以通过宿主机IP访问到8086。
那从外部来的流量为什么能够转发呢?
因为前面已经将所有的发往8086的流量转发到172.17.0.2上了。现在允许外部请求到宿主机上,就会触发上面的转发规则,就马上转发到172.17.0.2了。一气呵成。
需要注意的是,添加外部允许访问8086端口,必须在DOCKER链,添加INPUT链是没有用的。为什么呢?
iptables先判断nat表中的DOCKER链是否存在对应的端口的转发,如果有则使用filter表的DOCKER来过滤流量,而不再过滤INPUT链了。所以此时必须将允许外部IP访问的规则加在filter表的DOCKER才有用。
如果DOCKER链没有对应端口的转发,则转入INPUT链过滤。
至于为什么会这样,请阅读《一文讲清楚docker利用iptables实现容器网络的原理》。
这样操作之后,就为容器动态添加了端口映射了。把规则删除之后,端口映射即消失。
总结
容器的端口映射,核心就是一个端口转发。另外一个规则就是一个允许外部访问的规则。
原文地址: iptables实现docker容器动态端口映射实操-七秒鱼笔记