在 Day6 中,我們學會了用 Ingress 統一管理微服務的入口流量,透過 Path 與 Host 將請求導向不同的 Service。
但隨著流量管理需求越來越複雜,Ingress 的一些限制也逐漸浮現:
大量依賴 annotations、不同 Controller 的設定方式不一致、進階路由缺乏統一標準、角色與權限不容易拆分……
雖然不同 Ingress Controller 可以透過 annotations 或自訂功能實現 Header Routing、權重分流等能力,但寫法往往各不相同。
為了解決這些問題,Kubernetes 社群推出了 Gateway API —— 一套更具擴充性與角色分工能力的流量管理 API,也被視為 Ingress 的下一代方案。
今天內容包含:
以下操作皆在 master 節點執行。
回顧 Day6,Ingress 可以處理基本的 Host 與 Path Routing,但當需求變複雜後,就會遇到一些限制:
| 問題 | 說明 |
|---|---|
| 高度依賴 Annotations | Rewrite、限流、CORS 等進階功能常需要 Controller 自訂 Annotations,不同 Controller 的寫法可能不同 |
| 角色職責不容易拆分 | 基礎設施入口與應用路由通常集中在 Ingress Resource 中,平台團隊與應用團隊較難分工管理 |
| 進階路由缺乏統一標準 | Header Matching、權重分流等功能,過去常依賴不同 Controller 的自訂能力 |
| 跨 Namespace 管理較受限 | Ingress Backend 通常位於同一個 Namespace,跨 Namespace 能力往往依賴 Controller 額外實作 |
| 不同實作存在差異 | 雖然 Ingress API 相同,但部分進階功能與行為仍可能因 Controller 而不同 |
Gateway API 的設計,就是希望把這些需求變成更標準化、可擴充的 Kubernetes API。
Gateway API 很重要的一個設計概念就是 角色分離(Role-Oriented Design)。
不同角色負責不同層級的資源,讓基礎設施與應用路由可以分開管理。

| 角色 | 負責的資源 | 職責 |
|---|---|---|
| 基礎設施供應者 | GatewayClass | 定義 Gateway 類型,並指定由哪個 Gateway Controller 管理 |
| 叢集管理員 | Gateway | 建立實際的流量入口,設定 Listener、Port、Protocol、TLS,以及允許哪些 Route 掛載 |
| 應用開發者 | HTTPRoute | 定義應用程式的 Host、Path、Header 與 Backend 等路由規則 |
💡 Ingress vs Gateway API 的本質差異
Ingress 通常把入口與路由設定集中在同一種 Resource 中。
Gateway API 則把 基礎設施入口(Gateway) 與 應用路由(Route) 拆開,讓不同角色可以各自管理自己負責的部分。
這種設計在多人團隊與多租戶環境中特別有用。
Gateway API 的核心架構可以先理解成:

其中 HTTPRoute 透過 parentRefs 附加到 Gateway,再依照 Host、Path、Header 等規則,將符合條件的流量轉送到對應的 Backend(通常是 Service)。
| 資源 | 作用 | 對比 Ingress |
|---|---|---|
| GatewayClass | 定義 Gateway 類型,並指定由哪個 Controller 管理 | 類似 IngressClass |
| Gateway | 定義 Listener、Port、Protocol、TLS 等入口設定 | Ingress 沒有完全對應的獨立資源 |
| HTTPRoute | 定義 Host、Path、Header 等路由規則,並指定 Backend | 類似 Ingress 的 rules,但能力更完整 |
| Route 類型 | 用途 | 狀態 |
|---|---|---|
| HTTPRoute | HTTP / HTTPS 路由 | GA |
| GRPCRoute | gRPC 路由 | GA |
| TLSRoute | 基於 SNI 等 TLS 資訊進行路由 | GA |
| TCPRoute | TCP 流量路由 | 🧪 Experimental |
| UDPRoute | UDP 流量路由 | 🧪 Experimental |
💡 Gateway API 的穩定度
GatewayClass、Gateway、HTTPRoute、GRPCRoute、TLSRoute都已進入 Standard Channel。
TCPRoute與UDPRoute目前仍屬 Experimental Channel。
Gateway API 本身是透過 CRD(CustomResourceDefinition) 提供 API,再由對應的 Gateway Controller 實際處理這些資源。
安裝方式會依 Controller 而不同:
因此安裝前應先確認所使用 Controller 的官方文件。
⚠️ 注意 CRD 與 Controller 的版本相容性
Gateway Controller 必須支援叢集中安裝的 Gateway API CRD 版本。
如果 Controller 啟動時 CRD 尚未存在,可能暫時無法監看或處理 Gateway API 資源;CRD 安裝完成後,通常應再確認 Controller 是否正常 reconcile。
Gateway API 的 CRD 由 Kubernetes SIG Network 統一定義,提供 GatewayClass、Gateway、HTTPRoute 等標準 Resource。
本篇實作統一使用 Gateway API v1.2.1:
kubectl apply --server-side -f \
https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.1/standard-install.yaml
安裝完成後確認 CRD:
kubectl get crd | grep gateway
應該可以看到類似:

