Gateway API 是 Kubernetes 中的一個 API 資源集合,包括?GatewayClass、Gateway、HTTPRoute、TCPRouteService?等,這些資源共同為各種網(wǎng)絡(luò)用例構(gòu)建模型

Gateway API

Gateway API 最初設(shè)計用于管理從集群外部客戶端到集群內(nèi)部服務(wù)的流量(入口或北/南情況)。隨著時間的推移,服務(wù)網(wǎng)格用戶的興趣促使 GAMMA(Gateway API for Service Mesh)計劃的創(chuàng)建,以定義 Gateway API 如何用于同一集群內(nèi)的服務(wù)間或東/西流量。

Gateway API 的改進比當(dāng)前的 Ingress 資源對象有很多更好的設(shè)計:

還有一些其他值得關(guān)注的功能:

面向角色設(shè)計

無論是道路、電力、數(shù)據(jù)中心還是 Kubernetes 集群,基礎(chǔ)設(shè)施都是為了共享而建的,然而共享基礎(chǔ)設(shè)施提供了一個共同的挑戰(zhàn),那就是如何為基礎(chǔ)設(shè)施用戶提供靈活性的同時還能被所有者控制。

Gateway API 通過對 Kubernetes 服務(wù)網(wǎng)絡(luò)進行面向角色的設(shè)計來實現(xiàn)這一目標(biāo),平衡了靈活性和集中控制。它允許共享的網(wǎng)絡(luò)基礎(chǔ)設(shè)施(硬件負(fù)載均衡器、云網(wǎng)絡(luò)、集群托管的代理等)被許多不同的團隊使用,所有這些都受到集群運維設(shè)置的各種策略和約束。

一個集群運維人員創(chuàng)建了一個基于?GatewayClass?的?Gateway?資源,這個?Gateway?配置了它所代表的基礎(chǔ)網(wǎng)絡(luò)資源,集群運維和特定的團隊必須溝通什么可以附加到這個 Gateway 上來暴露他們的應(yīng)用。 集中的策略,如 TLS,可以由集群運維在 Gateway 上強制執(zhí)行,同時,Store 和 Site 應(yīng)用在他們自己的命名空間中運行,但將他們的路由附加到相同的共享網(wǎng)關(guān)上,允許他們獨立控制他們的路由邏輯。

這種關(guān)注點分離的設(shè)計可以使不同的團隊能夠管理他們自己的流量,同時將集中的策略和控制留給集群運維。

概念

在整個 Gateway API 中涉及到 3 個角色:基礎(chǔ)設(shè)施提供商、集群管理員、應(yīng)用開發(fā)人員,在某些場景下可能還會涉及到應(yīng)用管理員等角色。Gateway API 中定義了 3 種主要的資源模型GatewayClassGateway、Route。

GatewayClass

GatewayClass 定義了一組共享相同配置和動作的網(wǎng)關(guān)。每個 GatewayClass 由一個控制器處理,是一個集群范圍的資源,必須至少有一個 GatewayClass 被定義。

這與 Ingress 的 IngressClass 類似,在 Ingress v1beta1 版本中,與 GatewayClass 類似的是 ingress-class 注解,而在 Ingress V1 版本中,最接近的就是 IngressClass 資源對象。

Gateway

Gateway 網(wǎng)關(guān)描述了如何將流量轉(zhuǎn)化為集群內(nèi)的服務(wù),也就是說,它定義了一個請求,要求將流量從不了解 Kubernetes 的地方轉(zhuǎn)換到集群內(nèi)的服務(wù)。例如,由云端負(fù)載均衡器、集群內(nèi)代理或外部硬件負(fù)載均衡器發(fā)送到 Kubernetes 服務(wù)的流量。

它定義了對特定負(fù)載均衡器配置的請求,該配置實現(xiàn)了 GatewayClass 的配置和行為規(guī)范,該資源可以由管理員直接創(chuàng)建,也可以由處理 GatewayClass 的控制器創(chuàng)建。

Gateway 可以附加到一個或多個路由引用上,這些路由引用的作用是將流量的一個子集導(dǎo)向特定的服務(wù)。

Route 資源

路由資源定義了特定的規(guī)則,用于將請求從網(wǎng)關(guān)映射到 Kubernetes 服務(wù)。從?v1alpha2?版本開始,API 中包含四種 Route 路由資源類型。

HTTPRoute

