Skip to main content

Command Palette

Search for a command to run...

Ingress 的继任者 —— GatewayAPI

Updated
3 min read

在 Kubernetes 集群边缘对外提供网络服务的时候,通常需要借助 Ingress 对象,这个对象提供了暴露 Service 所必须的核心要素,例如基于主机名的路由、对 URL 路径的适配以及 TLS 配置等。但是在实际开放服务的时候,往往会有更多的具体需求,这时 Ingress 对象所提供的核心功能就有些力不从心了,各种 Ingress 控制器往往会使用 metadata.annotations 中的特定注解,来完成对 Ingress 特定行为的控制,完成各自的个性化功能,例如认证、路径变更、黑白名单等,这就让 Ingress 对象变成了一个奇怪的东西:结构化的核心结构,和非结构化的标注结合起来形成各种 Ingress 方言,并且后期还出现了 Traefik Middleware 这样的 CRD 配置,这给 Ingress 功能的集中管理造成了一个较大的困扰;另外 Ingress 中可以随意定制主机名、路径以及后端服务,也给共享集群的用户造成了一定的安全隐患。包括 Cotour、Traefik 在内的 Ingress 控制器后期都提供了各自的基于 CRD 的功能表达,客观上也让 Ingress 世界更为分裂。 例如要移除路径前缀,Nginx Ingress 控制器需要使用 nginx.ingress.kubernetes.io/rewrite-target 注解,而 Traefik 1.7 中则需要使用 traefik.ingress.kubernetes.io/rule-type: PathPrefixStrip 注解。

SIG-Network 基于实际现状和需求,提出了全新的 Gateway API 来作为 Ingress 的继任者,总体来说,相对于 Ingress,Gateway API 有几个显著特点:

  1. 职责分离,运维、开发等不同的角色都能够在适合的边界内完成工作;

  2. 扩展核心能力,并使用更结构化的方式进行表达;

  3. 易于扩展:Gateway API 为各种不同实现的控制器提供了一致的扩展方法。

目前该 API 还处于 Alpha 阶段,也仅有少量控制器提供了早期支持。下面做一些陈述和试验,来看看 Gateway API 有什么不一样。

概念层次

Ingress 中包含了 IngressClass/Ingress 两层概念,而 Gateway API 包含了三层概念:GatewayClass、Gateway 和 Route,其中的 Route 实际是包含了 HTTPRoute、TCPRoute、TLSRoute 和 UDPRoute 在内的一组对象。

GatewayClass

它是一个集群范围内的资源,由云基础设施中的 Gateway API 控制器提供,其职责和原有的 Ingress Class 类似。

Gateway

Gateway 对象是命名空间范围对象,可以视作是 GatewayClass 的一个实例,它通常是由具体机群的运维人员进行维护的,在 Gateway 对象中可以指定该对象负责的主机名称范围,用标签选择器选择对应的 Service,甚至还可以指定该 Gateway 生效的命名空间。这样就给具体应用的对外开放划定了一个范围,防止应用随意占用主机名,并完善命名空间的隔离能力。

Route

前文讲到,Route 对象除了像原有的 Ingress 对象一样提供 HTTP 服务的开放能力之外,还提供了 TCP、TLS 和 UDP 的对应资源,从而缓解了 Nginx、HAProxy Ingress 控制器使用 Configmap 配置 TCP/UDP 的窘境。HTTPRoute 除了提供基础的 Ingress 对象能力之外,还提供了一些“越界”的功能,例如对流量进行复制、分流;更重要的是其中还提供了 Filter 能力,这是一个扩展点,除了自带的核心处理能力之外,底层设施还可以在这里接入自己的 CRD,对流量进行处理,从而为流量处理能力的扩展提供了一个统一入口。UDPRoute 和 TCPRoute 也提供了对流量的判别能力,但是这部分仅提供了扩展点,而没有像 HTTP 一样的成熟能力。

举个栗子

目前 GKE 提供了 Gateway API 的公共预览版可以用于测试,仅限于以下区域的 1.20 以上版本的集群:

  • us-west1

  • us-east1

  • us-central1

  • europe-west4

  • europe-west3

  • europe-west2

  • europe-west1

  • asia-southeast1

不同区域的集群缺省开关可能不一致,注意需要在控制台的网络页面启用 HTTP 负载均衡功能,或者在命令行中的 --addons 参数值里加入 HttpLoadBalancing

使用如下命令部署网关资源:

