概述#

在 Service 章節,我們已經學會利用 Service 把叢集內的 Pod 暴露給外部使用。然而每個 Service 都需要綁定自己的對外連接埠,並且仰賴 Node 上的 port mapping,這代表「Service 越多,要管的 port number 也越多」。對使用者而言,網址後還要記憶連接埠號實在不夠友善,這正是 Ingress(路由守護神)登場的舞台。

什麼是 Ingress#

Ingress 可以替我們把對外的入口統一收斂在 HTTP 的 80 與 HTTPS 的 443 連接埠之上,並依照 hostname 或 pathname 將請求轉發到背後對應的 Service,等於是站在 Service 之上的一層 LoadBalancer。如此一來,前面提到 port number 雜亂的問題就被一次處理完。

Ingress 的職責#

  • 路徑導流(give services externally-reachable URLs):用設定好的 hostname 與 pathname 對應到 Service,再由 Service 串到後面的 Pod。
  • 流量負載均衡(load balance traffic):可以套用負載均衡演算法、後端權重等規則。
  • SSL Termination:Ingress 在前面替 HTTPS 做解密,後段 Service 與 Pod 之間就能以明文溝通。
  • 虛擬主機(offer name based virtual hosting):在同一個 IP 之下提供多個虛擬網域。

在 docker-desktop 上安裝 Ingress Controller#

本機環境只要套用官方提供的 Nginx Ingress 部署檔,Kubernetes 會替我們建立一個 ingress-nginx namespace 並把相關元件跑起來。

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.2.1/deploy/static/provider/cloud/deploy.yaml

確認安裝結果:

kubectl get all -n ingress-nginx

只要看到 ingress-nginx-controller 的 Pod 處於 Running,並且 Service 暴露出 80/443 連接埠,就代表 Ingress Controller 已經就緒。

實作一:以 defaultBackend 暴露單一 Service#

Ingress 提供 defaultBackend 設定,未匹配任何規則的流量都會被導到這個預設後端,等同於以 Ingress 統一暴露單一 Service。

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: foo-deployment
  labels:
    type: demo
spec:
  replicas: 2
  selector:
    matchLabels:
      type: demo
  template:
    metadata:
      labels:
        type: demo
    spec:
      containers:
        - name: foo
          image: mikehsu0618/foo
          ports:
            - containerPort: 8080
# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    type: demo
  type: NodePort
  ports:
    - protocol: TCP
      port: 8000
      targetPort: 8080
      nodePort: 30390
# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-ingress
spec:
  ingressClassName: nginx
  defaultBackend:
    service:
      name: my-service
      port:
        number: 8000

這裡刻意把 Service type 從 LoadBalancer 換成 NodePort,因為對外端口已交由 Ingress 統一管理。

部署並驗證:

kubectl apply -f deployment.yaml,service.yaml,ingress.yaml
kubectl get ingress
curl localhost

打到 localhost 的請求會循 Ingress → my-service → Pod 的路徑,回傳 {"data":"Hello foo"}

實作二:Fanout 與虛擬主機#

Fanout 指的是同一個 IP 來源依照不同的 URL(host 或 path)分流到多個 Service。下面用兩個網域 foo.combar.com 各自對應到不同的 Service。

# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-ingress
spec:
  ingressClassName: nginx
  rules:
    - host: foo.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: foo-service
                port:
                  number: 8000
    - host: bar.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: bar-service
                port:
                  number: 8000

部署完成後,可以用 kubectl describe ingress my-ingress 看到兩條 host 規則各自指向不同的 Service Endpoint。

由於是在本機跑虛擬網域,需要在 /etc/hosts 加入反向代理紀錄:

sudo vim /etc/hosts
# 新增
127.0.0.1 foo.com
127.0.0.1 bar.com

實測:

curl http://foo.com   # {"data":"Hello foo"}
curl http://bar.com   # {"data":"Hello bar"}

小結#

熟悉 Ingress 之後,本機上就有能力跑起一組「域名 + 路徑分流」的微型服務群。在 Service 之上把對外入口統一管理,是分散式系統與微服務架構走向成熟的第一步。

原文出處#

  • GitHub:https://github.com/MikeHsu0618/2022-ithelp/tree/main/Day9
  • iThome:https://ithelp.ithome.com.tw/articles/10288843