HTTPRoute?是用于 HTTP 或 HTTPS 連接,適用于我們想要檢查 HTTP 請求并使用 HTTP 請求進行路由或修改的場景,比如使用 HTTP Headers 頭進行路由,或在請求過程中對它們進行修改。

TLSRoute

TLSRoute 用于 TLS 連接,通過 SNI 進行區(qū)分,它適用于希望使用 SNI 作為主要路由方法的地方,并且對 HTTP 等更高級別協(xié)議的屬性不感興趣,連接的字節(jié)流不經(jīng)任何檢查就被代理到后端。

TCPRoute 和 UDPRoute

TCPRoute(和?UDPRoute)旨在用于將一個或多個端口映射到單個后端。在這種情況下,沒有可以用來選擇同一端口的不同后端的判別器,所以每個 TCPRoute 在監(jiān)聽器上需要一個不同的端口。你可以使用 TLS,在這種情況下,未加密的字節(jié)流會被傳遞到后端,當(dāng)然也可以不使用 TLS,這樣加密的字節(jié)流將傳遞到后端。

GRPCRoute

GRPCRoute 用于路由 gRPC 流量,支持 GRPCRoute 的網(wǎng)關(guān)必須支持 HTTP/2,無需從 HTTP/1 進行初始升級,因此可以確保 gRPC 流量正常進行。

組合

GatewayClass、Gateway、xRoute?和?Service?的組合定義了一個可實施的負(fù)載均衡器,下圖說明了不同資源之間的關(guān)系:

使用反向代理實現(xiàn)的網(wǎng)關(guān)的典型客戶端/網(wǎng)關(guān) API 請求流程如下所示:

與 Istio API 的區(qū)別

我們這里主要是講解 Gateway API 在服務(wù)網(wǎng)格中的使用,首先我們先了解下 Gateway API 與 Istio API 的區(qū)別。

Gateway API 與 Istio API(如?Gateway?和?VirtualService)有很多相似之處。主資源使用相同的?Gateway?名稱,并且這些資源服務(wù)于相類似的目標(biāo)。

新的 Gateway API 致力于從 Kubernetes 的各種 Ingress 實現(xiàn)(包括 Istio)中吸取經(jīng)驗,以構(gòu)建標(biāo)準(zhǔn)化的,獨立于供應(yīng)商的 API。這些 API 通常與 Istio Gateway 和 VirtualService 具有相同的用途,但依然有一些不同的地方:

實現(xiàn)

接下來我們就來了解下如何在 Istio z 中使用 Gateway API。默認(rèn)情況下 Kubernetes 集群中不會安裝 Gateway API,首先我們需要安裝 Gateway API CRD:

$ kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.0.0/standard-install.yaml
customresourcedefinition.apiextensions.k8s.io/gatewayclasses.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/gateways.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/httproutes.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/referencegrants.gateway.networking.k8s.io created

standard-install.yaml?包括所有已升級為 GA 或 Beta 的資源,包括?GatewayClass、GatewayHTTPRoute?和?ReferenceGrant,由于 Istio 已經(jīng)對 Gateway API 提供了支持,所以現(xiàn)在我們就可以直接使用了。比如現(xiàn)在就會自動創(chuàng)建一個?istio?的?GatewayClass?資源對象,如下所示(另外還有一個名為?istio-remote):

$ kubectl get gatewayclass
NAME CONTROLLER ACCEPTED AGE
istio istio.io/gateway-controller True 106s
istio-remote istio.io/unmanaged-gateway True 106s
$ kubectl get gatewayclass istio -oyaml
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: istio
spec:
controllerName: istio.io/gateway-controller
description: The default Istio GatewayClass

因為大部分場景下在一個 Kubernetes 集群中只會有一個 Istio 集群,所以我們可以直接使用默認(rèn)的 istio 這個 GatewayClass,如果你有多個 Istio 集群,那么你可以創(chuàng)建多個 GatewayClass 來區(qū)分不同的集群。

比如接下來我們來嘗試將 httpbin 應(yīng)用使用 Gateway API 暴露到外部,首先我們需要部署一個 httpbin 應(yīng)用:

kubectl apply -f samples/httpbin/httpbin.yaml

然后接下來同樣我們需要部署一個?Gateway?資源對象,用于將流量從外部負(fù)載均衡器轉(zhuǎn)發(fā)到集群內(nèi)的服務(wù),如下所示:

