今天將會講解 Ingress 這個元件
包括用途, 用法還有實際案例
假設我們佈署了一個 my-app 的 Pod
當想要透過瀏覽器去存去這個 my-app
有一個作法是建立一個 my-app 的外部 Service
這樣一來就可以透過 Service 的外部 IP 加上對外開放服務的 Port 來存取 my-app
但這種作法其實很不方便 因為必須去找到 Service 的外部 IP
這時候就會需要 Ingress 這個元件來提供域名服務
讓使用者透過域名來存取 my-app 如下圖:
apiVersion: v1
kind: Service
metadata:
name: myapp-external-service
spec:
selector:
app: myapp
type: LoadBalancer
ports:
- protocol: TCP
port: 8080
targetPort: 8080
nodePort: 35010
External Service 的類型是 LoadBalancer
代表會給予一個對外的 IP
指定的 nodePort 是給外部存取 Service 的接口
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: myapp-ingress
spec:
rules:
- host: myapp.com
http:
paths:
- backend:
serviceName: myapp-internal-service
servicePort: 8080
Ingress 的類型是 Ingress
spec 的 rules 是設定路由規則
以這邊的例子規則是所有域名是 myapp.com 的封包都 forward 到 myapp-internal-service:8080
paths 這邊就是設定 url
注意的是, 這裡的 http 指的是 myapp-internal-service 所服務的 protocol 而不是外部的 protocol
而對應的 Internal Service 如下:
apiVersion: v1
kind: Service
metadata:
name: myapp-internal-service
spec:
selector:
app: myapp
ports:
- protocol: TCP
port: 8080
targetPort: 8080
host 需要是一個合法的域名
host 用來把外部域名, 對應到 Node IP Address
除了剛剛設定之外
Ingress 發佈需要一個 Ingress 實作實體, 也就是所謂 Ingress Controller
Ingress Controller 是實際在執行路由的實體, 會根據 Ingress 的 rules 來直接封包路由
架構圖如下:
Ingress Controller 的實作有很多第三方套件可以用
k8s 官方提供可用的 Ingress Controller 選項
而實際佈署要選用哪種 Ingress Controller 要根據 k8s 叢集建制環境來決定
假設是建立在雲服務商上面的話
可能就有雲服務商提供的選項, 所謂 cloud load balancer
而使用雲服務商提供的 cloud load balancer, 舉例來說: AWS 的 ELB
好處就是不必自行去實現負載平衡的功能
架構如下:
但如果是沒有用雲服務商, 而是像是使用 minikube 這類 local run 的叢集的話
就要自行架設 load balancer, 可以是在叢集之內或是之外的伺服器
安全一點的作法一台伺服器當 proxy 轉發封包到 Ingress Controller
這樣對外的接口就只有那台 proxy server, 而 k8s cluster 就不需有對外的接口
架構圖如下:
minikube addons enable ingress
這個設定是自動啟用了 k8s Nginx 的 Ingress Controller
這個設定也可以用在 production 環境
當 ingress 被 enabled 之後可以查詢 namespace 是 ingress-nginx 的 Pod
執行以下指令
minikube dashboard
之後就可以透過 localhsot 察看 minikube 的 status
會發現多了一個 namespace 叫作 kubernete-dashboard
查詢這個 namespace 下面的 Pod
會發現 kubernetes-dashboard 這個 Service 沒有對外開放
所以需要使用 Ingress 建立路由才存取
目標建立 hostname 為 dashboard.com 來存取 kuberenetes-dashboard 這個 Service 內容
建立 dashboard-ingress.yaml 如下:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: dashboard-ingress
namespace: kubernetes-dashboard
spec:
rules:
- host: dashboard.com
http:
paths:
- pathType: ImplementationSpecific
backend:
service:
name: kubernetes-dashboard
port:
number: 80
語法參考自k8s 官方文件
關鍵點:
1 設定 host 為 dashboard.com(你想要設定域名)
2 設定 service 的 name 還有 port 到要連結的 Service
舉例來說: 這邊的 name 設定是 kubernetes-dashboard, port 是 80
因為前面 kubectl get pod -n kuberenetes-dashboard 結果
3 設定 metadata 內, 對應的 namespace 為 kuberenetes-dashboard
因為這是屬於 kuberenetes-dashboard 類別所有放在這個 namespace
另外因為這樣放 可以讓 service 的 name url 的比較簡單, 如果放在不同 namespace 則必須要指定 kuberenetes-dashboard Service 的 namespace
4 kind 是 Ingress 類別
5 pathType 是 ImplementationSpecific, 這裡類別有三種 Exact, Prefix, ImplementationSpecific. Exact 代表 path 要完全一樣才進入路由, Prefix 代表只要前置有包換就進入路由, ImplementationSpecific 代表根據 IngressClass 來決定
6 Ingress Class 是由 Ingress Controller 所提供的選項
每個 Ingress Controller 會實作自己的比對模式並對應到一種 Ingress Class
所以這邊的 Ingress Class 要去查詢 Ingress Controller 所提供的類別
使用以下指令, 發佈 Ingress
kubectl apply -f dashboard-ingress.yaml
檢查 ingress
這時候如果直接 到瀏覽器
從 http://dashboard.com 輸入
就會發現
原因是因為我們只設定好路由
但是沒做域名轉換
因此在本機電腦這邊需要 在 /etc/hosts 內 加入下面一行
192.168.49.2 dashboard.com
注意的是 這邊前面的 192.168.49.2 是從
kubectl get ingress -n kubernetes-dashboard
拿到的 IP 隨著每個人電腦不同會有不同的結果
修改完 /etc/hosts 存檔之後
重新再瀏覽器輸入 http://dashboard.com
透過以下指令
kubectl describe ingress dashboard-ingress -n kubernetes-dashboard
會發現有一個 Default backend 的欄位
而這個是 Ingress 用來處理沒被原本 Service 定義路由的 request
但這邊可以看到還沒有給予任何 Service 來處理
所以再處理 request 時 假設遇到沒有被 forward 過去的 Service 處理到的路由
就會跳出一些 404 網頁找不到 等等預設錯誤
而不是客製化的錯誤頁面
想要建立一個客製化頁面
來處理此問題的方式就是建立一個 Internal Service 對應到這個 default-http-backend
在這個 Internal Service去實作客製化的頁面
建立 default-http-backend.yaml 如下
apiVersion: v1
kind: Service
metadata:
name: default-http-backend
spec:
selector:
app: default-response-app
ports:
- protocol: TCP
port: 80
targetPort: 8080
由於處理方式很多, 在此就不做更深入實作
除了剛剛那個直接對應的 Service 的路由
Ingress 也支援多個路由對應的方式
舉例如下:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: simple-fanout-example
spec:
rules:
- host: myapp.com
http:
paths:
- path: "/analytics"
path: Prefix
backend:
service:
name: analytics-service
port:
number: 3000
- path: "/shopping"
pathType: Exact
backend:
service:
name: shopping-service
port:
number: 8080
就可以做到以下的路由
Ingress 也支援多個子域名對應的方式
舉例如下:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: name-virtual-host-ingress-no-third-host
spec:
rules:
- host: analytics.myapp.com
http:
paths:
- path: "/"
path: Prefix
backend:
service:
name: analytics-service
port:
number: 3000
- host: shopping.myapp.com
http:
- path: "/"
pathType: Exact
backend:
service:
name: shopping-service
port:
number: 8080
就可以做到以下的路由
要建立 TLS 必須要先建立 certificate 跟 key pair
然後把 certificate 跟 private 放在 Secret 內
建立 sample-tls-secret.yaml 如下:
apiVersion: v1
kind: Secret
metadata:
name: testsecret-tls
namespace: default
data:
tls.crt: base64 encoded cert
tls.key: base64 encoded key
type: kubernetes.io/tls
然後在 Ingress 設定內取用這個 Secret 在 tls 設定的部份
建立 tls-example-ingress.yaml 如下
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: tls-example-ingress
spec:
tls:
- hosts:
- https-example.foo.com
secretName: testsecret-tls
rules:
- host: https-example.foo.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: service1
port:
number: 80
關鍵點:
1 tls 設定的 hosts 一定要跟 rules 內指令的 host 一樣
2 這個範例是根據nginx的規則所寫, 不同的 Ingress Controller 可能會有不同的 tls 設定
3 secretName 對應到剛剛 TLS 建立的 Secret 名稱
4 Secret 與 Ingress 必須建立在同一個 Namespace