$ kubectl kustomize "github.com/kubernetes-sigs/gateway-api/config/crd?ref=v0.3.0" \
| kubectl apply -f -
customresourcedefinition.apiextensions.k8s.io/backendpolicies.networking.x-k8s.io created
customresourcedefinition.apiextensions.k8s.io/gatewayclasses.networking.x-k8s.io created
customresourcedefinition.apiextensions.k8s.io/gateways.networking.x-k8s.io created
customresourcedefinition.apiextensions.k8s.io/httproutes.networking.x-k8s.io created
customresourcedefinition.apiextensions.k8s.io/tcproutes.networking.x-k8s.io created
customresourcedefinition.apiextensions.k8s.io/tlsroutes.networking.x-k8s.io created
customresourcedefinition.apiextensions.k8s.io/udproutes.networking.x-k8s.io created

部署完成之后,网关控制器会被触发,创建两个 GatewayClass

$ kubectl get gatewayclasses
NAME          CONTROLLER                  AGE
gke-l7-gxlb   networking.gke.io/gateway   1s
gke-l7-rilb   networking.gke.io/gateway   1s

不难发现,我们使用的是 HTTP 负载均衡,新建的 Gateway Class 也都包含 l7 字样,其实官方文档也明确说明:

注意:GKE Gateway Controller 仅支持 GatewayClass、网关和 HTTPRoute。不支持 TCPRoute、UDPRoute 和 TLSRoute。

这里初始化了两个 GatewayClass,gxlb 用于外部,rilb 用于内部,所以我们要在外网测试,就要用 gxlb 创建网关。

一个简单的工作负载:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: flaskapp-v1
  name: flaskapp-v1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: flaskapp-v1
  template:
    metadata:
      labels:
        app: flaskapp-v1
    spec:
      containers:
      - image: dustise/flaskapp:v0.2.6
        name: flaskapp
        env:
        - name: "VERSION"
          value: "v1"
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: flaskapp-v1
  name: flaskapp-v1
spec:
  ports:
  - name: http
    port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: flaskapp-v1
  type: ClusterIP

创建 Gateway:

kind: Gateway
apiVersion: networking.x-k8s.io/v1alpha1
metadata:
  name: gateway-gen
spec:
  gatewayClassName: gke-l7-gxlb
  listeners:
  - protocol: HTTP
    port: 80
    hostname: "*.microservice.xyz"
    routes:
      kind: HTTPRoute
      selector:
        matchLabels:
          app: flaskapp-v1
      namespaces:
        from: "All"

这个 Gateway 对象中仅定义了一个 Listener 对象,其中规定可以使用的域名为 *.microservice.xyzHTTPRoute,选择器要求使用这个对象的 Route 必须使用 app=flaskapp-v1 的标签,可以在所有命名空间中进行引用。创建之后查看一下状态:

$ kubectl describe gateway gateway-gen

...
Spec:
  Gateway Class Name:  gke-l7-gxlb
  Listeners:
    Hostname:  *.microservice.xyz
    Port:      80
    Protocol:  HTTP
    Routes:
      Group:  networking.x-k8s.io
      Kind:   HTTPRoute
      Namespaces:
        From:  All
      Selector:
        Match Labels:
          App:  flaskapp-v1
Status:
  Addresses:
    Type:   IPAddress
    Value:  37.132.121.12
...

看到 Gateway 中已经得到了一个 IP 地址。接下来建立一个对应的 HTTPRoute 对象:

kind: HTTPRoute
apiVersion: networking.x-k8s.io/v1alpha1
metadata:
  name: flaskapp-v1
  labels:
    app: flaskapp-v1
spec:
  hostnames:
  - "v1.microservice.xyz"
  - "v1.microservice.rocks"
  rules:
  - forwardTo:
    - serviceName: flaskapp-v1
      port: 80

这个里面我们用了一个多余的域名:v1.microservice.rocks,看看运行结果:

$ http http://v1.microservice.xyz/env/VERSION
HTTP/1.1 200 OK
...
Server: nginx/1.15.3
Via: 1.1 google

v1


$ http http://v1.microservice.rocks/env/VERSION
HTTP/1.1 404 Not Found
...
Via: 1.1 google

default backend - 404

这里看到,“超纲”的域名会返回 404,被缺省后端截获。如果更换一个命名空间来创建 HTTPRoute 来引用 Gateway,会发现虽然在 Gateway 中定义了 namespaces.from: "All",但是仍旧会返回 404describe httproute 一下会发现,spec.gateways.allow 缺省被设置为 SameNamespace,因此显式定义 spec.gateways.allow=All,就能正常访问了。

分流

HTTPRoute 的 spec.rules 是一个数组,实际上这是一个分流支持,例如我们如此定义:

  rules:
  - forwardTo:
    - serviceName: flaskapp-v1
      port: 80
  - forwardTo:
    - serviceName: flaskapp-v2
      port: 80

然后循环测试,会发现 v1v2 在一定时间内会交替出现。forwardTo 还有一个 weight 属性,这个数字决定了流量在不同转发目标之间的分配比例。

