加餐 尝鲜Gateway API:更强大、更灵活、面向未来的Ingress
你好,我是Chrono。
距离《Kubernetes入门实战课》完结已经有一段时间了,不知道你是否还在持续地学习、实践这个云原生时代的操作系统呢?我想答案应该是肯定的吧。
这次我带来了一个关于Kubernetes的最新消息,长期以来被关注的“下一代” Ingress 对象:Gateway API,在经过了近4年的讨论、测试和验证之后,终于在2023年的11月正式发布,可以用于生产环境,也就是我们常说的GA(generally available)。
那么,什么是Gateway API呢?它与Ingress有什么样的联系和区别呢?我们应该怎样在Kubernetes里部署和使用它呢?
今天我就来深入探讨这个话题,来看看Kubernetes的最新进展。
什么是 Gateway API
我们在第21讲对Ingress做过详细的介绍,相信你已经对它很熟悉了,它管理集群的进出口流量,提供完善的负载均衡和反向代理功能,是部署Kubernetes应用必不可缺的组件。
但是,Ingress的缺点也很明显,它自身的能力较弱,只能编写有限的路由规则,不能完全满足各种实际需求,所以Ingress Controller的实现者(如NGINX Ingress Controller、Kong Ingress Controller等)不得不使用大量的annotation和CRD来定制、扩展功能。
这种实现混乱的局面被戏称为 “annotations wild west”,对Kubernetes用户非常不友好,同一个功能在不同的Ingress Controller之间用法差异极大,迁移的成本非常高,没有统一的标准导致Ingress使用起来相当麻烦。
有鉴于此,Kubernetes社区就打算参考Ingress,重新设计一组对象来解决这些问题。于是在2019年末圣地亚哥的KubeCon上,许多不同背景的人们发起了一个新的项目,也就是Gateway API,目标是全面替代和超越Ingress,更好地管理Kubernetes里各方向的流量。
在随后的几年,陆续有一些公司加入了Gateway API项目,因为有Ingress的“前车之鉴”,Gateway API一开始就具备了良好的架构,避免了Ingress所踩过的“坑”,所以它的进展还是比较顺利的。
下图来自Kubernetes的官方统计数据,显示了在这个过程中各个公司的贡献程度。
在2023年11月发布的1.0版本里包括3个已经成熟稳定的对象,Gateway Class、Gateway和HTTPRoute(相关的博客在这里)。
在Gateway API项目的主页上,对这3个重要的对象有简单的介绍。
由于Gateway API是Ingress的后继者,所以我们可以对比Ingress来理解它们的概念和作用。
最上层的Gateway Class类似于Ingress Class,由各个云厂商提供;中间的Gateway类似于Ingress Controller,由集群管理员管理;下面的HTTPRoute类似于Ingress,由开发人员管理,定义路由规则,规定流量将如何被Gateway分发到集群里的Service和Pod。
可以看到,Gateway API的结构非常清晰明了,很容易理解。
在Gateway API正式发布时(11月1日),大部分厂商还没有提供完整支持,基本都是预览版或测试版,但也有两家公司几乎同步实现了GA,这就是GKE(Google Kubernetes Engine)和Kong,所以下面我就以Kong为例,来介绍Gateway API的用法。
安装 Gateway API
首先要说的是,Gateway API只支持较新的Kubernetes,不能运行在Kubernetes 1.23上,为了体验Gateway API,我们最好使用最新版本的Kubernetes。
这里我选用了minikube 1.32.0,Kubernetes是1.28.3,你可以参考第9讲的内容来安装,或者直接用下面的命令。
# Intel x86_64
curl -Lo minikube https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
# Apple arm64
curl -Lo minikube https://storage.googleapis.com/minikube/releases/latest/minikube-linux-arm64
sudo install minikube /usr/local/bin/
minikube start --kubernetes-version=v1.28.3
kubectl version
有了合适的Kubernetes环境,下面就来部署Gateway API。
要注意的是Gateway API比较特殊,并不是集成在Kubernetes内部,而是在外部以相对独立的方式开发实现的,所以需要用YAML文件的形式来部署进Kubernetes。
你可以在Gateway API项目网站里找到安装命令,很简单,用 kubectl apply
就能够搞定。
wget https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.0.0/standard-install.yaml
kubectl apply -f standard-install.yaml
安装完成后,可以用 kubectl api-resources
命令来查看新的Gateway API对象。
这就证明Gateway API已经成功安装到了当前的Kubernetes集群里,而且你还应该注意到Gateway Class的缩写是 “gc”,Gateway的缩写是 “gtw”。
现在用YAML创建实验用的Gateway Class和Gateway对象,但目前它们还不能使用 kubectl create
生成样板,只能手动编写。
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: kong-gc
annotations:
konghq.com/gatewayclass-unmanaged: 'true'
spec:
controllerName: konghq.com/kic-gateway-controller
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: kong-gtw
spec:
gatewayClassName: kong-gc
listeners:
- name: proxy
port: 80
protocol: HTTP
这个YAML定义了一个叫 kong-gc 的Gateway Class对象,指定使用的Controller是 konghq.com/kic-gateway-controller。然后Gateway对象的名字是 kong-gtw,它关联了 kong-gc,在80端口上处理HTTP协议。
kubectl apply
后再用 kubectl get
命令,会看到它们已经创建成功。
安装 Kong Ingress Controller
刚才部署好了Kubernetes环境和Gateway API,现在就可以安装Kong Gateway,它的实现仍然是以Ingress Controller的形式,也就是Kong Ingress Controller,在之前的加餐中我也对它做过详细的介绍。
不过那个时候使用的Kong Ingress Controller还是2.7.0,而现在它已经升级到了3.0.0,里里外外都发生了很多变化,完全是一个新的应用。
Kong Ingress Controller 2.x 可以使用YAML文件直接安装,但3.0.0已经废弃了这种方式,只能够使用Helm或Operator来安装,这里我选用的是Helm。
你也许不太熟悉Helm,我来简单介绍一下。它类似于Linux里的yum、apt,对复杂的云原生应用非常有用,可以把众多的YAML文件组合成安装包的形式,再轻松地把应用部署进Kubernetes集群。但由于它不是我们今天的重点,所以就不多说了。
Helm的安装很简单,它提供了一个官方脚本,直接执行即可。
然后我们需要添加远端仓库(Helm charts),用法和yum、apt也很类似。
之后可以查看远端仓库里可用的安装包。
这里显示的 kong/ingress 就是Kong Ingress Controller。
使用命令 helm install
指定名字和安装包,再加上一些定制参数就可以安装Kong Ingress Controller。
helm install \
kong kong/ingress \
-n kong \
--create-namespace \
--set gateway.env.router_flavor=expressions
这里我多加了一个 “–set” 选项,启用Kong Ingress Controller的表达式路由,能够更好地支持Gateway API。
Kong Ingress Controller默认安装在kong名字空间,用 kubectl get
就可以看到它的Pod、Service等对象。
要注意的是kong-gateway-proxy这个Service,它的类型是LoadBalancer,也就是对外的服务接口,在实验环境里使用的端口是 31198,后续我们要使用这个端口来测试。
使用 curl
访问这个服务可以验证Kong Ingress Controller是否正常工作。
curl
命令的输出是404,这是因为我们还没有配置HTTPRoute资源,没有路由规则,所以Gateway无法处理流量。
这时再检查Gateway Class和Gateway对象,会看到 ACCEPTED 和 PROGRAMMED 字段都已经变成了 True,这就表示Gateway对象已经正确关联了Kong Ingress Controller。
准备后端服务
为了验证Gateway API的流量管理效果,我们还要创建测试用的后端服务,具体做法可以参考第20讲和第21讲,部署NGINX来输出简单的字符串。
下面的这个YAML是一个样板文件,你可以使用查找替换的方式生成多个不同名字的服务。
apiVersion: v1
kind: ConfigMap
metadata:
name: ngx-conf
data:
default.conf: |
server {
listen 80;
location / {
default_type text/plain;
return 200
'ngx\nsrv : $server_addr:$server_port\nhost: $hostname\nuri : $request_method $host $request_uri\n';
}
}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: ngx-dep
labels:
app: ngx-dep
spec:
replicas: 1
selector:
matchLabels:
app: ngx-dep
template:
metadata:
labels:
app: ngx-dep
spec:
volumes:
- name: ngx-conf-vol
configMap:
name: ngx-conf
containers:
- image: nginx:alpine
name: nginx
ports:
- containerPort: 80
volumeMounts:
- mountPath: /etc/nginx/conf.d
name: ngx-conf-vol
---
apiVersion: v1
kind: Service
metadata:
name: ngx-svc
spec:
selector:
app: ngx-dep
ports:
- port: 80
protocol: TCP
targetPort: 80
比如使用 sed
命令,就可以快速得到 “red-svc”“green-svc”“bule-svc”“black-svc” 等4个Service。
sed 's/ngx/red/g' backend.yml | kubectl apply -f -
sed 's/ngx/green/g' backend.yml | kubectl apply -f -
sed 's/ngx/blue/g' backend.yml | kubectl apply -f -
sed 's/ngx/black/g' backend.yml | kubectl apply -f -
使用 Gateway API
完成以上的准备工作,下面就要开始实际使用Gateway API了。
让我们先从最简单的路由开始,只使用域名规则,创建一个HTTPRoute对象。
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: ngx-host-route
spec:
parentRefs:
- name: kong-gtw
hostnames:
- "gtw.test"
rules:
- backendRefs:
- name: red-svc
port: 80
HTTPRoute对象和Ingress很相似,但要简洁一些,具体的内部字段含义和用法可以参考文档或者使用 kubectl explain
,我就不浪费时间细解释了。
这里使用 parentRefs
指定了路由使用的Gateway对象,用 hostnames
指定一个或多个域名,用 backendRefs
指定后端Service。合起来看,就是要求Gateway把域名 gtw.test 的流量都转发到 red-svc。
kubectl apply
创建这个路由对象后,可以用 curl
向 kong-gateway-proxy 发送请求来验证。
从截图里可以看到,因为我们指定了域名 gtw.test,匹配了路由规则,所以Gateway就把请求转发给了red-svc。
然后我们再来编写两个路由规则,分别使用路径匹配和头字段匹配,转发到green-svc和blue-svc。
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: ngx-path-route
spec:
parentRefs:
- name: kong-gtw
hostnames:
- "gtw.ops"
rules:
- matches:
- path:
type: PathPrefix
value: /hello
backendRefs:
- name: green-svc
port: 80
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: ngx-header-route
spec:
parentRefs:
- name: kong-gtw
hostnames:
- "gtw.dev"
rules:
- matches:
- headers:
- type: Exact
name: area
value: north
backendRefs:
- name: blue-svc
port: 80
和第一个路由相比,它们多了 matches
字段,可以在里面详细指定或组合各种匹配条件,支持精确匹配、前缀匹配、正则匹配等等。
使用 curl
测试的效果如图:
curl -i $(minikube ip):31198/hello -H 'host: gtw.ops'
curl -i $(minikube ip):31198 -H 'host: gtw.dev' -H 'area: north'
Gateway API不仅支持路由转发,它还能够轻松实现流量拆分,比如常见的金丝雀部署和蓝绿部署,只需要调整 backendRefs
字段即可。
这个YAML定义了两个HTTPRoute。
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: ngx-canary-route
spec:
parentRefs:
- name: kong-gtw
hostnames:
- "canary.test"
rules:
- backendRefs:
- name: blue-svc
port: 80
- matches:
- headers:
- name: traffic
value: canary
- path:
type: Exact
value: /login
backendRefs:
- name: green-svc
port: 80
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: ngx-blue-green-route
spec:
parentRefs:
- name: kong-gtw
hostnames:
- "blue-green.test"
rules:
- backendRefs:
- name: blue-svc
port: 80
weight: 70
- name: green-svc
port: 80
weight: 30
ngx-canary-route
里有两条规则,默认的后端服务是blue-svc,另一个加了匹配条件,只有访问特定的地址、使用特定的头字段才会转到后端服务green-svc。
ngx-blue-green-route
里也是两个后端服务,blue-svc和green-svc,但使用 weight 字段指定了不同的权重,这样在部署的时候就可以随意调节流量比例。
最后我们再来看一下Gateway API的filter特性,它可以对应到Kong Gateway的插件机制,实现对流量的附加处理,比如速率限制、改写数据、身份验证等等,不过目前标准的filter还不多,所以有的时候还是要依赖CRD资源定义Plugin。
下面的YAML添加了响应头和限速,其实这两个功能已经在之前讲Kong Ingress Controller的加餐里介绍过了,但现在改成了Gateway API,你可以对比看一下两者的区别。
apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
name: kong-rate-limiting-plugin
plugin: rate-limiting
config:
minute: 2
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: ngx-filter-route
annotations:
konghq.com/plugins: |
kong-rate-limiting-plugin
spec:
parentRefs:
- name: kong-gtw
hostnames:
- "filter.test"
rules:
- backendRefs:
- name: black-svc
port: 80
filters:
- type: ResponseHeaderModifier
responseHeaderModifier:
add:
- name: A-New-Header
value: k8s-gtw-api
同样的,发送 curl
请求,就可以看到新增加的响应头和限速信息。
小结
好了,今天我介绍了Kubernetes的最新成果:Gateway API,它是Ingress的继任者,功能更强大、用法更灵活,也是Kubernetes社区今后的重点发展方向,值得我们投入精力去研究。
简单小结一下今天的内容要点:
- Gateway API已经正式生产可用,目前包括3个成熟的对象,分别是Gateway Class、Gateway和HTTPRoute。
- Gateway API只能运行在较新版本的Kubernetes上,而且不内置在Kubernetes里,需要使用YAML额外安装。
- 大多数厂商都在某种程度上支持Gateway API,但只有少数达到了GA程度,这里使用的是Kong Ingress Controller。
- 使用Gateway API可以任意配置各种路由规则,如域名、路径、头字段、查询参数、流量权重等,功能非常丰富。
- Gateway API还支持filter功能,可以对流量做各种处理,但功能不如路由那么多。
另外再提醒一下,你可以在专栏的GitHub 项目里找到这节课的全部代码,还有更多的示例。
期待你的学习分享,我们留言区见!
- Geek_07ead6 👍(1) 💬(1)
老师,不是说Gateway Class、Gateway 和 HTTPRoute是代替原来的ingress、ingressclass、ingressController这三个东西的吗?为啥上面又多了kong ingress controller的部署,kong ingress controller不只是原来ingressController的一个实现吗。没看懂新的里面GateWay和kong ingress controller的关系是啥,老师能加张图说一下Gateway Class、Gateway、HTTPRoute、kong ingress controller这四者的关系吗?
2024-04-11 - Geek_768d90 👍(1) 💬(1)
感谢老师的坚持与付出!我认为学习K8S本身也要遵循CI/CD:不断学习新知识,集成进自己的大脑;不断地在现网中实践,提供更加优化的交付方案。 我坚信这门课在极客时间一定会是一门精品课!
2023-12-02 - sgcls 👍(0) 💬(1)
Gateway API 与 Ingress 类似的功能模块映射: Gateway --- Ingress Controller # 流量控制器 Gateway Class --- Ingress Class # 流量控制器类别 HTTPRoute --- Ingress # 路由规则集 这里 HTTPRoute 与 Ingress 的 Yaml 不同点: HTTPRoute 指定了 Controller(parentRefs.0.name -> Gateway 对象),而 Ingress 指定的是 Controller Class (spec.ingressClassName -> Ingress Class 对象) 为什么这里会不一致呢?有种割裂感。 是不是因为 Ingress Class 后面才引入的,所以在 Ingress 里明确设置 ingressClassName 指向 Ingress Class 以支持新特性呢..
2024-09-06 - 六月 👍(0) 💬(1)
看完本章马上去试了一下😂发现我的集群是1.23.17版本的。。。
2024-02-06 - Geek_479239 👍(0) 💬(1)
居然读到了最新的文章,新公司在用k8s开发,正好学习实战下
2023-12-01