本文主要介绍了K8S集群中的服务发现和流量暴露机制,包括K8S中的workload类型、service类型、DNS解析原理以及四层服务暴露和七层服务暴露的规则。
1、云原生基础概念
1.1 K8S架构
下图为K8S官方文档中对K8S架构设计的一个简要介绍示意图,这个架构图侧重于从云厂商的角度展示了云厂商的API
、K8S集群中控制面(Control Plane)
和工作节点(Node)
之间的关系,但是将留给第三方实现的如CRI、CNI、CSI
等从中剥离出去了。
在官方架构图的基础上我们将CRI和CNI引入到架构图中,可以得到下面的这个模型:
kube-apiserver
对外暴露了Kubernetes API。它是的 Kubernetes 前端控制层。它被设计为水平扩展,即通过部署更多实例来缩放。etcd
用于 Kubernetes 的后端存储。etcd 负责保存Kubernetes Cluster的配置信息和各种资源的状态信息,始终为 Kubernetes 集群的 etcd 数据提供备份计划。当数据发生变化时,etcd 会快速地通知Kubernetes相关组件。kube-scheduler
主要的工作就是调度新创建的Pod
,当集群中出现了新的Pod还没有确定分配到哪一个Node节点的时候,kube-scheduler
会根据各个节点的负载,以及应用对高可用、性能、数据亲和性的需求等各个方面进行分析并将其分配到最合适的节点上。kube-controller-manager
运行控制器,它们是处理集群中常规任务的后台线程。逻辑上,每个控制器是一个单独的进程,但为了降低复杂性,它们都被编译成独立的可执行文件,并在单个进程中运行。这些控制器包括:节点控制器(Node Controller
)、副本控制器(Replication Controller
)、端点控制器(Endpoints Controller
)、服务帐户和令牌控制器(Service Account & Token Controllers
)等kube-proxy
是集群中每个节点上运行的网络代理, kube-proxy通过维护主机上的网络规则并执行连接转发,实现了Kubernetes服务抽象。service在逻辑上代表了后端的多个Pod,外界通过service访问Pod。service接收到的请求就是通过kube-proxy转发到Pod上的,kube-proxy服务负责将访问service的TCP/UDP数据流转发到后端的容器。如果有多个副本,kube-proxy会实现负载均衡。- K8S的三大插件分别管控运行时、网络和存储,即
Container Runtime Interface (CRI)
、Container Network Interface (CNI)
和Container-Storage-Interface (CSI)
。注意CRI和CNI是每个K8S集群都必须要部署的基础组件,而CSI则不一定,一般来说只有在我们需要运行有状态服务的时候才需要用到CSI。
1.2 CNI基础
K8S本身不实现集群内的网络模型,而是通过将其抽象出来提供了CNI接口给第三方实现,这样一来节省了开发资源可以集中精力到K8S本身,二来可以利用开源社区的力量打造一整个丰富的生态,CNI的一些实现细节和要求我们都可以在github上面找到,我们这里暂不深入解析。
重点来看一下K8S对集群内的网络模型定义:
- K8S集群中任意两个POD可以直接通信,并且不需要进行NAT
- K8S集群中的每个Pod都必须要有自己的唯一、独立且可被访问的IP(IP-per-Pod)
K8S并不关心各个CNI如何具体实现上述基础规则,只要最终的网络模型符合标准即可。因此我们可以确保不论使用什么CNI,K8S集群内的Pod网络都是一张巨大的平面网络,每个Pod在这张网络中地位是平等的,这种设计对于集群内的服务发现、负载均衡、服务迁移、应用配置等诸多场景都带来了极大的便利。
1.3 Overlay networks
Overlay网络可以理解为建立在另一个网络之上的虚拟网络,这个概念在SDN里面里面经常出现。和虚拟网卡需要依赖实际存在的物理网卡才能通信类似,Overlay网络也不能凭空出现,它需要依赖的底层网络通常被称为Underlay网络。Underlay 网络是专门用来承载用户 IP 流量的基础架构层,它与 Overlay 网络之间的关系有点类似物理机和虚拟机。Underlay 网络和物理机都是真正存在的实体,它们分别对应着真实存在的网络设备和计算设备,而 Overlay 网络和虚拟机都是依托在下层实体使用软件虚拟出来的层级。
在使用了Overlay网络的K8S集群中,我们可以把底层的Underlay网络看作是K8S集群的Node节点所在的网络,而上层的Overlay网络一般用来处理Pod之间的网络通信。正常情况下,Underlay网络和Overlay网络之间互不干扰,两者并不知道对方的网络情况。但是由于Overlay网络是需要依赖Underlay网络进行传输数据的,因此在Overlay网络的数据发送到Underlay网络进行传输的时候,需要进行数据包的封装,将其变为Underlay网络可以理解的数据包;反之当数据从Underlay网络传送回Overlay网络的时候需要进行数据包的解封。在K8S的Overlay网络实现中,用于封装的两种常见网络协议是 VXLAN 和 IP-in-IP。
使用Overlay网络的主要优点是:
- 高度灵活性,Overlay网络和底层硬件网络设施分离,因此在跨机房、跨数据中心等场景有着传统的Underlay网络无法比拟的优势
使用Overlay网络的主要缺点是:
- 轻微的性能影响。封装数据包的过程占用少量 CPU,数据包中用于编码封装(VXLAN 或 IP-in-IP 标头)所需的额外字节减少了可以发送的内部数据包的最大大小,进而可以意味着需要为相同数量的总数据发送更多数据包。
- Pod IP 地址不可在集群外路由。
1.4 边界网关协议(BGP)
BGP(Border Gateway Protocol/边界网关协议)是一种基于标准的网络协议,用于在网络中共享路由。它是互联网的基本组成部分之一,具有出色的扩展特性。在K8S中,BGP是出场率很高的一个路由协议,有很多相关的CNI或者是LoadBalancer都会使用BGP协议来实现诸如路由可达或者是ECMP等特性。
目前对BGP协议支持最好、使用最广泛的CNI应该是Calico,另外Cilium也有仍处于beta阶段的BGP模式的支持。
1.5 可路由性(routability)
不同的K8S集群网络的一个重要区别就是Pod的IP在K8S集群外的可路由性。
由于K8S集群内的Pod之间必然是路由可达的,因此这里探讨的是集群外的服务到集群内的Pod之间的路由可达。
路由不可达
所谓路由不可达,即K8S集群外的机器没办法和集群内的Pod直接建立连接,集群外的服务器不知道如何将数据包路由到 Pod IP。
这种情况下当集群内的Pod需要主动和集群外的服务建立连接的时候,会通过K8S进行SNAT(Source Network Address Translation)。此时在集群外的服务器看到的连接对端IP是这个Pod所在的K8S集群节点的Node IP而不是Pod自身的IP,对于集群外的服务器发送返回数据的目的IP也永远都是这个K8S集群节点的Node IP,数据在Node IP上面再转换发送回Pod。这种情况下,集群外的服务器是无法得知Pod的IP,也无法直接获取真实的请求IP。
反之则更复杂,因为集群外的服务器不知道如何将数据包路由到 Pod IP ,所以也没办法主动去请求这些Pod,此时只能通过K8S的services(NodePort、LoadBalancer、Ingress)来将服务暴露到集群外,此时集群外的服务器的访问对象是某个K8S的服务,而不是具体的某个Pod。
路由可达
如果 Pod IP 地址可在集