跳转至

Kubernetes Gateway API

Gateway API 介绍

Gateway API 架构

Gateway API(之前叫 Service API)是由 SIG-NETWORK 社区管理的开源项目,项目地址:https://gateway-api.sigs.k8s.io/。主要原因是 Ingress 资源对象不能很好的满足网络需求,很多场景下 Ingress 控制器都需要通过定义 annotations 或者 crd 来进行功能扩展,这对于使用标准和支持是非常不利的,新推出的 Gateway API 旨在通过可扩展的面向角色的接口来增强服务网络。

Gateway API 是 Kubernetes 中的一个 API 资源集合,包括 GatewayClass、Gateway、HTTPRoute、TCPRoute、Service 等,这些资源共同为各种网络用例构建模型。

image-20240418200047857

Gateway API 的改进比当前的 Ingress 资源对象有很多更好的设计:

  • 面向角色 - Gateway 由各种 API 资源组成,这些资源根据使用和配置 Kubernetes 服务网络的角色进行建模。
  • 通用性 - 和 Ingress 一样是一个具有众多实现的通用规范,Gateway API 是一个被设计成由许多实现支持的规范标准。
  • 更具表现力 - Gateway API 资源支持基于 Header 头的匹配、流量权重等核心功能,这些功能在 Ingress 中只能通过自定义注解才能实现。
  • 可扩展性 - Gateway API 允许自定义资源链接到 API 的各个层,这就允许在 API 结构的适当位置进行更精细的定制。

还有一些其他值得关注的功能:

  • GatewayClasses - GatewayClasses 将负载均衡实现的类型形式化,这些类使用户可以很容易了解到通过 Kubernetes 资源可以获得什么样的能力。
  • 共享网关和跨命名空间支持 - 它们允许共享负载均衡器和 VIP,允许独立的路由资源绑定到同一个网关,这使得团队可以安全地共享(包括跨命名空间)基础设施,而不需要直接协调。
  • 规范化路由和后端 - Gateway API 支持类型化的路由资源和不同类型的后端,这使得 API 可以灵活地支持各种协议(如 HTTP 和 gRPC)和各种后端服务(如 Kubernetes Service、存储桶或函数)。

面向角色设计

无论是道路、电力、数据中心还是 Kubernetes 集群,基础设施都是为了共享而建的,然而共享基础设施提供了一个共同的挑战,那就是如何为基础设施用户提供灵活性的同时还能被所有者控制。

Gateway API 通过对 Kubernetes 服务网络进行面向角色的设计来实现这一目标,平衡了灵活性和集中控制。它允许共享的网络基础设施(硬件负载均衡器、云网络、集群托管的代理等)被许多不同的团队使用,所有这些都受到集群运维设置的各种策略和约束。下面的例子显示了是如何在实践中运行的。

image-20240418200229726

一个集群运维人员创建了一个基于 GatewayClass 的 Gateway 资源,这个 Gateway 部署或配置了它所代表的基础网络资源,集群运维和特定的团队必须沟通什么可以附加到这个 Gateway 上来暴露他们的应用。集中的策略,如 TLS,可以由集群运维在 Gateway 上强制执行,同时,Store 和 Site 应用在他们自己的命名空间中运行,但将他们的路由附加到相同的共享网关上,允许他们独立控制他们的路由逻辑。

这种关注点分离的设计可以使不同的团队能够管理他们自己的流量,同时将集中的策略和控制留给集群运维。

Gateway API 概念

在整个 Gateway API 中涉及到3个角色:基础设施提供商、集群管理员、应用开发人员,在某些场景下可能还会涉及到应用管理员等角色。Gateway API 中定义了3种主要的资源模型:GatewayClass、Gateway、Route。

GatewayClass

GatewayClass 定义了一组共享相同配置和动作的网关。每个GatewayClass 由一个控制器处理,是一个集群范围的资源,必须至少有一个 GatewayClass 被定义。

这与 Ingress 的 IngressClass 类似,在 Ingress v1beta1 版本中,与 GatewayClass 类似的是 ingress-class 注解,而在Ingress V1 版本中,最接近的就是 IngressClass 资源对象。

Gateway

