Gateway API 是 Kubernetes 中的一個 API 資源集合,包括?GatewayClass、Gateway、HTTPRouteTCPRoute、Service?等,這些資源共同為各種網絡用例構建模型

Gateway API

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

Gateway API 的改進比當前的 Ingress 資源對象有很多更好的設計:

還有一些其他值得關注的功能:

面向角色設計

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

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

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

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

概念

在整個 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,這樣加密的字節流將傳遞到后端。

GRPCRoute

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

組合

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

使用反向代理實現的網關的典型客戶端/網關 API 請求流程如下所示:

與 Istio API 的區別

我們這里主要是講解 Gateway API 在服務網格中的使用,首先我們先了解下 Gateway API 與 Istio API 的區別。

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

新的 Gateway API 致力于從 Kubernetes 的各種 Ingress 實現(包括 Istio)中吸取經驗,以構建標準化的,獨立于供應商的 API。這些 API 通常與 Istio Gateway 和 VirtualService 具有相同的用途,但依然有一些不同的地方:

實現

接下來我們就來了解下如何在 Istio z 中使用 Gateway API。默認情況下 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、Gateway、HTTPRoute?和?ReferenceGrant,由于 Istio 已經對 Gateway API 提供了支持,所以現在我們就可以直接使用了。比如現在就會自動創建一個?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 集群,所以我們可以直接使用默認的 istio 這個 GatewayClass,如果你有多個 Istio 集群,那么你可以創建多個 GatewayClass 來區分不同的集群。

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

kubectl apply -f samples/httpbin/httpbin.yaml

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

# default-gateway.yaml
apiVersion: gateway.networking.k8s.io/v1
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 # 允許所有命名空間

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

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

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

kubectl create namespace istio-ingress

然后直接應用這個資源對象即可:

$ 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 默認的 istio-ingressgateway 的 Deployment,他們的配置幾乎是一樣的。

接下來我們就需要去創建一個路由規則了,也就是想要如何訪問我們的 httpbin 應用,類似于 VirtualService,我們可以使用?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: # 具體的路由規則
- matches:
- path:
type: PathPrefix
value: /get # 匹配 /get 的請求
backendRefs: # 引用的后端服務
- name: httpbin
port: 8000

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

同樣直接應用該資源對象即可:

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

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

# $ 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 服務:

$ 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 標志可以將 Host HTTP 標頭設置為 httpbin.example.com。這一步是必需的,因為 HTTPRoute 已配置為處理 httpbin.example.com 的請求,但是在測試環境中,該主機沒有 DNS 綁定,只是將請求發送到入口 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 - -

證明我們的路由規則已經生效了。

同樣如果我們想要能夠正常訪問刀?/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: # 具體的路由規則
- matches:
- path:
type: PathPrefix
value: /get # 匹配 /get 的請求
- path:
type: PathPrefix
value: /headers # 匹配 /headers 的請求
filters:
- type: RequestHeaderModifier # 添加一個修改請求頭的過濾器
requestHeaderModifier:
add: # 添加一個標頭
- name: my-added-header
value: added-value
backendRefs: # 引用的后端服務
- name: httpbin
port: 8000

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

$ 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"
}
}

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

自動部署

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

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

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

配置請求路由

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

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

首先專門為 Bookinfo 應用創建一個?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 設置為 Same,表示允許同一命名空間中的所有路由資源對象都可以連接到這個網關。

然后為 Productpage 應用創建一個?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 # 引用的后端服務
port: 9080

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

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

同樣當我們創建了?Gateway?對象后,會自動在 default 命名空間中部署一個?gateway-istio?的 Deployment 和 對應的 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 應用了:

路由到版本 1

同樣的頁面上的評論區域會出現 3 種不同的狀態,因為我們背后有 3 個不同的版本的評論服務,我們可以創建一個如下所示的?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 服務,也就是 Service 對象。然后背后的我們會將流量全部路由到 v1 版本的 reviews 服務 reviews-v1

現在我們再應用上面的這個資源對象:

kubectl apply -f route-reviews-v1.yaml

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

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

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

基于用戶身份路由

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

創建一個如下所示的?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 的流量分為兩個規則,第一個規則匹配請求頭 end-user: jason,然后將流量路由到 reviews-v2 服務,第二個則是如果不匹配將流量路由到 reviews-v1 服務。

然后我們再次應用這個資源對象:

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

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

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

基于權重的路由

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

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

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

kubectl apply -f route-reviews-v1.yaml

接下來創建如下所示的資源對象,把 50% 的流量從?reviews:v1?轉移到?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 字段,用于設置權重,這里我們將 reviews-v1 和 reviews-v3 的權重都設置為 50,也就是說將流量平均分配到這兩個版本的服務上。

然后我們應用這個資源對象即可:

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

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

如果你認為?reviews:v3?微服務已經穩定,那么接下來我們就可以將 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

然后我們再次應用這個資源對象:

kubectl apply -f route-reviews-v3.yaml

現在,當我們訪問 Bookinfo 應用時,將始終看到帶有紅色星級評分的書評。

使用 TLS 暴露服務

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

首先要部署 httpbin 示例:

kubectl apply -f samples/httpbin/httpbin.yaml

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

首先創建用于服務簽名的根證書和私鑰:

$ 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?創建證書和私鑰:

$ 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

然后接下來需要為入口網關創建 Secret:

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

這里我們創建一個獨立的 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?協議,并在?tls?中配置使用?Terminate?模式,也就是說我們將在網關上終止 TLS 連接,然后在?certificateRefs?中引用了之前創建的 Secret 對象,用于配置網關的憑據。最后通過?allowedRoutes?字段配置了允許的路由,這里我們配置的是將流量路由到?default?命名空間中的所有路由資源對象。

接下來,通過定義相應的?HTTPRoute?配置網關的入口流量路由:

# 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 字段中引用了之前創建的 Gateway 對象。

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

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

同樣應用后會自動在 istio-system 命名空間中部署一個?gateway-istio?的 Deployment 和對應的 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?服務發送 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 來暴露服務。

TCP 路由

除了 HTTP 路由外,Gateway API 還支持 TCP 和 UDP 路由,配置 TCP 的路由規則,需要使用單獨的資源對象?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 資源對象連接到這個網關
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 服務的調用增加一個半秒的請求超時,可以使用下面的資源對象:

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 字段,用于設置請求超時時間。

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

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?過濾器,該過濾器用于將流量鏡像到另外的服務上去。

本文章轉載微信公眾號@k8s技術圈

上一篇:

NL2SQL之DB-GPT-Hub:text2sql任務的微調框架和基準對比

下一篇:

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

我們有何不同?

API服務商零注冊

多API并行試用

數據驅動選型,提升決策效率

查看全部API→
??

熱門場景實測,選對API

#AI文本生成大模型API

對比大模型API的內容創意新穎性、情感共鳴力、商業轉化潛力

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

#AI深度推理大模型API

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

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