Kubernetes 网络插图指南(系列三)

1,323 阅读5分钟

这是图解Kubernetes网络系列中的第三篇文章,如果你还没第一篇第二篇,我建议你回头读这两篇文章。

动态的集群

由于Kubernetes集群的动态性和分布式系统的特点,使得Pods(尤其是Pods的IP地址)总是不断发生变化。无论是Kubernetes滚动升级还是因为Pods或者Node节点的崩溃所引起的缩放事件都会使得使的Pod的IP地址不能被访问。

Kubernetes Service作为一组Pods IP(通过label筛选器选择)端点的虚拟IP地址,充当Pod的虚拟负载均衡器。即便是Pods IP地址发生改变,Kubernetes Service的IP地址也不变化。

虚拟IP地址的实际是一组被kube-proxy(Kubernetes组件)管理的iptables规则(最近的Kubernetes版本可以使用IPVS实现相同的功能)。早先kube-proxy很容易产生歧义,在1.0版本之前,kube-proxy的工作像网络代理,这也使得大量资源在用户态与核心态之间来回切换导致性能损耗。现在,kube-proxy就是Kubernetes中普通的控制器,监察api服务器端点的变化然后更新相应的iptables规则。

当网络包的目的地址指定为service ip地址,iptables的相关规则会将网络包的目的地址由service ip转换到任意一个pod相对应的endpoint的ip地址。正是由于iptables中DNAT的规则使得对service ip的请求可以平均分配到后端的pod上。

网络请求发生DNAT后,DNAT的相关信息(请求协议protocol,源IP地址srcIP,源端口srcPort,目的地址dstIP,目的端口dstPort)将会存储到conntrack表(保存网络请求的信息)中。当请求返回信息时,系统将会根据存储在conntrack表中DNAT的相关信息将返回请求的源IP地址由Pod的IP地址变更为Service的IP地址。这样就使得客户端即便成功完成请求后也无法得知网络请求在后端的工作的流程。

由于Kubernetes Service的DNAT的性质使得不同应用的Pod使用相同的Port端口也不会发生冲突,同时也使得服务发现变得很简单。我们可以将service的hostname硬编码到iptables规则中,也可以使用Kubernetes中预设的service host的IP地址和端口号。

注意:第二种方式将会节省很多不必要的DNS解析

出栈的流量

我们已经讨论很多关于集群内部Kubernetes Service的内容,然而在实际生产场景中应用需要访问外部的api或网站。

通常情况下节点都会有内部和外部IP地址,对于互联网的访问,节点的内部和外部IP地址存在1:1的NAT映射关系,在云环境中尤其如此。

对于从节点向外网请求的网络包,网络包的源IP地址将会由节点的内部IP变为节点的外部IP地址,外网返回的网络包在流入节点时会做相反的处理。然而当请求是由Pod发往外部时,云服务商提供NAT服务并不知道网络包的源IP地址是Pod的IP地址,它将会丢弃所有源IP地址不是节点IP地址的网络包。

想必你已经猜到可以使用iptables来解决上述问题。iptables的相关规则通过可以通过kube-proxy添加,SNAT(网络包的源IP地址转换)实现伪装网络包的源IP。它告诉系统内核将访问外网网络包的源IP由Pod IP地址变更为系统的网络设备接口的IP地址。Contrack表也会记录这些转换信息,当系统收到外网的回复信息时系统利用Contrack表中的相关信息进行逆方向的源地址转换。

入栈的流量

我们讨论了Pod间相互通信,Pod与外网通信。接下来我们将讨论Kubernetes集群如何处理来自外部用户的请求?截止目前为止,主要有以下方案:

Nodeport和云负载均衡(L4---IP和Port)

将Service的type设置为NodePort,并为该service分配在30000-33000之间的端口号。即便没有在任何节点上运行特殊的Pod,每个节点也会开启上文指定NodePort端口的进程。通过NodePort Service的入栈流量将会发送到任意一个Pod上,这个过程同样要使用iptables路由规则。

在云服务商提供的环境中将Service的type设置为LoadBalancer,Kubernetes将会为节点生成相对应的负载均衡器(ELB)。

Ingress (L7---HTTP/TCP)

有许多开源工具实现Ingress,诸如nginx、traefik、haproxy等。 他们的实现思路都是将http hostnames/paths的相关url路径与后端服务绑定。它是流量负载均衡器和节点NodePort服务的入口,通过一条ingress规则将入栈流量映射到相应的后端服务,而不是配置大量NodePort Servic的负载规则。

Network Policy

可以将Network Policy当作是Pods的安全组或者访问控制列表。Network Policy规则允许或拒绝去往Pod的访问。确切的说Network Policy是通过层或者网络插件实现的,但是通常情况下还是使用iptables规则。

到此为止,这一系列就将要结束。在前两篇文章中我们先后学习Kubernetes网络基础Overlays网络的工作原理。现在我们学习了如何在动态的集群内抽象出Service以及如何做服务发现。我们还简要介绍了请求出栈和入栈的工作流程以及使用Network Policy加固集群。

文章翻译自An illustrated guide to Kubernetes Networking [Part 3],行文时略有删减