Step by Step!教你如何在k3s集群上使用Traefik 2.x
本文来自边缘计算k3s社区
作者简介
Cello Spring,瑞士人。从电子起步,拥有电子工程学位。尔后开始关注计算机领域,在软件开发领域拥有多年的工作经验。
Traefik是一个十分可靠的云原生动态反向代理。轻量级Kubernetes发行版K3s早在去年就已经内置Traefik,将其作为集群的默认反向代理和Ingress Controller。然而,在本文成文时K3s中的默认内置Traefik版本为v1.7.14。这一版本固然也能很好地运行,但还是少了一些有用的功能。我最想用的功能是为正在使用的Ingress Route自动生成Let’s Encrypt证书。而使用Traefik 2.x版本可以获得这一功能,甚至还有更多其他功能。那么,我们来看看如何使用K3s设置并使用新版本的Traefik。
本文的目标是设置一个新的K3s集群、安装Traefik 2.x版本并配置一些Ingress,这些Ingress将由自动生成的Let’s Encrypt证书保护。
以下是我们将要进行的步骤:
-
在Civo上创建一个极小的K3s集群
-
将我们的域(我会使用我的虚拟域celleri.ch)指向集群IP
-
安装Klipper LB作为我们的LoadBalancer
-
在集群上安装Traefik v2
-
部署一个小型工作负载(whoami)到集群上
-
创建一个Traefik ingress到服务(分为有TLS termination或没有)
-
使用Traefik 中间件以通过基本身份验证访问Traefik Dashboard
创建Civo集群
为此,请到Civo(civo.com/)并创建一个只有2个节点的超小型集群。如果你还没有账户,可以注册并申请KUBE100 Beta项目以使用其提供的Kubernetes产品。
需要确保我们不使用基本设置安装Traefik(在“Architecture”选项卡上取消选择Traefik)
大约2分钟之后,我们将获得以下集群:
接下来,我们需要记下master节点的IP地址并下载kubeconfig文件。在本例中,它被命名为civo-k3s-with-traefik2-kubeconfig,因为我们将集群命名为k3s-with-traefik2。为了使用kubectl从命令行中访问该集群,我们需要将环境变量指向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
正如我们所见,带有1个master节点和1个worker节点的集群已经准备完毕!继续进行下一步。
将域名Celleri.ch指向新的集群IP地址
最近一段时间,我一直使用Cloudflare(cloudflare.com/dns/)的DNS服务来处理Kubernetes。它十分可靠,有友好的用户界面并且我使用的基本服务都是免费的。
在Cloudflare中我们应用以下设置:
本示例中,我们不想为可能用到的每个子域都创建一个CNAME条目,因此我们在此处创建一个通配符(*)条目作为CNAME。Traefik将确保稍后将流量路由到正确的位置。
安装Klipper LB作为我们的LoadBalancer
默认情况下,K3s内置的Traefik为v1.7.x版本。默认安装还部署了来自Rancher的内部LoadBalancer,Klipper LB。由于在设置集群时,我们没有安装Traefik,因此我们现在必须自己手动安装Klipper LB。
Klipper会将自己挂接到集群节点的主机端口上,并使用端口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似乎可以与在其中运行的3个容器(每个主机端口一个容器)一起工作。接下来,我们开始安装Traefik v2。
在集群中安装Traefik v2
Traefik v2附带了许多CRD,这似乎是扩展Kubernetes对象的一种新方法。我还没有完全把精力放在这些CRD上面,但是无论如何我们都要使用它们。你可以在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
这个命令应该会创建5个CRD。
为了让Traefik可以做它所需要做的事情,我们需要一个clusterrole和一个clusterrolebinding。我们可以使用以下命令:
# apply clusterrole and clusterrolebinding
kubectl apply -f 01-traefik-crd/traefik-clusterrole.yaml
请注意:我们将在命名空间kube-system中为ServiceAccount进行clusterrolebinding,因为稍后我们会将流量安装到该命名空间中。
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
这应该为我们提供一个LoadBalancer类型的服务,该服务具有集群的master节点的外部地址:
# 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
部署一个小型工作负载(whoami)到集群
现在是时候在我们的集群中创建一个服务并尝试通过我们的Traefik代理从外部调用它。本示例中我使用的是whoami服务,它也用于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
好像已经可以运行了。现在要到激动人心的部分了,Traefik Ingress。
为服务创建两个Traefik Ingress(有/没有TLS Termination)
我们想要从外部访问whoami服务,以让我们最终可以定义IngressRoute对象。没错,那些对象是在我们之前安装的CRD中定义的。现在它们派上用场了。我们按照以下方式部署IngressRoutes:
kubectl apply -f 03-workload/whoami-ingress-route.yaml
正如你在定义中所看到的,我们使用tls.certResolver=default指定了一个路由(附带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
现在启动浏览器并访问地址http://celleri.ch/notls,查看即将发生的事情——pod正在响应:
那么https://celleri.ch/tls发生了什么呢?它也在正常运行,但是告诉我们第一个连接不安全。如果我们查看证书,我们就能找到原因:
为了防止因我们的安装程序无法正常工作而使生产服务器受到许多请求的困扰,我们一开始没有使用Let’s Encrypt,而在Traefik Service中使用staging服务器。因此,我们接下来要更改这一设置并使用生产服务器获得一个真实的证书。
更改certresolver以使用Let’s Encrypt生产服务器
在我们定义的Traefik Deployment中,我们有以下参数:
... 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的方法来使用名为default的certificateresolvers。此外,我们还需要提供我们的邮件和证书的存储。我们在前文中还提到我们要使用staging caserver。
重点:在我们的部署中,我们没有存储提供程序或者volume。这意味着,部署重新加载后我们的证书就会消失。证书仅存在于我们的pod内存中。在生产环境中,我们必须解决这一问题并提供一个volume。
好的,让我们注释掉caserver行,然后重新部署Traefik deployment,看看我们是否获得了真实的证书:
... 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
一会儿之后,我们将获得一个有效证书:
在这之后,我们可以稍稍放轻松了。但是我们想更近一步,Traefik v2还有一个不错的dashboard,可以查看正在运行的所有Ingress内容。但是我们并不希望每个人都能访问我们的dashboard。有一些基本的身份验证可以更好地保护我们的dashboard。
使用Traefik 中间件以通过基本身份验证访问Traefik Dashboard
在Traefik 2.x中引入了一个新机制——中间件,该机制在处理传入请求时可以帮助我们完成许多任务。我们将使用basicAuth中间件来保护Traefik dashboard并将其暴露给外部。
首先,我们需要使用我们的用户名和哈希密码创建一个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内创建该Secret,因为Traefik Service及其Dashboard也位于此命名空间中。
然后我们部署该中间件以及IngressRoute到我们的集群:
kubectl apply -f 04-traefik-dashboard/traefik-admin-withauth.yaml
现在,我们访问https://traefik.celleri.ch,出现登陆提示:
使用正确的凭据,我们将访问Traefik v2的dashboard:
并且我们会获得许多关于我们Ingress Route的信息:
结 语
这就是教程的全部。在研究过程中,我没有发现太多关于如何在k3s中设置Traefik v2的示例,特别是Klipper LB的部分从未被提及。这就是为什么我想向大家分享我的经验,希望它能够帮助到你,退一万步来说,至少它对我的将来会有所帮助。
参考链接:
Github :
https://github.com/cellerich/k3s-with-traefik2
关于Rancher的Klipper LB:
https://github.com/rancher/klipper-lb
Traefik中间件文档: