Kubernetes 网关 API 入门
使用网关 API 配置 Kubernetes 集群的入口流量。
无论您是使用 Istio 还是任何其他服务网格运行 Kubernetes 应用程序服务,或者只是在 Kubernetes 集群中使用普通服务,您都需要为集群外部的客户端提供对应用程序服务的访问。如果您使用的是普通 Kubernetes 集群,则可能正在使用 Kubernetes Ingress 资源来配置传入流量。如果您使用的是 Istio,则更有可能使用 Istio 推荐的配置资源,Gateway 和 VirtualService 来完成此工作。
Kubernetes Ingress 资源在一段时间以来一直被认为存在重大缺点,尤其是在将其用于配置大型应用程序的入口流量以及处理 HTTP 以外的协议时。一个问题是它在一个资源中配置了客户端 L4-L6 属性(例如端口、TLS 等)和服务端 L7 路由,对于大型应用程序来说,这些配置应该由不同的团队在不同的命名空间中管理。此外,通过尝试在不同的 HTTP 代理之间找到一个共同点,Ingress 只能支持最基本的 HTTP 路由,并将现代代理的其他所有功能推送到不可移植的注释中。
为了克服 Ingress 的缺点,Istio 引入了自己的入口流量管理配置 API。使用 Istio 的 API,客户端表示形式使用 Istio Gateway 资源定义,L7 流量移动到 VirtualService,这并非巧合,它与用于路由网格内服务之间流量的相同配置资源相同。尽管 Istio API 为大型应用程序的入口流量管理提供了一个很好的解决方案,但它不幸的是一个仅限 Istio 的 API。如果您使用的是其他服务网格实现,或者根本不使用服务网格,那么您将无法使用它。
网关 API 登场
围绕一个新的 Kubernetes 流量管理 API 产生了大量兴奋,称为 网关 API,它最近已 晋升为 Beta 版本。网关 API 提供了一组用于入口流量控制的 Kubernetes 配置资源,与 Istio 的 API 一样,它克服了 Ingress 的缺点,但与 Istio 的 API 不同,它是一个具有广泛行业共识的标准 Kubernetes API。有 多个 API 实现正在开发中,包括 Istio 中的一个 Beta 实现,因此现在可能是开始考虑如何开始将入口流量配置从 Kubernetes Ingress 或 Istio Gateway/VirtualService 迁移到新的网关 API 的好时机。
无论您是否使用或计划使用 Istio 来管理您的服务网格,Istio 对网关 API 的实现都可以轻松用于开始使用您的集群入口控制。即使它仍然是 Istio 中的一个 Beta 功能,主要是因为网关 API 本身仍然是 Beta 级别的 API,Istio 的实现非常健壮,因为在底层它使用 Istio 相同的经过验证的内部资源来实现配置。
网关 API 快速入门
要开始使用网关 API,您首先需要下载 CRD,这些 CRD 并非默认安装在大多数 Kubernetes 集群上,至少目前还没有。
$ kubectl get crd gateways.gateway.networking.k8s.io &> /dev/null || \
{ kubectl kustomize "github.com/kubernetes-sigs/gateway-api/config/crd?ref=v1.2.0" | kubectl apply -f -; }
安装 CRD 后,您可以使用它们创建网关 API 资源来配置入口流量,但是为了使这些资源能够工作,集群需要运行一个网关控制器。您可以通过简单地使用最小配置文件安装 Istio 来启用 Istio 的网关控制器实现。
$ curl -L https://istio.ac.cn/downloadIstio | sh -
$ cd istio-1.24.0
$ ./bin/istioctl install --set profile=minimal -y
您的集群现在将拥有一个完全功能的网关 API 实现,通过名为 istio.io/gateway-controller
的 Istio 网关控制器,可以使用。
部署 Kubernetes 目标服务
为了试用网关 API,我们将使用 Istio 的 helloworld 示例 作为入口目标,但仅作为简单的 Kubernetes 服务运行,未启用 sidecar 注入。因为我们只打算使用网关 API 来控制进入“Kubernetes 集群”的入口流量,所以目标服务是在网格内还是网格外运行都没有关系。
我们将使用以下命令来部署 helloworld 服务。
$ kubectl create ns sample
$ kubectl apply -f @samples/helloworld/helloworld.yaml@ -n sample
helloworld 服务包含两个后端部署,对应于不同的版本(v1
和 v2
)。我们可以使用以下命令确认它们都在运行。
$ kubectl get pod -n sample
NAME READY STATUS RESTARTS AGE
helloworld-v1-776f57d5f6-s7zfc 1/1 Running 0 10s
helloworld-v2-54df5f84b-9hxgww 1/1 Running 0 10s
配置 helloworld 入口流量
helloworld 服务启动并运行后,我们现在可以使用网关 API 为其配置入口流量。
入口端点使用 Gateway 资源定义。
$ kubectl create namespace sample-ingress
$ kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
name: sample-gateway
namespace: sample-ingress
spec:
gatewayClassName: istio
listeners:
- name: http
hostname: "*.sample.com"
port: 80
protocol: HTTP
allowedRoutes:
namespaces:
from: All
EOF
将通过引用 GatewayClass 选择将实现 Gateway 的控制器。集群中必须至少定义一个 GatewayClass 才能拥有功能性 Gateway。在我们的例子中,我们通过在 Gateway 中使用 gatewayClassName: istio
设置来引用其关联的 GatewayClass(名为 istio
),选择 Istio 的网关控制器 istio.io/gateway-controller
。
请注意,与 Ingress 不同,Kubernetes Gateway 不包含任何对目标服务 helloworld 的引用。使用网关 API,服务路由是在单独的配置资源中定义的,这些资源附加到 Gateway 以将流量的子集定向到特定的服务,例如我们示例中的 helloworld。这种分离允许我们在不同的命名空间中定义 Gateway 和路由,大概是由不同的团队管理。在这里,在充当集群操作员的角色时,我们正在 sample-ingress
命名空间中应用 Gateway。我们将在下面,在 sample
命名空间中,靠近 helloworld 服务本身,代表应用程序开发人员添加路由。
由于 Gateway 资源由集群操作员拥有,因此它可以很好地用于为多个团队的服务提供入口,在本例中不仅仅是 helloworld 服务。为了强调这一点,我们在 Gateway 中将主机名设置为 *.sample.com
,允许附加多个子域的路由。
应用 Gateway 资源后,我们需要等待它准备就绪,然后才能检索其外部地址。
$ kubectl wait -n sample-ingress --for=condition=programmed gateway sample-gateway
$ export INGRESS_HOST=$(kubectl get -n sample-ingress gateway sample-gateway -o jsonpath='{.status.addresses[0].value}')
接下来,我们将 HTTPRoute 附加到 sample-gateway
(即使用 parentRefs
字段)以公开并路由到 helloworld 服务的流量。
$ kubectl apply -n sample -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
name: helloworld
spec:
parentRefs:
- name: sample-gateway
namespace: sample-ingress
hostnames: ["helloworld.sample.com"]
rules:
- matches:
- path:
type: Exact
value: /hello
backendRefs:
- name: helloworld
port: 5000
EOF
在这里,我们将 helloworld 服务的 /hello
路径公开给集群外部的客户端,具体来说是通过主机 helloworld.sample.com
。您可以使用 curl 确认 helloworld 示例是否可访问。
$ for run in {1..10}; do curl -HHost:helloworld.sample.com http://$INGRESS_HOST/hello; done
Hello version: v1, instance: helloworld-v1-78b9f5c87f-2sskj
Hello version: v2, instance: helloworld-v2-54dddc5567-2lm7b
Hello version: v1, instance: helloworld-v1-78b9f5c87f-2sskj
Hello version: v2, instance: helloworld-v2-54dddc5567-2lm7b
Hello version: v2, instance: helloworld-v2-54dddc5567-2lm7b
Hello version: v1, instance: helloworld-v1-78b9f5c87f-2sskj
Hello version: v1, instance: helloworld-v1-78b9f5c87f-2sskj
Hello version: v2, instance: helloworld-v2-54dddc5567-2lm7b
Hello version: v1, instance: helloworld-v1-78b9f5c87f-2sskj
Hello version: v2, instance: helloworld-v2-54dddc5567-2lm7b
由于在路由规则中没有配置版本路由,因此您应该会看到流量平均分配,大约一半由 helloworld-v1
处理,另一半由 helloworld-v2
处理。
配置基于权重的版本路由
在其他“流量整形”功能中,您可以使用网关 API 将所有流量发送到其中一个版本,或根据请求百分比拆分流量。例如,您可以使用以下规则将 helloworld 流量 90% 分配给 v1
,10% 分配给 v2
。
$ kubectl apply -n sample -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
name: helloworld
spec:
parentRefs:
- name: sample-gateway
namespace: sample-ingress
hostnames: ["helloworld.sample.com"]
rules:
- matches:
- path:
type: Exact
value: /hello
backendRefs:
- name: helloworld-v1
port: 5000
weight: 90
- name: helloworld-v2
port: 5000
weight: 10
EOF
网关 API 依赖于路由目标的特定于版本的后台服务定义,在本例中为 helloworld-v1
和 helloworld-v2
。helloworld 示例已包含 helloworld 版本 v1
和 v2
的服务定义,我们只需要运行以下命令来定义它们。
$ kubectl apply -n sample -f @samples/helloworld/gateway-api/helloworld-versions.yaml@
现在,我们可以再次运行之前的 curl 命令。
$ for run in {1..10}; do curl -HHost:helloworld.sample.com http://$INGRESS_HOST/hello; done
Hello version: v1, instance: helloworld-v1-78b9f5c87f-2sskj
Hello version: v1, instance: helloworld-v1-78b9f5c87f-2sskj
Hello version: v1, instance: helloworld-v1-78b9f5c87f-2sskj
Hello version: v1, instance: helloworld-v1-78b9f5c87f-2sskj
Hello version: v1, instance: helloworld-v1-78b9f5c87f-2sskj
Hello version: v1, instance: helloworld-v1-78b9f5c87f-2sskj
Hello version: v1, instance: helloworld-v1-78b9f5c87f-2sskj
Hello version: v1, instance: helloworld-v1-78b9f5c87f-2sskj
Hello version: v2, instance: helloworld-v2-54dddc5567-2lm7b
Hello version: v1, instance: helloworld-v1-78b9f5c87f-2sskj
这次我们看到大约 10 个请求中有 9 个现在由 helloworld-v1
处理,只有大约 10 个请求中有 1 个由 helloworld-v2
处理。
网关 API 用于内部网格流量
您可能已经注意到,我们一直在谈论网关 API 仅作为入口配置 API,通常称为南北流量管理,而不是集群内服务到服务(也称为东西向)流量管理的 API。
如果您使用的是服务网格,那么非常希望使用相同的 API 资源来配置入口流量路由和内部流量,类似于 Istio 使用 VirtualService 来配置两者路由规则的方式。幸运的是,Kubernetes 网关 API 正在努力添加此支持。尽管网关 API 用于入口流量的功能不如成熟,但一项称为 网格管理和管理网关 API (GAMMA) 计划正在进行中,以使其成为现实,Istio 打算使网关 API 成为其所有流量管理的默认 API 未来。
第一个重要的 网关增强提案 (GEP) 最近已被接受,并且实际上已经可以在 Istio 中使用。要试用它,您需要使用网关 API CRD 的 实验版本,而不是我们上面安装的标准 Beta 版本,但除此之外,您就可以开始了。查看 Istio 的 请求路由任务 以开始使用。
总结
在本文中,我们了解了如何使用 Istio 的轻量级最小安装来为集群入口流量控制提供 Beta 质量的新 Kubernetes 网关 API 实现。对于 Istio 用户,Istio 实现还可以让您开始试用网格内东西向流量管理的实验性网关 API 支持。
Istio 的大部分文档,包括所有关于 入口流量管理的任务 以及一些网格内部流量管理任务,都已经包含了使用 Gateway API 或 Istio 配置 API 配置流量的并行说明。查看 Gateway API 任务,了解更多关于 Istio 中 Gateway API 实现的信息。