作者:黄鑫,英特尔云原生软件开发工程师
随着Istio 1.16.0[1]的正式发布,也宣布了 Istio 基于Kubernetes Gateway API[2]的实现进入到了 Beta 阶段,这意味着 Istio 中所有南北向(Ingress)流量管理都可以迁移至 Kubernetes Gateway API。未来随着 Kubernetes Gateway API 的发展和成熟,Istio 东西向(Mesh)流量管理 API 也会被其慢慢代替。
本文将会介绍 Kuberenetes Gateway API 的背景,并且手把手带你体验使用 Kuberenetes Gateway API 代替 Istio Ingress Gateway。
大家一定都知道 Kubernetes 中的 Ingress API 是对集群中服务的外部访问进行管理的 API 对象,同时也可以提供负载均衡,SSL 卸载以及虚拟路由等功能。Ingress API 也伴随着 Kubernetes 稳定服役了 5 年之久,但是慢慢随着集群规模的扩大,业务管理的复杂化,Ingress API 渐渐无法满足日益多样化的需求,人们发现 Ingress API 提供的功能太少且很难被扩展,各个厂商都提供了不同的扩展方式,例如加入不同的 annotation, 这导致应用很难在不同的厂商之间无缝迁移, 并且提供的权限管理模型也过于单一。
于是 Kubernetes 社区便孕育出了新一代的流量管理标准:Gateway API。相较于 Ingress API, Gateway API 的主要改进在于:
在理解 Kubernetes Gateway API 是如何工作之前,需要先对其中几个基本资源类型有个大致的了解:
下图展示了这三种资源类型的拓扑关系:
接着我们通过一个实际例子说明基于角色的 Kubernetes Gateway API 是如果工作的,如下图所示,一个集群管理员创建了一个 Gateway 资源,该 Gateway 对访问 foo.example.com
的流量进行了 TLS 配置并设置了默认策略。在和集群管理员达成一致后,负责存储的开发人员创建了一个 HTTPRoute,将访问foo.example.com/store/*
的流量导入到 Store Namespace 下的 foo-store 服务中,并且对这些流量进行了加权分发,将 90%的流量导入到 foo-store v1 中,另外 10%的流量导入到 foo-store v2 中。另外一边,负责网站的开发人员也创建了一个 HTTPRoute,将访问foo.example.com/site/*
的流量导入到 Site Namespace 下的 foo-site 服务中。这种用户模型在为基础设施提供灵活性的同时也保证了对不同角色之间的控制。
接下来,我们通过实际操作展示在 Istio 中通过 Kubernetes Gateway API 将一个服务暴露到集群外部。
安装 Kubernetes Gateway API CRD
大多数 Kubernetes 集群中没有默认安装 Gateway API,因此需要手动安装 Gateway API 的 CRD:
$ kubectl get crd gateways.gateway.networking.k8s.io || kubectl kustomize "github.com/kubernetes-sigs/gateway-api/config/crd?ref=v0.5.1" | kubectl apply -f -;
使用最小配置安装 Istio, 因为我们不再需要istio-ingressgateway
$ istioctl install --set profile=minimal -y
安装完成后,Istio 会自动创建一个 GatewayClass,Controller 为istio.io/gateway-controller
$ kubectl get gatewayclass
NAME CONTROLLER ACCEPTED AGE
istio istio.io/gateway-controller True 6s
创建服务并配置 Gateway
需要加以区分的是,这里的
Gateway
指的是 Kuberentes Gateway APIgateway.networking.k8s.io/v1beta1
中的 Gateway 资源,而不是 Istio APInetworking.istio.io/v1beta1
中的Gateway
。
我们会部署一个名为httpbin
的简单服务,并且将它用Gateway
暴露到集群外部。
httpbin
服务:$ kubectl apply -f https://raw.githubusercontent.com/istio/istio/1.16.0/samples/httpbin/httpbin.yaml
Gateway
和HTTPRoute
,将访问httpbin.example.com/get/*
的流量导入到httpbin
服务中:$ kubectl create namespace istio-ingress
$ kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
name: gateway
namespace: istio-ingress
spec:
gatewayClassName: istio # 这里指定使用istio gatewayclass
listeners:
- name: default
hostname: "*.example.com"
port: 80
protocol: HTTP
allowedRoutes:
namespaces:
from: All
---
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
name: http
namespace: default
spec:
parentRefs:
- name: gateway
namespace: istio-ingress
hostnames: ["httpbin.example.com"]
rules:
- matches:
- path:
type: PathPrefix
value: /get
backendRefs:
- name: httpbin
port: 8000
EOF
Gateway
部署完成并设置Ingress Host
环境变量:$ kubectl wait -n istio-ingress --for=condition=ready gateways.gateway.networking.k8s.io gateway
$ export INGRESS_HOST=$(kubectl get gateways.gateway.networking.k8s.io gateway -n istio-ingress -ojsonpath='{.status.addresses[*].value}')
默认情况下,每个Gateway
会自动创建同名的Service
和Deployment
,如果更新了Gateway
,它们也会随之更新:
$ kubectl get deployment -n istio-ingress
NAME READY UP-TO-DATE AVAILABLE AGE
gateway 1/1 1 1 3m29s
$ kubectl get service -n istio-ingress
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
gateway LoadBalancer 10.107.79.229 <pending> 15021:30088/TCP,80:31690/TCP 4m13s
httpbin
服务:$ curl -s -I -HHost:httpbin.example.com "http://$INGRESS_HOST/get"
HTTP/1.1 200 OK
server: istio-envoy
...
请注意,这里使用了
-H
将 HTTP Header 中的Host
设置为httpbin.example.com
, 因为 HTTPRoute 配置的是处理httpbin.example.com
的流量。
/headers
,会得到 HTTP 404 的错误:$ curl -s -I -HHost:httpbin.example.com "http://$INGRESS_HOST/headers"
HTTP/1.1 404 Not Found
...
/headers
的流量也暴露出来,并给请求加上 header:$ kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
name: http
namespace: default
spec:
parentRefs:
- name: gateway
namespace: istio-ingress
hostnames: ["httpbin.example.com"]
rules:
- matches:
- path:
type: PathPrefix
value: /get
- path:
type: PathPrefix
value: /headers
filters:
- type: RequestHeaderModifier
requestHeaderModifier:
add:
- name: my-added-header
value: added-value
backendRefs:
- name: httpbin
port: 8000
EOF
/headers
:$ curl -s -HHost:httpbin.example.com "http://$INGRESS_HOST/headers"
{
"headers": {
"Accept": "*/*",
"Host": "httpbin.example.com",
"My-Added-Header": "added-value",
...
清理
$ kubectl delete -f samples/httpbin/httpbin.yaml
$ kubectl delete httproute http
$ kubectl delete gateways.gateway.networking.k8s.io gateway -n istio-ingress
$ istioctl uninstall -y --purge
$ kubectl delete ns istio-system
$ kubectl delete ns istio-ingress
$ kubectl kustomize "github.com/kubernetes-sigs/gateway-api/config/crd?ref=v0.5.1" | kubectl delete -f -
很遗憾,目前无法自动将现有的 Istio Ingress Gateway 资源迁移成 Kubernetes Gateway API 资源,不过社区也注意到了这个需求并创建了ingress2gateway[4]项目,目标是可以自动读取 Ingress 配置并翻译成 Gateway API 相关的资源,不过目前只支持ingress-nginx
。
如果 Istio Ingress Gateway 已经满足了你对流量管控的所有需求,建议继续使用 Istio Ingress Gateway,因为 Istio Ingress Gateway 经过时间的检验已经非常稳定。根据 Kubernetes 社区的讨论,Ingress API 和 Gateway API 在未来长时间内会并存。Istio 社区也表示,Istio 相关的 API(Gateway、VirtualService 和 DestinationRule)在可预见的未来也将继续得到支持。
如果你对流量管控有更多的需求,希望在不同厂商之间无缝迁移,那么未来 Kubernetes Gateway API 必然会是一个好选择。在得到社区认可和力挺后,随着 Kuberenetes Gateway API 日益成熟,生态的不断完善,相信在不久的将来我们能使用 Kubernetes Gateway API 这样的规范 API 来管理 Mesh,L4 层的流量。我们也在积极参与社区的相关讨论,欢迎加入我们:https://gateway-api.sigs.k8s.io/contributing/。
[1]
Istio 1.16.0: https://istio.io/latest/news/releases/1.16.x/announcing-1.16/#kubernetes-gateway-api-implementation-promoted-to-beta
[2]
Kubernetes Gateway API: https://gateway-api.sigs.k8s.io/
[3]
GAMMA(Gateway API Mesh Management and Administration): https://gateway-api.sigs.k8s.io/contributing/gamma/
[4]
ingress2gateway: https://github.com/kubernetes-sigs/ingress2gateway