原文鏈接:一文搞懂 Traefik2.1 的使用
Traefik 是一個開源的可以使服務(wù)發(fā)布變得輕松有趣的邊緣路由器效五。它負(fù)責(zé)接收你系統(tǒng)的請求,然后使用合適的組件來對這些請求進行處理撵彻。
除了眾多的功能之外墙贱,Traefik 的與眾不同之處還在于它會自動發(fā)現(xiàn)適合你服務(wù)的配置艺演。當(dāng) Traefik 在檢查你的服務(wù)時粮呢,會找到服務(wù)的相關(guān)信息并找到合適的服務(wù)來滿足對應(yīng)的請求。
Traefik 兼容所有主流的集群技術(shù)钞艇,比如 Kubernetes啄寡,Docker,Docker Swarm哩照,AWS挺物,Mesos,Marathon飘弧,等等识藤;并且可以同時處理多種方式。(甚至可以用于在裸機上運行的比較舊的軟件次伶。)
使用 Traefik痴昧,不需要維護或者同步一個獨立的配置文件:因為一切都會自動配置,實時操作的(無需重新啟動冠王,不會中斷連接)赶撰。使用 Traefik,你可以花更多的時間在系統(tǒng)的開發(fā)和新功能上面柱彻,而不是在配置和維護工作狀態(tài)上面花費大量時間豪娜。
核心概念
Traefik 是一個邊緣路由器,是你整個平臺的大門哟楷,攔截并路由每個傳入的請求:它知道所有的邏輯和規(guī)則瘤载,這些規(guī)則確定哪些服務(wù)處理哪些請求;傳統(tǒng)的反向代理需要一個配置文件卖擅,其中包含路由到你服務(wù)的所有可能路由鸣奔,而 Traefik 會實時檢測服務(wù)并自動更新路由規(guī)則,可以自動服務(wù)發(fā)現(xiàn)惩阶。
首先挎狸,當(dāng)啟動 Traefik 時,需要定義 entrypoints
(入口點)琳猫,然后伟叛,根據(jù)連接到這些 entrypoints 的路由來分析傳入的請求,來查看他們是否與一組規(guī)則相匹配脐嫂,如果匹配统刮,則路由可能會將請求通過一系列中間件轉(zhuǎn)換過后再轉(zhuǎn)發(fā)到你的服務(wù)上去。在了解 Traefik 之前有幾個核心概念我們必須要了解:
-
Providers
用來自動發(fā)現(xiàn)平臺上的服務(wù)账千,可以是編排工具侥蒙、容器引擎或者 key-value 存儲等,比如 Docker匀奏、Kubernetes鞭衩、File -
Entrypoints
監(jiān)聽傳入的流量(端口等…),是網(wǎng)絡(luò)入口點娃善,它們定義了接收請求的端口(HTTP 或者 TCP)论衍。 -
Routers
分析請求(host, path, headers, SSL, …),負(fù)責(zé)將傳入請求連接到可以處理這些請求的服務(wù)上去聚磺。 -
Services
將請求轉(zhuǎn)發(fā)給你的應(yīng)用(load balancing, …)坯台,負(fù)責(zé)配置如何獲取最終將處理傳入請求的實際服務(wù)。 -
Middlewares
中間件瘫寝,用來修改請求或者根據(jù)請求來做出一些判斷(authentication, rate limiting, headers, ...)蜒蕾,中間件被附件到路由上,是一種在請求發(fā)送到你的服務(wù)之前(或者在服務(wù)的響應(yīng)發(fā)送到客戶端之前)調(diào)整請求的一種方法焕阿。
安裝
由于 Traefik 2.X 版本和之前的 1.X 版本不兼容咪啡,我們這里選擇功能更加強大的 2.X 版本來和大家進行講解,我們這里使用的鏡像是 traefik:2.1.1
暮屡。
在 Traefik 中的配置可以使用兩種不同的方式:
- 動態(tài)配置:完全動態(tài)的路由配置
- 靜態(tài)配置:啟動配置
靜態(tài)配置
中的元素(這些元素不會經(jīng)常更改)連接到 providers 并定義 Treafik 將要監(jiān)聽的 entrypoints撤摸。
在 Traefik 中有三種方式定義靜態(tài)配置:在配置文件中、在命令行參數(shù)中褒纲、通過環(huán)境變量傳遞
動態(tài)配置
包含定義系統(tǒng)如何處理請求的所有配置內(nèi)容愁溜,這些配置是可以改變的,而且是無縫熱更新的外厂,沒有任何請求中斷或連接損耗冕象。
安裝 Traefik 到 Kubernetes 集群中的資源清單文件我這里提前準(zhǔn)備好了,直接執(zhí)行下面的安裝命令即可:
$ kubectl apply -f https://www.qikqiak.com/k8strain/network/manifests/traefik/crd.yaml
$ kubectl apply -f https://www.qikqiak.com/k8strain/network/manifests/traefik/rbac.yaml
$ kubectl apply -f https://www.qikqiak.com/k8strain/network/manifests/traefik/deployment.yaml
$ kubectl apply -f https://www.qikqiak.com/k8strain/network/manifests/traefik/dashboard.yaml
其中 deployment.yaml
我這里是固定到 master 節(jié)點上的汁蝶,如果你需要修改可以下載下來做相應(yīng)的修改即可渐扮。我們這里是通過命令行參數(shù)來做的靜態(tài)配置:
args:
- --entryPoints.web.address=:80
- --entryPoints.websecure.address=:443
- --api=true
- --api.dashboard=true
- --ping=true
- --providers.kubernetesingress
- --providers.kubernetescrd
- --log.level=INFO
- --accesslog
其中前兩項配置是來定義 web
和 websecure
這兩個入口點的,--api=true
開啟,=掖棉,就會創(chuàng)建一個名為 api@internal
的特殊 service墓律,在 dashboard 中可以直接使用這個 service 來訪問,然后其他比較重要的就是開啟 kubernetesingress
和 kubernetescrd
這兩個 provider幔亥。
dashboard.yaml
中定義的是訪問 dashboard 的資源清單文件耻讽,可以根據(jù)自己的需求修改。
$ kubectl get pods -n kube-system
NAME READY STATUS RESTARTS AGE
traefik-867bd6b9c-lbrlx 1/1 Running 0 6m17s
......
$ kubectl get ingressroute
NAME AGE
traefik-dashboard 30m
部署完成后我們可以通過在本地 /etc/hosts
中添加上域名 traefik.domain.com
的映射即可訪問 Traefik 的 Dashboard 頁面了:
ACME
Traefik 通過擴展 CRD 的方式來擴展 Ingress 的功能帕棉,除了默認(rèn)的用 Secret 的方式可以支持應(yīng)用的 HTTPS 之外针肥,還支持自動生成 HTTPS 證書饼记。
比如現(xiàn)在我們有一個如下所示的 whoami
應(yīng)用:
apiVersion: v1
kind: Service
metadata:
name: whoami
spec:
ports:
- protocol: TCP
name: web
port: 80
selector:
app: whoami
---
kind: Deployment
apiVersion: apps/v1
metadata:
name: whoami
labels:
app: whoami
spec:
replicas: 2
selector:
matchLabels:
app: whoami
template:
metadata:
labels:
app: whoami
spec:
containers:
- name: whoami
image: containous/whoami
ports:
- name: web
containerPort: 80
然后定義一個 IngressRoute 對象:
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: simpleingressroute
spec:
entryPoints:
- web
routes:
- match: Host(`who.qikqiak.com`) && PathPrefix(`/notls`)
kind: Rule
services:
- name: whoami
port: 80
通過 entryPoints
指定了我們這個應(yīng)用的入口點是 web
,也就是通過 80 端口訪問慰枕,然后訪問的規(guī)則就是要匹配 who.qikqiak.com
這個域名具则,并且具有 /notls
的路徑前綴的請求才會被 whoami
這個 Service 所匹配。我們可以直接創(chuàng)建上面的幾個資源對象具帮,然后對域名做對應(yīng)的解析后博肋,就可以訪問應(yīng)用了:
在 IngressRoute
對象中我們定義了一些匹配規(guī)則,這些規(guī)則在 Traefik 中有如下定義方式:
如果我們需要用 HTTPS 來訪問我們這個應(yīng)用的話蜂厅,就需要監(jiān)聽 websecure
這個入口點匪凡,也就是通過 443 端口來訪問,同樣用 HTTPS 訪問應(yīng)用必然就需要證書掘猿,這里我們用 openssl
來創(chuàng)建一個自簽名的證書:
$ openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=who.qikqiak.com"
然后通過 Secret 對象來引用證書文件:
# 要注意證書文件名稱必須是 tls.crt 和 tls.key
$ kubectl create secret tls who-tls --cert=tls.crt --key=tls.key
secret/who-tls created
這個時候我們就可以創(chuàng)建一個 HTTPS 訪問應(yīng)用的 IngressRoute 對象了:
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: ingressroutetls
spec:
entryPoints:
- websecure
routes:
- match: Host(`who.qikqiak.com`) && PathPrefix(`/tls`)
kind: Rule
services:
- name: whoami
port: 80
tls:
secretName: who-tls
創(chuàng)建完成后就可以通過 HTTPS 來訪問應(yīng)用了病游,由于我們是自簽名的證書,所以證書是不受信任的:
除了手動提供證書的方式之外 Traefik 還支持使用 Let’s Encrypt
自動生成證書术奖,要使用 Let’s Encrypt
來進行自動化 HTTPS礁遵,就需要首先開啟 ACME
,開啟 ACME
需要通過靜態(tài)配置的方式采记,也就是說可以通過環(huán)境變量佣耐、啟動參數(shù)等方式來提供,我們這里還是直接使用啟動參數(shù)的形式來開啟唧龄,在 Traefik 的部署文件中添加如下命令行參數(shù):
args:
......
# 使用 dns 驗證方式
- --certificatesResolvers.ali.acme.dnsChallenge.provider=alidns
# 郵箱配置
- --certificatesResolvers.ali.acme.email=ych_1024@163.com
# 保存 ACME 證書的位置
- --certificatesResolvers.ali.acme.storage=/etc/acme/acme.json
ACME 有多種校驗方式 tlsChallenge
兼砖、httpChallenge
和 dnsChallenge
三種驗證方式,之前更常用的是 http 這種驗證方式既棺,關(guān)于這幾種驗證方式的使用可以查看文檔:https://www.qikqiak.com/traefik-book/https/acme/ 了解他們之間的區(qū)別讽挟。要使用 tls 校驗方式的話需要保證 Traefik 的 443 端口是可達(dá)的,dns 校驗方式可以生成通配符的證書丸冕,只需要配置上 DNS 解析服務(wù)商的 API 訪問密鑰即可校驗耽梅。我們這里用 DNS 校驗的方式來為大家說明如何配置 ACME。
上面我們通過設(shè)置 --certificatesResolvers.ali.acme.dnsChallenge.provider=alidns
參數(shù)來指定指定阿里云的 DNS 校驗胖烛,要使用阿里云的 DNS 校驗我們還需要配置3個環(huán)境變量:ALICLOUD_ACCESS_KEY
眼姐、ALICLOUD_SECRET_KEY
、ALICLOUD_REGION_ID
佩番,分別對應(yīng)我們平時開發(fā)阿里云應(yīng)用的時候的密鑰众旗,可以登錄阿里云后臺獲取,由于這是比較私密的信息趟畏,所以我們用 Secret 對象來創(chuàng)建:
$ kubectl create secret generic traefik-alidns-secret --from-literal=ALICLOUD_ACCESS_KEY=<aliyun ak> --from-literal=ALICLOUD_SECRET_KEY=<aliyun sk>--from-literal=ALICLOUD_REGION_ID=cn-beijing -n kube-system
創(chuàng)建完成后將這個 Secret 通過環(huán)境變量配置到 Traefik 的應(yīng)用中贡歧。還有一個值得注意的是驗證通過的證書我們這里存到 /etc/acme/acme.json
文件中,我們一定要將這個文件持久化,否則每次 Traefik 重建后就需要重新認(rèn)證利朵,而 Let’s Encrypt
本身校驗次數(shù)是有限制的律想。最后我們這里完整的 Traefik 的配置資源清單如下所示:
apiVersion: apps/v1
kind: Deployment
metadata:
name: traefik
namespace: kube-system
labels:
app: traefik
spec:
selector:
matchLabels:
app: traefik
template:
metadata:
labels:
app: traefik
spec:
serviceAccountName: traefik
terminationGracePeriodSeconds: 60
tolerations:
- operator: "Exists"
nodeSelector:
kubernetes.io/hostname: ydzs-master
volumes:
- name: acme
hostPath:
path: /data/k8s/traefik/acme
containers:
- image: traefik:2.1.1
name: traefik
ports:
- name: web
containerPort: 80
hostPort: 80
- name: websecure
containerPort: 443
hostPort: 443
args:
- --entryPoints.web.address=:80
- --entryPoints.websecure.address=:443
- --api=true
- --api.dashboard=true
- --ping=true
- --providers.kubernetesingress
- --providers.kubernetescrd
- --log.level=INFO
- --accesslog
# 使用 dns 驗證方式
- --certificatesResolvers.ali.acme.dnsChallenge.provider=alidns
# 郵箱配置
- --certificatesResolvers.ali.acme.email=ych_1024@163.com
# 保存 ACME 證書的位置
- --certificatesResolvers.ali.acme.storage=/etc/acme/acme.json
# 下面是用于測試的ca服務(wù),如果https證書生成成功了哗咆,則移除下面參數(shù)
# - --certificatesresolvers.ali.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory
envFrom:
- secretRef:
name: traefik-alidns-secret
# ALICLOUD_ACCESS_KEY
# ALICLOUD_SECRET_KEY
# ALICLOUD_REGION_ID
volumeMounts:
- name: acme
mountPath: /etc/acme
resources:
requests:
cpu: "50m"
memory: "50Mi"
limits:
cpu: "200m"
memory: "100Mi"
securityContext:
allowPrivilegeEscalation: true
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
readinessProbe:
httpGet:
path: /ping
port: 8080
failureThreshold: 1
initialDelaySeconds: 10
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 2
livenessProbe:
httpGet:
path: /ping
port: 8080
failureThreshold: 3
initialDelaySeconds: 10
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 2
直接更新 Traefik 應(yīng)用即可蜘欲。更新完成后現(xiàn)在我們來修改上面我們的 whoami
應(yīng)用:
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: ingressroutetls
spec:
entryPoints:
- websecure
routes:
- match: Host(`who.qikqiak.com`) && PathPrefix(`/tls`)
kind: Rule
services:
- name: whoami
port: 80
tls:
certResolver: ali
domains:
- main: "*.qikqiak.com"
其他的都不變益眉,只需要將 tls 部分改成我們定義的 ali
這個證書解析器晌柬,如果我們想要生成一個通配符的域名證書的話可以定義 domains
參數(shù)來指定,然后更新 IngressRoute 對象郭脂,這個時候我們再去用 HTTPS 訪問我們的應(yīng)用(當(dāng)然需要將域名在阿里云 DNS 上做解析):
我們可以看到訪問應(yīng)用已經(jīng)是受瀏覽器信任的證書了年碘,查看證書我們還可以發(fā)現(xiàn)該證書是一個通配符的證書。
中間件
中間件是 Traefik2.0 中一個非常有特色的功能展鸡,我們可以根據(jù)自己的各種需求去選擇不同的中間件來滿足服務(wù)屿衅,Traefik 官方已經(jīng)內(nèi)置了許多不同功能的中間件,其中一些可以修改請求莹弊,頭信息涤久,一些負(fù)責(zé)重定向,一些添加身份驗證等等忍弛,而且中間件還可以通過鏈?zhǔn)浇M合的方式來適用各種情況响迂。
同樣比如上面我們定義的 whoami 這個應(yīng)用,我們可以通過 https://who.qikqiak.com/tls
來訪問到應(yīng)用细疚,但是如果我們用 http
來訪問的話呢就不行了蔗彤,就會404了,因為我們根本就沒有簡單80端口這個入口點疯兼,所以要想通過 http
來訪問應(yīng)用的話自然我們需要監(jiān)聽下 web
這個入口點:
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: ingressroutetls-http
spec:
entryPoints:
- web
routes:
- match: Host(`who.qikqiak.com`) && PathPrefix(`/tls`)
kind: Rule
services:
- name: whoami
port: 80
注意這里我們創(chuàng)建的 IngressRoute 的 entryPoints 是 web
然遏,然后創(chuàng)建這個對象,這個時候我們就可以通過 http 訪問到這個應(yīng)用了吧彪。
但是我們?nèi)绻幌M脩敉ㄟ^ https 來訪問應(yīng)用的話呢待侵?按照以前的知識,我們是不是可以讓 http 強制跳轉(zhuǎn)到 https 服務(wù)去姨裸,對的秧倾,在 Traefik 中也是可以配置強制跳轉(zhuǎn)的,只是這個功能現(xiàn)在是通過中間件來提供的了啦扬。如下所示中狂,我們使用 redirectScheme
中間件來創(chuàng)建提供強制跳轉(zhuǎn)服務(wù):
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: redirect-https
spec:
redirectScheme:
scheme: https
然后將這個中間件附加到 http 的服務(wù)上面去,因為 https 的不需要跳轉(zhuǎn):
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: ingressroutetls-http
spec:
entryPoints:
- web
routes:
- match: Host(`who.qikqiak.com`) && PathPrefix(`/tls`)
kind: Rule
services:
- name: whoami
port: 80
middlewares:
- name: redirect-https
這個時候我們再去訪問 http 服務(wù)可以發(fā)現(xiàn)就會自動跳轉(zhuǎn)到 https 去了扑毡。關(guān)于更多中間件的用法可以查看文檔 Traefik Docs胃榕。
灰度發(fā)布
Traefik2.0 的一個更強大的功能就是灰度發(fā)布,灰度發(fā)布我們有時候也會稱為金絲雀發(fā)布(Canary),主要就是讓一部分測試的服務(wù)也參與到線上去勋又,經(jīng)過測試觀察看是否符號上線要求苦掘。
比如現(xiàn)在我們有兩個名為 appv1
和 appv2
的服務(wù),我們希望通過 Traefik 來控制我們的流量楔壤,將 3?4 的流量路由到 appv1鹤啡,1/4 的流量路由到 appv2 去,這個時候就可以利用 Traefik2.0 中提供的帶權(quán)重的輪詢(WRR)來實現(xiàn)該功能蹲嚣,首先在 Kubernetes 集群中部署上面的兩個服務(wù)递瑰。為了對比結(jié)果我們這里提供的兩個服務(wù)一個是 whoami,一個是 nginx隙畜,方便測試抖部。
appv1 服務(wù)的資源清單如下所示:(appv1.yaml)
apiVersion: apps/v1
kind: Deployment
metadata:
name: appv1
spec:
selector:
matchLabels:
app: appv1
template:
metadata:
labels:
use: test
app: appv1
spec:
containers:
- name: whoami
image: containous/whoami
ports:
- containerPort: 80
name: portv1
---
apiVersion: v1
kind: Service
metadata:
name: appv1
spec:
selector:
app: appv1
ports:
- name: http
port: 80
targetPort: portv1
appv2 服務(wù)的資源清單如下所示:(appv2.yaml)
apiVersion: apps/v1
kind: Deployment
metadata:
name: appv2
spec:
selector:
matchLabels:
app: appv2
template:
metadata:
labels:
use: test
app: appv2
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
name: portv2
---
apiVersion: v1
kind: Service
metadata:
name: appv2
spec:
selector:
app: appv2
ports:
- name: http
port: 80
targetPort: portv2
直接創(chuàng)建上面兩個服務(wù):
$ kubectl apply -f appv1.yaml
$ kubectl apply -f appv2.yaml
# 通過下面的命令可以查看服務(wù)是否運行成功
$ kubectl get pods -l use=test
NAME READY STATUS RESTARTS AGE
appv1-58f856c665-shm9j 1/1 Running 0 12s
appv2-ff5db55cf-qjtrf 1/1 Running 0 12s
在 Traefik2.1 中新增了一個 TraefikService
的 CRD 資源,我們可以直接利用這個對象來配置 WRR议惰,之前的版本需要通過 File Provider慎颗,比較麻煩,新建一個描述 WRR 的資源清單:(wrr.yaml)
apiVersion: traefik.containo.us/v1alpha1
kind: TraefikService
metadata:
name: app-wrr
spec:
weighted:
services:
- name: appv1
weight: 3 # 定義權(quán)重
port: 80
kind: Service # 可選言询,默認(rèn)就是 Service
- name: appv2
weight: 1
port: 80
然后為我們的灰度發(fā)布的服務(wù)創(chuàng)建一個 IngressRoute 資源對象:(ingressroute.yaml)
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: wrringressroute
namespace: default
spec:
entryPoints:
- web
routes:
- match: Host(`wrr.qikqiak.com`)
kind: Rule
services:
- name: app-wrr
kind: TraefikService
不過需要注意的是現(xiàn)在我們配置的 Service 不再是直接的 Kubernetes 對象了俯萎,而是上面我們定義的 TraefikService 對象,直接創(chuàng)建上面的兩個資源對象运杭,這個時候我們對域名 wrr.qikqiak.com
做上解析夫啊,去瀏覽器中連續(xù)訪問 4 次,我們可以觀察到 appv1 這應(yīng)用會收到 3 次請求县习,而 appv2 這個應(yīng)用只收到 1 次請求涮母,符合上面我們的 3:1
的權(quán)重配置。
流量復(fù)制
除了灰度發(fā)布之外躁愿,Traefik 2.0 還引入了流量鏡像服務(wù)叛本,是一種可以將流入流量復(fù)制并同時將其發(fā)送給其他服務(wù)的方法,鏡像服務(wù)可以獲得給定百分比的請求同時也會忽略這部分請求的響應(yīng)彤钟。
同樣的在 2.0 中只能通過 FileProvider 進行配置来候,在 2.1 版本中我們已經(jīng)可以通過 TraefikService
資源對象來進行配置了,現(xiàn)在我們部署兩個 whoami 的服務(wù)逸雹,資源清單文件如下所示:
apiVersion: v1
kind: Service
metadata:
name: v1
spec:
ports:
- protocol: TCP
name: web
port: 80
selector:
app: v1
---
kind: Deployment
apiVersion: apps/v1
metadata:
name: v1
labels:
app: v1
spec:
selector:
matchLabels:
app: v1
template:
metadata:
labels:
app: v1
spec:
containers:
- name: v1
image: nginx
ports:
- name: web
containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: v2
spec:
ports:
- protocol: TCP
name: web
port: 80
selector:
app: v2
---
kind: Deployment
apiVersion: apps/v1
metadata:
name: v2
labels:
app: v2
spec:
selector:
matchLabels:
app: v2
template:
metadata:
labels:
app: v2
spec:
containers:
- name: v2
image: nginx
ports:
- name: web
containerPort: 80
直接創(chuàng)建上面的資源對象:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
v1-77cfb86999-wfbl2 1/1 Running 0 94s
v2-6f45d498b7-g6qjt 1/1 Running 0 91s
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
v1 ClusterIP 10.96.218.173 <none> 80/TCP 99s
v2 ClusterIP 10.99.98.48 <none> 80/TCP 96s
現(xiàn)在我們創(chuàng)建一個 IngressRoute 對象营搅,將服務(wù) v1 的流量復(fù)制 50% 到服務(wù) v2,如下資源對象所示:(mirror-ingress-route.yaml)
apiVersion: traefik.containo.us/v1alpha1
kind: TraefikService
metadata:
name: app-mirror
spec:
mirroring:
name: v1 # 發(fā)送 100% 的請求到 K8S 的 Service "v1"
port: 80
mirrors:
- name: v2 # 然后復(fù)制 50% 的請求到 v2
percent: 50
port: 80
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: mirror-ingress-route
namespace: default
spec:
entryPoints:
- web
routes:
- match: Host(`mirror.qikqiak.com`)
kind: Rule
services:
- name: app-mirror
kind: TraefikService # 使用聲明的 TraefikService 服務(wù)梆砸,而不是 K8S 的 Service
然后直接創(chuàng)建這個資源對象即可:
$ kubectl apply -f mirror-ingress-route.yaml
ingressroute.traefik.containo.us/mirror-ingress-route created
traefikservice.traefik.containo.us/mirroring-example created
這個時候我們在瀏覽器中去連續(xù)訪問4次 mirror.qikqiak.com
可以發(fā)現(xiàn)有一半的請求也出現(xiàn)在了 v2
這個服務(wù)中:
TCP
另外 Traefik2.0 已經(jīng)支持了 TCP 服務(wù)的转质,下面我們以 mongo 為例來了解下 Traefik 是如何支持 TCP 服務(wù)得。
簡單 TCP 服務(wù)
首先部署一個普通的 mongo 服務(wù)帖世,資源清單文件如下所示:(mongo.yaml)
apiVersion: apps/v1
kind: Deployment
metadata:
name: mongo-traefik
labels:
app: mongo-traefik
spec:
selector:
matchLabels:
app: mongo-traefik
template:
metadata:
labels:
app: mongo-traefik
spec:
containers:
- name: mongo
image: mongo:4.0
ports:
- containerPort: 27017
---
apiVersion: v1
kind: Service
metadata:
name: mongo-traefik
spec:
selector:
app: mongo-traefik
ports:
- port: 27017
直接創(chuàng)建 mongo 應(yīng)用:
$ kubectl apply -f mongo.yaml
deployment.apps/mongo-traefik created
service/mongo-traefik created
創(chuàng)建成功后就可以來為 mongo 服務(wù)配置一個路由了休蟹。由于 Traefik 中使用 TCP 路由配置需要 SNI
,而 SNI
又是依賴 TLS
的,所以我們需要配置證書才行赂弓,如果沒有證書的話绑榴,我們可以使用通配符 *
進行配置,我們這里創(chuàng)建一個 IngressRouteTCP
類型的 CRD 對象(前面我們就已經(jīng)安裝了對應(yīng)的 CRD 資源):(mongo-ingressroute-tcp.yaml)
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRouteTCP
metadata:
name: mongo-traefik-tcp
spec:
entryPoints:
- mongo
routes:
- match: HostSNI(`*`)
services:
- name: mongo-traefik
port: 27017
要注意的是這里的 entryPoints
部分盈魁,是根據(jù)我們啟動的 Traefik 的靜態(tài)配置中的 entryPoints 來決定的翔怎,我們當(dāng)然可以使用前面我們定義得 80 和 443 這兩個入口點,但是也可以可以自己添加一個用于 mongo 服務(wù)的專門入口點:
......
- image: traefik:2.1.1
name: traefik
ports:
- name: web
containerPort: 80
hostPort: 80
- name: websecure
containerPort: 443
hostPort: 443
- name: mongo
hostPort: 27017
containerPort: 27017
args:
- --entryPoints.web.address=:80
- --entryPoints.websecure.address=:443
- --entryPoints.mongo.address=:27017
......
這里給入口點添加 hostPort
是為了能夠通過節(jié)點的端口訪問到服務(wù)杨耙,關(guān)于 entryPoints 入口點的更多信息赤套,可以查看文檔 entrypoints 了解更多信息。
然后更新 Traefik 后我們就可以直接創(chuàng)建上面的資源對象:
$ mongo-ingressroute-tcp.yaml
ingressroutetcp.traefik.containo.us/mongo-traefik-tcp created
創(chuàng)建完成后按脚,同樣我們可以去 Traefik 的 Dashboard 頁面上查看是否生效:
然后我們配置一個域名 mongo.local
解析到 Traefik 所在的節(jié)點于毙,然后通過 27017 端口來連接 mongo 服務(wù):
$ mongo --host mongo.local --port 27017
mongo(75243,0x1075295c0) malloc: *** malloc_zone_unregister() failed for 0x7fffa56f4000
MongoDB shell version: 2.6.1
connecting to: mongo.local:27017/test
> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB
到這里我們就完成了將 mongo(TCP)服務(wù)暴露給外部用戶了敦冬。
帶 TLS 證書的 TCP
上面我們部署的 mongo 是一個普通的服務(wù)辅搬,然后用 Traefik 代理的,但是有時候為了安全 mongo 服務(wù)本身還會使用 TLS 證書的形式提供服務(wù)脖旱,下面是用來生成 mongo tls 證書的腳本文件:(generate-certificates.sh)
#!/bin/bash
#
# From https://medium.com/@rajanmaharjan/secure-your-mongodb-connections-ssl-tls-92e2addb3c89
set -eu -o pipefail
DOMAINS="${1}"
CERTS_DIR="${2}"
[ -d "${CERTS_DIR}" ]
CURRENT_DIR="$(cd "$(dirname "${0}")" && pwd -P)"
GENERATION_DIRNAME="$(echo "${DOMAINS}" | cut -d, -f1)"
rm -rf "${CERTS_DIR}/${GENERATION_DIRNAME:?}" "${CERTS_DIR}/certs"
echo "== Checking Requirements..."
command -v go >/dev/null 2>&1 || echo "Golang is required"
command -v minica >/dev/null 2>&1 || go get github.com/jsha/minica >/dev/null
echo "== Generating Certificates for the following domains: ${DOMAINS}..."
cd "${CERTS_DIR}"
minica --ca-cert "${CURRENT_DIR}/minica.pem" --ca-key="${CURRENT_DIR}/minica-key.pem" --domains="${DOMAINS}"
mv "${GENERATION_DIRNAME}" "certs"
cat certs/key.pem certs/cert.pem > certs/mongo.pem
echo "== Certificates Generated in the directory ${CERTS_DIR}/certs"
將上面證書放置到 certs 目錄下面堪遂,然后我們新建一個 02-tls-mongo
的目錄,在該目錄下面執(zhí)行如下命令來生成證書:
$ bash ../certs/generate-certificates.sh mongo.local .
== Checking Requirements...
== Generating Certificates for the following domains: mongo.local...
最后的目錄如下所示萌庆,在 02-tls-mongo
目錄下面會生成包含證書的 certs 目錄:
$ tree .
.
├── 01-mongo
│ ├── mongo-ingressroute-tcp.yaml
│ └── mongo.yaml
├── 02-tls-mongo
│ └── certs
│ ├── cert.pem
│ ├── key.pem
│ └── mongo.pem
└── certs
├── generate-certificates.sh
├── minica-key.pem
└── minica.pem
在 02-tls-mongo/certs
目錄下面執(zhí)行如下命令通過 Secret 來包含證書內(nèi)容:
$ kubectl create secret tls traefik-mongo-certs --cert=cert.pem --key=key.pem
secret/traefik-mongo-certs created
然后重新更新 IngressRouteTCP
對象溶褪,增加 TLS 配置:
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRouteTCP
metadata:
name: mongo-traefik-tcp
spec:
entryPoints:
- mongo
routes:
- match: HostSNI(`mongo.local`)
services:
- name: mongo-traefik
port: 27017
tls:
secretName: traefik-mongo-certs
同樣更新后,現(xiàn)在我們直接去訪問應(yīng)用就會被 hang 住践险,因為我們沒有提供證書:
$ mongo --host mongo.local --port 27017
MongoDB shell version: 2.6.1
connecting to: mongo1.local:27017/test
這個時候我們可以帶上證書來進行連接:
$ mongo --host mongo.local --port 27017 --ssl --sslCAFile=../certs/minica.pem --sslPEMKeyFile=./certs/mongo.pem
MongoDB shell version v4.0.3
connecting to: mongodb://mongo.local:27017/
Implicit session: session { "id" : UUID("e7409ef6-8ebe-4c5a-9642-42059bdb477b") }
MongoDB server version: 4.0.14
......
> show dbs;
admin 0.000GB
config 0.000GB
local 0.000GB
可以看到現(xiàn)在就可以連接成功了猿妈,這樣就完成了一個使用 TLS 證書代理 TCP 服務(wù)的功能,這個時候如果我們使用其他的域名去進行連接就會報錯了巍虫,因為現(xiàn)在我們指定的是特定的 HostSNI:
$ mongo --host mongo.k8s.local --port 27017 --ssl --sslCAFile=../certs/minica.pem --sslPEMKeyFile=./certs/mongo.pem
MongoDB shell version v4.0.3
connecting to: mongodb://mongo.k8s.local:27017/
2019-12-29T15:03:52.424+0800 E NETWORK [js] SSL peer certificate validation failed: Certificate trust failure: CSSMERR_TP_NOT_TRUSTED; connection rejected
2019-12-29T15:03:52.429+0800 E QUERY [js] Error: couldn't connect to server mongo.qikqiak.com:27017, connection attempt failed: SSLHandshakeFailed: SSL peer certificate validation failed: Certificate trust failure: CSSMERR_TP_NOT_TRUSTED; connection rejected :
connect@src/mongo/shell/mongo.js:257:13
@(connect):1:6
exception: connect failed
當(dāng)然我們也可以使用 ACME
來為我們提供一個合法的證書彭则,這樣在連接的使用就不需要指定證書了,如下所示:
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRouteTCP
metadata:
name: mongo-traefik-tcp
spec:
entryPoints:
- mongo
routes:
- match: HostSNI(`mongo.qikqiak.com`)
services:
- name: mongo-traefik
port: 27017
tls:
certResolver: ali
domains:
- main: "*.qikqiak.com"
這樣當(dāng)我們連接的時候就只需要如下的命令即可:
$ mongo --host mongo.qikqiak.com --port 27017 --ssl
掃描下面的二維碼(或微信搜索k8s技術(shù)圈)關(guān)注我們的微信公眾帳號占遥,在微信公眾帳號中回復(fù) 加群 即可加入到我們的 kubernetes 討論群里面共同學(xué)習(xí)俯抖。