其实这个问题我在17年10、11月份的时候就遇到了,当时没有解决。当时我在AWS上折腾docker,我有一个php的docker想要访问宿主机的mysql,两个机器网络不一样(docker的傻逼网络?还是我tmd从aws处又拿了一个内网ip?),宿主机是172.31.x.x,docker是172.32.x.x,需求就是如何在docker里访问宿主机的mysql。(讲道理可以直接访问啊,但最后还是没搞定,具体情况记不清了,后来那台ubuntu好像sshd挂了,又开了一台amz linux ami,就没复现这个问题)
现在这个问题在我配置mysql innodb cluster时遇到的。我原本配置的mysql router(windows)有莫名奇妙的问题。在并发创建mysql连接时返回socket错误。
2018-01-24 17:27:39 DEBUG [48d4] Write error: SystemError: 无法立即完成一个非阻止性套接字操作。 2018-01-24 17:27:39 DEBUG [48d4] [routing:TestCluster_default_rw] Routing stopped (up:16477b;down:290b) Copy server-client failed: SystemError: 无法立即完成一个非阻止性套接字操作。
于是只能在virtualbox的ubuntu中重新配置一个mysql router。而又因为原来宿主机上的mysql集群同步ip配置的是172的外网网卡ip,不想去修改那个配置,导致mysql router读取集群配置时获取的链接因网段不同无法登陆。
当然出于我对于linux以及对计算机网络的了解,我觉得这件事情只需要修改docker中发出数据包的目的地址ip就可以达成目的。
原理是比较简单,但iptables是linux下成名已久的复杂工具。虽然有很多教程,但是很多都语焉不详。
iptables有四表五链,表以功能划分,链则表现数据包的生命周期。
Filter表——过滤数据包 Nat表——Network Address Translation。分为SNAT、DNAT、MASQUERADE、REDIRECT,修改ip和端口 Mangle表——修改TOS、TTL,MARK、SECMARK、CONNSECMARK标记数据包可实现路由 Raw表——过滤数据是否经过iptables INPUT链 OUTPUT链 FORWARD链 PREROUTING链 POSTROUTING链
然而并不是所有数据包能走过所有数据链。一般是三种
1、PREROUTING→FORWARD→POSTROUTING
2、PREROUTING→INPUT
3、OUTPUT→POSTROUTING
看图
于是对于这个需求,我原来天然的以为只要
iptables -t nat -A INPUT -p tcp -d 172.31.35.109 --dport 3316 -j DNAT --to-destination 192.168.56.102 #或者 iptables -t nat -A PREROUTING -p tcp -d 172.31.35.109 --dport 3316 -j DNAT --to-destination 192.168.56.102
当然这根本不起效果,因为数据包根本没有通过PREROUTING、INPUT链。
所以正解是
iptables -t nat -A OUTPUT -p tcp -d 172.31.35.109 --dport 3316 -j DNAT --to-destination 192.168.56.102
iptables设置固化还需要另外2个工具,分别是iptables-save和iptables-restore。于是固化可以这样实现。
#>重定向符的权限问题,通过提升bash权限把语句当脚本执行 sudo sh -c "iptables-save > /etc/iptables.rules" sudo iptables-restore < /etc/iptables.rules #固化 #/etc/network/interface #文件后添加 pre-up iptables-restore < /etc/iptables.rules post-down iptables-save > /etc/iptables.rules
万一把iptables玩崩了,还可以这么重置iptables。
iptables -F iptables -X iptables -t nat -F iptables -t nat -X iptables -t mangle -F iptables -t mangle -X iptables -t raw -F iptables -t raw -X iptables -t security -F iptables -t security -X iptables -P INPUT ACCEPT iptables -P FORWARD ACCEPT iptables -P OUTPUT ACCEPT
net.ipv4.ip_forward=1这个设置好像只影响FORWARD链,这里的例子不需要设置这个值。
然而只设置iptables并不能起作用,原因见最后一张图,数据包先经过路由再到OUTPUT链上。而目的ip 172.31.35.109这个内网地址就被吸到路由黑洞里了,所以还需要配上路由。(据说有用路由黑洞挡ddos的操作)
route add -host 172.31.35.109 dev enp0s3 #静态路由 #/etc/network/interface #文件后添加 up route add -host 172.31.35.109 dev enp0s3
参考:
https://www.netfilter.org/projects/iptables/index.html
https://www.frozentux.net/iptables-tutorial/iptables-tutorial.html
https://en.wikipedia.org/wiki/Iptables
https://wiki.archlinux.org/index.php/iptables
https://help.ubuntu.com/community/IptablesHowTo
https://www.cnblogs.com/clouders/p/6544584.html