# default-gateway.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: gateway
namespace: istio-ingress # 網(wǎng)關(guān)資源對象所在的命名空間
spec:
gatewayClassName: istio # 使用默認(rèn)的 istio GatewayClass
listeners: # 監(jiān)聽器
- name: default
hostname: "*.example.com"
port: 80
protocol: HTTP
allowedRoutes: # 允許的路由
namespaces:
from: All # 允許所有命名空間

Gateway?代表了邏輯負(fù)載均衡器的實例化,它是根據(jù)一個 istio 這個?GatewayClass?進行模板化的,網(wǎng)關(guān)在 80 端口上監(jiān)聽 HTTP 流量,這個特定的?GatewayClass?在部署后會自動分配一個 IP 地址,該地址會顯示在?Gateway.status?中。

需要注意的是這里我們聲明使用的命名空間為 istio-ingress, 這是因為 Istio Gateway 可以直接使用 istio ingressgateway 的 Deployment,而這個 Deployment 默認(rèn)是部署在 istio-system 命名空間中的,我們這里單獨將 Gateway 資源對象創(chuàng)建在 istio-ingress 命名空間中,那么就會自動在這個命名空間中部署一個網(wǎng)關(guān)控制器,用于區(qū)分默認(rèn)的 istio ingressgateway。

所以我們需要在?istio-ingress?命名空間中創(chuàng)建這個?Gateway?資源對象,這樣才能讓 istio ingressgateway 通過?Gateway?資源對象來獲取配置。

kubectl create namespace istio-ingress

然后直接應(yīng)用這個資源對象即可:

$ kubectl apply -f default-gateway.yaml
$ kubectl get gateway -n istio-ingress
NAME CLASS ADDRESS PROGRAMMED AGE
gateway istio False 8m23s
$ kubectl get deploy -n istio-ingress
NAME READY UP-TO-DATE AVAILABLE AGE
gateway-istio 1/1 1 1 8m35s
$ kubectl get svc -n istio-ingress
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
gateway-istio LoadBalancer 10.100.103.86 <pending> 15021:31509/TCP,80:32530/TCP 9m38s

我們也可以去對比下上面生成的 gateway-istio 和 Istio 默認(rèn)的 istio-ingressgateway 的 Deployment,他們的配置幾乎是一樣的。

接下來我們就需要去創(chuàng)建一個路由規(guī)則了,也就是想要如何訪問我們的 httpbin 應(yīng)用,類似于 VirtualService,我們可以使用?HTTPRoute?資源對象來定義這個路由規(guī)則,如下所示:

# httpbin-route.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: httpbin
namespace: default
spec:
parentRefs: # 引用定義的 Gateway 對象
- name: gateway
namespace: istio-ingress
hostnames: ["httpbin.example.com"] # 域名
rules: # 具體的路由規(guī)則
- matches:
- path:
type: PathPrefix
value: /get # 匹配 /get 的請求
backendRefs: # 引用的后端服務(wù)
- name: httpbin
port: 8000

在上面的 HTTPRoute 對象中我們通過 parentRefs 字段指定要連接到的網(wǎng)關(guān),只要網(wǎng)關(guān)允許這種連接,這將允許路由接收來自父網(wǎng)關(guān)的流量,在 backendRefs 中定義將要發(fā)送流量的后端。但是需要注意我們這里只定義了匹配 /get 這個路徑的請求,然后將要訪問的域名通過 hostnames 來定義。

同樣直接應(yīng)用該資源對象即可:

$ kubectl apply -f httpbin-route.yaml
$ kubectl get httproute
NAME HOSTNAMES AGE
httpbin ["httpbin.example.com"] 5s

然后我們就可以通過?httpbin.example.com?來訪問 httpbin 應(yīng)用了:

# $ export INGRESS_HOST=$(kubectl get gateways.gateway.networking.k8s.io gateway -n istio-ingress -ojsonpath='{.status.addresses[0].value}')
export GATEWAY_URL=$(kubectl get po -l istio.io/gateway-name=gateway -n istio-ingress -o 'jsonpath={.items[0].status.hostIP}'):$(kubectl get svc gateway-istio -n istio-ingress -o 'jsonpath={.spec.ports[?(@.name=="default")].nodePort}')

如果你的集群可以正常使用 LoadBalancer,那么?Gateway?控制器在部署后會自動分配一個 IP 地址,該地址會顯示在?Gateway.status?中。我們這里暫不支持,所以還是可以通過 NodePort 方式來進行訪問。然后可以使用?curl?訪問 httpbin 服務(wù):

