本文來(lái)自邊緣計(jì)算k3s社區(qū)
作者簡(jiǎn)介
Cello Spring,瑞士人。從電子起步,擁有電子工程學(xué)位。爾后開(kāi)始關(guān)注計(jì)算機(jī)領(lǐng)域,在軟件開(kāi)發(fā)領(lǐng)域擁有多年的工作經(jīng)驗(yàn)。
Traefik是一個(gè)十分可靠的云原生動(dòng)態(tài)反向代理。輕量級(jí)Kubernetes發(fā)行版K3s早在去年就已經(jīng)內(nèi)置Traefik,將其作為集群的默認(rèn)反向代理和Ingress Controller。然而,在本文成文時(shí)K3s中的默認(rèn)內(nèi)置Traefik版本為v1.7.14。這一版本固然也能很好地運(yùn)行,但還是少了一些有用的功能。我最想用的功能是為正在使用的Ingress Route自動(dòng)生成Let’s Encrypt證書(shū)。而使用Traefik 2.x版本可以獲得這一功能,甚至還有更多其他功能。那么,我們來(lái)看看如何使用K3s設(shè)置并使用新版本的Traefik。
本文的目標(biāo)是設(shè)置一個(gè)新的K3s集群、安裝Traefik 2.x版本并配置一些Ingress,這些Ingress將由自動(dòng)生成的Let’s Encrypt證書(shū)保護(hù)。
以下是我們將要進(jìn)行的步驟:
在Civo上創(chuàng)建一個(gè)極小的K3s集群
將我們的域(我會(huì)使用我的虛擬域celleri.ch)指向集群IP
安裝Klipper LB作為我們的LoadBalancer
在集群上安裝Traefik v2
部署一個(gè)小型工作負(fù)載(whoami)到集群上
創(chuàng)建一個(gè)Traefik ingress到服務(wù)(分為有TLS termination或沒(méi)有)
使用Traefik 中間件以通過(guò)基本身份驗(yàn)證訪問(wèn)Traefik Dashboard
創(chuàng)建Civo集群
為此,請(qǐng)到Civo(civo.com/)并創(chuàng)建一個(gè)只有2個(gè)節(jié)點(diǎn)的超小型集群。如果你還沒(méi)有賬戶,可以注冊(cè)并申請(qǐng)KUBE100 Beta項(xiàng)目以使用其提供的Kubernetes產(chǎn)品。
需要確保我們不使用基本設(shè)置安裝Traefik(在“Architecture”選項(xiàng)卡上取消選擇Traefik)
大約2分鐘之后,我們將獲得以下集群:
接下來(lái),我們需要記下master節(jié)點(diǎn)的IP地址并下載kubeconfig文件。在本例中,它被命名為civo-k3s-with-traefik2-kubeconfig,因?yàn)槲覀儗⒓好麨閗3s-with-traefik2。為了使用kubectl從命令行中訪問(wèn)該集群,我們需要將環(huán)境變量指向kubeconfig文件并將上下文更改至我們的新集群。
# set env variable with new cluster config
export KUBECONFIG=./civo-k3s-with-traefik2-kubeconfig
kubectl config use-context k3s-with-traefik2
#check the available nodes
kubectl get nodes
NAME STATUS ROLES AGE VERSION
kube-master-de56 Ready master 9m15s v1.16.3-k3s.2
kube-node-40e7 Ready 7m21s v1.16.3-k3s.2
正如我們所見(jiàn),帶有1個(gè)master節(jié)點(diǎn)和1個(gè)worker節(jié)點(diǎn)的集群已經(jīng)準(zhǔn)備完畢!繼續(xù)進(jìn)行下一步。
將域名Celleri.ch指向新的集群IP地址
最近一段時(shí)間,我一直使用Cloudflare(cloudflare.com/dns/)的DNS服務(wù)來(lái)處理Kubernetes。它十分可靠,有友好的用戶界面并且我使用的基本服務(wù)都是免費(fèi)的。
在Cloudflare中我們應(yīng)用以下設(shè)置:

