iT邦幫忙

2026 iThome 鐵人賽

DAY 3
0
Kubernetes

Kubernetes ingress架構系列 第 3

# Day 3|建置 kind 實驗環境 + 裝上 Gateway API

  • 分享至 

  • xImage
  •  

本篇環境

元件 版本
Kubernetes v1.35
kind 最新 stable
Gateway API v1.5(Standard + Experimental)
Helm 3.x
容器執行環境 Docker 或 Podman

今天要解決什麼

  1. 建一個乾淨、可重建、可拋棄的 kind 叢集
  2. 解決 kind 上「流量進不來」的兩個經典坑
  3. 裝上 Gateway API v1.5 CRD,讓叢集認得 GatewayHTTPRoute

選 kind 而不是 minikube / k3d 的關鍵理由是乾淨:這個系列要裝三個實作互相比較,任何預裝元件(例如 k3d 內建的 Traefik)都是干擾。


鎖定版本:versions.env

# versions.env
export CLUSTER_NAME="gwapi-lab"
export KIND_NODE_IMAGE="kindest/node:v1.35.0"
export GATEWAY_API_VERSION="v1.5.0"
export ENVOY_GATEWAY_VERSION="v1.8.0"
export TRAEFIK_CHART_VERSION="37.0.0"    # Traefik Proxy v3.7
export ISTIO_VERSION="1.29.0"
export GIE_VERSION="v1.2.0"

核心:kind 叢集設定檔

# kind-cluster.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: gwapi-lab

nodes:
  - role: control-plane          # 兼任資料平面的入口節點
    kubeadmConfigPatches:
      - |
        kind: InitConfiguration
        nodeRegistration:
          kubeletExtraArgs:
            node-labels: "ingress-ready=true"
    extraPortMappings:
      - containerPort: 80
        hostPort: 80
        protocol: TCP
      - containerPort: 443
        hostPort: 443
        protocol: TCP
  - role: worker                 # 讓應用 Pod 有地方跑
  - role: worker

networking:
  podSubnet: "10.244.0.0/16"
  serviceSubnet: "10.96.0.0/16"

extraPortMappings 是最關鍵的六行,它把主機的 80/443 埠直接映射到 control-plane 容器的 80/443:

瀏覽器 :80 → 主機 :80 → control-plane 容器 :80 → Envoy / Traefik → 後端 Service

有了它就能直接以 docker inspect 取得容器 IP,也避開了 macOS / Windows 上無法直連容器 IP 的限制。

node-labels: "ingress-ready=true" 則讓三個實作的 Helm chart 都能用 nodeSelector 把資料平面 Pod 釘死在 control-plane 上。這很重要——extraPortMappings 只對 control-plane 生效,資料平面若被排到 worker,流量就過不來了。應用 Pod 則落在兩個 worker 上,「流量進 control-plane → 轉發到 worker 上的 Pod」這條跨節點路徑因此是可觀察的,比單節點拓撲更接近實際部署。


建立叢集

source versions.env
kind create cluster --config kind-cluster.yaml --image $KIND_NODE_IMAGE

kubectl get nodes                          # 1. 三個節點都 Ready
kubectl get nodes -l ingress-ready=true    # 2. 標籤貼在 control-plane
docker port gwapi-lab-control-plane        # 3. 埠映射生效
NAME                      STATUS   ROLES           AGE   VERSION
gwapi-lab-control-plane   Ready    control-plane   65s   v1.35.0
gwapi-lab-worker          Ready    <none>          48s   v1.35.0
gwapi-lab-worker2         Ready    <none>          48s   v1.35.0

443/tcp -> 0.0.0.0:443
80/tcp -> 0.0.0.0:80

三項都對,叢集就成了。


安裝 Gateway API CRD

Gateway API 不是 Kubernetes 內建的 API——它是一組 CRD,必須先安裝,API Server 才認得這些型別:

$ kubectl get gateways.gateway.networking.k8s.io
error: the server doesn't have a resource type "gateways"

Standard vs Experimental

通道 內容 穩定性承諾
Standard GA 資源:GatewayClassGatewayHTTPRouteGRPCRouteReferenceGrantBackendTLSPolicy 有向後相容保證
Experimental Standard 的超集,多了 TCPRouteTLSRouteUDPRoute 與 alpha 欄位 無保證,可能破壞性變更

兩者不能同時安裝。本系列裝 Experimental,因為需要 L4 Route 對外開放 Redis、DNS 這類非 HTTP 服務。正式環境請裝 Standard。

kubectl apply --server-side -f "https://github.com/kubernetes-sigs/gateway-api/releases/download/${GATEWAY_API_VERSION}/experimental-install.yaml"
# 正式環境把 experimental-install.yaml 換成 standard-install.yaml

驗證

kubectl get crd httproutes.gateway.networking.k8s.io          # 最重要的一項
kubectl get crd -o name | grep -c 'gateway\.networking\.k8s\.io'

第二條精確數出 10。若改用寬鬆的 kubectl get crd | grep gateway.networking 會得到 12 行,因為它同時命中另一個 API 群組 gateway.networking.x-k8s.ioXBackendTrafficPolicyXMesh 這兩個更早期的孵化資源,本系列不會用到)。十個正式資源裡的 listenersets 是 v1.5 才進來的 XListenerSet,本篇不涉及但計數時會看到。Day 2 講的「Route 家族」到這裡具體化了。

CRD 裝好後,kubectl explain 就是隨身文件:

kubectl explain httproute.spec.rules.matches
kubectl explain gateway.spec.listeners.allowedRoutes

這正是 Day 2 談的「設定必須結構化且可驗證」的具體收益——欄位定義可直接向叢集查詢,不必依賴外部文件。


部署共用測試應用

映像檔採用 traefik/whoami,它會把收到的 HTTP request 原樣回吐,便於觀察路由行為與驗證標頭改寫。

# manifests/demo-apps.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: demo
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: whoami-v1
  namespace: demo
spec:
  replicas: 2
  selector:
    matchLabels: { app: whoami, version: v1 }
  template:
    metadata:
      labels: { app: whoami, version: v1 }
    spec:
      containers:
        - name: whoami
          image: traefik/whoami:v1.10
          args: ["--name=whoami-v1"]
          ports:
            - { name: http, containerPort: 80 }
---
apiVersion: v1
kind: Service
metadata:
  name: whoami-v1
  namespace: demo
spec:
  selector: { app: whoami, version: v1 }
  ports:
    - { name: http, port: 80, targetPort: http }

把上面的 Deployment 與 Service 再複製一份、v1 全部改成 v2,就得到第二組可區分的後端。

kubectl apply -f manifests/demo-apps.yaml
kubectl -n demo get pods -o wide

kubectl -n demo run curlpod --rm -it --image=curlimages/curl:8.11.1 \
  --restart=Never -- curl -s http://whoami-v1/ | head -3

注意 -o wideNODE 欄位——Pod 都在 worker 上,而資料平面被釘在 control-plane。這個跨節點路徑就是本系列各篇流量實際走的路。


上一篇
# Day 2|入口流量的分層模型:從 L4 Service 到 L7 Gateway
下一篇
# Day 4|Gateway API 的核心模型:三層資源與角色分離
系列文
Kubernetes ingress架構7
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言