$ curl -s -HHost:httpbin.example.com "http://$GATEWAY_URL/get"
HTTP/1.1 200 OK
server: istio-envoy
date: Mon, 18 Dec 2023 07:29:11 GMT
content-type: application/json
content-length: 494
access-control-allow-origin: *
access-control-allow-credentials: true
x-envoy-upstream-service-time: 2

請注意,使用 -H 標(biāo)志可以將 Host HTTP 標(biāo)頭設(shè)置為 httpbin.example.com。這一步是必需的,因為 HTTPRoute 已配置為處理 httpbin.example.com 的請求,但是在測試環(huán)境中,該主機沒有 DNS 綁定,只是將請求發(fā)送到入口 IP。

訪問其他沒有被顯式暴露的 URL 時,正常就會看到 HTTP 404 錯誤:

$ curl -s -I -HHost:httpbin.example.com "http://$INGRESS_HOST/headers"
HTTP/1.1 404 Not Found
date: Mon, 18 Dec 2023 07:31:51 GMT
server: istio-envoy
transfer-encoding: chunked

同樣我們也可以查看下?gateway-istio?的日志,可以看到類似于下面的日志:

$ kubectl -n istio-ingress logs -f gateway-istio-7474cd4d9b-8dw2k
# ......
2023-12-18T07:04:33.164968Z info cache returned workload trust anchor from cache ttl=23h59m59.835033638s
2023-12-18T07:04:33.621616Z info Readiness succeeded in 813.201909ms
2023-12-18T07:04:33.621946Z info Envoy proxy is ready
[2023-12-18T07:29:11.177Z] "HEAD /get HTTP/1.1" 200 - via_upstream - "-" 0 0 2 2 "10.244.1.1" "curl/7.29.0" "68f51e89-8125-4d9e-be36-e0cdc6e6ead8" "httpbin.example.com" "10.244.1.131:80" outbound|8000||httpbin.default.svc.cluster.local 10.244.1.132:46066 10.244.1.132:80 10.244.1.1:55626 - default.httpbin.0
[2023-12-18T07:31:52.051Z] "HEAD /headers HTTP/1.1" 404 NR route_not_found - "-" 0 0 0 - "10.244.1.1" "curl/7.29.0" "a9a1002f-d65c-40d8-861d-ec99d4a4a442" "httpbin.example.com" "-" - - 10.244.1.132:80 10.244.1.1:53583 - -

證明我們的路由規(guī)則已經(jīng)生效了。

同樣如果我們想要能夠正常訪問刀?/headers?路由,那么我們可以更新下?HTTPRoute?對象:

# httpbin-route.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: httpbin
namespace: default
spec:
parentRefs: # 引用定義的 Gateway 對象
- name: gateway
namespace: istio-ingress
hostnames: ["httpbin.example.com"] # 域名
rules: # 具體的路由規(guī)則
- matches:
- path:
type: PathPrefix
value: /get # 匹配 /get 的請求
- path:
type: PathPrefix
value: /headers # 匹配 /headers 的請求
filters:
- type: RequestHeaderModifier # 添加一個修改請求頭的過濾器
requestHeaderModifier:
add: # 添加一個標(biāo)頭
- name: my-added-header
value: added-value
backendRefs: # 引用的后端服務(wù)
- name: httpbin
port: 8000

我們除了在?rules?中添加了一個匹配?/headers?的規(guī)則外,還添加了一個?RequestHeaderModifier?過濾器,用于添加一個 Header 頭信息,更新這個資源對象后再次訪問?/headers,注意到?My-Added-Header?標(biāo)頭已被添加到請求中了:

$ curl -s -HHost:httpbin.example.com "http://$GATEWAY_URL/headers"
{
"headers": {
"Accept": "*/*",
"Host": "httpbin.example.com",
"My-Added-Header": "added-value", # 添加了一個Header頭
"User-Agent": "curl/7.29.0",
"X-Envoy-Attempt-Count": "1",
"X-Envoy-Internal": "true",
"X-Forwarded-Client-Cert": "By=spiffe://cluster.local/ns/default/sa/httpbin;Hash=11fc66a1c6c9fc44e65ec67f7f8b16d06fbaa73d9729e141e0bd91134dc59db3;Subject=\"\";URI=spiffe://cluster.local/ns/istio-ingress/sa/gateway-istio"
}
}

