CKAD 學習筆記 - Ingress

如果 service 不夠滿足需求,那你有試過 ingress 嗎

一言以蔽之的話,ingress 是一種較為進階的 service。它被引進於解決幾個需求:如端對端加密、及對於不同的 URI path 會需要由不同組 service 來響應。

設想一組情境,今天我們有一個購物網站,那麼對於瀏覽商品的 /item 頁面有一組服務,而對於購物車的 /cart 有另一組服務;如果我們只依賴 service,那麼就會有幾種奇耙的情境:

  • 使用 node port service:這兩個頁面就會分屬不同的 nodePort
  • 使用 load balancer service:這兩個頁面會分屬不同的 load balancer,因此需要以不同的 IP 訪問;另外你還要付兩組 load balancer 的錢

不管哪個都聽起來有點詭異,那解決方案就是在 cluster 以外再多加一層 proxy 去根據 path 自動把請求往對應的 service 打,疊床架屋下大概成了這個德性:

目前看來似乎能動了,然後就變開發者的下個惡夢——東西太多難以管理。薑薑,ingress 就是一個把這堆需求壓在一起解決的工具。而其中的這個控制器則稱為 ingress controller。

使用 ingress

ingress 雖然是個 kubernetes 應用上常見的元件,但 kubernetes 本身並不內建 ingress controller。可用的解決方案在 官方文件 上洋洋灑灑寫了 21 項;而其中 Nginx ingress controllerGKE ingress 擇是有得到官方支援。

要使用 ingress 則會包含兩個部分:ingress controller 及 ingress resource;前者為負責處理流量的實體,而後者則是導流規則。

ingress controller 的部署得回歸各實作的官方建議,而 ingress resource 則在 kubernetes 上有公用的介面。

Ingress controller - 以 Nginx ingress controller 為例

Nginx ingress controller 基本上就是利用 Nginx 去作為這個控制器,對於這個控制器官方提供了十分懶人的指令給我們直接帶走:

helm upgrade --install ingress-nginx ingress-nginx \
--repo https://kubernetes.github.io/ingress-nginx \
--namespace ingress-nginx --create-namespace

而如果去檢視背後的資源檔,大概包含了幾個元件:

Deployment

控制器本身終究需要個實體才能運轉,故這裡的核心就是 Deployment。

這裡節錄其定義的一部分來聊聊:

apiVersion: apps/v1
kind: Deployment
metadata:
name: ingress-nginx-controller
spec:
template:
spec:
containers:
- args:
- /nginx-ingress-controller
- --publish-service=$(POD_NAMESPACE)/ingress-nginx-controller
- --election-id=ingress-controller-leader
- --controller-class=k8s.io/ingress-nginx
- --ingress-class=nginx
- --configmap=$(POD_NAMESPACE)/ingress-nginx-controller
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
image: registry.k8s.io/ingress-nginx/controller:v1.4.0@sha256:34ee929b111ffc7aa426ffd409af44da48e5a0eea1eb2207994d9e0c0882d143
name: controller
ports:
- containerPort: 80
name: http
protocol: TCP
- containerPort: 443
name: https
protocol: TCP
dnsPolicy: ClusterFirst
serviceAccountName: ingress-nginx

image 它使用了 registry.k8s.io/ingress-nginx/controller 而不是 nginx,雖然 nginx ingress controller 是使用 nginx 去做導流,但會去使用到這個特化的 build 來去符合一些 ingress 需要的細部行為。另外 env 中給了兩組特殊的輸入 POD_NAMEPOD_NAMESPACE 則算是擴充了這組 deployment 所能搜集到的資訊,肇因於原生的 Kubernetes 下 pod 被依定程度的包裝了而無法搜集外界的資訊 (ref)。

ConfigMap

在不修改 image 的情況下植入自定義的設定的標準操作

Service

控制器本身畢竟就只是組 deployment,一樣是被閉關在叢集內的,我們需要為它建立起一組 node port service 以方便外面的流量走得進來。

ServiceAccount, Role, ClusterRole and RoleBinding

用於賦予控制器一些額外的權限以監控叢集的狀態,讓控制器根據狀態給出一些調整。

Ingress resource

ingress resource 用於定義路由的規則,我們可以根據 host name 及 path 去設定請求要被導向某個服務。以下是一個 resource 的範例:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: cart-ingress
spec:
rules:
# 當 host 沒有設定時則這個 rule 會對所有 host 生效
- host: example.com
http:
paths:
- path: /cart
pathType: Exact # Exact / Prefix
backend:
service:
name: cart
port:
number: 80
defaultBackend:
service:
name: defualt-http-backend
port:
number: 80

rules 應該不會有太大的問題,列出所有導流規則及目標然後就能用了;在看這段的時候發現文件有提及 Prefix 會真的去比對 URI path 而不是單純的字串比對,故 /foo 這個前綴可以中 /foo/bar 但不會中 /foobar

另外這裡有個 defaultBackend,這是在如果一個請求無法被任一 rule 包含時會被導向的位置。

補充:Gateway API

ingress 很美好,但似乎還不夠達成人們的理想。SIG 最近推出了新的 API 架構——Gateway API,它在今(2022)年 7 月才進入 Beta,我們可以理解它是一個比 ingress 更完整的的上位替代品。

相較於 ingress 是以一組 deployment 的形式存在、還需要仰賴 service 來做前期導流,gateway API 則是直接從更前端的 gateway 就進行導流,這樣帶來幾個好處:

  • 省掉了在 ingress 前端的 service 的部分,設定起來更省事
  • 成為跨命名空間的 ingress 機制、不同的命名空間的服務們得以共享同一個負載平衡器

更多資訊可以參考 Shawn 的 K8S Gateway API (I): Contour的進化版