Gateway 网关描述了如何将流量转化为集群内的服务,也就是说,它定义了一个请求,要求将流量从不了解 Kubernetes 的地方转换到集群内的服务。例如,由云端负载均衡器、集群内代理或外部硬件负载均衡器发送到 Kubernetes 服务的流量。

它定义了对特定负载均衡器配置的请求,该配置实现了 GatewayClass 的配置和行为规范,该资源可以由管理员直接创建,也可以由处理 GatewayClass 的控制器创建。

Gateway 可以附加到一个或多个路由引用上,这些路由引用的作用是将流量的一个子集导向特定的服务。

Route 资源

路由资源定义了特定的规则,用于将请求从网关映射到 Kubernetes 服务。

从 v1alpha2 版本开始,API 中包含四种 Route 路由资源类型,对于其他未定义的协议,鼓励采用特定实现的自定义路由类型,当然未来也可能会添加新的路由类型。

HTTPRoute

HTTPRoute 适用于 HTTP 或 HTTPS 连接,适用于我们想要检查 HTTP 请求并使用 HTTP 请求进行路由或修改的场景,比如使用 HTTP Headers 头进行路由,或在请求过程中对它们进行修改。

TLSRoute

TLSRoute 用于 TLS 连接,通过 SNI 进行区分,它适用于希望使用 SNI 作为主要路由方法的地方,并且对 HTTP 等更高级别协议的属性不感兴趣,连接的字节流不经任何检查就被代理到后端。

TCPRoute 和 UDPRoute

TCPRoute(和UDPRoute)旨在用于将一个或多个端口映射到单个后端。在这种情况下,没有可以用来选择同一端口的不同后端的判别器,所以每个 TCPRoute 在监听器上需要一个不同的端口。你可以使用 TLS,在这种情况下,未加密的字节流会被传递到后端,当然也可以不使用 TLS,这样加密的字节流将传递到后端。

Gateway API 概念之间关系

使用反向代理实现的网关的典型客户端/网关 API 请求流程如下所示:

  1. 客户端向 http://foo.example.com 发出请求
  2. DNS 将域名解析为 Gateway 网关地址
  3. 反向代理在监听器上接收请求,并使用 Host Header 来匹配HTTPRoute
  4. (可选)反向代理可以根据 HTTPRoute 的匹配规则进行路由
  5. (可选)反向代理可以根据 HTTPRoute 的过滤规则修改请求,即添加或删除 headers
  6. 最后,反向代理根据 HTTPRoute 的 forwardTo 规则,将请求转发给集群中的一个或多个对象,即服务。

image-20240418200359141

2. Gateway API 应用案例

配置 Kubernetes 网关提供程序并部署/公开服务

1. 创建 01-rbac.yaml 文件
---
## 创建 ServiceAccount

apiVersion: v1
kind: ServiceAccount
metadata:
  namespace: traefik
  name: traefik-ingress-controller

---

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: gateway-role
rules:
  - apiGroups:
      - ""
    resources:
      - namespaces
    verbs:
      - list
      - watch
  - apiGroups:
      - ""
    resources:
      - services
      - endpoints
      - secrets
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - gateway.networking.k8s.io
    resources:
      - gatewayclasses
      - gateways
      - httproutes
      - tcproutes
      - tlsroutes
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - gateway.networking.k8s.io
    resources:
      - gatewayclasses/status
      - gateways/status
      - httproutes/status
      - tcproutes/status
      - tlsroutes/status
    verbs:
      - update

---

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: gateway-controller

roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: gateway-role

subjects:
  - kind: ServiceAccount
    name: traefik-ingress-controller
    namespace: traefik
2. 创建 rbac 相关资源
kubectl apply -f 01-rbac.yaml
1. 编写 02-gatewayapi.yaml 资源清单
---
## 创建 gatewayclass 资源

apiVersion: networking.x-k8s.io/v1alpha1
kind: GatewayClass
metadata:
  name: my-gateway-class
spec:
  controller: traefik.io/gateway-controller

---

## 创建 gateway 资源
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: Gateway
metadata:
  name: my-gateway
  namespace: default
spec:
  gatewayClassName: my-gateway-class
  listeners: # Use GatewayClass defaults for listener definition.
    - name: http
      protocol: HTTP
      port: 80