GKE 的分流好像比较弱,一百个请求测试,有时分配也并不明显。

条件分支

多个 Rule 之间还可以使用条件进行分流,例如:

  rules:
  - forwardTo:
    - serviceName: flaskapp-v1
      port: 80
  - forwardTo:  
    - serviceName: flaskapp-v2
      port: 80
    matches:
    - headers:
        type: Exact
        values:
          version: v2

测试一下有无特定 Header 的结果:

$ http http://v1.microservice.xyz/env/VERSION version:v2
...
v2

$ http http://v1.microservice.xyz/env/VERSION
...
v1

其他

在直接的 forwardTo 之外,Gateway API 还可以通过 Filter 的方式支持扩展能力,这个能够在转发之前进行流量处理的功能分为三种层级:

  • Core:所有实现者都必须实现该能力,例如 RequestHeaderModifier

  • Extended:建议实现这个层级的能力,例如 RequestMirror

  • Custom:实现者可以在这个层级实现各种扩展能力,如果多个厂商都实现了该功能,则可能升级到 Extended 或者 Core。

GKE 的公共 Gateway 并不支持流量复制,现阶段也不提供 TCP/UDP 的支持,可能需要靠其它控制器来实现。

More from this blog

龙虾恐慌:AIOps 又要改名了?

ChatGPT 开始,把 AI 拉近到普罗大众的面前,让无数人感受到 AI 的亲民魅力。而龙虾,则把大模型驱动的自动化能力,突然间变得水灵灵、活泼泼地走进千家万户。它不只是“风口上的猪”,而是风口本身。热度高到让 Mac mini 一度断货,不知道这在不在库克的预料之内。 每代人都有每代人的鸡蛋,春节期间,我就领了我的鸡蛋。翻出古老的 MacBook Air M1,充值各种大模型。当然了,这个工具

Mar 9, 20261 min read

再见 2025

我猜不少人以为这个号废了吧?并没有,只是今年变化有点大,一直有种抄起键盘,无从说起的感觉,所以一直偷懒到今天,2025 的最后一天。 今年是我的第四个本命年,去年末一期播客里,大内说本命年不是灾年,是变化年,有危也有机。可是讲真啊,只看到危,没看到机。 各种因缘际会,从鹅厂跳槽到前东家,已经接近四年,第一个合同期已经进入尾声。除了前两年还在云原生领域嗷嗷叫,后两年基本都是些鸡零狗碎的东西了,用老东家的术语说是——偏离主航道,可谓是前景暗淡了。 一旦确定要滚蛋,反倒心思轻松起来,每天骑着我的小红车...

Jan 5, 20261 min read

辅助编程?dora 说:我知道你很急可是请你别急

从 OpenGPT 把大模型的火烧旺了之后,这三年来,相信很多组织或摩拳擦掌、或躬身入局,希望借助聪明能干的大模型,或想偿还技术宅,或想降本增效,或想弯道超车。一时间,沉寂许久的 AIxx 又活过来了,LLM Ops、Vibe Coding、中医大模型、GPT 算命等等,全都老树发新芽,焕发了勃勃生机。那么视角拉回从业者最关注的饭碗相关的领域之一——AI 辅助开发,产生了什么触动,应该如何拥抱呢? DORA 的年度报告中给出了很有意思的结论——强者恒强。 执行摘要部分总结了几个有趣的点: 问题...

Oct 6, 20251 min read

[译]dora:ai 辅助软件开发状态报告

执行摘要 在 2025 年,科技领导者面临的核心问题已不再是“是否要采用 AI”,而是“如何实现其价值”。 DORA 的研究基于超过 100 小时的定性访谈和来自全球近 5,000 名技术专业人士的问卷调查。研究揭示了一个关键事实:AI 在软件开发中的主要角色是“放大器”。它会放大高效能组织的优势,也会凸显组织的缺陷。 关键结论:AI 是放大器 AI 投资的最大回报并非来自工具本身,而是来自组织底层系统的战略性建设: 高质量的内部平台 清晰的工作流 团队的协同能力 缺少这些基础,AI ...

Oct 2, 202514 min read

僭越了,有人在用 Rust 写 Kubernetes

一个新语言问世,最爱做的事情之一,就是重写存量软件了。 云原生喝酒 SIG 重点扶持项目——rk8s(https://github.com/rk8s-dev/rk8s) 也可以归在这个范畴里,只不过这个项目重写的东西比较大,是 Kubernetes。 从 2025 年 1 月第一个 Commit 开始,到现在有了 200 多次 Commit,十几万行代码。当然距离 Kubernetes 的几百万行代码还差得远——老马就是喜欢整这种大无畏项目。 另外该项目也是国内第一个脱离 Cargo 转向使用 ...

Sep 27, 20253 min read

【伪】架构师

342 posts