大家好,我是张晋涛。
因为前段时间家人生病住院 & 我换了份工作。所以断更了一段时间,后续就会逐步恢复更新了。
最近在 Kubernetes 生态中比较大的事情就是 KubeCon NA 2023 成功举办了,会上有很多不错的内容,也有很多重磅消息。目前会议的相关视频已经全部上传完成, 感兴趣的小伙伴可以通过 https://sourl.cn/xTUCvn 观看。
这其中就包括了本篇文章的主角: Gateway API 发布了 v1.0 版本,正式 GA!
在聊 Gateway API 能否成为 Kubernetes 中流量管理的未来这个话题前,我先来介绍一些背景。
Kubernetes 中的流量管理实际上分为两个主要的部分:
- 南北向流量
- 东西向流量
南北向流量管理
在 Kubernetes 场景中的 南北向流量 主要指从集群外到集群内的流量,客户端想要访问部署在 Kubernetes 集群中的服务时, 需要将集群内的服务暴露出来,最常用的方式就是通过 NodePort 或 LoadBalancer 类型的 Service。
这两种方式相对来说都比较简单,但是如果用户有很多服务需要暴露到集群外,通过使用这些方式就会浪费很多的端口(NodePort)或者浪费很多 IP(Loadbalancer), 而且由于 Service 的 API 在设计时定位相对清晰,并不包含一些 LB 或者网关的能力,比如根据域名进行代理,或者认证,鉴权等能力,所以如果仅仅通过 Service 来将 集群内服务暴露出去的话,就很不合理了。
基于上述的考虑,社区在 2015 年 Kubernetes v1.1 版本时就增加了 Ingress 这种内置的资源/API,可以看到它的定义是非常简单的,对于每个 Ingress 资源,仅仅能指定 Host, Path 以及应用的 Service,Port 和协议。
// An Ingress is a way to give services externally-reachable urls. Each Ingress is a
// collection of rules that allow inbound connections to reach the endpoints defined by
// a backend.
type Ingress struct {
unversioned.TypeMeta `json:",inline"`
// Standard object's metadata.
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata
v1.ObjectMeta `json:"metadata,omitempty"`
// Spec is the desired state of the Ingress.
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status
Spec IngressSpec `json:"spec,omitempty"`
// Status is the current state of the Ingress.
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status
Status IngressStatus `json:"status,omitempty"`
}
// IngressSpec describes the Ingress the user wishes to exist.
type IngressSpec struct {
// TODO: Add the ability to specify load-balancer IP just like what Service has already done?
// A list of rules used to configure the Ingress.
// http://<host>:<port>/<path>?<searchpart> -> IngressBackend
// Where parts of the url conform to RFC 1738.
Rules []IngressRule `json:"rules"`
}
// IngressRule represents the rules mapping the paths under a specified host to the related backend services.
type IngressRule struct {
// Host is the fully qualified domain name of a network host, or its IP
// address as a set of four decimal digit groups separated by ".".
// Conforms to RFC 1738.
Host string `json:"host,omitempty"`
// Paths describe a list of load-balancer rules under the specified host.
Paths []IngressPath `json:"paths"`
}
// IngressPath associates a path regex with an IngressBackend.
// Incoming urls matching the Path are forwarded to the Backend.
type IngressPath struct {
// Path is a regex matched against the url of an incoming request.
Path string `json:"path,omitempty"`
// Define the referenced service endpoint which the traffic will be forwarded to.
Backend IngressBackend `json:"backend"`
}
// IngressBackend describes all endpoints for a given Service, port and protocol.
type IngressBackend struct {
// Specifies the referenced service.
ServiceRef v1.LocalObjectReference `json:"serviceRef"`
// Specifies the port of the referenced service.
ServicePort util.IntOrString `json:"servicePort,omitempty"`
// Specifies the protocol of the referenced service.
Protocol v1.Protocol `json:"protocol,omitempty"`
}
这对于大多数场景来说,只是达到了 基本可用 的状态,但没有包含其他常规的功能(比如根据 Request Header/Method 进行匹配,或者 Path Rewrite 等)。
此外,Ingress 也仅仅是一套 API 规范,是一种资源。想要让它生效还需要有 Controller 来支持。
Ingress 是如何工作的
上文提到 Ingress 是一种资源,用户在 Kubernetes 集群中创建 Ingress 资源后,需要经历一系列的过程:
- AuthZ/AuthN 认证鉴权;
- Admission Controller:这其中比较典型的是 Mutating Admission 和 Validating Admission,对这部分感兴趣的小伙伴可以查看我之前的文章 理清 Kubernetes 中的准入控制(Admission Controller) | MoeLove
- 通过上述所有步骤后,Ingress 资源才能真正写入 etcd 进行存储;
但是 Kubernetes 在引入 Ingress 资源的时候,并没有像 Deployment、Pod 等资源那样,一并实现其对应的 Controller。
所以它需要有一个 Ingress controller 存在,将 Ingress 资源中定义的规则翻译成数据面可以识别的配置,并在数据面上生效。这样才能为客户端请求提供服务,当前仅列在 Kubernetes 官方文档中的 Ingress controller 实现就有 30 种 https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/
同时,由于 Ingress API 只定义了有限的内容,它的表现力不够,为了满足不同场景下的需求,各个 Ingress controller 实现的时候,只好通过创建自己的 CRD(Custom Resource Definition)或者通过为 Ingress 资源增加 annotations 的方式来实现对应的需求,以 Kubernetes Ingress-NGINX 项目为例:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/rewrite-target: /$2
name: rewrite
namespace: default
spec:
ingressClassName: nginx
rules:
- host: rewrite.bar.com
http:
paths:
- path: /something(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: http-svc
port:
number: 80
想要实现 Rewrite rewrite.bar.com/something/new
到 rewrite.bar.com/new
的操作,就需要为 Ingress 资源添加对应的 annotations 才能实现。而且目前 Kubernetes Ingress-NGINX 项目为了满足不同场景下的需求,已经有 130+ 的 annotation 存在了。
此外,目前这些 Ingress controller 为了实现其各自需求,彼此之间的 annotations 并不兼容, 用户想要在两套不同的 Ingress Controller 间进行迁移是非常困难的。
东西向流量管理
接下来聊聊 Kubernetes 中的东西向流量。
在 Kubernetes 场景中的 东西向流量 主要指集群内服务内彼此之间的流量,主要是服务之间的调用管理。 通常情况下,我们可以选择用 Service 来完成。
集群内的应用,通过 Service 最终将请求发送到目标 Pod 中。
但我们也知道通常情况下集群内的 Service 使用的是 Overlay 网络,它存在一定的性能损耗。所以某些场景下,我们并不希望通过 Service 来访问其他的 Pod,而是希望能直接拿到对应的 Pod IP 进行直连。
这种场景下,Service 作用可以认为只是一个 DNS name, 或者说自动化管理 endpoint/endpointslices 等资源/信息的组件。
➜ ~ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 9d
➜ ~ kubectl get endpoints
NAME ENDPOINTS AGE
kubernetes 131.83.127.119:6443 9d
➜ ~ kubectl get endpointslices
NAME ADDRESSTYPE PORTS ENDPOINTS AGE
kubernetes IPv4 6443 131.83.127.119 9d
当然,在涉及到 Kubernetes 集群内东西向流量管理的时候,我们很容易想到例如 Istio/Linkerd/Kuma 等服务网格的项目,这些项目都是通过创建自己的 CRD 来完成东西向流量管理的。 例如如下是 Kuma 的一个 TrafficRoute 的配置,可以完成东西向流量的切割。
apiVersion: kuma.io/v1alpha1
kind: TrafficRoute
mesh: default
metadata:
name: api-split
spec:
sources:
- match:
kuma.io/service: frontend_default_svc_80
destinations:
- match:
kuma.io/service: backend_default_svc_80
conf:
http:
- match:
path:
prefix: "/api"
split:
- weight: 90
destination:
kuma.io/service: backend_default_svc_80
version: '1.0'
- weight: 10
destination:
kuma.io/service: backend_default_svc_80
version: '2.0'
destination: # default rule is applied when endpoint does not match any rules in http section
kuma.io/service: backend_default_svc_80
version: '1.0'
各个实现之间的配置也互相不兼容,虽然功能差异性并不大,但导致了用户想要进行迁移的时候,可能出现迁移困难的情况。
聊完了 Kubernetes 中的流量管理,我们简单总结一下:
- Kubernetes 中的南北向流量尽管制定了规范 Ingress,但由于该资源的表达能力弱,不同的厂商在实现的时候,彼此互不兼容,迁移成本很高;
- Kubernetes 中的东西向流量管理,并未制定任何规范,各个厂商需要声明各自的 CRD 以满足需求。但同样的,也彼此互不兼容。
Gateway API 是什么
Gateway API 为何会出现
2018 年 Kubernetes 社区做的一项统计,调查用户的 Ingress 资源中包含多少为了实现特定需求而增加的 annotations。 从结果上可以看到只有 8% 的用户没有额外添加 annotations。尽管后来我们没有再做这方面的调查,但我们一直也是根据用户的需求、反馈来持续增加 annotations 功能的, 现在这个没有额外添加 annotations 的用户比例大概会下降很多。
同时,可以看到将近 85% 的用户期望 Ingress 可以具备良好的移植性。这样用户在不同的环境、集群中就可以更容易的采用相同配置进行处理了。 而且相比于更具备表达能力而言,用户更希望的还是可以具备移植性。
从整个调研结果来看,用户对于 Ingress API 的情况是:很多人都再用,但改进点还有很多。
所以在 2019 年圣地亚哥 KubeCon 大会上,来自 Kubernetes Networking Special Interest Group (SIG Network) 的很多人共同商量讨论了 Ingress 的现状,用户需求等相关内容,并达成一致要启动 Gateway API 规范的制定。具体背后的故事可以参考 Gateway API: From Early Years to GA
Gateway API 的现状
由于在设计 Gateway API 的时候,Ingress 资源已经存在了 4 年时间,并且收到了很多用户真实环境的反馈。基于这些经验,设计出的 Gateway API 有如下特点:
- 面向角色
Gateway API 的设计是面向角色的,具体而言:
GatewayClass
: 这是由基础设施供应商提供/配置的,可用于定义一些和基础设施相关的能力,例如下面的配置就定义了使用哪个 controller 来完成这些能力,可使用的 IP 地址池有哪些。
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: internet
spec:
controllerName: "example.net/gateway-controller"
parametersRef:
group: example.net/v1alpha1
kind: Config
name: internet-gateway-config
---
apiVersion: example.net/v1alpha1
kind: Config
metadata:
name: internet-gateway-config
spec:
ip-address-pool: internet-vips
Gateway
: 这个是由集群运维来管理的,可以定义一些 Gateway 自身相关的能力,例如监听哪些端口,支持哪些协议等; 例如下面的配置就定义了一个只监听 80 端口,支持 HTTP 协议的 Kong Gateway 。
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: kong-http
spec:
gatewayClassName: kong
listeners:
- name: proxy
port: 80
protocol: HTTP
HTTPRoute
/TCPRoute
/*Route
: 路由规则可以由应用开发者进行管理和发布,因为应用开发知道应用需要暴露出哪些端口,访问路径,以及需要 Gateway 配合哪些功能等。 例如下面的配置就定义了 client 带前缀/echo
访问时,请求将会被发送到echo
Service 的 1027 端口。
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: echo
spec:
parentRefs:
- name: kong
rules:
- matches:
- path:
type: PathPrefix
value: /echo
backendRefs:
- name: echo
kind: Service
port: 1027
而且,我们可以看到,这个配置中没有任何和 provider 有关的内容,那么它的移植性是非常好的,无论是在不同的环境之间进行迁移, 还是在不同的 Ingress Controller 之间进行迁移都会很方便。
- 表现力更强
Gateway API 相比于 Ingress 具有更强的表现力,也就是说通过它原生在 spec 中定义的内容,就可以达到预期的效果,而不需要像 Ingress 资源那样添加 annotations 进行辅助。
正如我前面介绍 Ingress 时提到的 rewrite 的需求,在 Gateway API 的 HTTPRoute 资源中可以使用如下方式进行表示。
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
name: http-filter-rewrite
spec:
hostnames:
- rewrite.example
rules:
- filters:
- type: URLRewrite
urlRewrite:
hostname: elsewhere.example
path:
type: ReplacePrefixMatch
replacePrefixMatch: /fennel
backendRefs:
- name: example-svc
weight: 1
port: 80
- 可扩展
GatewayAPI 在设计的时候,预留了一些可以进行扩展的点,例如以下的配置,可以在 spec.parametersRef
中关联到任意的自定义资源,从而实现更为复杂的需求。
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: internet
spec:
controllerName: "example.net/gateway-controller"
parametersRef:
group: example.net/v1alpha1
kind: Config
name: internet-gateway-config
---
apiVersion: example.net/v1alpha1
kind: Config
metadata:
name: internet-gateway-config
spec:
ip-address-pool: internet-vips
总的来说, Gateway API 就是一套基于角色,更富有表现力,和可扩展性的规范。由于它的这些特点,也使得 Gateway API 的配置可以更具备可移植性,在不同的 provider 中进行迁移会更加简单。
Gateway API 会是 Kubernetes 中流量管理的未来吗?
Gateway API 最近正式 GA,但达到 GA 的也只有 GatewayClass
,Gateway
, HTTPRoute
这三个资源,还有很多其他已经定义但尚处于 beta 阶段的资源,所以目前可以认为它主要能满足一些常规的 L7 的需求。
GAMMA(Gateway API for Service Mesh) 也还在持续演进中,用于定义如何使用 Gateway API 来管理 Kubernetes 中东西向的流量。
按照目前 Kubernetes 的发展和社区的演进来看,Gateway API 是下一代 Kubernetes 流量管理的规范。
而且我个人认为,不会再出现其他用来取代 Gateway API 的规范了。
此外,社区中也有一个想法是希望借助 Gateway API 来简化本文中提到多次的 Service API,以此来简化 Service API 的维护成本。
让我们拭目以待!
欢迎订阅我的文章公众号【MoeLove】