网关发展
初始的 Kubernetes 内部服务向外暴露,使用的是自身的 LoadBlancer 和 NodePort 类型的Service,在集群规模逐渐扩大的时候,这种 Service 管理的方式满足不了我们的需求,比如 NodePort 需要大量的端口难以维护,多了一层NAT,请求量大会对性能有影响;LoadBlancer 需要每个 Service 都有一个外部负载均衡器。接着 Kubernetes 提供了一个内置的资源对象 Ingress API 来暴露 HTTP 服务给外部用户,它的创建是为了标准化的将 Kubernetes 中的服务流量暴露给外部,Ingress API 通过引入路由功能,克服了默认服务类型 NodePort 和 LoadBalancer 的限制。在创建 Ingress 资源的时候通过 IngressClass 指定该网关使用的控制器,主要是靠 Ingress 控制器不断监听 Kubernetes API Server 中 IngressClass 以及 Ingress 资源的的变动,配置或更新入口网关和路由规则。IngressClass实现了网关与后台的解耦,但也有着很多的局限性。Ingress 配置过于简单,只支持 http 和 https 协议的服务路由和负载均衡,缺乏对其他协议和定制化需求的支持,而且 http 路由只支持 host 和 path 的匹配,对于高级路由只能通过注解来实现,当然这取决于 Ingress 控制器的实现方式,不同的 Ingress 控制器使用不同的注解,来扩展功能,使用注解对于 Ingress 的可用性大打折扣;路由无法共享一个命名空间的网关,不够灵活;网关的创建和管理的权限没有划分界限,开发需要配置路由以及网关。当然也有很多第三方的网关组件,例如 istio 和 apisix 等,提供了丰富的流量管理功能,如负载均衡、动态路由、动态 upstream、A/B测试、金丝雀发布、限速、熔断、防御恶意攻击、认证、监控指标、服务可观测性、服务治理等,还可以处理南北流量以及服务之间的东西向流量。对外提供路由功能,对内提供流量筛选,已经很好的满足了当下网络环境的所有需求。但对于小集群来说,这两个网关的部署成本有点高;而且太多类型的网关,不同的配置项、独立的开发接口、接口的兼容性、学习成本、使用成本、维护成本以及迁移成本都很高。急需一种兼容所有厂商 API 的接口网关。所以应运而生,Kubernetes 推出了 Gateway API。Gateway API 是 Kubernetes 1.19 版本引入的一种新的 API 规范,会成为 Ingress 的下一代替代方案。它有着 Ingress 的所有功能,且提供更丰富的功能,它支持更多的路由类型选择,除了 http路由外,还支持 tcp 以及 grpc 路由类型;它通过角色划分将各层规则配置关注点分离,实现规则配置上的解耦;并提供跨 namespace 的路由与网关支持使其更适应多云环境等。与 Ingress Api 工作类似的,Gateway Controller 会持续监视 Kubernetes API Server 中的 GatewayClass 和 Gateway 对象的变动,根据集群运维的配置来创建或更新其对应的网关和路由。API 网关、入口控制器和服务网格的核心都是一种代理,目的在于内外部服务通信。更多的功能并不等于更好的工具,尤其是在 Kubernetes 中,工具的复杂性可能是一个杀手。
https://gateway-api.sigs.k8s.io/implementations/
目前 Gateway API 还处于开发阶段,但已经有多个项目表示支持或计划支持 Gateway API。主要包括:
除此之外,各大云服务商都在积极跟进 Gateway API 进展,预计未来会在相应的服务中提供 Gateway API 支持。可以看出,尽管 Gateway API 还不算成熟和稳定,但由于其强大的功能和作为 Kubernetes 官方项目的影响力,已经获得大量项目的支持和兼容。
https://gateway-api.sigs.k8s.io/
Gateway API 用于管理和配置 k8s 集群中的网关以及集群内部的流量流向并执行丰富的负载均衡操作以及通过统一配置方式,来管理和配置不同类型的网关,简化了网关的管理和部署。Kubernetes Gateway 使用场景:
Gateway API 目标:
https://gateway-api.sigs.k8s.io/concepts/api-overview/ https://gateway-api.sigs.k8s.io/faq/
Gateway Api 特性之一就是通过角色划分将各层规则配置关注点分离,实现规则配置上的解耦。基础设施都是为了共享而建的,共享基础设施有一个共同的挑战,那就是如何为基础设施用户提供灵活性的同时还能被所有者控制。Gateway API 通过面向角色的设计来实现这一目标,通过将资源对象分离,实现配置上的解耦,可以由不同的角色的人员来管理,平衡了灵活性和集中控制,解决了入口网关创建与管理职责界限的划分。如下图:
GatewayClass资源是集群统一部署的,由平台提供。一个集群管理员创建了从 GatewayClass 派生的 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 服务中。Store 和 Site 团队在他们自己的 namespaces 中运行,但是将他们的 Routes 绑定到同一个共享 Gateway,允许他们独立控制自己的路由逻辑。这种用户模型在为基础设施提供灵活性的同时也保证了对不同角色之间的控制。如下图:
https://gateway-api.sigs.k8s.io/references/spec
官网现在支持两个 api 版本:
Gateway API 的资源模型中,最主要有三种对象类型:GatewayClass、Gateway、Route。
GatewayClass 定义了一组共享配置和行为的 Gateway,GatewayClass 是一个集群范围的资源,必须至少定义一个 GatewayClass,Gateway 引用该 GatewayClass 才能够生效。每个 GatewayClass 必须关联一个控制器 controller ,控制器 controller 可以处理多个 GatewayClass。控制器 controller 作用就是持续监视 Kubernetes API Server 中的 GatewayClass 和 Gateway 对象的变动,创建或更新其对应的网关和路由配置。通俗讲 GatewayClass 就是一类 Gateway 的集合的入口,Gateway 想要实现转发必须要关联到某一个 GatewayClass 上,而 GatewayClass 也需要关联到一个网关控制器 controller,控制器 controller 可以监听 API Server 资源中 GatewayClass 以及 Gateway 的变化。比如 Istio、Traefik、Apisix 等根据 Gateway Api 的标准和要求,实现了一个 controller,能够让 Gateway 依赖 GatewayClass 创建对应的网关。一般 GatewayClass 不需要人工手动创建,参与支持 Gateway Api 工程的第三方网关组件安装时会自动创建。GatewayClass 与控制器 controller 使用 spec.controllerName 关联。
kind: GatewayClass
apiVersion: gateway.networking.k8s.io/v1beta1
metadata:
name: istio
spec:
controllerName: istio.io/gateway-controller // 关联istio的控制器 controller
Gateway 负责外部请求到集群内的流量接入以及往后转发,定义了对特定负载均衡器配置的请求。Gateway 实现了 GatewayClass 配置和行为的约定,该资源可以由运维人员直接创建,也可以由处理 GatewayClass 的控制器创建。Gateway 资源是一个中间层,需要定义所要监听的端口、协议、TLS 配置等信息,可以将网络流量的管理和控制集中到一个位置,提高集群的可用性和安全性。配置完成后,由 GatewayClass 绑定的 Controller 为我们提供一个具体存在的 Pod 作为流量入口。Gateway 规范中定义了以下内容:
tls的两种协议类型:Terminate、Passthrough
不同的监听器协议,支持不同的 TLS 模式和路由类型:
监听器协议 | TLS 模式 | 路由类型 |
---|---|---|
TLS | Passthrough | TLSRoute |
TLS | Terminate | TCPRoute |
HTTPS | Terminate | HTTPRoute |
apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
name: gateway-istio
spec:
gatewayClassName: istio ## 关联指定的 GatewayClass,名字必须与 GatewayClass 中定义的名称相同
listeners:
- name: foo-https
protocol: HTTPS ## 网关使用的协议
port: 443 ## 网关监听端口
hostname: tls.example.com
tls: #为 HTTPS 配置加密协议
mode: Terminate #加密协议类型 Terminate
certificateRefs:
- kind: Secret
group: ""
name: foo-example-com-cert
- name: wildcard-https
protocol: HTTPS ## 网关使用的协议
port: 443 ## 网关监听端口
hostname: "*.cai-inc.com" # 通配符
tls: #为 HTTPS 配置加密协议
mode: Terminate #加密协议类型 Terminate
certificateRefs:
- kind: Secret
group: "" # 空字符串 Kubernetes API 核心组
name: wildcard-example-com-cert
ReferenceGrant 可用于在 Gateway API 中启用跨命名空间引用。特别的,Router 可能会将流量转发到其他命名空间中的后端,或者 Gateway 可能会引用另一个命名空间中的 Secret。
如果从其命名空间外部引用一个对象,则该对象的所有者必须创建一个 ReferenceGrant 资源以显式允许该引用,否则跨命名空间引用是无效的。ReferenceGrant 由两个列表组成,一个是引用来源的资源列表,另一个是被引用的资源列表。
以下示例显示命名空间 foo 中的 HTTP 路由如何引用命名空间 bar 中的服务。在此示例中,bar 命名空间中的引用授予明确允许从 foo 命名空间中的 HTTP 路由引用服务。
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
name: foo
namespace: foo
spec:
rules:
- matches:
- path: /bar
backendRefs:
- name: bar
namespace: bar
---
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
name: bar
namespace: bar
spec:
from:
- group: gateway.networking.k8s.io
kind: HTTPRoute
namespace: foo
to:
- group: ""
kind: Service
网关配置引用不同名称空间中的证书,在目标命名空间中创建 ReferenceGrant 以允许跨命名空间引用。如果没有该引用授予,则跨名称空间引用将无效。
apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
name: cross-namespace-tls-gateway
namespace: gateway-api-example-ns1
spec:
gatewayClassName: istio
listeners:
- name: https
protocol: HTTPS
port: 443
hostname: "*.cai-inc.com"
tls:
mode: Terminate
certificateRefs:
- kind: Secret
group: ""
name: allow-ns1-gateways-to-ref-secrets
namespace: gateway-api-example-ns2
---
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
name: allow-ns1-gateways-to-ref-secrets
namespace: gateway-api-example-ns2
spec:
from:
- group: gateway.networking.k8s.io
kind: Gateway
namespace: gateway-api-example-ns1
to:
- group: ""
kind: Secret
一个 Gateway 可以包含一个或多个 Route 引用,每个Route都要绑定一个Gateway,这些 Route 的作用是将一个子集的流量引导到一个特定的服务上。目前支持4种路由类型:HTTPRoute、TLSRoute、TCPRoute、GRPCRoute。
HTTPRoute 适用于多路复用 HTTP 或 HTTPS 请求,并使用 HTTP 请求进行路由或修改的场景,比如使用 HTTP Headers 头进行路由,url路径的重定向或者重写,灰度发布涉及权重等。HTTPRoute 的规范中包括:
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
name: httproute-example
spec:
parentRefs:
- name: gateway-istio ## 引用 Gateway
hostnames:
- "zcy.cai-inc.com"
rules:
- matches: ## 前缀为 monitor 的服务转发到后端 monitor-center 的6099端口
- path:
type: PathPrefix
value: /monitor
backendRefs:
- name: monitor-center1 ## 90%的流量转发给monitor-center1
port: 6099
weight: 90
- name: monitor-center2
port: 6099
weight: 10
- matches: ## 请求头包含 env=prod 前缀为 /api2/otel 的服务转发到 otel-dashboard 的7099
- headers: ## Exact 精确匹配,也可以使用支持 POSIX、PCRE 或正则表达式
- name: env
type: Exact
value: prod
path:
type: PathPrefix
value: "/api2/otel"
filters:
- type: RequestHeaderModifier ## 请求头添加 cookie=test1
requestHeaderModifier:
add:
- name: cookie
value: test1
# - type: RequestHeaderModifier ## 请求头修改已存在的 cookie=test2
# requestHeaderModifier:
# set:
# - name: cookie
# value: test2
# - type: RequestHeaderModifier ## 删除请求头 cookie
# requestHeaderModifier:
# remove: ["cookie"]
- type: ResponseHeaderModifier ## 添加响应头
responseHeaderModifier:
add:
- name: X-Header-Add-1
value: header-add-1
- name: X-Header-Add-2
value: header-add-2
- type: RequestRedirect
requestRedirect:
scheme: https
statusCode: 301
backendRefs:
- name: otel-dashboard
port: 7099
TLSRoute 用于 TLS 连接,通过 SNI 进行区分,它适用于希望使用 SNI 作为主要路由方法的地方,并且对 HTTP 等更高级的协议不感兴趣,连接的字节流不经任何检查就被代理到后端。它的配置和 HTTPRoute 类似,不同点在于 rules 的配置中只有 backendRefs, 没有 matches 和 filters,官网对 TLSRoute 介绍不多,可能建议 tls 统一配置在 Gateway 处,详情请看上面 Gateway tls 配置。
TCPRoute(和UDPRoute)可以根据目的IP地址、目的端口号和协议等匹配规则来确定流量,将TCP流量动态路由到符合应用需求的后端服务上。完成TCPRoute 路由的配置,需要在绑定的 Gateway listeners.protocol 为 TCP。Gateway 中的 listeners.name 与 TCPRoute 的 parentRefs.sectionName 绑定区分后端的服务service。通过这种方式,每个 TCP 路由将自身绑定到网关上的不同端口,以便 Gateway.listeners.name --> TCPRoute.parentRefs.sectionName --> service 从集群外部获取端口 XXX 的流量。它的配置和 TLSRoute 类似,与 HTTPRoute 相比 rules 的配置中只有 backendRefs,没有 matches 和 filters。
apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
name: tcp-gateway
spec:
gatewayClassName: istio
listeners:
- name: foo # 与 TCPRoute sectionName 绑定
protocol: TCP # tcp 协议
port: 8080
allowedRoutes: # 绑定为 TCPRoute
kinds:
- kind: TCPRoute
- name: bar
protocol: TCP
port: 8090
allowedRoutes:
kinds:
- kind: TCPRoute
---
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: TCPRoute
metadata:
name: tcp-app-1
spec:
parentRefs:
- name: tcp-gateway
sectionName: foo
rules:
- backendRefs:
- name: foo-service
port: 6000
---
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: TCPRoute
metadata:
name: tcp-app-2
spec:
parentRefs:
- name: tcp-gateway
sectionName: bar
rules:
- backendRefs:
- name: bar-service
port: 6000
GRPCRoute 用于路由 gRPC 请求,包括按主机名、gRPC服务、gRPC方法或HTTP/2头匹配请求的能力。支持 GRPCRoute 的网关需要支持 HTTP/2,否则报错不支持的协议“UnsupportedProtocol”。虽然可以通过 HTTPRoute 或自定义的 CRD 来路由 gRPC,但从长远来看,这会导致生态系统的碎片化。gRPC 是业界广泛采用的流行 RPC 框架,该协议在 Kubernetes 项目本身中被广泛地应用于许多接口,由于 gRPC 在 Kubernetes 项目和应用层网络中的重要性,因此不允许过度细分,强制规定使用 GRPCRoute 来路由 gRPC。支持 GRPC 路由的实现必须强制 GRPCRoute 和 HTTPRoute 之间 hostnames 的唯一性。如果 HTTP 路由或 GRPC 路由类型的路由 (A) 附加到 Gateway Listener,并且该 Listener 已经绑定了其他类型的另一个路由 (B),并且 A 和 B 的 hostnames 交集非空,则路由 A 不会实现,建议对gRPC和非gRPC HTTP流量使用不同的主机名。GRPCRoute 与 HTTPRoute rules 规则基本一致,定义了基于条件匹配 matches、过滤器 filters 以及将请求转发到后端 backendRefs。不同点在于 GRPCRoute.rules.matches 只支持 method 和 headers,headers参数为数组类型,method 参数为非数组。method 和 headers 匹配只支持精确匹配 Exact 和正则匹配 RegularExpression,且不能包含 / 字符。GRPCRoute.rules.filters 中不支持 url 重定向以及重写。
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: GRPCRoute
metadata:
name: grpcroute
spec:
parentRefs: # 绑定 Gateway
- name: gateway-istio
hostnames: # 绑定主机名
- my.example.com
rules:
- matches:
- method: # 包含 com.example.User.Login 方法且头部包含 version: "2"
service: com.example.User
method: Login
headers:
- type: Exact
name: version
value: "2"
backendRefs: # 不配置则不会转发
- name: foo-svc
port: 50051
- matches: # 包含 grpc.reflection.v1.ServerReflection 方法添加请求头my-header:bar
- method:
service: grpc.reflection.v1.ServerReflection
filters:
- type: RequestHeaderModifier
requestHeaderModifier:
add:
- name: my-header
value: bar
backendRefs:
- name: bar-svc1
port: 50052
weight: 90
- name: bar-svc2
port: 50052
weight: 10
https://gateway-api.sigs.k8s.io/guides/multiple-ns/
Gateway API 支持跨 Namespace 路由 Route,网关 Gateway 和路由 Route 可以部署到不同的命名空间中。路由可以跨 Namespace 边界绑定到网关的能力需要 Gateway 和 Route 双方协商同意。Route 和 Gateway 资源具有内置的控制,以允许或限制它们之间如何相互选择。当 Route 绑定到 Gateway 时,代表应用在 Gateway 上配置了底层的负载均衡器或代理。一个 Kubernetes 集群管理员在 Infra 命名空间中部署了一个名为 shared-gw 的 Gateway,供不同的应用团队使用,以便将其应用暴露在集群之外。团队 A 和团队 B(分别在命名空间 "A" 和 "B" 中)将他们的 Route 绑定到这个 Gateway。它们互不相识,只要它们的 Route 规则互不冲突,就可以继续隔离运行。团队 C 有特殊的网络需求(可能是性能、安全或关键性),他们需要一个专门的 Gateway 来代理他们的应用到集群外。团队 C 在 "C" 命名空间中部署了自己的 Gateway dedicated-gw,该 Gateway 只能由 "C" 命名空间中的应用使用。不同命名空间及 Gateway 与 Route 的绑定关系如下图所示:
网关和路由的绑定是双向的:只有网关所有者和路由所有者都同意绑定才会成功。这种双向关系存在的原因有两个:
1. 路由所有者不想通过他们不知道的路径过度暴露他们的应用程序。
2. 网关所有者不希望某些应用程序或团队在未经允许的情况下使用网关。例如,内部服务不应该通过互联网网关可访问。
网关支持管理路由来源约束,使用 listeners 字段限制可以附加的路由。网关支持命名空间和路由类型作为附加约束,不符合附加约束的任何路由都无法附加到该网关上。路由通过父引用字段 parentRefs 显式引用它们要附加到的网关。这些共同创建了一个协议,使基础设施所有者和应用程序所有者能够独立定义应用程序如何通过网关公开,有效地降低了管理开销。如何将路由与网关绑定:
将路由附加到网关包括以下步骤:
1. Route 需要在其 parentRefs 字段中引用 Gateway;
2. Gateway 上至少有一个监听器允许其附加。
总之,网关选择路由,路由控制它们的暴露。当网关选择一个允许自己暴露的路由时,那么该路由将与网关绑定。当路由与网关绑定时,意味着它们的集体路由规则被配置在了由该网关管理的底层负载均衡器或代理服务器上。
https://gateway-api.sigs.k8s.io/concepts/api-overview/#attaching-routes-to-gateways
Gateway 根据 Route 元数据,Route 资源的种类、命名空间和标签来选择 Route。Route 实际上被绑定到 Gateway 中的监听器上,因此每个监听器都有一个 listeners.allowedRoutes 字段,它通过以下一个或多个标准来选择 Route。
下面的 Gateway 将在集群中的所有 Namespace 中选择 otel: true 的所有 HTTPRoute 资源。
apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
name: gatewayDemo
namespace: gateway-istio
spec:
gatewayClassName: istio
listeners:
- name: http
port: 80
protocol: HTTP
allowedRoutes:
kinds:
- kind: HTTPRoute
namespaces:
from: Selector
selector:
matchLabels:
otel: "true"
---
apiVersion: v1
kind: Namespace
metadata:
name: routeDemo-example
labels:
otel: "true"
---
# HTTPRoute
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
name: routeDemo
namespace: routeDemo-example
spec:
parentRefs:
- kind: Gateway
name: gatewayDemo
namespace: gateway-istio
rules:
- backendRefs:
- name: otel
port: 7099
GatewayClass、Gateway、xRoute 和 Service 的组合将定义一个可实现的负载均衡器。下图说明了不同资源之间的关系。
使用反向代理实现的网关的一个客户端请求网关 / 流程是:
$ wget https://github.com/kubernetes-sigs/gateway-api/releases/download/v0.6.1/standard-install.yaml
$ kubectl apply -f standard-install.yaml
# 查看安装的 CRD 资源
$ kubectl get crd |grep networking.k8s.io
image.png
# 查看安装的 controller
$ kubectl get pod -n gateway-system
# 对于 status 为 Completes 状态的 pod,他们的作用是为了初始化 gateway-api-admission-server-665c77984-nhzmd
# 可以删除
$ kubectl delete pod -n gateway-system gateway-api-admission-patch-ll7lq
$ kubectl delete pod -n gateway-system gateway-api-admission-wnlz2
需要注意的是,standard-install.yaml 中的镜像因为某些原因,不可拉取,所以我们需要找国内别人已经保存的镜像,或者是自己把缓存镜像。推荐几个镜像:
lianyuxue1020/kube-webhook-certgen:v1.1.1
acmestack/admission-server:v0.6.1
Gateway Api Controller 不需要手动去安装,所有实现了 Gateway Api 接口的网关都会集成 Controller。目前的网关流行的有 istio、apisix、nginx等,此处以 istio 网关为例安装演示。
istio 在 1.6 版本后开始支持 Gateway Api
# 查看机器型号
$ uname -m
# 选择相对应的版本
$ wget https://github.com/istio/istio/releases/download/1.17.2/istio-1.17.2-linux-amd64.tar.gz
# 解压
$ tar -zxvf istio-1.17.2-linux-amd64.tar.gz
# 设置命令全局路径
$ cd istio-1.17.2/
$ export PATH=$PWD/bin:$PATH
# 查看 istioctl 版本
$ istioctl version
# 最小化安装 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 以及 http 路由,这里的 gateway 指的是 Gateway API gateway.networking.k8s.io/v1beta1 中的 Gateway 资源,而不是 Istio API networking.istio.io/v1beta1 中的 Gateway。istio 中有一个例子,可以测试使用,部署一个名为 httpbin 的简单服务,并且将它用 Gateway 暴露到集群外部。1、部署httpbin服务:
# 如果需要 istio 注入,需要在 namespace 打标
$ kubectl label namespace xxx istio-injection=enabled
$ cd istio-1.17.2/samples/httpbin
$ kubectl apply -f httpbin.yaml
serviceaccount/httpbin created
service/httpbin created
deployment.apps/httpbin created
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
httpbin ClusterIP 10.96.0.85 <none> 8000/TCP 10s
$ kubectl get pod
NAMESPACE NAME READY STATUS RESTARTS AGE
default httpbin-85d76b4bb6-gr84d 1/1 Running 0 15s
2、部署 Gateway 和 HTTPRoute,将访问 httpbin.example.com/get/* 的流量导入到 httpbin 服务中:
apiVersion: v1
kind: Namespace
metadata:
name: istio-ingress
spec:
finalizers:
- kubernetes
---
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
查看 Gateway 和 HTTPRoute 创建。默认情况下,每个Gateway 会自动创建同名的 Service 和 Deployment,如果更新了 Gateway,它们也会随之更新。当然也可以手动配置 Service 和 Deployment 并将其绑定到 Gateway,详情查看官网。
$ kubectl get gateway -n istio-ingress
NAME CLASS ADDRESS PROGRAMMED AGE
gateway istio gateway.istio-ingress.svc.cluster.local:80 26s
$ kubectl get deployment -n istio-ingress
NAME READY UP-TO-DATE AVAILABLE AGE
gateway 1/1 1 1 30s
$ kubectl get svc -n istio-ingress
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
gateway LoadBalancer 10.96.0.36 <pending> 15021:31764/TCP,80:32701/TCP 36s
$ kubectl get HTTPRoute
NAME HOSTNAMES AGE
http ["httpbin.example.com"] 40s
# 查看 HTTPRoute 是否被绑定,status 中的 conditions 的 reason: Accepted, status:"True"
$ kubectl get HTTPRoute http -o yaml
3、等待Gateway部署完成并设置Ingress Host环境变量:
$ 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.96.0.36 15021:30088/TCP,80:31690/TCP 4m13s
$ kubectl wait -n istio-ingress --for=condition=ready gateways.gateway.networking.k8s.io gateway
gateway.gateway.networking.k8s.io/gateway condition met
$ export INGRESS_HOST=$(kubectl get gateways.gateway.networking.k8s.io gateway -n istio-ingress -ojsonpath='{.status.addresses[*].value}')
4、访问httpbin 服务:
# 使用了-H将 HTTP Header 中的 Host 设置为 httpbin.example.com
$ curl -s -I -HHost:httpbin.example.com "http://$INGRESS_HOST/get"
HTTP/1.1 200 OK
server: istio-envoy
date: Mon, 05 Jun 2023 07:33:41 GMT
content-type: application/json
content-length: 1356
access-control-allow-origin: *
access-control-allow-credentials: true
x-envoy-upstream-service-time: 3
5、访问没有配置过的路由/headers,会得到 HTTP 404 的错误:
$ curl -s -I -HHost:httpbin.example.com "http://$INGRESS_HOST/headers"
HTTP/1.1 404 Not Found
更新路由规则也会暴露 /headers 并为请求添加标头:
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
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
# 删除 namespace、 httproute 和 gateways
$ kubectl delete -f demo.yaml
# 删除 istio
$ istioctl uninstall -y --purge
$ kubectl delete ns istio-system
删除 Kubernetes Gateway API CRD
$ kubectl delete -f standard-install.yaml
Gateway Pod 默认只有 1 个,不能真正满足实际生产需要,多数情况至少需要多个 Pod 一起负载压力。使用 HPA 需要在 Gateway 的 Deployment 中设置对应的 request/limit 资源。
apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
name: gateway
namespace: istio-ingress
spec:
gatewayClassName: istio
listeners:
- name: default
hostname: "*.example.com"
port: 80
protocol: HTTP
allowedRoutes:
namespaces:
from: All
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: gateway
namespace: istio-ingress
spec:
# 通过引用与生成的 Deployment 匹配
# 注意不要使用 `kind: Gateway`
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
# Gateway Deployment 名称
name: gateway
minReplicas: 2
maxReplicas: 5
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: gateway
namespace: istio-ingress
spec:
minAvailable: 1
selector:
# Match the generated Deployment by label
matchLabels:
istio.io/gateway-name: gateway