Envoy Gateway HTTP 路由實戰:Host、Path、Header 與流量分流
前兩篇先讓你知道「這套東西是什麼」。這篇開始進入大家最常真的會改的地方:HTTPRoute。
你可以把 HTTPRoute 想成入口流量的分流腳本,只是這次不是寫 Nginx 規則,也不是背 controller annotation,而是用 Kubernetes 原生資源來描述。
先講一個容易混淆的點:這篇只講 HTTPRoute,因為它處理的是 HTTP/HTTPS 語意。如果你是 gRPC,會更偏 GRPCRoute;如果是原始 TCP,則是 TCPRoute;如果是 TLS passthrough,則可能會用 TLSRoute。不要什麼都硬塞成 HTTPRoute,那樣 YAML 會開始長出一股「勉強湊合」的味道。
最常見的路由:看 Host 與 Path
下面這段是最典型的例子:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: app-route
spec:
parentRefs:
- name: eg
hostnames:
- "app.example.com"
rules:
- matches:
- path:
type: PathPrefix
value: /api
backendRefs:
- name: api-service
port: 8080意思是:
- 只有
Host: app.example.com會吃這條 route - 且路徑要符合
/api開頭 - 最後送到
api-service:8080
如果你把 hostnames 拿掉,這條 route 就會更像「只看 path,不看網域」。
matches 到底能匹配什麼
HTTPRoute 最核心的概念之一就是 matches。
你可以把它理解成「什麼樣的請求會命中這條 rule」。
常見可匹配條件有:
pathheadersqueryParamsmethod
例如:
matches:
- method: GET
path:
type: PathPrefix
value: /api
headers:
- name: version
value: v2意思就是:
- 只接受
GET - path 要以
/api開頭 - header 還得帶
version: v2
這種規則非常適合做 API 版本試跑或客戶分批放量。
一條 Route 可以寫多組規則
同一個 HTTPRoute 裡,可以根據不同路徑把流量送去不同服務:
rules:
- matches:
- path:
type: PathPrefix
value: /api
backendRefs:
- name: api-service
port: 8080
- matches:
- path:
type: PathPrefix
value: /admin
backendRefs:
- name: admin-service
port: 8080這很適合一個網域底下切不同子路徑。像:
/api給 API service/admin給後台 service
這種寫法的好處是,一眼就能看出路由邏輯,不像某些老配置檔,讀到一半會懷疑自己是不是在看 RPG 技能樹。
Header、Method、Query 也能當條件
HTTPRoute 不只看 path,還能看 request header、method、query param。
例如只有帶 version: v2 的請求才進新版服務:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: versioned-route
spec:
parentRefs:
- name: eg
rules:
- matches:
- path:
type: PathPrefix
value: /api
headers:
- name: version
value: v2
backendRefs:
- name: api-v2
port: 8080
- matches:
- path:
type: PathPrefix
value: /api
backendRefs:
- name: api-v1
port: 8080這個模式很適合:
- 灰度釋出
- 特定客戶先試用新版本
- 手動驗證新功能
比起直接切一個全新網域,header-based routing 通常更細,也更方便做實驗。
filters:匹配後還能先加工
HTTPRoute 不只是「符合就轉送」,它還能在轉送前後做一些處理,這就是 filters。
常見用法像是加 header:
rules:
- filters:
- type: RequestHeaderModifier
requestHeaderModifier:
add:
- name: x-env
value: prod
backendRefs:
- name: api-service
port: 8080這種做法常見於:
- 往後端補追蹤資訊
- 在入口層加固定 header
- 做 request/response 調整
不過要記得,filter 雖然好用,但它不是拿來把入口層寫成一個巨大神奇中介層的。太多行為堆在 route 上,之後除錯會很想把鍵盤拿去餵水獺。
權重分流:最常見的流量切分
如果你想把 90% 流量送舊版、10% 送新版,可以直接用 weight:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: canary-route
spec:
parentRefs:
- name: eg
hostnames:
- "app.example.com"
rules:
- backendRefs:
- name: api-v1
port: 8080
weight: 90
- name: api-v2
port: 8080
weight: 10這就是最基礎的 canary / traffic splitting。
你不需要一開始就上 service mesh 才能做一點流量實驗。對很多 API 服務來說,這已經夠用了,而且設定乾淨很多。
💡
weight是相對值,不一定非得加到 100。9和1也能表達一樣的比例。
timeouts:不要讓請求掛到天荒地老
HTTPRoute 也能在 rule 層設定 timeout。這點很重要,因為入口層如果沒有邊界,爛請求就會像奧客一樣坐著不走。
rules:
- matches:
- path:
type: PathPrefix
value: /reports
timeouts:
request: 10s
backendRequest: 2s
backendRefs:
- name: report-service
port: 8080可以這樣理解:
request:整個 client 請求最多等多久backendRequest:Gateway 跟後端單次請求最多等多久
通常 backendRequest 不能比 request 還大,不然邏輯上就很像說「整趟旅程 10 分鐘內要到,但其中一段公車我可以等 20 分鐘」,宇宙會先困惑。
兩個常見細節
1. parentRefs 可以指定 listener
如果一個 Gateway 有多個 listener,你可以只綁其中一個:
parentRefs:
- name: eg
sectionName: http這代表這條 route 只掛到 http 這個 listener。
對有多個入口設定的場景,這會比全部亂掛更可控。
2. 沒寫 matches,等於全匹配
如果某條 rule 沒寫 matches,預設效果接近匹配 /。
也就是說,它很像預設規則,所有請求都可能進來。
這很方便,但也很容易蓋掉你以為更精準的規則,所以不要因為偷懶少寫,最後把自己坑進去。
驗證路由有沒有生效
路由配置完,可以直接用 curl 驗證:
curl -H "Host: app.example.com" http://$GATEWAY_HOST/api/users
curl -H "Host: app.example.com" -H "version: v2" http://$GATEWAY_HOST/api/users如果你想看 HTTPRoute 狀態:
kubectl get httproute app-route -o yaml特別注意:
status.parentsAcceptedcondition
有時候 YAML 看起來很正確,但 Gateway 沒接受,那就像投履歷沒回信,不能當成入職成功。
一句話總結
HTTPRoute 的核心就是「先匹配,再轉送」。你掌握了 host、path、header、weight,日常 80% 的 HTTP 入口需求就差不多能處理。
下一步
下一篇來處理正式上線一定會碰到的事:HTTPS、憑證、TLS 終止與安全設定: 👉 TLS 與安全