在上面的示例中,在配置網(wǎng)關(guān)之前,我們并沒有去安裝 Ingress 網(wǎng)關(guān)的 Deployment,因為在默認(rèn)配置中會根據(jù) Gateway 配置自動分發(fā)網(wǎng)關(guān) Deployment 和 Service。但是對于高級別的場景可能還是需要去手動部署。

自動部署

默認(rèn)情況下,每個 Gateway 將自動提供相同名稱的 Service 和 Deployment。如果 Gateway 發(fā)生變化(例如添加了一個新端口),這些配置將會自動更新。這些資源可以通過以下幾種方式進行定義:

apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
name: gateway
spec:
addresses:
- value: 192.0.2.0 # 僅能指定一個地址
type: IPAddress

手動部署

如果您不希望使用自動部署,可以進行手動配置 Deployment 和 Service。完成此選項后,您將需要手動將 Gateway 鏈接到 Service,并保持它們的端口配置同步。

要將 Gateway 鏈接到 Service,需要將?addresses?字段配置為指向單個 Hostname。

apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
name: gateway
spec:
addresses:
- value: ingress.istio-gateways.svc.cluster.local
type: Hostname

當(dāng)然我們這里只是一個最簡單的示例,我們將在后面的課程中繼續(xù)介紹 Gateway API 的更多功能。

配置請求路由

接下來我們來了解下如果通過 Gateway API 將請求動態(tài)路由到微服務(wù)的多個版本。

同樣我們這里以 Bookinfo 示例為例(首先要部署 Bookinfo 應(yīng)用),我們首先將所有流量路由到微服務(wù)的 v1 (版本 1),然后將應(yīng)用規(guī)則根據(jù) HTTP 請求 header 的值路由流量。

首先專門為 Bookinfo 應(yīng)用創(chuàng)建一個?Gateway?資源對象,如下所示:

# bookinfo-gateway.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: bookinfo-gateway
spec:
gatewayClassName: istio
listeners:
- name: http
port: 80
protocol: HTTP
allowedRoutes:
namespaces:
from: Same

上面的 Gateway 資源對象與之前的示例類似,只是這里我們將 allowedRoutes 設(shè)置為 Same,表示允許同一命名空間中的所有路由資源對象都可以連接到這個網(wǎng)關(guān)。

然后為 Productpage 應(yīng)用創(chuàng)建一個?HTTPRoute?資源對象,如下所示:

# productpage-route.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: bookinfo
spec:
parentRefs:
- name: bookinfo-gateway # 引用上面定義的 Gateway 對象
rules:
- matches:
- path:
type: Exact
value: /productpage
- path:
type: PathPrefix
value: /static
- path:
type: Exact
value: /login
- path:
type: Exact
value: /logout
- path:
type: PathPrefix
value: /api/v1/products
backendRefs:
- name: productpage # 引用的后端服務(wù)
port: 9080

和以前 VirtualService 的類似,我們?yōu)?Productpage 應(yīng)用配置了幾個路由規(guī)則,這樣我們就可以在頁面上正常訪問應(yīng)用了。直接應(yīng)用上面的兩個資源清單文件即可:

kubectl apply -f bookinfo-gateway.yaml
kubectl apply -f productpage-route.yaml

同樣當(dāng)我們創(chuàng)建了?Gateway?對象后,會自動在 default 命名空間中部署一個?gateway-istio?的 Deployment 和 對應(yīng)的 Service:

$ kubectl get pods -l istio.io/gateway-name=bookinfo-gateway
NAME READY STATUS RESTARTS AGE
bookinfo-gateway-istio-548556df95-kggs2 1/1 Running 0 112s
$ kubectl get svc bookinfo-gateway-istio
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
bookinfo-gateway-istio LoadBalancer 10.111.86.147 <pending> 15021:30357/TCP,80:30749/TCP 2m19s

正常我們就可以通過上面的?30749?這個 NodePort 端口來訪問 Productpage 應(yīng)用了:

路由到版本 1

同樣的頁面上的評論區(qū)域會出現(xiàn) 3 種不同的狀態(tài),因為我們背后有 3 個不同的版本的評論服務(wù),我們可以創(chuàng)建一個如下所示的?HTTPRoute?資源對象,首先將流量路由到 v1 版本:

