今天同事接到一个需求, 需求是这样的: 需要对接友商的系统(域名, 公网能够解析),但是友商系统有个白名单, 只有在白名单中的ip才能有权限访问, 这个ip应该是什么?
补充一下架构背景: 应用都是部署在kubernetes集群中的, kubernetes部署在阿里云ECS上. CNI使用的flannel
这么长时间跟kubernetes打交道, 还真没想过这个问题, 刚好可以测试一翻, 探探究竟
期间会涉及到CNI(container network interface)相关的话题,在这里不会做深入研究, 一笔带过.详细的学习记录在这里可以找到Kubernetes学习(flannel深入学习)
思考
那么把这个问题改变下, ?在Pod中请求baidu.com时,到底发生了什么
- kubernetes集群中的应用是否能够访问公网, 如果都出不去,那都不用谈对接友商的系统了
- 如果能够访问公网, 那出现在友商时的remote-ip一定不会是pod的ip,也不会是clusterIP, 如果是这两个ip的话在response回来的路由上是无法找到这两个地址的, 因为这两个都是集群内部才能够访问
- 那么就有可能是nodeIP或者是公司公网出口IP, 后者可能性大一点.
- 请求会不会经过flannel.
关于第3点, 可以打办公网的例子, 在办公网内的所有机器的ip都是内网ip, 内网的ip都是在同一局域网有效的, 那么在访问外网的时候都会通过一个公网IP(可能多个)出去, 所以在办公网通过myips.com
查看自己ip的时候看到的ip跟使用ip addr
看到的不一样
那么在办公网访问公网的过程中一定是发生了某个转换, 这样请求response返回时能够区分是我这台机器请求的.
NAT
nat分为SNAT、DNAT
SNAT Source Network Address Translation 源网络地址转换,其作用是将ip数据包的源地址转换成另外一个地址,可能有人觉得奇怪,好好的为什么要进行ip地址转换啊,为了弄懂这个问题,我们假设局域网内网主机A(192.168.2.8)要和外网主机B(61.132.62.131)通信,A向B发出IP数据包,如果没有SNAT对A主机进行源地址转换,A与B主机的通讯会不正常中断,因为当路由器将内网的数据包发到公网IP后,公网IP会给你的私网IP回数据包,这时,公网IP根本就无法知道你的私网IP应该如何走了。所以问它上一级路由器,当然这是肯定的,因为从公网上根本就无法看到私网IP,因此你无法给他通信。为了实现数据包的正确发送及返回,网关必须将A的址转换为一个合法的公网地址,同时为了以后B主机能将数据包发送给A,这个合法的公网地址必须是网关的外网地址,如果是其它公网地址的话,B会把数据包发送到其它网关,而不是A主机所在的网关,A将收不到B发过来的数据包,所以内网主机要上公网就必须要有合法的公网地址,而得到这个地址的方法就是让网关进行SNAT(源地址转换),将内网地址转换成公网址(一般是网关的外部地址)
上例145机器可以访问外网。其他局域网机器可以将请求发送给145机器,145机器发出去后,再原路将响应数据返还给局域网机器。这个过程就是 NAT 转换。
NAT 映射表记录了转换关系。
源IP | 源端口 | NAT 网关机器IP | NAT 网关机器端口 | 目标IP | 目标端口 |
---|---|---|---|---|---|
192.168.128.140 | 8080 | 192.168.128.145 | 18080 | 172.217.27.142 | 80 |
- 192.168.128.140:8080 [request google.com] –> 192.168.128.145:18080
- 192.168.128.145:18080 [request google.com] –> google.com (172.217.27.14:80)
- google.com [response] –> 192.168.128.145:18080
- 查找映射表得知,18080获得的数据返还给 192.168.128.140:8080
- google.com [response] –> 192.168.128.140:8080
这就是办公网访问公网的原理
同时还有一个DNAT Destination Network Address Translation 目的网络地址转换, 原理差不多, 可自行百度
为什么要说到SNAT呢, 其实在kube-proxy操作iptables时也大量地存在SNAT的情况, 大家可能使用命令查看下
iptables -v -t nat -nL
那么问题又变成: 这个IP是nodeIP还是也有个类似公网IP的ip
?
测试如下:
通过在一个Kubernetes集群中的Pod(A)中访问另一个Kubernetes的Pod(B), 查看Pod B,日志如下:
x-forward-for
的这个ip并不是Pod A所有的NodeIP, 也不是我知道的任何一个机器的ip,那这个ip会不会是做SNAT操作所在机器的IP呢?
为何找不到请求是如何转发到这个IP的
为了查看这个118.xxx 到底是什么IP, 使用tcpdump
进行抓包, 发现并没有这个ip相关的记录, 按道理说, 想要做SNAT转换,在源机器的iptables中会有所发现, 或者网关指向SNAT机器, 但是一番操作后, 并没有任何跟改IP相关的请求流,而且我也没权限登录改机器
没办法, 最后只能问it机房的同事, 得到的反馈的信息是, 这个IP确认是SNAT网关的公网IP
那么毫无疑问, 这个IP是SNAT后的IP, 但是为何找不到请求是如何转发到这个IP的呢?
原来,在阿里的vpc环境下, 除了使用指定网关或者是使用iptables劫持流量的方式外, console可以直接指定使用SNAT, 只需要添加一个NAT网关, NAT网关上绑定一个公网IP, 同时指定在该vpc下的哪些ECS机器需要使用nat, 那么这些机器的所有访问公网的流量都会通过NAT网关到公网.详情见阿里云NAT网关
这个操作因为是直接在VPC网络进行的操作, 是不需要对ECS机器做操作的, 因此在ECS机器上是没有相关配置的
那么问题就很清晰了, 从pod中curl google.com会经过SNAT, 那又有一个问题, 这样的请求会不会经过flannel?
是否会经过flannel?
由于篇幅这里不会细究flannel的网络模块, 只说实验结论.
可以再做个测试, 由于访问公网无法在对端进行tcpdump, 因此测试kubernetes集群间访问
在ClusterA 的podA访问ClusterB的PodB, podB中是一个nginx, 绑定了一个域名, 在内网可以解析
curl xxx.com
在podA所在的宿主机上进行tcpdump
tcpdump -i any host 172.16.104.207 -vvv -nn
在对端(212)主机上进行tcpdump
tcpdump -i any host 172.16.104.207 -vvv -nn
结论
会发现: 当两台机器都处于不同的kubernetes集群,如果机器间跨内网能够通信的话,那么就不需要SNAT的地址转换了, 而且不会经过flannel, 直接从pod–> docker –> eth0 –> 到达对端, 这点可以通过tcpdump在两边抓包查看
再通过上面对公网的实验可以得到, 在kubernetes的pod中访问公网的资源,也是不会经过flannel的, 因为flannel解决的集群间的跨主机访问, 在对路由匹配的时候不会匹配到flannel的路由, 因此直接通过eth0就转向公网.
科普
这里要简单提一下, 为何会存在SNAT的情况?
当然, 如果没有SNAT的话,少一层转换理论上肯定是更快的, 但是对于办公网或者多云管理场景下, 不可能为所有的机器都配置公网IP(弹性IP), 在IPV4已经枯竭的情况下, ip资源显得更加可贵, 因此目前基本所有的云厂商的弹性IP都是收费的, 通过SNAT这种方式, 只需要一个公网IP就能够解决成百上千在内网的机器(或者说Pod)访问公网的需求,还是很必要的.