Kubernetes Gateway API 入門
使用 Gateway API 為您的 Kubernetes 集群配置入口流量。
無論您是使用 Istio 還是任何服務網格來運行 Kubernetes 應用服務,或者只是在 Kubernetes 集群中使用普通的服務,您都需要為集群外部的客戶端提供對您的應用服務的訪問權限。如果您使用的是普通的 Kubernetes 集群,您可能會使用 Kubernetes Ingress 資源來配置傳入的流量。如果您使用的是 Istio,您更有可能使用 Istio 推薦的配置資源 Gateway 和 VirtualService 來完成這項工作。
一段時間以來,人們都知道 Kubernetes Ingress 資源存在重大缺陷,尤其是在使用它為大型應用程式配置入口流量以及使用 HTTP 以外的協定時。一個問題是它在單一資源中配置了用戶端 L4-L6 屬性(例如,埠、TLS 等)和服務端 L7 路由,對於大型應用程式而言,這些配置應由不同的團隊在不同的命名空間中管理。此外,透過試圖在不同的 HTTP 代理之間尋找共同點,Ingress 只能支援最基本的 HTTP 路由,並最終將現代代理的其他功能推向不可移植的註解。
為了克服 Ingress 的缺點,Istio 引入了自己的入口流量管理配置 API。借助 Istio 的 API,用戶端表示使用 Istio Gateway 資源定義,而 L7 流量則移至 VirtualService,這並非巧合,VirtualService 是用於在網格內部的服務之間路由流量的相同配置資源。儘管 Istio API 為大規模應用程式的入口流量管理提供了一個很好的解決方案,但不幸的是,它是一個僅限於 Istio 的 API。如果您使用的是不同的服務網格實作,或者根本沒有使用任何服務網格,那麼您就沒那麼幸運了。
進入 Gateway API
一個名為 Gateway API 的新 Kubernetes 流量管理 API 引起了很多關注,該 API 最近已升級為 Beta 版。Gateway API 提供了一組 Kubernetes 配置資源,用於入口流量控制,與 Istio 的 API 類似,它克服了 Ingress 的缺點,但與 Istio 的 API 不同的是,它是一個具有廣泛行業共識的標準 Kubernetes API。目前有多個 API 實作正在進行中,包括 Istio 中的 Beta 版實作,所以現在可能是一個很好的時機,開始考慮如何將入口流量配置從 Kubernetes Ingress 或 Istio Gateway/VirtualService 遷移到新的 Gateway API。
無論您是否使用或計劃使用 Istio 來管理您的服務網格,Istio 的 Gateway API 實作都可以輕鬆地用於開始您的集群入口控制。儘管它在 Istio 中仍然是一個 Beta 功能,主要是因為 Gateway API 本身仍然是一個 Beta 級別的 API,但 Istio 的實作非常穩健,因為它在底層使用 Istio 相同的、經過試驗和驗證的內部資源來實作配置。
Gateway API 快速入門
要開始使用 Gateway API,您需要先下載 CRD,它們在大多數 Kubernetes 集群上預設不會安裝,至少目前還沒有。
$ kubectl get crd gateways.gateway.networking.k8s.io &> /dev/null || \
{ kubectl kustomize "github.com/kubernetes-sigs/gateway-api/config/crd?ref=v1.2.0" | kubectl apply -f -; }
安裝 CRD 後,您可以使用它們來建立 Gateway API 資源以配置入口流量,但是為了讓這些資源正常工作,集群需要運行一個網關控制器。您可以透過簡單地安裝具有最小配置文件的 Istio 來啟用 Istio 的網關控制器實作。
$ curl -L https://istio.dev.org.tw/downloadIstio | sh -
$ cd istio-1.24.0
$ ./bin/istioctl install --set profile=minimal -y
您的集群現在將透過 Istio 名為 istio.io/gateway-controller
的網關控制器,完整地實作了 Gateway API,隨時可以使用。
部署 Kubernetes 目標服務
為了試用 Gateway API,我們將使用 Istio helloworld 範例作為入口目標,但僅作為未啟用 Sidecar 注入的簡單 Kubernetes 服務運行。因為我們僅將使用 Gateway API 來控制進入「Kubernetes 集群」的入口流量,所以目標服務是否在網格內部或外部運行沒有區別。
我們將使用以下命令來部署 helloworld 服務
$ kubectl create ns sample
$ kubectl apply -f @samples/helloworld/helloworld.yaml@ -n sample
helloworld 服務包括兩個後端部署,分別對應不同的版本(v1
和 v2
)。我們可以使用以下命令確認它們都在運行
$ kubectl get pod -n sample
NAME READY STATUS RESTARTS AGE
helloworld-v1-776f57d5f6-s7zfc 1/1 Running 0 10s
helloworld-v2-54df5f84b-9hxgww 1/1 Running 0 10s
配置 helloworld 入口流量
在 helloworld 服務啟動並運行後,我們現在可以使用 Gateway API 為其配置入口流量。
入口點是使用 Gateway 資源定義的
$ kubectl create namespace sample-ingress
$ kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
name: sample-gateway
namespace: sample-ingress
spec:
gatewayClassName: istio
listeners:
- name: http
hostname: "*.sample.com"
port: 80
protocol: HTTP
allowedRoutes:
namespaces:
from: All
EOF
將實作 Gateway 的控制器透過引用 GatewayClass 進行選擇。必須在集群中至少定義一個 GatewayClass 才能擁有功能正常的 Gateway。在我們的案例中,我們透過在 Gateway 中使用 gatewayClassName: istio
設定引用其關聯的 GatewayClass(名為 istio
)來選擇 Istio 的網關控制器 istio.io/gateway-controller
。
請注意,與 Ingress 不同,Kubernetes Gateway 不包含對目標服務 helloworld 的任何引用。使用 Gateway API,服務的路由在單獨的配置資源中定義,這些資源附加到 Gateway,以將流量的子集導向特定的服務,例如我們範例中的 helloworld。這種分離允許我們在不同的命名空間中定義 Gateway 和路由,據推測它們由不同的團隊管理。在這裡,當扮演集群運營商的角色時,我們在 sample-ingress
命名空間中套用 Gateway。我們將在下方、在 sample
命名空間中、在 helloworld 服務本身旁邊代表應用程式開發人員新增路由。
由於 Gateway 資源由集群運營商擁有,因此它可以很好地用於為多個團隊的服務提供入口,在我們的案例中,不僅僅是 helloworld 服務。為了強調這一點,我們在 Gateway 中將主機名設定為 *.sample.com
,允許附加多個子網域的路由。
在套用 Gateway 資源後,我們需要等待它準備就緒,然後才能檢索其外部位址
$ kubectl wait -n sample-ingress --for=condition=programmed gateway sample-gateway
$ export INGRESS_HOST=$(kubectl get -n sample-ingress gateway sample-gateway -o jsonpath='{.status.addresses[0].value}')
接下來,我們將 HTTPRoute 附加到 sample-gateway
(即使用 parentRefs
欄位)以公開流量並將流量路由到 helloworld 服務
$ kubectl apply -n sample -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
name: helloworld
spec:
parentRefs:
- name: sample-gateway
namespace: sample-ingress
hostnames: ["helloworld.sample.com"]
rules:
- matches:
- path:
type: Exact
value: /hello
backendRefs:
- name: helloworld
port: 5000
EOF
在這裡,我們已將 helloworld 服務的 /hello
路徑暴露給集群外部的客戶端,特別是透過主機 helloworld.sample.com
。您可以使用 curl 確認 helloworld 範例可存取
$ for run in {1..10}; do curl -HHost:helloworld.sample.com http://$INGRESS_HOST/hello; done
Hello version: v1, instance: helloworld-v1-78b9f5c87f-2sskj
Hello version: v2, instance: helloworld-v2-54dddc5567-2lm7b
Hello version: v1, instance: helloworld-v1-78b9f5c87f-2sskj
Hello version: v2, instance: helloworld-v2-54dddc5567-2lm7b
Hello version: v2, instance: helloworld-v2-54dddc5567-2lm7b
Hello version: v1, instance: helloworld-v1-78b9f5c87f-2sskj
Hello version: v1, instance: helloworld-v1-78b9f5c87f-2sskj
Hello version: v2, instance: helloworld-v2-54dddc5567-2lm7b
Hello version: v1, instance: helloworld-v1-78b9f5c87f-2sskj
Hello version: v2, instance: helloworld-v2-54dddc5567-2lm7b
由於路由規則中未配置任何版本路由,您應該會看到流量的均等分配,大約一半由 helloworld-v1
處理,另一半由 helloworld-v2
處理。
配置基於權重的版本路由
在其他「流量塑形」功能中,您可以使用 Gateway API 將所有流量發送到其中一個版本,或根據請求百分比拆分流量。例如,您可以使用以下規則將 helloworld 流量的 90% 分配到 v1
,10% 分配到 v2
$ kubectl apply -n sample -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
name: helloworld
spec:
parentRefs:
- name: sample-gateway
namespace: sample-ingress
hostnames: ["helloworld.sample.com"]
rules:
- matches:
- path:
type: Exact
value: /hello
backendRefs:
- name: helloworld-v1
port: 5000
weight: 90
- name: helloworld-v2
port: 5000
weight: 10
EOF
Gateway API 依賴於路由目標的特定版本後端服務定義,在此範例中為 helloworld-v1
和 helloworld-v2
。helloworld 範例已經包含 helloworld 版本 v1
和 v2
的服務定義,我們只需要執行以下命令來定義它們
$ kubectl apply -n sample -f @samples/helloworld/gateway-api/helloworld-versions.yaml@
現在,我們可以再次執行先前的 curl 命令
$ for run in {1..10}; do curl -HHost:helloworld.sample.com http://$INGRESS_HOST/hello; done
Hello version: v1, instance: helloworld-v1-78b9f5c87f-2sskj
Hello version: v1, instance: helloworld-v1-78b9f5c87f-2sskj
Hello version: v1, instance: helloworld-v1-78b9f5c87f-2sskj
Hello version: v1, instance: helloworld-v1-78b9f5c87f-2sskj
Hello version: v1, instance: helloworld-v1-78b9f5c87f-2sskj
Hello version: v1, instance: helloworld-v1-78b9f5c87f-2sskj
Hello version: v1, instance: helloworld-v1-78b9f5c87f-2sskj
Hello version: v1, instance: helloworld-v1-78b9f5c87f-2sskj
Hello version: v2, instance: helloworld-v2-54dddc5567-2lm7b
Hello version: v1, instance: helloworld-v1-78b9f5c87f-2sskj
這次我們看到大約十分之九的請求現在由 helloworld-v1
處理,只有大約十分之一的請求由 helloworld-v2
處理。
用於內部網格流量的 Gateway API
您可能已經注意到,我們一直以來都只將 Gateway API 作為入口配置 API 來討論,通常稱為南北流量管理,而不是用於集群內服務到服務(也稱為東西)流量管理的 API。
如果您正在使用服務網格,那麼很希望使用相同的 API 資源來配置入口流量路由和內部流量,這類似於 Istio 使用 VirtualService 來配置兩者的路由規則的方式。幸運的是,Kubernetes Gateway API 正在努力新增此支援。儘管不像用於入口流量的 Gateway API 那樣成熟,但一項稱為 用於網格管理和管理的 Gateway API (GAMMA) 的工作正在進行中,以實現這一目標,並且 Istio 打算將 Gateway API 作為其所有流量管理的預設 API 在未來。
第一個重要的 Gateway 增強提案 (GEP) 最近已被接受,實際上,已經可以在 Istio 中使用。要試用它,您需要使用 Gateway API CRD 的實驗版本,而不是我們上面安裝的標準 Beta 版本,但除此之外,您已經準備就緒。請查看 Istio 請求路由任務以開始使用。
總結
在本文中,我們已經了解如何使用 Istio 的輕量級最小化安裝,為叢集入口流量控制提供 Kubernetes Gateway API 的 Beta 品質實作。對於 Istio 用戶來說,Istio 的實作還讓您開始嘗試在網格內用於東西向流量管理的實驗性 Gateway API 支援。
Istio 的許多文件,包括所有的入口任務和幾個網格內部流量管理任務,已經包含了使用 Gateway API 或 Istio 配置 API 配置流量的平行說明。請查閱Gateway API 任務,以了解更多關於 Istio 中 Gateway API 實作的資訊。