---

## 创建 HTTPRoute 资源
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: HTTPRoute
metadata:
  name: http-app
  namespace: default

spec:
  parentRefs:
    - name: my-gateway

  hostnames:
    - tencent.k8s.huichengcheng.com

  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /

      backendRefs:
        - name: whoami
          port: 80
          weight: 1
2. 创建 gatewayapi 相关资源
kubectl apply -f 02-gatewayapi.yaml
1. 编写 03-whoami.yaml 资源清单
---
## 创建 whoami deployment 资源

apiVersion: apps/v1
kind: Deployment
metadata:
  name: whoami

spec:
  replicas: 2
  selector:
    matchLabels:
      app: whoami

  template:
    metadata:
      labels:
        app: whoami

    spec:
      containers:
        - name: whoami
          image: traefik/whoami

---
## 创建 whoami service 资源

apiVersion: v1
kind: Service
metadata:
  name: whoami

spec:
  selector:
    app: whoami

  ports:
    - protocol: TCP
      port: 80
2. 创建 whoami 相关资源
kubectl apply -f 03-whoami.yaml
1. 编写 04-Traefik.yaml
---
## 1. 编写 traefik 资源清单

apiVersion: apps/v1
kind: DaemonSet
metadata:
  namespace: traefik
  name: traefik
  labels:
    app: traefik
spec:
  selector:
    matchLabels:
      app: traefik
  template:
    metadata:
      labels:
        app: traefik
    spec:
      serviceAccountName: traefik-ingress-controller
      terminationGracePeriodSeconds: 1
      containers:
        - name: traefik
          image: traefik:v2.11
          args:
            - --configfile=/config/traefik.yaml
            - --experimental.kubernetesgateway
            - --providers.kubernetesgateway
          volumeMounts:
            - mountPath: /config
              name: config
          ports:
            - name: web
              containerPort: 80
              hostPort: 80  ## 将容器端口绑定所在服务器的 80 端口
            - name: websecure
              containerPort: 50000
              hostPort: 50000  ## 将容器端口绑定所在服务器的 443 端口
            - name: admin
              containerPort: 8080  ## Traefik Dashboard 端口
            - name: tcpep
              containerPort: 8083
              hostPort: 8083  ## 将容器端口绑定所在服务器的 8083 端口
            - name: udpep
              containerPort: 8084
              hostPort: 8084  ## 将容器端口绑定所在服务器的 8084 端口
              protocol: UDP
      volumes:
        - name: config
          configMap:
            name: traefik
      tolerations:              ## 设置容忍所有污点,防止节点被设置污点
        - operator: "Exists"
      nodeSelector:             ## 设置node筛选器,在特定label的节点上启动
        IngressProxy: "true"

---
## 2. 创建 service
apiVersion: v1
kind: Service
metadata:
  name: traefik
  namespace: traefik
  labels:
    app: traefik 
    app.kubernetes.io/name: traefik-dashboard
spec:
  ports:
    - protocol: TCP
      name: web
      port: 80
    - protocol: TCP
      name: websecure     ## 代替 443
      port: 50000
  selector:
    app: traefik
2. 创建 traefik 资源
kubectl apply -f 04-Traefik.yaml

3. 通过 Gateway API方式暴露 WEB 应用

配置 Kubernetes gateway api 网关提供程序并部署/公开服务

1. 编写 01-gatewayapi-web.yaml 文件
---
## 1. 创建 deployment 资源
kind: Deployment
apiVersion: apps/v1
metadata:
  name: nginx-web-gatewayapi
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: gatewayweb
  template:
    metadata:
      labels:
        app: gatewayweb
    spec:
      containers:
        - name: nginx-web
          image: nginx:latest
          lifecycle:
            postStart:
              exec:
                command:  ["/bin/sh", "-c", "echo gatewayweb > /usr/share/nginx/html/index.html"]
          ports:
            - containerPort: 80

---

## 2. 创建 deployment 对应的 service 资源
apiVersion: v1
kind: Service
metadata:
  name: nginx-web-gatewayapi-svc
  namespace: default