# route-reviews-v1.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: reviews
spec:
parentRefs: # 這里我們引用的是 reviews 這個 Service 對象
- group: ""
kind: Service
name: reviews
port: 9080
rules:
- backendRefs:
- name: reviews-v1
port: 9080

注意上面的資源對象中我們是通過 parentRefs 字段引用的是 reviews 這個 Service 對象,而不是 Gateway 對象了,因為我們流量并不是從 Gateway 直接過來的,而是通過 Productpage 訪問 reviews 服務(wù),也就是 Service 對象。然后背后的我們會將流量全部路由到 v1 版本的 reviews 服務(wù) reviews-v1。

現(xiàn)在我們再應(yīng)用上面的這個資源對象:

kubectl apply -f route-reviews-v1.yaml

不過這里需要注意不同于 Istio API 使用?DestinationRule?子集來定義服務(wù)的版本, Kubernetes Gateway API 將為此使用后端 Service 服務(wù)來進行定義。所以我們還需要運行以下命令為三個版本的?reviews?服務(wù)創(chuàng)建后端服務(wù)定義:

kubectl apply -f samples/bookinfo/platform/kube/bookinfo-versions.yaml

然后我們可以通過再次刷新 Bookinfo 應(yīng)用程序,現(xiàn)在我們無論刷新多少次,頁面的評論部分都不會顯示評級星標(biāo),這是因為我們將 Istio 配置為將評論服務(wù)的所有流量路由到版本?reviews:v1,而此版本的服務(wù)不訪問星級評分服務(wù)。

基于用戶身份路由

接下來我們來更改路由配置,將來自特定用戶的所有流量路由到特定服務(wù)版本,比如將來自名為 Jason 的用戶的所有流量被路由到服務(wù) reviews:v2(包含星級評分功能的版本)。

創(chuàng)建一個如下所示的?HTTPRoute?資源對象:

# route-reviews-jason-v2.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: reviews
spec:
parentRefs:
- group: ""
kind: Service
name: reviews
port: 9080
rules:
- matches:
- headers: # 匹配請求頭 end-user: jason
- name: end-user
value: jason
backendRefs:
- name: reviews-v2
port: 9080
- backendRefs:
- name: reviews-v1
port: 9080

在上面的 HTTPRoute 資源對象中,我們將訪問 reviews 的流量分為兩個規(guī)則,第一個規(guī)則匹配請求頭 end-user: jason,然后將流量路由到 reviews-v2 服務(wù),第二個則是如果不匹配將流量路由到 reviews-v1 服務(wù)。

然后我們再次應(yīng)用這個資源對象:

kubectl apply -f route-reviews-jason-v2.yaml

然后我們在 Bookinfo 頁面上面,可以以用戶?jason?身份進行登錄,登錄后無論如何刷新瀏覽器,我們將始終在頁面上看到黑色的星級評分,也就是 reviews 的 v2 版本。

我們也可以切換成其他用戶,或者不登錄,刷新瀏覽器,那么就不會顯示星級評分了。

基于權(quán)重的路由

接下來我們再來測試下基于權(quán)重的路由,常常我們有將流量從微服務(wù)的一個版本逐步遷移到另一個版本的需求,同樣使用 Gateway API 來實現(xiàn)也非常簡單。

下面我們將會把 50% 的流量發(fā)送到 reviews:v1,另外,50% 的流量發(fā)送到 reviews:v3。接著,再把 100% 的流量發(fā)送到 reviews:v3 來完成遷移。

首先重新運行下面的命令將所有流量路由到 review 服務(wù)的 v1 版本。

kubectl apply -f route-reviews-v1.yaml

接下來創(chuàng)建如下所示的資源對象,把 50% 的流量從?reviews:v1?轉(zhuǎn)移到?reviews:v3

# route-reviews-50-v3.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: reviews
spec:
parentRefs:
- group: ""
kind: Service
name: reviews
port: 9080
rules:
- backendRefs:
- name: reviews-v1
port: 9080
weight: 50
- name: reviews-v3
port: 9080
weight: 50

上面的對象中在 backendRefs 中我們使用了一個 weight 字段,用于設(shè)置權(quán)重,這里我們將 reviews-v1 和 reviews-v3 的權(quán)重都設(shè)置為 50,也就是說將流量平均分配到這兩個版本的服務(wù)上。

然后我們應(yīng)用這個資源對象即可:

kubectl apply -f route-reviews-50-v3.yaml

