基于不同的业务场景中,我们该如何在 Kubernetes 生态集群中规划我们应用程序接口的访问策略呢?
通常,我们使用基于 Kubernetes 生态中的 Service 资源在内部或外部暴露所运行的应用程序:即为应用程序定义一个入口点,该入口点将分布式流量自动路由到可用的 Pod 。由于 Pod 会来回飘移-即在同一时刻运行的Pod 集合可能与稍后运行该应用程序的 Pod 集合不同, Service 将它们与标签选择器组合在一起。
然而,在实际的业务场景中我们需要将流量从外部源路由调度至部署至 Kubernetes 集群中的内部服务。虽然目前有多种方法可以实施此操作,但最常见的方法依然是基于 Service 资源。
何为 Service ?首先,我们先来看看官方文档中关于 Service 概念的定义:
代码语言:javascript复制Service,即将运行在一组 Pods 上的应用程序公开为网络服务的抽象方法。使用 Kubernetes,无需修改应用程序即可使用不熟悉的服务发现机制。Kubernetes 为 Pods 提供自己的 IP 地址,并为一组 Pod 提供相同的 DNS 名,并且可以在它们之间进行负载均衡。
基于上述的概念解释,我们可以获知:K8S 中的 Service 并不是我们所理解的“服务” 这种概念,Service 是基于网关层的一种概念抽象,即可描述为若干个 Pod 的流量入口或流量均衡器。
那么,为什么要使用 Service 呢?对于这种问题,官方文档也给予相关的说明,具体如下:
代码语言:javascript复制Kubernetes Pod 本身就具有生命周期。可以被创建,而且销毁之后不会再启动。如果我们使用 Deployment 来运行我们的应用程序,则它可以动态地创建和销毁 Pod。每个 Pod 都有自己的 IP 地址,但是在 Deployment 中,在同一时刻运行的 Pod 集合可能与稍后运行该应用程序的 Pod 集合不同。这导致了一个问题:如果一组 Pod(称为“后端”)为群集内的其他 Pod(称为“前端”)提供功能, 那么前端如何找出并跟踪要连接的 IP 地址,以便前端可以使用工作量的后端部分?
基于上述的解释,我们可以得知:针对网络管理这块,Kubernetes 有它自己的规划与设计(或许很多细节可能在不断的优化与完善),K8S 集群内的每一个 Pod 都分配有自己的 IP(实际业务场景中,一般多个 Pod 共享于一台 Node (节点-服务器)上,K8S 对其配置网络隔离策略),在 K8S 集群内部还有 DNS 等网络服务,一个 K8S 集群就如同管理了多区域的 Node ,并对其进行了复杂的网络拓扑规划。
Service 按类型细分,常用的方案主要包括以下三种:ClusterIP、NodePort 以及 LoadBalancer。每种提供了不同对外提供服务的方式,并且基于不同的业务场景环境。
其实,针对 Service ,作为 K8S 的核心组成部分,其本质屏蔽了服务细节,统一对外暴露服务接口。列举一个简单的场景:假设我们有一个应用服务 Admin-demo,对其定义了 2 个备份,也就意味着 有 2 个 Pod;针对用户或者调用方而言,其只需要关注一个 Service 的入口即可,而无需关注究竟应该请求哪一个 Pod。此种场景下优势非常明显:
1、外部用户或调用方无须感知因为 Pod 上服务的意外崩溃、K8S 重新拉起 Pod 而造成的 IP 变更,外部用户也不需要感知因升级、变更服务带来的 Pod 替换而造成的 IP 变化。
2、Service 具备流量负载均衡功能,类似Nginx等。
关于上述部分内容解析,大家有兴趣的话可以具体可参考之前的文章:Kubernetes Service解析。
本文重点介绍另外一种类型的Service,即 Ingress。(据官网描述最终将会在 K8S 1.19版本中正式发布),并试图从另外一种思维角度去熟悉 Kubernetes 生态的网络访问模型。
什么是 Ingress ?
首先,我们来看一下官方对其的定义:
代码语言:javascript复制Ingress 是对集群中服务的外部访问进行管理的 API 对象,典型的访问方式是 HTTP。Ingress 可以提供负载均衡、SSL 终结和基于名称的虚拟托管。
基于官方的描述,Ingress 是一种 Kubernetes Ingress API 资源,它提供了一种简单的方法来描述从群集外部到群集内服务的HTTP和HTTPS路由,基于HTTP协议的应用程序获取更细粒度的7层负载均衡的Service。
基于上述,我们也可以这样理解,Ingress 是整个 K8S 集群的接入层,复杂集群内外通讯。其将集群内部的 Service 通过 HTTP/HTTPS方式暴露到集群外部,并通过规则定义 HTTP/HTTPS 的路由。Ingress 具备如下特性:集群外部可访问URL、负载均衡、SSL Termination、按域名路由(name-based virtual hosting)。具体我们可以看下面的简要拓扑图:
其实,从本质上来讲,Ingress 核心为提供一种描述高级流量管理约束的方法,特别是针对 基于HTTP协议。使用 Ingress,我们可以定义路由流量的规则,而无需创建一堆负载均衡器或在节点上公开每个服务。同时,我们也可以将其配置为提供服务外部可访问的URL,负载平衡流量,终止SSL / TLS并提供基于名称的虚拟主机和基于内容的路由。
我们首先来看下简单示例,具体如下所示:
代码语言:javascript复制apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: echo-admin-service
namespace: default
spec:
rules:
- host: echo-admin.luga.com
http:
paths:
- backend:
serviceName: echo-admin
servicePort: 80
path: /
现在我们就针对 Ingress组件进行简要分析,具体,我们先来了解下Ingress Controller。
Ingress Controller
Ingress 是一种没有定义内置控制器的内置 API 之一,实际上,在实际的业务场景中需要借助 Ingress 控制器来实现 Ingress API。
Ingress 由Ingress API对象和 Ingress Controller 组成。如前所述,Kubernetes Ingress是一个API对象,它描述了暴露部署到 Kubernetes 集群的服务所需的状态。因此,要使其成为 Ingress控制器,我们需要对 Ingress API 的实现,以读取和处理Ingress资源的信息。
Ingress 控制器通常是作为 Kubernetes 集群中的 Pod 运行并根据入口资源配置负载均衡器的应用程序。负载平衡器可以是群集中运行的软件负载平衡器,也可以是外部运行的硬件或云负载平衡器。不同的负载平衡器需要不同的入口控制器。由于 Ingress API 实际上只是一种元数据,因此 Ingress 控制器会进行繁重的工作。可以使用各种 Ingress 控制器,并且对于每种用例都必须谨慎选择合适的 Ingress Controller 。
同时,我们也可以在同一集群中规划多个 Ingress Controller ,并为每个 Ingress 设置所需的 Ingress 控制器。通常,我们最终将这些控制器的组合用于同一集群中的不同场景。例如,我们可能有一个用于处理进入群集的外部流量,其中包括与SSL证书的绑定,还有一个内部的没有SSL绑定的内部流量,用于处理群集内流量。具体我们可参考如下模型图:
单服务 Ingress
没有规则的 Ingress 将所有流量发送到单个默认后端。如果没有主机或路径与 Ingress 对象中的HTTP请求匹配,则流量将路由到默认后端。具体如下所示:
代码语言:javascript复制apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: echo-service
namespace: default
spec:
backend:
serviceName: echo
servicePort: 80
Fanout配置根据请求的HTTP URI路由流量。它允许我们使用单个负载平衡器和IP地址来服务多个后端服务。其简要拓扑结构如下所示:
此拓扑场景下配置文件如下所示:
代码语言:javascript复制apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-service
namespace: default
spec:
rules:
- host: services.domain.com
http:
paths:
- backend:
serviceName: User
servicePort: 8111
path: /User
- backend:
serviceName: Order
servicePort: 9111
path: /Order
基于主机名的路由支持使用一个负载均衡器来处理指向同一IP地址的不同主机名的流量。其简要拓扑结构如下所示:
此拓扑场景下配置文件如下所示:
代码语言:javascript复制apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: public-services
namespace: default
spec:
rules:
- host: user.luga.com
http:
paths:
- backend:
serviceName: User
servicePort: 8111
path: "/"
- host: order.luga.com
http:
paths:
- backend:
serviceName: Order
servicePort: 9111
path: "/"
多服务 Ingress
如上述所述,可以在一个集群中运行多个 Ingress Controller 。如果集群中存在多个Controller,则每个 Ingress 都应指定一个类来指示应使用哪个 Ingress Controller 。在Kubernetes 1.18之前,我们基于注释(kubernetes.io/ingress.class)来指定Ingress 类。 在1.18中,新的Ingress Class Name字段已添加到 Ingress 规范中,该字段用于引用用于实现 Ingress 的 Ingress Class 资源。具体文件参数配置如下所示:
代码语言:javascript复制apiVersion: networking.k8s.io/v1beta1
kind: IngressClass
metadata:
name: external-lb
spec:
controller: example.com/ingress-controller
parameters:
apiGroup: k8s.example.com/v1alpha
kind: IngressParameters
name: external-lb
IngressClass 资源包含一个可选参数字段。这可以用来引用所讨论类的其他配置。我们可以将特定的IngressClass标记为集群的默认值。在IngressClass资源上将ingressclass.kubernetes.io/is-default-class批注设置为 true 可以确保将未指定 ingressClassName字段的新 Ingress 分配给默认 IngressClass。
以上为 Ingress 资源的相关解析,大家有问题可以随时留言沟通、探讨。关于在服务网格领域体系中Ingress的侵入,例如,以 Istio为例, 暂不在本篇中描述,大家可关注此公众号留意后续的文章。
最后,我们了解下关于 Ingress 的未来发展情况,据相关方获悉,在 Kubernetes 1.19 版本中,Ingress API 即将从 Beta 版升级到 稳定的 API 版本。之后,它很可能会进入稳定的维护模式,并将继续提供一种简单的方式来管理 Kubernetes 工作负载的入站网络流量。 我们应该关注,还有其他在 Kubernetes 上进行 Ingress 的方法,并且目前还有可能在着手开发一套高度可配置的新 API,这些 API 或许在将来能够提供Ingress的替代方案。