spec:
  ports:
    - name: http
      port: 80
  selector:
    app: gatewayweb
2. 创建 01-gatewayapi-web.yaml 资源
kubectl apply -f 01-gatewayapi-web.yaml
1. 编写 02-gatewayapi-web-gateway.yaml 文件
---
## 1. 创建 gatewayclass 资源

apiVersion: networking.x-k8s.io/v1alpha1
kind: GatewayClass
metadata:
  name: my-gateway-class
spec:
  controller: traefik.io/gateway-controller

---

## 2. 创建 gateway 资源
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: Gateway
metadata:
  name: nginx-web-gateway
spec:
  gatewayClassName: my-gateway-class
  listeners:
    - name: http
      protocol: HTTP
      port: 50000

---

## 3. 创建 gateway-httproute
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: HTTPRoute
metadata:
  name: nginx-web-gateway-api-route

spec:
  parentRefs:
    - name: nginx-web-gateway

  hostnames:
    - "nginx.k8s.huichengcheng.com"

  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /
      backendRefs:
        - name: nginx-web-gatewayapi-svc
          port: 80
          weight: 1
2. 创建 gatewayapi 相关资源
kubectl apply -f 02-gatewayapi-web-gateway.yaml
3. 查看 gatewayapi 相关资源
kubectl get httproute

4. 金丝雀发布

Gateway APIs 规范可以支持的另一个功能是金丝雀发布,假设你想在一个端点上运行两个不同的服务(或同一服务的两个版本),并将一部分请求路由到每个端点,则可以通过修改你的 HTTPRoute 来实现。

配置 Kubernetes gateway api 网关提供程序并进行金丝雀发布

1. 编写 01-gateway-cn-deploy.yaml 文件
---

## 1. 创建 v1 版本
kind: Deployment
apiVersion: apps/v1
metadata:
  name: nginx-web1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: v1
  template:
    metadata:
      labels:
        app: v1
    spec:
      containers:
        - name: nginx-web-c
          image: nginx:latest
          lifecycle:
            postStart:
              exec:
                command:  ["/bin/sh", "-c", "echo svc1 > /usr/share/nginx/html/index.html"]
          ports:
            - containerPort: 80
---

## 2. 创建 v2 版本
kind: Deployment
apiVersion: apps/v1
metadata:
  name: nginx-web2
spec:
  replicas: 1
  selector:
    matchLabels:
      app: v2
  template:
    metadata:
      labels:
        app: v2
    spec:
      containers:
        - name: nginx-web-c
          image: nginx:latest
          lifecycle:
            postStart:
              exec:
                command:  ["/bin/sh", "-c", "echo svc2 > /usr/share/nginx/html/index.html"]
          ports:
            - containerPort: 80

---

## 3. 创建 两个版本的 service
apiVersion: v1
kind: Service
metadata:
  name: svc1

spec:
  ports:
    - name: http
      port: 80
  selector:
    app: v1
---
apiVersion: v1
kind: Service
metadata:
  name: svc2

spec:
  ports:
    - name: http
      port: 80
  selector:
    app: v2
2. 创建演示服务
kubectl apply -f 01-gateway-cn-deploy.yaml 
1. 编写 02-gateway.yaml 文件
---
## 1. 创建 gatewayclass 资源

apiVersion: networking.x-k8s.io/v1alpha1
kind: GatewayClass
metadata:
  name: my-gateway-class
spec:
  controller: traefik.io/gateway-controller

---

## 2. 创建 gateway 资源
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: Gateway
metadata:
  name: nginx-web-gateway
spec:
  gatewayClassName: my-gateway-class
  listeners:
    - name: http
      protocol: HTTP
      port: 50000

---

## 3. 创建 gateway-httproute

apiVersion: gateway.networking.k8s.io/v1alpha2
kind: HTTPRoute
metadata:
  name: nginx-web-gateway-api-route

spec:
  parentRefs:
    - name: nginx-web-gateway

  hostnames:
    - "nginx1.k8s.huichengcheng.com"

  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /
      backendRefs:
        - name: svc1
          port: 80
          weight: 3  # 3/4 的请求到svc1
        - name: svc2
          port: 80
          weight: 3  # 3/4 的请求到svc1