💡 版本提醒
本篇截圖與實作皆以 Gateway API
v1.2.1為基準。Gateway API 後續版本仍持續更新,實際使用時請再確認 Gateway Controller 支援的版本。
Gateway API 只定義 API 規格,本身不會實際處理流量,因此還需要安裝對應的 Gateway Controller。
常見實作包括:
| Controller | 底層技術 | 特色 | 適合環境 |
|---|---|---|---|
| Envoy Gateway | Envoy Proxy | 專注於 Gateway API,安裝與使用方式相對直接 | 通用環境 |
| Cilium | eBPF + Envoy | 同時提供 CNI、網路安全與 Gateway API | 已使用 Cilium 的叢集 |
| NGINX Gateway Fabric | NGINX | NGINX 官方的 Gateway API 實作 | 熟悉 NGINX 的團隊 |
| Istio | Envoy | 可與 Service Mesh 整合 | 已使用 Istio 的叢集 |
本篇使用 Envoy Gateway。
為了和後續實作與截圖保持一致,本篇固定使用 Envoy Gateway v1.3.0:
helm install eg oci://docker.io/envoyproxy/gateway-helm \
--version v1.3.0 \
-n envoy-gateway-system \
--create-namespace
安裝完成後確認 Controller 是否正常:
kubectl get pods -n envoy-gateway-system
💡 版本提醒
本篇使用 Gateway API
v1.2.1與 Envoy Gatewayv1.3.0進行實作。這些版本並非目前最新版本,實際部署時應確認 Gateway API 與 Controller 的版本相容性。
安裝 Envoy Gateway Controller 後,還需要建立一個 GatewayClass,告訴 Kubernetes 這類 Gateway 要由哪個 Controller 管理。
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: eg
spec:
controllerName: gateway.envoyproxy.io/gatewayclass-controller
建立 GatewayClass:
kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: eg
spec:
controllerName: gateway.envoyproxy.io/gatewayclass-controller
EOF
確認狀態:
kubectl get gatewayclass
正常情況下,可以看到:

⚠️
controllerName必須對應到正確的 ControllerEnvoy Gateway v1.3 預設使用:
gateway.envoyproxy.io/gatewayclass-controller如果
controllerName填錯,Envoy Gateway 就不會管理這個 GatewayClass,狀態也不會正常進入Accepted=True。可以查看 Envoy Gateway 的設定確認實際使用的 Controller Name。
先建立兩個簡單的後端服務(延用 Day6 的概念):
vim gateway-test-apps.yaml
# App A
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-a
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: app-a
template:
metadata:
labels:
app: app-a
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
lifecycle:
postStart:
exec:
command: ["/bin/sh", "-c", "echo 'Hello from App A (via Gateway API)' > /usr/share/nginx/html/index.html"]
---
apiVersion: v1
kind: Service
metadata:
name: app-a
namespace: default
spec:
selector:
app: app-a
ports:
- port: 80
---
# App B
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-b
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: app-b
template:
metadata:
labels:
app: app-b
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
lifecycle:
postStart:
exec:
command: ["/bin/sh", "-c", "echo 'Hello from App B (via Gateway API)' > /usr/share/nginx/html/index.html"]
---
apiVersion: v1
kind: Service
metadata:
name: app-b
namespace: default
spec:
selector:
app: app-b
ports:
- port: 80
kubectl apply -f gateway-test-apps.yaml
# 確認 Pod 和 Service 都正常
kubectl get pods -l 'app in (app-a, app-b)'
kubectl get svc app-a app-b

這是叢集管理員的工作 — 定義流量入口的監聽設定:
vim my-gateway.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: my-gateway
namespace: default
spec:
gatewayClassName: eg # 使用 Envoy Gateway 提供的 GatewayClass
listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: Same # 只允許同 Namespace 的 Route 掛載
kubectl apply -f my-gateway.yaml
# 確認 Gateway 狀態
kubectl get gateway my-gateway
在沒有 LoadBalancer 實作的裸機環境中,可能會看到:

⚠️ 裸機環境可能出現
PROGRAMMED=FalseEnvoy Gateway 預設會建立
LoadBalancer類型的 Envoy Proxy Service。如果裸機叢集沒有 MetalLB 等 LoadBalancer 實作,就可能因為無法取得 Address,而讓 Gateway 顯示:
Programmed: False Reason: AddressNotAssigned可以使用:
kubectl describe gateway my-gateway查看
Accepted、Programmed、Listener 等 Condition,確認實際狀態。如果需要在裸機環境取得 LoadBalancer IP,可以搭配 MetalLB;也可以依環境改用 NodePort 或 Port Forward 進行測試。
💡 Gateway 建立後發生了什麼?
Envoy Gateway 會根據 Gateway 設定建立 Envoy Proxy 的資料面資源,實際負責接收與轉送流量。
可以查看:
kubectl get pods -n envoy-gateway-system kubectl get svc -n envoy-gateway-system
這是應用開發者的工作 — 只需要關心路由規則:
vim my-httproute.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: my-route
namespace: default
spec:
parentRefs:
- name: my-gateway # 掛載到哪個 Gateway
rules:
- matches:
- path:
type: PathPrefix
value: /app-a
filters:
- type: URLRewrite
urlRewrite:
path:
type: ReplacePrefixMatch
replacePrefixMatch: /
backendRefs:
- name: app-a
port: 80
- matches:
- path:
type: PathPrefix
value: /app-b
filters:
- type: URLRewrite
urlRewrite:
path:
type: ReplacePrefixMatch
replacePrefixMatch: /
backendRefs:
- name: app-b
port: 80
kubectl apply -f my-httproute.yaml
# 確認 HTTPRoute 狀態
kubectl get httproute my-route

先取得 Gateway 的存取方式:
# 取得 Envoy Gateway 的 Service
kubectl get svc -n envoy-gateway-system
# 如果是 NodePort 類型,取得 NodePort
GATEWAY_PORT=$(kubectl get svc -n envoy-gateway-system \
-l gateway.envoyproxy.io/owning-gateway-name=my-gateway \
-o jsonpath='{.items[0].spec.ports[?(@.name=="http")].nodePort}')
echo "Gateway NodePort: ${GATEWAY_PORT}"
先查看 Envoy Gateway 建立的 Service:
kubectl get svc -n envoy-gateway-system
在這個 Lab 環境中,可以直接使用 Envoy Proxy Service 的 ClusterIP 測試:
# App A
curl http://10.103.239.156/app-a
# → Hello from App A (via Gateway API)
# App B
curl http://10.103.239.156/app-b
# → Hello from App B (via Gateway API)

💡 跟 Ingress 比較一下
這裡的 Path Rewrite 不需要使用 Controller 專屬 Annotation,而是透過 Gateway API 標準定義的
URLRewriteFilter。不過
URLRewrite屬於 Extended Feature,實際使用前仍要確認 Gateway Controller 是否支援。
Gateway API 原生支援許多 Ingress 做不到(或需要靠 annotation 才能做)的功能。
根據請求的 HTTP Header 來決定路由:
vim header-route.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: header-route
namespace: default
spec:
parentRefs:
- name: my-gateway
rules:
- matches:
- headers:
- name: x-version
value: v2
backendRefs:
- name: app-b # Header 帶 x-version: v2 → 導到 app-b
port: 80
- backendRefs:
- name: app-a # 其他流量 → 導到 app-a(預設)
port: 80
kubectl apply -f header-route.yaml
# 不帶 Header → app-a
curl http://<NODE_IP>:${GATEWAY_PORT}/
# → Hello from App A
# 帶 Header → app-b
curl -H "x-version: v2" http://<NODE_IP>:${GATEWAY_PORT}/
# → Hello from App B

將流量按比例分配到不同後端 — 這是金絲雀發布的基礎:
vim weight-route.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: weight-route
namespace: default
spec:
parentRefs:
- name: my-gateway
rules:
- matches:
- path:
type: PathPrefix
value: /canary
filters:
- type: URLRewrite
urlRewrite:
path:
type: ReplacePrefixMatch
replacePrefixMatch: /
backendRefs:
- name: app-a
port: 80
weight: 90 # 90% 流量給 app-a(穩定版)
- name: app-b
port: 80
weight: 10 # 10% 流量給 app-b(金絲雀版)
kubectl apply -f weight-route.yaml
# 多打幾次,觀察回應比例
for i in $(seq 1 20); do
curl -s http://<NODE_IP>:${GATEWAY_PORT}/canary
done

