开发者学堂课程【从概念、部署到优化,Kubernetes Ingress 网关的落地实践:从概念、部署到优化,Kubernetes Ingress 网关的落地实践】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/1013/detail/15070
从概念、部署到优化,Kubernetes Ingress 网关的落地实践
内容介绍:
一.什么是 Kubernetes Ingress
二.Kubernets Ingress 最佳实战
三.动手实践
首先介绍一下背景,随着容器和 K8s 兴起,集群入口流量管理的方式逐渐通用化和标准化,k8s 通过定义 ingress 资源用来管理外部访问集群内部的服务。
目前, ingress 实现的 provider 非常多,用户可以根据自身的应用场景和应用规模进行产品选型。
一.什么是 Kubernets Ingress
首先介绍一下 K8s Ingress,通常情况下,K8s网络集群内的网络环境与外部是隔离的,也就是说,K8s 集群外部的客户端是无法访问集群内部的服务的。
一般的做法是为目标集群引入一个入口点,所有的外部请求目标集群的流量必须访问这个入口点,然后由这个入口点将外部请求转发给目标节点。K8s 生态在解决集群外部流量管理这一方面也是通过增设入口点来做到的。
K8s 一贯的手法是通过定一个标准来解决同一类问题,在解决对外流量管理的问题上,也是这样做的,K8s 对集群入口点进行了一个统一抽象,对这类场景提出了三种解决方案,分别是 NodePort、LoadBalancer 和 Ingress。
NodePort 思想是对 K8s 内部的对外开放一个端口,外部的流量只要访问这个 NodePort,就会将流量转发至服务。但 NodePort 的缺点就是每个端口只能挂载一个 Service,另外一方面必须要求集群内部的 Node 具有公网 IP,这样的话外网才能访问进来。第三个就是端口范围只能是30000-32767,这样的话没办法处理一些标准的 gps 流量,因为它对外是8043,这是 NodePort 无法做到的。另一个是 LoadBalancer,是在外部引用一个负载均衡,主要是提供给各个云产商来做具体的实现,对应到阿里云上的产品就是SLB,SLB 主要帮助的是四层流量转发,每个 LoadBalancer 只能对应一个 Service,无法暴露多个 Service,如果后端有多个服务都想报给官网的话,可能需要创建同等数量的 SLB,这样的话不但会增加机器成本,也会增加运维成本,从经济角度来看是不合适的。第三种方案是 Ingress,主要关注的是七层流量治理,主要对于的是 HTTP 和 HTTPS流量,通过 Ingress 可以做到多个 Service 对外暴露,因为对外只暴露一个 Ingress provider,Ingress provider 是理解新增流量的,所以它理解请求内容可以感觉请求的 hostpath 做对应的服务分发,内部服务想对外暴露只需要在 Ingress 中配规则即可完成通过一个 Ingress provider 就可以暴露集群中的服务。注意一点,Ingress 不等于 Ingress provider, Ingress 是 K8s 定义路由规则的资源,Ingress provider 是 Ingress 规则的实现者。
当前 K8s 的现状虽然 K8s 对集群入口流量管理的方式进行了标准化的统一,但仅仅覆盖了基本的 HTTP 和 HTTPS 流量的转发,无法满足真实应用场景的具体问题。
一个标准的 Ingress 资源只能配一些 K8s 相关的,比如可以配一个证书是什么,rules 主要定义一些七层的流量转发,可以基于 hostpath 做一些基本的流量分发。标准的 Ingress 是无法解决流量分流、重写重定向等较为常见的流量策略,针对这种情况,业界有两种解决方案。
第一种方案是在 Ingress 的 Annotations 中新增键值对的方式拓展 Ingress,比较流行的 nginx Ingress 就是方案一做到的。图中一部分的 Annotations 的定义,这个例子是关于在服务发布做的拓展。第二种方案是抛弃 K8s 标准的 Ingress,就是基于 K8s 的CI,定义新的流量治理资源,比如标准 istio 通过定义 VirtualService 处理七层流量。
Gateway 主要处理四层到六层的流量,通过 Gateway 同样可以定义证书和域名。另一方面是通过 istio Gateway 可以自定义对外暴露的端口,在标准的 Ingress 只能对外暴露80和43,在 istio Gateway 可以根据应用场景自定义对外端口。
VirtualService 主要对应的是七层流量,跟标准 Ingress 中的 rules 作用是一样的,可以基于 hostpath 做一些流量分发,同样也增加了一些跨域重写重定向,可以做服务发布。
二.Kubernetes Ingress 最佳实践
最佳实现主要是从以下几个方面展开,第一个是流量隔离,推荐用户在自己的 K8s 集群中部署多套 Ingress,这样可以缩小爆炸半径。第二个是灰度发布,介绍如何利用 Ingress Annotation 进行灰度发布,保证服务发布过程中流量无损。第三个是业务域拆分,介绍如何安装业务域进行 API 设计,进行统一的可观测和任务健成的建设。第四个是零信任,简单介绍什么是零信任以及为什么需要零信任,最后给出在真实的业务场景中如何落地零信任。最后是性能调优,分享一些实用的性能调优方法。
在实际应用场景中,集群后端服务需要对外部服务或者内部其他集群提供服务,一般会将外部访问内部的流量称为南北向流量,将内部服务之间的流量称为东西向流量。一般情况下,为了节省机器成本,可能会选择将南北向流量与东西向流量共用一个 Ingress provider,虽然解决了一个机器成本和运维,但是会引入一个新的问题,就是无法针对外部流量和内部流量分别做一个精细化的流量管理。另一方面,共用一个 Ingress provider,故障影响面可能比较大,比如当 Ingress provider 挂掉之后,那么对外对内都不可用了,这是不可接受的。最佳的做法是针对外网和内网场景分别独立部署 Ingress provider,并且根据实际的请求规模以及业务流量,定制各个 provider 的副本数,这样可以做到在缩小爆炸半径的同时,尽可能的提高利用率。在业务持续发展过程中,业务服务始终面临的问题就是版本的频繁升级,如何既能满足业务快速迭代的诉求,又保证升级过程中业务对外服务的高可用。如图所示,怎样将一个 Service 无损的从 v1 升级到 v2,需要解决一下几个核心问题。
第一个就是如何减少升级的影响面,当新版本进行升级时,如果新版本出现故障或者 bug 时,怎么能做到最小化的降低损失。
第二个就是当故障发生时怎么快速的回迁流量到稳定版本。第三个怎么解决标准 Ingress 不支持流量拆分的问题,因为标准 Ingress 只支持一个基本的流量转发,无法做到做的流量分发。
针对前两个问题,业界共识比较通用的做法是采用灰度发布,大家比较熟悉的名字叫金丝雀发布,思想是将少量的请求引流到新版本上,新版本的服务只需要极少数的机器,验证新版本符合预期后,可以逐步调整流量,使得流量慢慢的从老版本迁移至新版本,期间可以根据新老版本的流量比,调整新老版本上的机器的使用规格或者数量,这样可以做到资源的最大化利用。
但三个问题在 Ingress 现状的小节中提到过目前比较流行的拓展 Ingress 的方法,这里主要介绍第一种方案,就是通过 Ingress Annotation 拓展 Ingress。具体做法可以在 Annotation 中定义灰度发布需要的参数配置,比如可以配置灰度领域的 header、cookie 或者值的比喻方式,支持精确匹配或者正则匹配。
另外当定义新的 Annotation 之后,需要由 Ingress provider 识别新定义的 Annotation,并且识别自身的路由规则,可以在数据链真正的做到一个灰度发布。
在进行小流量验证服务新版本是否符合预期的阶段,可以有选择的将线上的一部分流量认为是小流量,可以从请求内容做文章,比如可以基于请求内容的header、cookie 做一个灰度流量。针对同一个 API,可以定义某些 header 或者 cookie 是灰度流量,如果真实场景中线上流量的 header、cookie 是无差别的,那么可以基于线上环境手动制造一些带有灰度 header 的流量验证,可以基于线上环境进行测试。同样的做法可以按照客户端的重要性分批进行新版本的验证,一般用户信息和客户端信息会存在 cookie中,可以按照 cookie 进行流量分发。对于普通用户,请求可以优先访问新版本,当灰度验证完毕之后再逐步引流到重要的用户比如 VIP 用户。这里主要是以 Nginx Ingress 为例, Nginx Ingress 是通过 Annotation 拓展的。这个图左边是一个标准的 K8s Ingress,定义了一个路由匹配规则,就是 app 的这条 path 会访问到 app-v1 的版本,在 app 后端服务升级到 v2 过程中,可以创建一个灰度的 Ingress,通过 Annotation 配置灰度的一些标识,比如 header 是 stage, value 必须是 gray,这一部分流量会认为是灰度流量。当灰度 Ingress 被 Nginx 解析之后,带有 stage、header 值为 gray 的流量就会访问 v2 版本。灰度发布完毕之后就可以更改正式的 Ingress 的目标服务,由 v1 直接切到 v2,这是一个比较稳妥的方法。
在按照 header 的灰度方式中可以对特定的请求或者用户提供服务新版本,但是有另一个缺点就是无法很好的评估访问新版本的请求规模,因此在为新版本分配机器时,无法做到资源最大利用化。另一个常见的做法就是按照权重进行灰度,这样可以精确控制流量比例,在机器资源分配上可以做到游刃有余,通过前期的小流量验证,后期可以通过调整新老版本的流量比例,逐步完成版本升级,这种方法相比于基于 header 是比较简单的,易于操作的。
这种方法也会有缺点,就是会无差别对待线上流量,可以会影响到业务的比较重要的用户的体验。同样以 Nginx Ingress 为例,同样按照 Annotation 拓展,只不过这里 Annotation 跟之前按照 header 不一样,这里需要指定灰度的权重以及灰度权重的总和。当灰度流量验证完毕后,可以直接调整灰度流量的权重比为一百,这样完成版本的升级。
业务域拆分就是如何设计对外暴露的 API,主要的想法是针对原来的架构向微服务架构转换的过程中出现的问题,之前为了应对单体架构存在的敏捷性不足和灵活性不强的问题,开发者将原来的单体架构进行了细腻度拆分,单体用这个服务模块拆分成一个个独立部署的微服务。这样的话虽然在灵活性上更强,但是也会有一些新的问题,比如之前是单体应用通过四层 SLB 就可以完成对外暴露服务,但拆分成分布式应用之后,就依赖于 Ingress 提供的七层流量转发的原理,这时如何更好的设计路由规则就变的非常重要。