等待幾秒鐘,等待新的規(guī)則傳播到代理中生效,然后我們再次刷新瀏覽器中的 productpage 頁面,大約有 50% 的幾率會看到頁面中帶紅色星級的評價內(nèi)容。 這是因為 reviews 的 v3 版本可以訪問帶星級評價,但 v1 版本不能。

如果你認(rèn)為?reviews:v3?微服務(wù)已經(jīng)穩(wěn)定,那么接下來我們就可以將 100% 的流量路由?reviews:v3

# route-reviews-v3.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: reviews
spec:
parentRefs:
- group: ""
kind: Service
name: reviews
port: 9080
rules:
- backendRefs:
- name: reviews-v3
port: 9080

然后我們再次應(yīng)用這個資源對象:

kubectl apply -f route-reviews-v3.yaml

現(xiàn)在,當(dāng)我們訪問 Bookinfo 應(yīng)用時,將始終看到帶有紅色星級評分的書評。

使用 TLS 暴露服務(wù)

接下來我們來看下如何通過 TLS 來暴露服務(wù),這里我們以 httpbin 示例進行說明。

首先要部署 httpbin 示例:

kubectl apply -f samples/httpbin/httpbin.yaml

然后接下來生成客戶端和服務(wù)器證書和密鑰,這里我們使用 openssl 工具來生成。

首先創(chuàng)建用于服務(wù)簽名的根證書和私鑰:

$ mkdir example_certs1
$ openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=example Inc./CN=example.com' -keyout example_certs1/example.com.key -out example_certs1/example.com.crt

為?httpbin.example.com?創(chuàng)建證書和私鑰:

$ openssl req -out example_certs1/httpbin.example.com.csr -newkey rsa:2048 -nodes -keyout example_certs1/httpbin.example.com.key -subj "/CN=httpbin.example.com/O=httpbin organization"
$ openssl x509 -req -sha256 -days 365 -CA example_certs1/example.com.crt -CAkey example_certs1/example.com.key -set_serial 0 -in example_certs1/httpbin.example.com.csr -out example_certs1/httpbin.example.com.crt

然后接下來需要為入口網(wǎng)關(guān)創(chuàng)建 Secret:

kubectl create -n istio-system secret tls httpbin-credential \
--key=example_certs1/httpbin.example.com.key \
--cert=example_certs1/httpbin.example.com.crt

這里我們創(chuàng)建一個獨立的 Kubernetes Gateway:

# httpbin-gateway.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: mygateway
namespace: istio-system
spec:
gatewayClassName: istio
listeners:
- name: https
hostname: "httpbin.example.com"
port: 443
protocol: HTTPS
tls:
mode: Terminate
certificateRefs:
- name: httpbin-credential
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
kubernetes.io/metadata.name: default

在上面的對象中我們配置了?HTTPS?協(xié)議,并在?tls?中配置使用?Terminate?模式,也就是說我們將在網(wǎng)關(guān)上終止 TLS 連接,然后在?certificateRefs?中引用了之前創(chuàng)建的 Secret 對象,用于配置網(wǎng)關(guān)的憑據(jù)。最后通過?allowedRoutes?字段配置了允許的路由,這里我們配置的是將流量路由到?default?命名空間中的所有路由資源對象。

接下來,通過定義相應(yīng)的?HTTPRoute?配置網(wǎng)關(guān)的入口流量路由:

# httpbin-route.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: httpbin
spec:
parentRefs:
- name: mygateway
namespace: istio-system
hostnames: ["httpbin.example.com"]
rules:
- matches:
- path:
type: PathPrefix
value: /status
- path:
type: PathPrefix
value: /delay
backendRefs:
- name: httpbin
port: 8000

這里最重要的就是在 parentRefs 字段中引用了之前創(chuàng)建的 Gateway 對象。

然后我們直接應(yīng)用上面的兩個資源對象即可:

kubectl apply -f httpbin-gateway.yaml
kubectl apply -f httpbin-route.yaml

同樣應(yīng)用后會自動在 istio-system 命名空間中部署一個?gateway-istio?的 Deployment 和對應(yīng)的 Service:

$ kubectl get pods -n istio-system
NAME READY STATUS RESTARTS AGE
mygateway-istio-64676bfc88-q8nft 1/1 Running 0 32s
$ kubectl get svc -n istio-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
mygateway-istio LoadBalancer 10.111.175.36 <pending> 15021:31206/TCP,443:32597/TCP 74s