會發現大約 90% 的回應來自 App A,10% 來自 App B。
⚠️ 在 Ingress 中做權重分流
以 NGINX Ingress Controller 為例,通常需要搭配
nginx.ingress.kubernetes.io/canary等 annotations,並建立額外的 Ingress 資源。Gateway API 則可以直接在同一個
HTTPRoute裡透過backendRefs.weight設定流量比例,寫法更集中,也更標準化。
Gateway API 支援跨 Namespace 引用 Backend,但需要由目標 Namespace 明確授權。
例如:

因為 HTTPRoute 和 Service 位於不同 Namespace,所以需要在 backend-ns 建立 ReferenceGrant。
以下是概念範例:
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
name: allow-default-routes
namespace: backend-ns
spec:
from:
- group: gateway.networking.k8s.io
kind: HTTPRoute
namespace: default
to:
- group: ""
kind: Service
💡 ReferenceGrant 的安全設計
跨 Namespace 的 Backend Reference 必須由目標 Namespace 主動授權。
沒有對應的
ReferenceGrant,HTTPRoute 對另一個 Namespace Service 的引用就是無效的。
| 比較項目 | Ingress | Gateway API |
|---|---|---|
| 設計理念 | 以單一 Ingress Resource 管理入口與路由 | 角色分離:GatewayClass → Gateway → Route |
| 路由能力 | 標準 API 主要支援 Host + Path | 支援 Host、Path、Header、QueryParam、Method 等更完整的匹配能力 |
| 權重分流 | 通常依賴 Controller 自訂功能或 annotations | 標準支援 backendRefs.weight |
| TLS 設定 | TLS 與路由設定集中在 Ingress 中 | Listener / TLS 主要由 Gateway 管理 |
| 跨 Namespace | 標準 Ingress Backend 主要位於同 Namespace | 支援跨 Namespace Reference,並透過 ReferenceGrant 授權 |
| 進階功能 | 常依賴 Controller-specific annotations / CRD | 更多能力直接由 Gateway API 標準欄位定義 |
| 協定支援 | 主要針對 HTTP / HTTPS | HTTP、gRPC、TLS、TCP、UDP 等 |
| 角色分工 | 平台與應用設定較容易混在一起 | Gateway 與 Route 可以由不同角色管理 |
| API 狀態 | GA,但 API 已 Frozen,不再新增功能 | Standard API 持續演進,功能仍在擴充 |
💡 該用哪個?
- 新專案:可以優先評估 Gateway API
- 現有 Ingress 運作良好:不需要為了更換而立即遷移
- 需要 Header Routing、權重分流、跨 Namespace 或角色分工:Gateway API 通常更適合
Kubernetes 官方目前推薦使用 Gateway,而不是在新設計中繼續擴充 Ingress;不過 Ingress API 本身沒有移除計畫。
今天我們學會了 Kubernetes 更現代的流量入口管理方式 —— Gateway API。
相比 Ingress,Gateway API 把基礎設施入口與應用路由拆開,提供更清楚的角色分工,以及更標準化的進階路由能力。
| 學到的東西 | 一句話總結 |
|---|---|
| 設計理念 | 角色分離 —— GatewayClass、Gateway、Route 由不同角色負責 |
| 核心資源 | GatewayClass 定義類型、Gateway 定義入口、HTTPRoute 定義路由 |
| 路由能力 | 支援 Path、Host、Header 等匹配方式,也能做權重分流 |
| 跨 Namespace | 透過 ReferenceGrant 控制跨 Namespace Backend Reference |
| 標準化 | 更多能力直接由 Gateway API 欄位定義,減少對 Controller-specific annotations 的依賴 |
| 與 Ingress 的關係 | Gateway API 是更現代的下一代方案,現有 Ingress 仍可繼續使用 |
一路從 Service → Ingress → Gateway API,我們已經從單純暴露服務,逐步學到如何集中管理入口流量、拆分平台與應用職責,並處理更進階的路由需求。
下一篇我們將進入 CRD 與 Operator —— 學習 Kubernetes 如何擴展自己的 API,建立自訂資源與控制邏輯!