Kubernetes网络模型设计的一个基础原则是:每个Pod都拥有一个独立的IP地址,并假定所有Pod都在一个可以直接连通的、扁平的网络空间中。所以不管这些Pod是否运行在同一个Node中,都要求它们可以直接通过对方的IP进行访问。由于Kubernetes的网络模型假设Pod之间访问时使用的是对方Pod的实际地址,所以一个Pod内部的应用程序看到的自己的IP地址和端口与集群内其他Pod看到的一样。基于这个原则设计的原因是,用户不需要额外考虑如何建立Pod之间的连接,也不需要考虑如何将容器端口映射到主机端口等问题。
IP-per-Pod模型是一个简单的网络模型。从该模型的网络的端口分配、域名解析、服务发现、负载均衡、应用配置和 迁移等角度来看,Pod都能够被看作一台独立的虚拟机或物理机。按照这个网络抽象原则,所有Pod都可以在不用NAT的方式下同别的Pod通信。
根据Kubernetes管理的容器、Pod、Service、Namespace、Node、Cluster等资源对象,Kubernetes中的网络通信可以划分为如下场景:
(1) 同一个Pod不同容器间的通信
(2) 同一个Node上不同Pod间的通信
(3) 同一个集群,同一个Namespace,不同Node上Pod间的通信
(4) 同一个集群,不同Namespace,不同Node上Pod间的通信
(5) 不同集群间的通信
对应逻辑视图如下:
注意,一个Service对应的Pod可能分布在多个不同的Node上,这里为简化作图,将其放置在同一个Node上。
一、同一个Pod不同容器间的通信
在Kubernetes中,同一个Pod内的容器间通信就如同在同一台机器上,甚至可以用localhost地址访问彼此的端口。同一个Pod中的容器通信是是不会跨宿主机的。Kubernetes为每个Pod都分配了唯一的IP地址,称之为Pod IP,这个IP也是Paus容器的IP。而同一个Pod中的不同容器通过Pause容器共享同一个网络命名空间(Network Namespace),共享同一个Linux协议栈。同一个Pod内不同容器间通信的逻辑视图如下:
二、同一个Node上不同Pod间的通信
Pod容器既有可能在同一个Node上运行,也有可能在不同的Node上运行,这里先讨论同一个Node上不同Pod间的通信。
Kubernetes通过Pause容器实现了同一个Pod内多个容器间的自由通信(共享网络命名空间)。但是,不同Pod间的通信,每个Pod都拥有独立的IP,也使用独立的网络命名空间。为实现不同Pod间通信做准备,pause 容器会为每个容器创建一个虚拟以太网接口,一个保留在宿主机上(称为veth X,其中x表示具体值),另一个保留在容器网络命名空间内并重命名为eth0。这两个虚拟接口的两端是连接在一起的,从一端进入的数据会从另一端出来。而Node则确保pod上的虚拟以太网接口与Node上分配的以太网接口(称为eth X,其中)通过网桥(Network Bridge)连接起来。其逻辑视图如下所示:
这样,通过Node上的网桥以及虚拟以太网接口的映射,实现了同一个Node上不同Pod间的通信。
同一个Node上不同Service间通信
尽管客户端应用可以直接通过Pod的IP地址和端口号访问提供的服务,但是,提供服务的容器应用通常是分布式的,且Pod副本的数量可能在运行过程中动态改变(如动态扩缩),且单个Pod的IP地址也可能发生变化(如发生了故障恢复)。
对客户端应用来说,要实现动态感知服务后端实例的变化,以及将请求发送到多个后端实例的负载均衡机制,都会大大增加客户端系统实现的复杂度。Kubernetes的Service就是用于解决这些问题的核心组件。通过Service的定义,可以对客户端应用屏蔽后端Pod实例数量及Pod IP地址的变化,通过负载均衡策略实现请求到后端Pod实例的转发,为客户端应用提供一个稳定的服务访问入口地址。
Service是一种持久性资源,它可以提供一个或多个Endpoint,并通过标签选择器选择指向集群中的一组Pod。Service使用ClusterIP来提供内部集群的网络连接,ClusterIP是固定的虚拟IP,这样就可以通过Service来访问Pod,而不需要直接使用Pod的动态IP地址。
Kubernetes集群的每个Node上都会运行一个kube-proxy服务进程,可以把这个进程看作Service的透明代理兼负载均衡器,其核心功能是将到某个Service的访问请求转发到后端的多个Pod实例上。
kube-proxy在运行过程中动态创建与Service相关的iptables规则,这些规则实现了将访问服务(ClusterIP或 NodePort)的请求负载分发到后端Pod的功能。由于iptables机制针对的是本地的kube-proxy端口,所以在每个Node上都要运行kube-proxy组件,这样一来,在Kubernetes集群内部,可以在任意Node上发起对Service的访问请求。
注意,客户端向Service请求的流量通过IPVS(IP Virtual Server)转发到目标Pod,不经过kube-proxy进程的转发,kube-proxy进程只承担了控制层面的功能,即通过API Server的Watch接口实时跟踪Service与Endpoint的变更信息,并更新Node节点上的IPVS。
同一个Node上,不同Service通信的逻辑视图如下所示:
从一个Pod到一个Service的请求,先达到IPVS,IPVS根据从iptables规则,明确需要访问的服务。这里,记录服务(ClusterIP或NodePort)到后端Pod的iptables规则有Node上的kube-proxy通过监听API Server来维护。由于这里仅考虑同一个Node上不同Service的通信,所以需要访问的Service对应的Pod在同一个Node上。此时,IPVS直接将请求转发对应的Pod即可。
注意,Service的访问通常采用域名的方式,所以还需依赖DNS服务。这里为简化流程,并没有体现DNS服务。
三、同一个集群,同一个Namespace,不同Node上Pod间的通信
同一个Node上不同Pod间通过网桥以及虚拟以太网口实现通信,但对于不同Node上的Pod间的通信则更复杂一些。针对不同Node上Pod间通信,Kubernetes基于CNI(Container Network Interface,容器网络接口)标准实现对多种不同的网络插件的支持。也就是说,不同Node上Pod间的通信可以由多种实现。主流的不同Node上Pod间通信主要有以下几种:
(1) 基于隧道的overlay网络:如docker原生的overlay网络就是基于vxlan隧道实现的。Flannel插件已默认基于vxlan实现overlay网络。
(2) 基于包封装的overlay网络:基于UDP封装等数据包包装方式,在docker集群上实现跨主机网络。典型实现方案有Weave等。
(3) 基于三层实现SDN网络:基于三层协议和路由,直接在三层上实现跨主机网络,并且通过iptables实现网络的安全隔离。典型实现方案是Calico。同时对不支持三层路由的环境,Project Calico还提供了基于IPIP封装的实现。
这里简化网络插件的处理方式上的差异,选择一种比较容易理解的方式来简单说明,逻辑视图如下所示:
这样,在 Kubernetes 里,一个Pod里的容器与另外主机上的Pod容器能够直接通信。
同一个Namespace,不同Node上不同Service间通信
同一个Namespace,不同Node上不同Service间通信与同一个Node上,不同Service通信的差异不大,只是在IPVS转发请求时,需要转发给另一个Node的IPVS,然后由这个IPVS将请求转发给对应的Pod。其逻辑视图如下所示:
从一个Pod到一个Service的请求,先达到IPVS,IPVS根据从iptables规则,明确需要访问的服务。同样地,记录服务(ClusterIP或NodePort)到后端Pod的iptables规则有Node上的kube-proxy通过监听API Server来维护。因为这里仅考虑不同Node上不同Service的通信,所以需要访问的Service对应的Pod在其他Node上。此时,IPVS会将请求转发给对应Node的IPVS,然后由对端的IPVS将请求转发给对应的Pod。
四、同一个集群,不同Namespace,不同Node上Pod间的通信
对同一集群来说,不同Namespace不会影响不同Node上Pod间的通信。因为Namespace实现了集群内基于命名空间的对象(如Service、Deployment等资源对象)的隔离,而没有实现集群范围的对象(如StorageClass、Nodes、PersistentVolumes等)的隔离。原文如下:
text
In Kubernetes, namespaces provides a mechanism for isolating groups of resources within a single cluster.
Names of resources need to be unique within a namespace, but not across namespaces.
Namespace-based scoping is applicable only for namespaced objects (e.g. Deployments, Services, etc)
and not for cluster-wide objects (e.g. StorageClass, Nodes, PersistentVolumes, etc).
当然,如果期望实现集群范围的对象的隔离,Kubernetes也提供了一定的支持。如实现Namespace级别的网络隔离可以参考[Kubernetes之网络隔离(https://www.chenshaowen.com/blog/network-policy-of-kubernetes.html)这篇文章,这里不展开讨论。
同一个集群,不同Namespace,不同Node上不同Service间通信
同一个集群下,Service间通信是支持跨Namespace。Service的域名表示方法为..svc.,servicename为服务的名称,namespace为其所在 namespace的名称,clusterdomain为Kubernetes集群设置的域名后缀。这里不再赘述,其逻辑视图可以参考同一个Namespace不同Node上不同Service间通信的逻辑视图。
五、不同集群间的通信
Pod的IP仅支持集群内部使用,对于跨集群间通信或集群与外部服务的通信则无法使用。而Service的Cluster IP同样也只能在Kubernetes集群内被访问。尽管Pod都有唯一的IP地址,但是如果没有Service,Pod IP 无法公开到集群外部,Service实现应用接收流量。所以在实际的应用中,并没有直接暴露Pod到集群外部,而是通过Service的形式。接下来介绍如何将Service暴露到集群外部。
为将Service暴露到集群外部,Service支持通过设置Service资源对象的类型字段"type"进行设置,常用的有:NodePort、LoadBalancer。同时,Kubernetes还提供了Ingress实现将多个Service暴露到集群外部。此外,还可以使用Istio Gateway或API Gateway实现将多个Service暴露到集群外部。
NodePort
NodePort是暴露给集群外部的直接、有效的常见做法。其实现方式是将Service资源对象的类型字段"type"设置为"NodePort"。NodePort的本质是,在Kubernetes集群的每个Node上都为需要外部访问的Service开启一个对应的TCP监听端口,外部系统只要用任意一个Node的IP地址+NodePort端口号即可访问此服务,在任意Node上运行netstat命令,就可以看到有NodePort端口被监听。对需要debug某个后端应用的时候,常常通过这种方式将服务暴露出去,并在使用完成后,关闭这个端口。此外,对于管理整个应用的负载均衡服务,因为整个应用只有一个,所以可可以选择通过NodePort将服务暴露出去。
NodePort将Service的端口号映射到每个Node的一个端口号上,这样集群中的任意Node都可以作为Service的访问入口地址,即NodeIP:NodePort。其逻辑视图如下:
NodePort使用示例如下:
yaml
apiVersion: v1
kind: Service
metadata:
name: demo-service
spec:
type: NodePort # 指定服务类型为NodePort
ports:
- port: bbb # 指定Service的端口,用于集群内部访问
protocol: TCP # 指定协议
targetPort: ccc # 指定后端Pod的容器端口
nodePort: aaa # 对外暴露的端口,注意端口范围,用于集群外部访问
selector:
app: demo-service # 指定关联Pod的标签
在默认情况下,Node的kube-proxy会在全部网卡(0.0.0.0)上绑定NodePort端口号。但是在很多环境中,一台主机会配置多块网卡,所以还需将其绑定到特定网卡上。kube-proxy可以通过设置特定的IP地址将NodePort绑定到特定的网卡上,而无须绑定在全部网卡上,其设置方式为配置启动参数"--nodeport-addresses",指定需要绑定的网卡IP地址,多个地址之间使用逗号分隔。
LoadBalancer
对于Service来说,除了可以通过将Service资源对象的类型字段"type"设置为"NodePort"来暴露服务给集群外部,还可以将Service资源对象的类型字段"type"设置为"NodePort"来暴露服务给集群外部。
如果说NodePort解决了将服务暴露给集群外部,但是仍没有解决负载均衡的问题。负载均衡器组件独立于Kubernetes集群之外,通常是一个硬件的负载均衡器,也有以软件方式实现的,如 Nginx。对于Service,如果需要配置一个对应的负载均衡器实例来转发流量到后端的Node上,这会增加工作量及出错率。
LoadBalancer可以将Service映射到一个已存在的负载均衡器的IP地址上,通常在公有云环境中使用。云服务商提供LoadBalancer可以直接将流量转发到后端Pod上,无需再通过kube-proxy提供的负载均衡机制进行流量转发,且负载分发机制依赖于云服务商的具体实现。LoadBalancer的本质是将Pod关联到LoadBalancer,然后通过LoadBalancer将流量路由到Pod中。
LoadBalancer通过负载均衡器向集群外部公开服务,这样集群外的服务就可以通过负载均衡器间接访问Pod。其逻辑视图如下:
LoadBalancer使用示例如下:
yaml
apiVersion: v1
kind: Service
metadata:
name: demo-service
spec:
type: LoadBalancer # 指定服务类型为LoadBalancer
ports:
- port: xxx # LoadBalancer监听的端口
protocol: TCP # 指定协议
targetPort: ccc # 指定后端Pod的容器端口
selector:
app: demo-service # 指定关联Pod的标签
可见 port 是LoadBalancer监听的端口,targetPort 是应用程序在Pod/容器中监听的端口。Kubernetes与云服务提供商配合,创建负载均衡器并将到达端口(这里是xxx)上的流量转发到目标端口(这里是ccc)的Pod/容器上。
注意,在服务创建成功之后,云服务商会在Service的定义中补充LoadBalancer的IP地址(status字段):
yaml
status:
loadBalancer:
ingress:
- ip: 192.0.2.127
或者通过"kubectl describe service xxx-service"的命令方式确认loadBalancer的IP。
Ingress
NodePort和LoadBalancer虽然都可将服务暴露给集群外部,但是每新增一个服务就需要添加一个NodePort或LoadBalancer的声明,显然,这种方式不适用多个Service的场景。为了支持多Service的处理,同时为了支持虚拟主机、隐藏和节省 IP 地址等特性,Kubernetes提供了基于Ingress的方式来暴露服务。Kubernetes通过引入Ingress,实现将Kubernetes集群外的客户端请求路由到集群内部的服务上,同时提供7层(HTTP和HTTPS)路由功能。Kubernetes Ingress 原理如下所示:
Kubernetes使用了一个Ingress资源对象和一个具体提供转发服务的Ingress Controller,两者结合,实现了基于灵活Ingress策略定义的服务路由功能。同时,由于一个Kubernetes集群内,可能部署多个不同类型的Ingress Controller。为了管理Ingress资源对象和Ingress Controller的对应关系,Kubernetes 引入资源对象IngressClass对其进行规范定义。
当流量进入到Kubernetes时,Ingress 作为 Kubernetes 集群外访问集群的入口,将外部的URL请求转发到不同的Service上。这里,Ingress相当于Nginx等负载均衡反向代理服务器,其中还包括路由规则,即URL的路由信息,将集群外部的请求流量转发到集群内部的服务。注意,Ingress资源对象本身不能进行"路由转发",只是一组规则的集合。完成套接字监听及流量转发的组件则是Ingress Controller。还需要注意的是,Ingress只能以HTTP和HTTPS提供服务 ,对于使用其他网络协议的服务,可以通过设置Service的类型(type)为NodePort或LoadBalancer对集群外部的客户端提供服务。
使用Ingress进行服务路由时,Ingress Controller基于Ingress规则将客户端请求直接转发到Service对应的后端Endpoint(Pod)上,这样会跳过kube-proxy设置的路由转发规则 ,以提高网络转发效率。
Ingress Controller实现基于不同HTTP URL向后转发的负载分发规则,并可以灵活设置七层负载分发策略。目前Ingress Controller已经有许多实现方案,如Nginx、HAProxy、Kong、Istio 等开源软件的实现,以及公有云GCE、Azure、AWS等提供的Ingress应用网关。
在Kubernetes中,Ingress Controller会持续监控API Server的/ingress 接口(即用户定义的到后端服务的转发规则),动态感知集群中Ingress的规则变化(无需手动绑定Ingress Controller和Ingress资源对象)。当/ingress接口后 端的服务信息发生变化时,Ingress Controller会自动更新其转发规则。
注意,Ingress Controller 不同于Deployment 控制。因为 Ingress Controller 不直接运行为kube-controller-manager的一部分,它仅仅是Kubernetes集群的一个插件,类似于CoreDNS,需要在集群上单独部署。
其它
除了使用NodePort、LoadBalancer、Ingress来暴露服务,还可以通过Kubernetes新引入的Kubernetes Gateway来暴露服务。此外,还有Istio Gateway、API Gateway等暴露服务。
Kubernetes 起初只能使用 NodePort 和 LoadBalancer 类型的 Service 对象来暴露集群内服务,后来诞生 Ingress。Ingress 的主要目标是用简单的、声明性的语法来暴露 HTTP 应用。Kubernetes支持部署多种不同 Ingress Controller,并通过 IngressClass 指定该Ingress使用的Ingress Controller。Kubernetes 支持大量的第三方 Ingress Controller。如Nginx、HAProxy、Kong、Istio 等开源软件,以及公有云GCE、Azure、AWS等提供的Ingress应用网关。
虽然Ingress实现了入口网关与后台实现的解耦,但是它仍然有着巨大的局限性:Ingress 的配置过于简单,仅支持 HTTP 协议路由;HTTP 路由仅支持 host 和 path 匹配,对于高级路由功能没有通用配置,只能通过 annotation 来实现,无法适应可编程路由的需求;不同命名空间中的服务要绑定到同一个网关中的情况在实际情况下经常出现,而入口网关无法在多个命名空间中共享;入口网关的创建和管理的职责没有划分界限,导致开发者不仅要配置网关路由,还需要自己创建和管理网关。
Gateway API 作为 Kubernetes 入口网关的最新成果,它通过角色划分将关注点分离,并提供跨 namespace 支持使其更适应多云环境,已获得大多数 API 网关的支持。
Gateway API 是一个 API 资源的集合 ------ GatewayClass、Gateway、HTTPRoute、TCPRoute、ReferenceGrant 等。Gateway API 暴露了一个更通用的代理 API,可以用于更多的协议,而不仅仅是 HTTP,并为更多的基础设施组件建模,为集群运营提供更好的部署和管理选项。另外 Gateway API 通过将资源对象分离,实现配置上的解耦,可以由不同的角色的人员来管理。
此外,主流的服务网格 Istio 提供了基于Istio Gateway来暴露服务。Istio Gateway的功能与 Kubernetes Ingress 类似,它负责进出集群的南北流量。Istio Gateway 描述了一个负载均衡器,用于承载进出服务网格边缘的连接。该规范描述了一组开放端口和这些端口所使用的协议,以及用于负载均衡的 SNI 配置等。Istio Gateway 资源本身只能配置 L4 到 L6 的功能,例如暴露的端口、TLS 设置等;但 Gateway 可与 VirtualService 绑定,在 VirtualService 中可以配置七层路由规则,例如按比例和版本的流量路由,故障注入,HTTP 重定向,HTTP 重写等所有 Mesh 内部支持的路由规则。
最后,介绍下基于API网关来暴露服务。API网关是位于客户端和后端服务之间的 API 管理工具,一种将客户端接口与后端实现分离的方式,在微服务中得到了广泛的应用。当客户端发出请求时,API 网关会将其分解为多个请求,然后将它们路由到正确的位置,生成响应,并跟踪所有内容。
API Gateway 是微服务架构体系中的一类型特殊服务,它是所有微服务的入口,它的职责是执行路由请求、协议转换、聚合数据、认证、限流、熔断等。大多数企业 API 都是通过 API Gateway 部署的。
参考
https://www.zhihu.com/tardis/zm/art/468291559 10本 Kubernetes 学习书籍推荐
《Kubernetes权威指南 从Docker到Kubernetes实践全接触》 龚正 吴治辉 闫健勇 编著
https://blog.csdn.net/qq_45808700/article/details/132714651 K8s进阶之网络
https://matthewpalmer.net/kubernetes-app-developer/articles/kubernetes-networking-guide-beginners.html Kubernetes Networking Guide for Beginners
https://www.51cto.com/article/620287.html Kubernetes之POD、容器之间的网络通信
https://www.airplane.dev/blog/kubernetes-networking An introduction to Kubernetes networking
https://kuboard.cn/learning/k8s-intermediate/service/network.html Kubernetes网络模型
https://zhuanlan.zhihu.com/p/596955458 深入探索 Kubernetes 网络模型和网络通信
https://zhuanlan.zhihu.com/p/81667781 一篇文章为你图解kubernetes网络通信原理
https://cloud.tencent.com/developer/article/1618617 docker bridge 到 k8s pod 跨节点网络通信机制演进
https://www.oreilly.com/library/view/devops-with-kubernetes/9781789533996/db0ae859-4904-4bfd-9345-86012fd08666.xhtml Pod communication within the same node
https://www.chenshaowen.com/blog/network-policy-of-kubernetes.html Kubernetes 之网络隔离
https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/ Namespaces
https://humalect.com/blog/kubernetes-namespace#how-do-pods-communicate-across-different-namespaces-in-kubernetes How do Pods Communicate across Different Namespaces in Kubernetes?
https://theithollow.com/2019/02/06/kubernetes-namespaces/ Kubernetes -- Namespaces
https://blog.csdn.net/weixin_39869791/article/details/118906811 k8s pod之间不能通信,如何使多个Pod在kubernetes上相互通信
https://kubernetes.io/zh-cn/docs/tutorials/kubernetes-basics/expose/expose-intro/ 使用 Service 暴露你的应用
https://zhuanlan.zhihu.com/p/405545050 如何理解 Istio Ingress, 它与 API Gateway 有什么区别?
https://zhuanlan.zhihu.com/p/618328589 一篇搞定K8s Ingress
https://www.nginx.co.jp/blog/kubernetes-networking-101/ Kubernetes Networking 101
https://nigelpoulton.com/explained-kubernetes-service-ports/ Explained: Kubernetes Service Ports
https://rtfm.co.ua/en/kubernetes-part-1-architecture-and-main-components-overview/ Kubernetes: part 1 -- architecture and main components overview
https://code4projects.altervista.org/kubernetes-services-cluster-ip-vs-nodeport-vs-loadbalancer-vs-ingress/ Kubernetes Cluster IP vs NodePort vs LoadBalancer vs Ingress
https://zhuanlan.zhihu.com/p/302452502 ingress控制器
https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/ Ingress Controllers
https://kubernetes.io/docs/concepts/services-networking/ingress/ Ingress
https://www.zhihu.com/question/54852255/answer/2742620556 Kubernetes 中的入口网关和 Gateway API简介
https://zhuanlan.zhihu.com/p/580043250 Gateway API:Kubernetes 和服务网格入口中网关的未来
https://jimmysong.io/blog/why-do-you-need-istio-when-you-already-have-kubernetes/ 为什么在使用了 Kubernetes 后你可能还需要 Istio?