antony@notes:~/kubernetes$ cat "Cilium-Gateway-API.md"
Cilium Gateway API
Cilium Gateway API — Host Network Mode 部署手冊
適用環境: RKE2 bare-metal / VM + 外部 F5 / Nginx L4 LB Envoy 模式: Standalone DaemonSet(Cilium 1.16+ 預設) 目標: 讓 Cilium Gateway API 透過 hostNetwork 直接在 worker node 上監聽 80/443,由外部 LB 分流
架構總覽

流量路徑說明:
- 外部 Client 發出請求
- F5 / Nginx (L4 LB) 將流量分發到 3 台標記為
role=gateway的 worker node IP - 每台 worker node 上的 Cilium Envoy 以 hostNetwork 模式監聽 80/443
- Cilium 的 eBPF 透過 TPROXY 機制將封包透明轉發給 Envoy
- Gateway API 的 HTTPRoute 根據 host / path 規則路由到對應的後端 K8s Service
前置條件
- Cilium 版本 >= 1.16(hostNetwork 模式自 1.16 起支援)
- Gateway API CRDs 已安裝
kubeProxyReplacement已啟用- 至少 3 台 worker node 可供對外使用
Step 0:事前確認與備份
確認 Cilium 版本:
cilium version
helm list -n kube-system | grep cilium匯出目前的 Helm values 備份(重要,萬一需要 rollback):
helm get values cilium -n kube-system -o yaml > cilium-backup-values.yamlStep 1:為 Worker Node 打上 Label
選擇要對外的 3 台 worker node,為它們打上 role=gateway label:
kubectl label node <worker-node-1> role=gateway
kubectl label node <worker-node-2> role=gateway
kubectl label node <worker-node-3> role=gateway驗證 label 是否正確:
kubectl get nodes -l role=gateway預期輸出:
NAME STATUS ROLES AGE VERSION
worker-node-1 Ready <none> 30d v1.xx.x
worker-node-2 Ready <none> 30d v1.xx.x
worker-node-3 Ready <none> 30d v1.xx.xStep 2:準備 Helm Values 檔案
建立 cilium-gateway-hostnetwork-values.yaml:
# ── Gateway API 設定 ──────────────────────────────────────────
gatewayAPI:
enabled: true
hostNetwork:
enabled: true
# 只在有 role=gateway label 的 node 上暴露 listener
# 如果要所有 node 都暴露,移除 nodes 區塊即可
nodes:
matchLabels:
role: gateway
# ── Envoy (Standalone DaemonSet 模式) ────────────────────────
envoy:
enabled: true
securityContext:
capabilities:
# 允許 Envoy bind 80/443 等 privileged port
keepCapNetBindService: true
envoy:
- NET_ADMIN
- PERFMON
- BPF
- NET_BIND_SERVICE
# ── 基礎設定(通常已在現有 values 中,僅供確認)──────────────
kubeProxyReplacement: true重點說明:
gatewayAPI.hostNetwork.enabled: true:啟用後會自動停用 LoadBalancer type Service,兩者互斥(Cilium 不再為 Gateway 建立 LoadBalancer type Service,改為直接在 host network 上監聽。)gatewayAPI.hostNetwork.nodes.matchLabels:控制只在指定 node 上暴露 listenerNET_BIND_SERVICEcapability:允許 Envoy bind 80/443 等 1024 以下的 privileged port
Step 3:執行 Helm Upgrade
helm upgrade cilium cilium/cilium \
--namespace kube-system \
--reuse-values \
-f cilium-gateway-hostnetwork-values.yaml注意:
--reuse-values會保留現有的所有設定,只覆蓋 values 檔案中指定的項目。
Step 4:重啟 Cilium 元件
Helm upgrade 後需重啟相關元件使設定生效:
kubectl -n kube-system rollout restart deployment/cilium-operator
kubectl -n kube-system rollout restart daemonset/cilium
kubectl -n kube-system rollout restart daemonset/cilium-envoy等待所有 Pod 就緒:
kubectl -n kube-system rollout status deployment/cilium-operator --timeout=120s
kubectl -n kube-system rollout status daemonset/cilium --timeout=120s
kubectl -n kube-system rollout status daemonset/cilium-envoy --timeout=120s確認 Cilium 整體狀態正常:
cilium statusStep 5:安裝 Gateway API CRDs
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.4.1/config/crd/standard/gateway.networking.k8s.io_gatewayclasses.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.4.1/config/crd/standard/gateway.networking.k8s.io_gateways.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.4.1/config/crd/standard/gateway.networking.k8s.io_httproutes.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.4.1/config/crd/standard/gateway.networking.k8s.io_referencegrants.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.4.1/config/crd/standard/gateway.networking.k8s.io_grpcroutes.yaml確認 GatewayClass 已存在:
kubectl get gatewayclass執行結果:
NAME CONTROLLER ACCEPTED AGE
cilium io.cilium/gateway-controller True 3m52sStep 6:部署 Demo App
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.11/samples/bookinfo/platform/kube/bookinfo.yaml檢視 Pods
$ kubectl get pods執行結果:
NAME READY STATUS RESTARTS AGE
details-v1-75c85494bb-b76bs 1/1 Running 0 36s
productpage-v1-7c9bf9c988-f7cpf 1/1 Running 0 36s
ratings-v1-6456c55c66-7fczr 1/1 Running 0 36s
reviews-v1-545b8ff7c8-4fdxk 1/1 Running 0 36s
reviews-v2-5f6c87ccdb-68ws8 1/1 Running 0 36s
reviews-v3-74fb78c454-2s6ng 1/1 Running 0 36sStep 7:Deploy the Cilium Gateway
⚠️ 關於
spec.addresses欄位: 由於 Issue #42786 的緣故,hostNetwork 模式下此欄位不會生效也不會被填入 Gateway status。以下 YAML 中保留此欄位僅作為「此 Gateway 實際對外的 node IP」的文件化註記,Cilium 並不會使用它來分配位址。
cat <<'EOF' | kubectl apply -f -
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: my-gateway
spec:
gatewayClassName: cilium
addresses:
- type: IPAddress
value: "192.168.11.173"
- type: IPAddress
value: "192.168.11.174"
- type: IPAddress
value: "192.168.11.175"
listeners:
- protocol: HTTP
port: 80
name: web-gw
allowedRoutes:
namespaces:
from: Same
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: http-app-1
spec:
parentRefs:
- name: my-gateway
namespace: default
rules:
- matches:
- path:
type: PathPrefix
value: /details
backendRefs:
- name: details
port: 9080
- matches:
- headers:
- type: Exact
name: magic
value: foo
queryParams:
- type: Exact
name: great
value: example
path:
type: PathPrefix
value: /
method: GET
backendRefs:
- name: productpage
port: 9080
EOFStep 8:驗證端到端連通
⚠️ 驗證方法變更: 由於 Issue #42786,Gateway 層的
Programmed條件會永遠是False,因此不能用它來判定部署成功。請改用以下方法驗證。
8.1 檢查 Gateway 與 Listener 狀態(預期輸出已修正)
kubectl get gateway my-gateway
kubectl describe gateway my-gateway預期輸出(受 Issue #42786 影響):
NAME CLASS ADDRESS PROGRAMMED AGE
my-gateway cilium False 1m在 kubectl describe gateway 的輸出中,請確認以下條件皆為 True(這些才是部署成功的實際指標):
| 層級 | Condition Type | 預期 Status | 預期 Reason |
|---|---|---|---|
| Gateway | Accepted | True | Accepted |
| Listener | Accepted | True | Accepted |
| Listener | ResolvedRefs | True | ResolvedRefs |
| Gateway | Programmed | False(Known Issue,忽略) | AddressNotAssigned |
| Listener | Programmed | False(Known Issue,忽略) | Pending |
只要上表中 Accepted 與 ResolvedRefs 為 True,即代表 Gateway 設定被成功接受與解析,可以進入下一步實際連通驗證。
8.2 確認 Envoy 已在 Gateway node 上監聽
SSH 到任一 gateway node,確認 Envoy 有在監聽 80 port:
ss -tlnp | grep ':80'應該可以看到 cilium-envoy 程序正在監聽 0.0.0.0:80。
8.3 從外部實測 HTTP 連通性(最重要的驗證)
從外部用 3 台 gateway node 的 IP 分別測試:
curl -H "Host: app.example.com" http://<worker-node-1-ip>/details/1
curl -H "Host: app.example.com" http://<worker-node-2-ip>/details/1
curl -H "Host: app.example.com" http://<worker-node-3-ip>/details/1只要能成功取得 HTTP 回應,代表整個路徑是通的 —— Gateway status 顯示 Programmed: False 純粹是 Issue #42786 的顯示問題,不影響流量。
8.4 建議作法:用 listener 條件代替 gateway 條件作為自動化等待點
若有 CI/CD 需要「等待 Gateway 就緒」,請使用以下替代方式之一:
方式 A:等待 Listener Accepted(不受 Issue #42786 影響)
kubectl wait --for=jsonpath='{.status.listeners[0].conditions[?(@.type=="Accepted")].status}'=True \
gateway/my-gateway --timeout=60s方式 B:直接 curl 實測
until curl -sf -H "Host: app.example.com" http://<worker-node-1-ip>/details/1 > /dev/null; do
sleep 2
done
echo "Gateway is serving traffic"⚠️ 請勿使用:
# 這個會永遠 timeout,因為 Programmed 條件受 Issue #42786 影響
kubectl wait --for=condition=Programmed gateway/my-gateway --timeout=60sF5 / Nginx LB 設定
在外部 L4 LB 上設定 pool members,指向 3 台 gateway node:
| Protocol | Pool Members |
|---|---|
| HTTP | <worker-node-1-ip>:80, <worker-node-2-ip>:80, <worker-node-3-ip>:80 |
| HTTPS | <worker-node-1-ip>:443, <worker-node-2-ip>:443, <worker-node-3-ip>:443 |
Health Check 建議:
- TCP monitor(最簡單):對 port 80 / 443 做 TCP 連線檢查
- HTTP monitor(更精確):HTTP GET
/,回傳 404 也算健康(Envoy 在沒有匹配 route 時的預設行為)
💡 提示: F5/Nginx 的 pool member 設定不會從 Gateway
status.addresses讀取。所以 Issue #42786 不影響此設定流程,你仍然是手動指定 3 台 gateway node 的 IP。
注意事項
- hostNetwork 與 LoadBalancer 互斥: 啟用 hostNetwork 後,Cilium 不會再為 Gateway 建立 LoadBalancer type Service
- Port 衝突風險: 確保 80/443 在 gateway node 上沒有被其他程式佔用(例如原本的 Ingress-Nginx)
- 移除舊 Ingress-Nginx: 如果要完全切換,記得先停用 RKE2 內建的 Ingress-Nginx,避免 port 衝突
- Gateway listener port 唯一性: 每個 Gateway 資源的 listener port 在所有 node 上必須唯一
- Rollback 方式: 如需還原,使用備份的 values 重新 upgrade 即可:
helm upgrade cilium cilium/cilium \ --namespace kube-system \ -f cilium-backup-values.yaml - Gateway status 顯示問題(Issue #42786): 在 hostNetwork 模式下 Gateway 永遠顯示
Programmed: False/AddressNotAssigned,此為已知的 upstream issue(截至 2026-04-20 仍為 Open 狀態,尚無關聯修復 PR)。此現象僅影響 status 顯示,不影響實際流量。驗證部署是否成功請以 Listener 的Accepted/ResolvedRefs條件與實際curl測試為準,詳見 Step 8。
⚠️ 已知問題(Known Issue)— 必讀
Upstream Issue: cilium/cilium#42786 — Gateway API with Host Network: Gateway stays PROGRAMMED: False 最後確認時間: 2026-04-20
症狀
kubectl get gateway 的 ADDRESS 欄空白、PROGRAMMED 欄顯示 False(Reason: AddressNotAssigned)。Listener 層的 Programmed 同樣為 False(Reason: Pending)。
Issue 現狀(截至 2026-04-20)
GitHub Issue 仍為 Open 狀態,無關聯修復 PR,Cilium v1.19.x 亦未修復。詳細追蹤資訊請見文末附錄。
影響範圍與根因
回報者 @dke 在 issue 中明確指出:
“Not a regression, but wanted to state that I can get a working gateway-api if I don’t use host network mode, but rather a setup with, for example, cilium and bgp based load balancers.”
也就是說:
- 這不是 regression(不是新版本把舊版本弄壞的問題)
- 這是 hostNetwork 模式特有的行為——一旦改用 Cilium + BGP LoadBalancer(有真正的 Service 可供 LB-IPAM 指派 IP)就會恢復正常
- 根因是 Cilium 的
spec.addresses實作與 LB-IPAM 強耦合,而 hostNetwork 模式會停用 LoadBalancer Service,導致 status.addresses 沒有 IP 來源可以填入
此問題不影響的部分 ✅
- 實際流量 routing 完全正常:Envoy 仍會在 host 的 80/443 port 監聽,F5 → node IP → Envoy → backend 的路徑運作如預期
- Listener 的
Accepted與ResolvedRefs都會是 True:代表 Gateway / HTTPRoute 的設定本身是有效、可接受的 - HTTPRoute 功能正常:path 比對、header 比對、backend 轉發都會生效
此問題會影響的部分 ❌
kubectl get gateway的 ADDRESS 欄永遠是空白- Gateway 層的
Programmed條件永遠是False - 不可使用
kubectl wait --for=condition=programmed gateway/<name>作為 CI/CD 或部署自動化的等待條件