實操教程丨如何在K8S叢集中部署Traefik Ingress Controller
注:本文使用的Traefik為1.x的版本
在生產環境中,我們常常需要控制來自網際網路的外部進入叢集中,而這恰巧是Ingress的職責。
Ingress的主要目的是將HTTP和HTTPS從叢集外部暴露給該叢集中執行的服務。這與Ingress控制如何將外部流量路由到叢集有異曲同工之妙。接下來,我們舉一個實際的例子來更清楚的說明Ingress的概念。
首先,想象一下在你的Kubernetes叢集中有若干個微服務(小型應用程式之間彼此通訊)。這些服務能夠在叢集內部被訪問,但我們想讓我們的使用者從叢集外部也能夠訪問它們。因此,我們需要做的是使用反向代理將每個HTTP(S)(例如,service.yourdomain.com
使用Ingress資源和關聯的Ingress Controller,你可以實現以下目標:
將你的域
app.domain.com
指向你的私有網路中的微服務應用程式將路徑
domain.com/web
指向你的私有網路中的微服務web將你的域
backend.domain.com
指向你的私有網路中的微服務後端,並在該微服務的多個例項之間(Pod)進行負載均衡
現在,你理解了Ingress的重要性。它能夠幫助將HTTP路由指向在Kubernetes叢集中特定的微服務。
但是,流量路由並不是Ingress在Kubernetes中的唯一功能。例如,還可以將Ingress配置為負載均衡流量到你的應用程式、終止SSL、執行基於名稱的虛擬主機、在不同服務之間分配流量、設定服務訪問規則等。
Kubernetes有一個特別的Ingress API資源,它能夠支援上述所有功能。但是,簡單地建立一個Ingress API資源是沒有作用的。你還需要一個Ingress Controller。目前,Kubernetes支援許多Ingress controller,如Contour、HAProxy、NGINX以及Traefik。
在本文中,我將使用Traefik Ingress Controller建立Ingress。它能夠實現現代HTTP反向代理和負載均衡器的功能,從而簡化了微服務的部署。此外,Traefik對Docker、Marathon、Consul、Kubernetes、Amazon ECS等系統和環境都提供了強大的支援。
Traefik對於諸如Kubernetes之類的靈活性較強的系統十分有用。在Kubernetes中,每天需要多次新增、刪除或升級服務,而Traefik可以監聽服務映象倉庫/編排器 API並立即生成或更新路由,因此你的微服務無需手動配置即可與外界連線。
除此之外,Traefik支援多個負載均衡演算法、Let’s Encrypt的HTTPS(支援通配證書)、斷路器、WebSoket、GRPC和多個監控程式(Rest、Prometheus、Statsd、Datadog、InfluxDB等)。有關Traefik中可用功能的更多資訊,請參考其官方文件:
https://docs.traefik.cn/
Ingress 資源
在教程開始之前,我們先來簡單地討論一下Ingress資源是如何工作的。以下是隱式使用Nginx Ingress Controller的Ingress示例。
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-example
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- http:
paths:
- path: /microservice1
backend:
serviceName: test
servicePort: 80
以上Ingress manifest 包含了一系列HTTP規則,它們用於規定controller如何路由流量。
可選主機。如果未指定主機(如上所示),則該規則適用於通過指定IP地址的所有入站HTTP流量。如果提供了主機(如yourhost.com
),則該規則僅適用於該主機。
一個路徑列表(如,/microservice1
),它指向由serviceName
和servicePort
定義的關聯後端。
一個後端。向Ingress發出的HTTP(和HTTPS)請求將與給定規則的主機和路徑匹配,然後將其路由到該規則中指定的後端服務。
在以上例子中,我們配置了一個名為”test“的後端,它將接收所有來自/microservice
路徑的流量。然而,我們也可以配置一個預設後端,它將將為任何不符合規範中路徑的使用者請求提供服務。同時,如果不定義任何規則,Ingress將路由所有流量到預設後端。例如:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: test-ingress
spec:
backend:
serviceName: defaultbackend
servicePort: 80
在本例中,所有流量都被轉發到預設後端中defaultbackend
。現在,我們理解了Ingress 資源的基本概念,接下來我們來看看一些具體的例子。
Step 0:前期準備
如上文我們所說的,定義一個Ingress資源沒有任何作用,除非你使用了Ingress Controller。在本教程中,我們在Kubernetes叢集中將Traefik設定為Ingress Controller。
要完成教程,你需要進行以下準備:
一個正在執行的Kubernetes叢集。
一個安裝好的命令列工具,kubectl。並配置為與叢集通訊。
請注意:以下示例均假設你在本地計算上使用Minikube執行Kubernetes叢集。
Step 1:啟用RBAC
首先,我們需要向Traefik授予一些許可權,以訪問叢集中執行的Pod、endpoint和服務。為此,我們將使用ClusterRole
和ClusterRoleBinding
資源。但是,你也可以對名稱空間範圍內的RoleBindings
使用最小特權方法。通常,如果叢集的名稱空間不會動態更改,並且Traefik無法監視所有叢集的名稱空間,那麼這是首選的方法。
讓我們建立一個新的ServiceAccount
,為Traefik提供叢集中的身份。
apiVersion: v1
kind: ServiceAccount
metadata:
name: traefik-ingress
namespace: kube-system
要建立一個ServiceAccount
,需要在traefik-service-acc.yaml
中儲存以上manifest並執行:
kubectl create -f traefik-service-acc.yaml
serviceaccount "traefik-ingress" created
接下來,讓我們建立一個具有一組許可權的ClusterRole,該許可權將應用於Traefik ServiceAccount
。通過ClusterRole
,Traefik可以管理和監視叢集中所有名稱空間中的資源,例如服務、endpoint、secret以及Ingress。
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: traefik-ingress
rules:
- apiGroups:
- ""
resources:
- services
- endpoints
- secrets
verbs:
- get
- list
- watch
- apiGroups:
- extensions
resources:
- ingresses
verbs:
- get
- list
- watch
將這一規範儲存到檔案traefik-cr.yaml
中,並執行:
kubectl create -f traefik-cr.yaml
clusterrole.rbac.authorization.k8s.io “traefik-ingress” created
最後,啟用這些許可權,我們應該將ClusterRole
繫結到Traefik ServiceAccount
中。使用ClusterRoleBinding
manifest可以完成這一操作:
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: traefik-ingress
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: traefik-ingress
subjects:
- kind: ServiceAccount
name: traefik-ingress
namespace: kube-system
儲存這一規範到traefik-crb.yaml
中,並執行以下命令:
kubectl create -f traefik-crb.yaml
clusterrolebinding.rbac.authorization.k8s.io “traefik-ingress” created
Step 2:部署Traefik到叢集
接下來,我們將部署Traefik到Kubernetes叢集。官方Traefik文件支援三種類型的部署:使用Deployment物件、使用DaemonSet物件或使用Helm Chart。
在本教程中,我們將使用Deployment manifest。相比其他選項,Deployment有諸多優勢。例如,它們能確保更好的可伸縮性,併為滾動更新提供良好支援。
讓我們看一下 Deployment manifest:
kind: Deployment
apiVersion: extensions/v1beta1
metadata:
name: traefik-ingress
namespace: kube-system
labels:
k8s-app: traefik-ingress-lb
spec:
replicas: 1
selector:
matchLabels:
k8s-app: traefik-ingress-lb
template:
metadata:
labels:
k8s-app: traefik-ingress-lb
name: traefik-ingress-lb
spec:
serviceAccountName: traefik-ingress
terminationGracePeriodSeconds: 60
containers:
- image: traefik
name: traefik-ingress-lb
ports:
- name: http
containerPort: 80
- name: admin
containerPort: 8080
args:
- --api
- --kubernetes
- --logLevel=INFO
這個Deployment將在kube-system 名稱空間中建立一個Traefik副本。Traefik容器將使用此manifest中指定的埠80和8080。
將這個manifest儲存到traefik-deployment.yaml
檔案中,並執行以下命令建立Deployment:
kubectl create -f traefik-deployment.yaml
deployment.extensions “traefik-ingress” created
現在,讓我們檢查一下Traefik Pod是否都成功建立了:
kubectl --namespace=kube-system get pods
NAME READY STATUS RESTARTS AGE
....
storage-provisioner 1/1 Running 3 23d
traefik-ingress-54d6d8d9cc-ls6cs 1/1 Running 0 1m
如你所見,Deployment Controller啟動了一個Traefik副本,並在正在執行,敲棒的!
Step 3:為外部訪問建立NodePorts
讓我們建立一個服務來從叢集外部訪問Traefik。為此,我們需要一個暴露兩個NodePorts的服務。
kind: Service
apiVersion: v1
metadata:
name: traefik-ingress-service
namespace: kube-system
spec:
selector:
k8s-app: traefik-ingress-lb
ports:
- protocol: TCP
port: 80
name: web
- protocol: TCP
port: 8080
name: admin
type: NodePort
將這個manifest儲存到traefik-svc.yaml
,並建立服務:
kubectl create -f traefik-svc.yaml
service “traefik-ingress-service” created
現在,讓我們驗證該服務是否建立:
kubectl describe svc traefik-ingress-service --namespace=kube-system
Name: traefik-ingress-service
Namespace: kube-system
Labels: <none>
Annotations: <none>
Selector: k8s-app=traefik-ingress-lb
Type: NodePort
IP: 10.102.215.64
Port: web 80/TCP
TargetPort: 80/TCP
NodePort: web 30565/TCP
Endpoints: 172.17.0.6:80
Port: admin 8080/TCP
TargetPort: 8080/TCP
NodePort: admin 30729/TCP
Endpoints: 172.17.0.6:8080
Session Affinity: None
External Traffic Policy: Cluster
Events: <none>
如你所見,我們現在有兩個NodePorts(web
和admin
),它們分別路由到Traefik Ingress Controller的80和8080容器埠。“admin” NodePort將用於訪問Traefik Web UI,“web” NodePort將用於訪問通過Ingress暴露的服務。
Step 4:訪問Traefik
為了能在瀏覽器中訪問Traefik Web UI,你可以使用“admin”NodePort 30729(請注意,你的NodePort值可能會有所不同)。因為我們還沒有新增任何前端,所以UI此時應該是空的。
由於我們尚未給Traefik進行任何配置,因此我們會收到404的響應。
curl $(minikube ip):30565
404 page not found
Step 5 :新增Ingress 到叢集
現在我們在Kubernetes叢集中已經將Traefik作為Ingress Controller了。然而,我們依舊需要定義Ingress資源和暴露Traefik Web UI的服務。
首先,我們建立一個服務:
apiVersion: v1
kind: Service
metadata:
name: traefik-web-ui
namespace: kube-system
spec:
selector:
k8s-app: traefik-ingress-lb
ports:
- name: web
port: 80
targetPort: 8080
儲存manifest到traefik-webui-svc.yaml
中,並執行:
kubectl create -f traefik-webui-svc.yaml
service “traefik-web-ui” created
讓我們驗證服務是否已經建立:
kubectl describe svc traefik-web-ui --namespace=kube-system
Name: traefik-web-ui
Namespace: kube-system
Labels: <none>
Annotations: <none>
Selector: k8s-app=traefik-ingress-lb
Type: ClusterIP
IP: 10.98.230.58
Port: web 80/TCP
TargetPort: 8080/TCP
Endpoints: 172.17.0.6:8080
Session Affinity: None
Events: <none>
如你所見,服務的ClusterIP是10.98.230.58
,並在manifest中分配指定埠。
接下來,我們需要建立一個Ingress資源,指向Traefik Web UI後端:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: traefik-web-ui
namespace: kube-system
spec:
rules:
- host: traefik-ui.minikube
http:
paths:
- path: /
backend:
serviceName: traefik-web-ui
servicePort: web
本質上,Ingress將所有請求路由到traefik-ui.minikube
,在上述步驟中建立的服務暴露Traefik Web UI。
將規範儲存到traefik-ingress.yaml
,並執行:
kubectl create -f traefik-ingress.yaml
ingress.extensions “traefik-web-ui” created
為了能夠通過traefik-ui.minikube
在瀏覽器中可以訪問Traefik Web UI,我們需要新增新的條目到我們/etc/hosts
檔案中。該條目將包含Minikube IP和主機名。你可以通過執行minikube ip來獲取minkube例項的IP地址,然後將新主機的名稱儲存到/etc/hosts
檔案中,如下所示:
echo "$(minikube ip) traefik-ui.minikube" | sudo tee -a /etc/hosts
192.168.99.100 traefik-ui.minikube
現在,你應該能夠在瀏覽器中訪問http://traefik-ui.minikube:<AdminNodePort>
並檢視Traefik Web UI。別忘了附加”admin”NodePort到主機地址。
在dashboard中,你可以點選Health 連結來檢視應用程式的健康狀況:
Step 6:實現基於名稱的路由
現在,我們來演示如何使用Traefik Ingress Controller為前端列表設定基於名稱的路由。我們將使用簡單的單頁網站建立3個Deployment,並顯示動物影象:熊、野兔和駝鹿。
---
kind: Deployment
apiVersion: extensions/v1beta1
metadata:
name: bear
labels:
app: animals
animal: bear
spec:
replicas: 2
selector:
matchLabels:
app: animals
task: bear
template:
metadata:
labels:
app: animals
task: bear
version: v0.0.1
spec:
containers:
- name: bear
image: supergiantkir/animals:bear
ports:
- containerPort: 80
---
kind: Deployment
apiVersion: extensions/v1beta1
metadata:
name: moose
labels:
app: animals
animal: moose
spec:
replicas: 2
selector:
matchLabels:
app: animals
task: moose
template:
metadata:
labels:
app: animals
task: moose
version: v0.0.1
spec:
containers:
- name: moose
image: supergiantkir/animals:moose
ports:
- containerPort: 80
---
kind: Deployment
apiVersion: extensions/v1beta1
metadata:
name: hare
labels:
app: animals
animal: hare
spec:
replicas: 2
selector:
matchLabels:
app: animals
task: hare
template:
metadata:
labels:
app: animals
task: hare
version: v0.0.1
spec:
containers:
- name: hare
image: supergiantkir/animals:hare
ports:
- containerPort: 80
每個Deployment都將有兩個Pod副本,而每個Pod將在containerPort
80上服務“動物“網站。
讓我們儲存這些Deployment manifest到animals-deployment.yaml
中,並執行:
kubectl create -f animals-deployment.yaml
deployment.extensions “bear” created
deployment.extensions “moose” created
deployment.extensions “hare” created
現在,讓我們為每個Deployment建立一個服務,使得Pod可以訪問:
---
apiVersion: v1
kind: Service
metadata:
name: bear
spec:
ports:
- name: http
targetPort: 80
port: 80
selector:
app: animals
task: bear
---
apiVersion: v1
kind: Service
metadata:
name: moose
spec:
ports:
- name: http
targetPort: 80
port: 80
selector:
app: animals
task: moose
---
apiVersion: v1
kind: Service
metadata:
name: hare
annotations:
traefik.backend.circuitbreaker: "NetworkErrorRatio() > 0.5"
spec:
ports:
- name: http
targetPort: 80
port: 80
selector:
app: animals
task: hare
請注意:第三項服務使用斷路器annotation。斷路器是Traefik的一項功能,可防止發生故障的伺服器承受高負載。在本例中,我們防止伺服器上的高負載超過50%。當此條件匹配時,CB進入“跳閘“狀態,在該狀態中它會使用預定義的HTTP狀態程式碼進行響應或重定向到另一個前端。
儲存這些服務manifest到animals-svc.yaml
並執行:
kubectl create -f animals-svc.yaml
service “bear” created
service “moose” created
service “hare” created
最後,為每個Deployment建立一個有3個前後端對的Ingress。bear.minikube
、moose.minikube
和hare.minikube
將是我們指向相應後端服務的前端。
Ingress manifest如下所示:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: animals
annotations:
kubernetes.io/ingress.class: traefik
spec:
rules:
- host: hare.minikube
http:
paths:
- path: /
backend:
serviceName: hare
servicePort: http
- host: bear.minikube
http:
paths:
- path: /
backend:
serviceName: bear
servicePort: http
- host: moose.minikube
http:
paths:
- path: /
backend:
serviceName: moose
servicePort: http
儲存規範到animals-ingress.yaml
並執行:
kubectl create -f animals-ingress.yaml
ingress.extensions “animals” created
現在,在Traefik dashboard內,你可以看到每個主機的前端以及相應的後端列表:
如果你再次編輯etc/hosts
,你應該能夠在你的瀏覽器中訪問動物網頁:
echo “$(minikube ip) bear.minikube hare.minikube moose.minikube” | sudo tee -a /etc/hosts
你應該使用“web“NodePort來訪問特定網頁。例如,http://bear.minikube:<WebNodePort>
我們也可以將三個前端重新配置為在一個域下提供服務,如下所示:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: all-animals
annotations:
kubernetes.io/ingress.class: traefik
traefik.frontend.rule.type: PathPrefixStrip
spec:
rules:
- host: animals.minikube
http:
paths:
- path: /bear
backend:
serviceName: bear
servicePort: http
- path: /moose
backend:
serviceName: moose
servicePort: http
- path: /hare
backend:
serviceName: hare
servicePort: http
如果你啟用這個Ingress,使用相應的路徑,三個動物在一個域下都能夠訪問——animals.minikube
。別忘了將這個域新增到/etc/hosts
。
echo “$(minikube ip) animals.minikube” | sudo tee -a /etc/hosts
請注意:我們正在配置Traefik,以使用traefik.frontend.rule.type
註釋,從URL路徑中刪除字首。這樣我們可以直接使用上一個示例中的容器。由於traefik.frontend.rule.type: PathPrefixStrip
規則,你必須使用http://animals.minikube:32484/moose/
而不是http://animals.minikube:32484/moose
Step 7:實現流量分配
藉助Traefik,使用者可以使用服務權重以受控方式在多個deployment之間分配Ingress流量。這一功能可用於金絲雀釋出,它最初應該獲得少量但持續增長的流量。
讓我們使用以下manifest在兩個微服務之間分配Traefik:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
traefik.ingress.kubernetes.io/service-weights: |
animals-app: 99%
animals-app-canary: 1%
name: animals-app
spec:
rules:
- http:
paths:
- backend:
serviceName: animals-app
servicePort: 80
path: /
- backend:
serviceName: animals-app-canary
servicePort: 80
path: /
請注意traefik.ingress.kubernetes.io/service-weights
的註釋。它指定了流量如何在指定後端服務(animals-app
和animals-app-canary
)之間分配。Traefik將把99%的使用者請求路由到animals-app
deployment支援的Pod,並將1%的使用者請求路由到animals-app-canary
deployment支援的Pod。
要使此設定正常工作,需要滿足一些條件:
所有服務後端必須共享相同的路徑和主機。
跨服務後端共享的請求總數應總計為100%。
百分比值應該在支援的精度範圍內,目前Traefik支援3個小數位的權重。
總結
如你所見,Ingress是將外部流量路由到Kubernetes叢集中相應後端服務的強大工具。使用者可以使用Kubernetes支援的許多Ingress controller來實現Ingress。在本教程中,我們重點介紹了Traefik Ingress controller,該控制器支援基於名稱的路由,負載均衡以及Ingress controller的其他常見任務。