然后我們可以通過?32597?這個 NodePort 端口向?httpbin?服務(wù)發(fā)送 HTTPS 請求:

$ curl -v -HHost:httpbin.example.com --resolve "httpbin.example.com:32597:192.168.0.100" --cacert example_certs1/example.com.crt "https://httpbin.example.com:32597/status/418"

* Initializing NSS with certpath: sql:/etc/pki/nssdb
* CAfile: example_certs1/example.com.crt
CApath: none
* SSL connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
* Server certificate:
* subject: O=httpbin organization,CN=httpbin.example.com
* start date: Dec 22 07:13:47 2023 GMT
* expire date: Dec 21 07:13:47 2024 GMT
* common name: httpbin.example.com
* issuer: CN=example.com,O=example Inc.
# ......

-=[ teapot ]=-

_...._
.' _ _ `.
| ." ^ ". _, \_;"---"|// | ;/ \_ _/ """ * Connection #0 to host httpbin.example.com left intact

正常我們就可以看到輸出一個茶壺,這樣我們完成了通過 TLS 來暴露服務(wù)。

TCP 路由

除了 HTTP 路由外,Gateway API 還支持 TCP 和 UDP 路由,配置 TCP 的路由規(guī)則,需要使用單獨的資源對象?TCPRoute,如下所示:

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: tcp-echo-gateway
spec:
gatewayClassName: istio
listeners:
- name: tcp-31400
protocol: TCP
port: 31400
allowedRoutes: # 只允許 TCPRoute 資源對象連接到這個網(wǎng)關(guān)
kinds:
- kind: TCPRoute
---
apiVersion: v1
kind: Service
metadata:
name: tcp-echo-v1
spec:
ports:
- port: 9000
name: tcp
selector:
app: tcp-echo
version: v1
---
apiVersion: v1
kind: Service
metadata:
name: tcp-echo-v2
spec:
ports:
- port: 9000
name: tcp
selector:
app: tcp-echo
version: v2
---
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: TCPRoute # TCPRoute 資源對象
metadata:
name: tcp-echo
spec:
parentRefs: # 引用定義的 Gateway 對象
- name: tcp-echo-gateway
sectionName: tcp-31400
rules:
- backendRefs:
- name: tcp-echo-v1
port: 9000
weight: 80
- name: tcp-echo-v2
port: 9000
weight: 20

其他使用

其他的流量管理比如故障注入、熔斷這些,Gateway API 尚不支持。

但是支持請求超時,比如對 reviews 服務(wù)的調(diào)用增加一個半秒的請求超時,可以使用下面的資源對象:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: reviews
spec:
parentRefs:
- group: ""
kind: Service
name: reviews
port: 9080
rules:
- backendRefs:
- name: reviews-v2
port: 9080
timeouts:
request: 500ms

上面的對象中我們添加了一個 timeouts 字段,用于設(shè)置請求超時時間。

同樣還支持流量鏡像,如下的資源對象:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: httpbin
spec:
parentRefs:
- group: ""
kind: Service
name: httpbin
port: 8000
rules:
- filters:
- type: RequestMirror
requestMirror:
backendRef:
name: httpbin-v2
port: 80
backendRefs:
- name: httpbin-v1
port: 80

在上面的資源對象中我們添加了一個?RequestMirror?過濾器,該過濾器用于將流量鏡像到另外的服務(wù)上去。

本文章轉(zhuǎn)載微信公眾號@k8s技術(shù)圈

上一篇:

NL2SQL之DB-GPT-Hub:text2sql任務(wù)的微調(diào)框架和基準(zhǔn)對比

下一篇:

如何使用 DeepSeek-R1、LangChain和 Ollama 搭建隱私優(yōu)先的RAG系統(tǒng)
#你可能也喜歡這些API文章!

我們有何不同?

API服務(wù)商零注冊

多API并行試用

數(shù)據(jù)驅(qū)動選型,提升決策效率

查看全部API→
??

熱門場景實測,選對API

#AI文本生成大模型API

對比大模型API的內(nèi)容創(chuàng)意新穎性、情感共鳴力、商業(yè)轉(zhuǎn)化潛力

25個渠道
一鍵對比試用API 限時免費

#AI深度推理大模型API

對比大模型API的邏輯推理準(zhǔn)確性、分析深度、可視化建議合理性

10個渠道
一鍵對比試用API 限時免費