本示例中,我們不想為可能用到的每個(gè)子域都創(chuàng)建一個(gè)CNAME條目,因此我們?cè)诖颂巹?chuàng)建一個(gè)通配符(*)條目作為CNAME。Traefik將確保稍后將流量路由到正確的位置。
安裝Klipper LB作為我們的LoadBalancer
默認(rèn)情況下,K3s內(nèi)置的Traefik為v1.7.x版本。默認(rèn)安裝還部署了來(lái)自Rancher的內(nèi)部LoadBalancer,Klipper LB。由于在設(shè)置集群時(shí),我們沒(méi)有安裝Traefik,因此我們現(xiàn)在必須自己手動(dòng)安裝Klipper LB。
Klipper會(huì)將自己掛接到集群節(jié)點(diǎn)的主機(jī)端口上,并使用端口80、443和8080。
你可以在我的Github Repo里找到我所提到的所有文件:
https://github.com/cellerich/k3s-with-traefik2
# install KlipperLB
kubectl apply -f 00-klipper-lb/klipper.yaml
# see if klipper hooked up to the host ports and is working
kc get pods --all-namespaces | grep svclb
kube-system svclb-traefik-gc8lg 3/3 Running 0 96s
kube-system svclb-traefik-pqbzb 3/3 Running 0 96s
這些Pod似乎可以與在其中運(yùn)行的3個(gè)容器(每個(gè)主機(jī)端口一個(gè)容器)一起工作。接下來(lái),我們開(kāi)始安裝Traefik v2。
在集群中安裝Traefik v2
Traefik v2附帶了許多CRD,這似乎是擴(kuò)展Kubernetes對(duì)象的一種新方法。我還沒(méi)有完全把精力放在這些CRD上面,但是無(wú)論如何我們都要使用它們。你可以在Traefik文檔(https://docs.traefik.io/v2.0/user-guides/crd-acme/)中找到正確的yaml文件,或者你可以使用我在Github repo上提供的01-traefik-crd/traefik-crd.yaml文件。
# apply traefik crd's
kubectl apply -f 01-traefik-crd/traefik-crd.yaml
這個(gè)命令應(yīng)該會(huì)創(chuàng)建5個(gè)CRD。
為了讓Traefik可以做它所需要做的事情,我們需要一個(gè)clusterrole和一個(gè)clusterrolebinding。我們可以使用以下命令:
# apply clusterrole and clusterrolebinding
kubectl apply -f 01-traefik-crd/traefik-clusterrole.yaml
請(qǐng)注意:我們將在命名空間kube-system中為ServiceAccount進(jìn)行clusterrolebinding,因?yàn)樯院笪覀儠?huì)將流量安裝到該命名空間中。
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: traefik-ingress-controller
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: traefik-ingress-controller
subjects:
- kind: ServiceAccount
name: traefik-ingress-controller
namespace: kube-system
最后,我們把Traefik Service、ServiceAccount以及Deployment部署到集群中:
kubectl apply -f 02-traefik-service/traefik.yaml
這應(yīng)該為我們提供一個(gè)LoadBalancer類(lèi)型的服務(wù),該服務(wù)具有集群的master節(jié)點(diǎn)的外部地址:
# get traefik service
kubectl get svc -n kube-system | grep traefik
traefik LoadBalancer 192.168.211.177 185.136.232.122 80:32286/TCP,443:30108/TCP,8080:30582/TCP 3m43s
部署一個(gè)小型工作負(fù)載(whoami)到集群
現(xiàn)在是時(shí)候在我們的集群中創(chuàng)建一個(gè)服務(wù)并嘗試通過(guò)我們的Traefik代理從外部調(diào)用它。本示例中我使用的是whoami服務(wù),它也用于Traefik文檔中的所有示例。讓我們部署它:
# deploy `whoami` in namespace `default`
kubectl apply -f 03-workload/whoami-service.yaml
#check the deployment
kubectl get pods | grep whoami
whoami-bd6b677dc-lfxbx 1/1 Running 0 5m37s
whoami-bd6b677dc-92jzj 1/1 Running 0 5m37s
好像已經(jīng)可以運(yùn)行了?,F(xiàn)在要到激動(dòng)人心的部分了,Traefik Ingress。
為服務(wù)創(chuàng)建兩個(gè)Traefik Ingress(有/沒(méi)有TLS Termination)
我們想要從外部訪問(wèn)whoami服務(wù),以讓我們最終可以定義IngressRoute對(duì)象。沒(méi)錯(cuò),那些對(duì)象是在我們之前安裝的CRD中定義的?,F(xiàn)在它們派上用場(chǎng)了。我們按照以下方式部署IngressRoutes:
kubectl apply -f 03-workload/whoami-ingress-route.yaml
正如你在定義中所看到的,我們使用tls.certResolver=default指定了一個(gè)路由(附帶PathPrefix '/tls')。該certResolver在我們的02-traefik-service / traefik.yaml文件中定義。
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: ingressroute-notls
namespace: default
spec:
entryPoints:
- web
routes:
- match: Host(`celleri.ch`) && PathPrefix(`/notls`)
kind: Rule
services:
- name: whoami
port: 80
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: ingressroute-tls
namespace: default
spec:
entryPoints:
- websecure
routes:
- match: Host(`celleri.ch`) && PathPrefix(`/tls`)
kind: Rule
services:
- name: whoami
port: 80
tls:
certResolver: default
現(xiàn)在啟動(dòng)瀏覽器并訪問(wèn)地址http://celleri.ch/notls,查看即將發(fā)生的事情——pod正在響應(yīng):
那么https://celleri.ch/tls發(fā)生了什么呢?它也在正常運(yùn)行,但是告訴我們第一個(gè)連接不安全。如果我們查看證書(shū),我們就能找到原因:
為了防止因我們的安裝程序無(wú)法正常工作而使生產(chǎn)服務(wù)器受到許多請(qǐng)求的困擾,我們一開(kāi)始沒(méi)有使用Let’s Encrypt,而在Traefik Service中使用staging服務(wù)器。因此,我們接下來(lái)要更改這一設(shè)置并使用生產(chǎn)服務(wù)器獲得一個(gè)真實(shí)的證書(shū)。
更改certresolver以使用Let’s Encrypt生產(chǎn)服務(wù)器
在我們定義的Traefik Deployment中,我們有以下參數(shù):
... cropped for readability ...
spec:
serviceAccountName: traefik-ingress-controller
containers:
- name: traefik
image: traefik:v2.0
args:
- --api.insecure
- --accesslog
- --entrypoints.web.Address=:80
- --entrypoints.websecure.Address=:443
- --providers.kubernetescrd
- --certificatesresolvers.default.acme.tlschallenge
- --certificatesresolvers.default.acme.email=me@myself.com
- --certificatesresolvers.default.acme.storage=acme.json
# Please note that this is the staging Let's Encrypt server.
- --certificatesresolvers.default.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory
... cropped for readability ...
我們告訴Traefik使用tlschallenge的方法來(lái)使用名為default的certificateresolvers。此外,我們還需要提供我們的郵件和證書(shū)的存儲(chǔ)。我們?cè)谇拔闹羞€提到我們要使用staging caserver。
重點(diǎn):在我們的部署中,我們沒(méi)有存儲(chǔ)提供程序或者volume。這意味著,部署重新加載后我們的證書(shū)就會(huì)消失。證書(shū)僅存在于我們的pod內(nèi)存中。在生產(chǎn)環(huán)境中,我們必須解決這一問(wèn)題并提供一個(gè)volume。
好的,讓我們注釋掉caserver行,然后重新部署Traefik deployment,看看我們是否獲得了真實(shí)的證書(shū):
... cropped for readability ...
spec:
serviceAccountName: traefik-ingress-controller
containers:
- name: traefik
image: traefik:v2.0
args:
- --api.insecure
- --accesslog
- --entrypoints.web.Address=:80
- --entrypoints.websecure.Address=:443
- --providers.kubernetescrd
- --certificatesresolvers.default.acme.tlschallenge
- --certificatesresolvers.default.acme.email=me@myself.com
- --certificatesresolvers.default.acme.storage=acme.json
# Please note that this is the staging Let's Encrypt server.
# - --certificatesresolvers.default.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory
... cropped for readability ...
# deploy the changed file
kubectl apply -f 02-traefik-service/traefik.yaml
一會(huì)兒之后,我們將獲得一個(gè)有效證書(shū):
在這之后,我們可以稍稍放輕松了。但是我們想更近一步,Traefik v2還有一個(gè)不錯(cuò)的dashboard,可以查看正在運(yùn)行的所有Ingress內(nèi)容。但是我們并不希望每個(gè)人都能訪問(wèn)我們的dashboard。有一些基本的身份驗(yàn)證可以更好地保護(hù)我們的dashboard。
使用Traefik 中間件以通過(guò)基本身份驗(yàn)證訪問(wèn)Traefik Dashboard
在Traefik 2.x中引入了一個(gè)新機(jī)制——中間件,該機(jī)制在處理傳入請(qǐng)求時(shí)可以幫助我們完成許多任務(wù)。我們將使用basicAuth中間件來(lái)保護(hù)Traefik dashboard并將其暴露給外部。
首先,我們需要使用我們的用戶名和哈希密碼創(chuàng)建一個(gè)Secret,以便basicAuth 中間件在以后使用:
# create user:password file 'user'
htpasswd -c ./user cellerich
# enter password twice...
# create secret from password file
kubectl create secret generic traefik-admin --from-file user -n kube-system
確保在命名空間kube-system內(nèi)創(chuàng)建該Secret,因?yàn)門(mén)raefik Service及其Dashboard也位于此命名空間中。
然后我們部署該中間件以及IngressRoute到我們的集群:
kubectl apply -f 04-traefik-dashboard/traefik-admin-withauth.yaml
現(xiàn)在,我們?cè)L問(wèn)https://traefik.celleri.ch,出現(xiàn)登陸提示:
使用正確的憑據(jù),我們將訪問(wèn)Traefik v2的dashboard:
并且我們會(huì)獲得許多關(guān)于我們Ingress Route的信息:
結(jié) 語(yǔ)
這就是教程的全部。在研究過(guò)程中,我沒(méi)有發(fā)現(xiàn)太多關(guān)于如何在k3s中設(shè)置Traefik v2的示例,特別是Klipper LB的部分從未被提及。這就是為什么我想向大家分享我的經(jīng)驗(yàn),希望它能夠幫助到你,退一萬(wàn)步來(lái)說(shuō),至少它對(duì)我的將來(lái)會(huì)有所幫助。
參考鏈接:
Github :
https://github.com/cellerich/k3s-with-traefik2
關(guān)于Rancher的Klipper LB:
https://github.com/rancher/klipper-lb
Traefik中間件文檔: