跳到主要内容

3.Traefik-中间件

Middleware 中间件

中间件是 Traefik2.x 中一个非常有特色的功能,我们可以根据自己的各种需求去选择不同的中间件来满足服务,Traefik 官方已经内置了许多不同功能的中间件,其中一些可以修改请求,头信息,一些负责重定向,一些添加身份验证等等,而且中间件还可以通过链式组合的方式来适用各种情况。

ce481e1095af

跳转 https

同样比如上面我们定义的 whoami 这个应用,我们可以通过 https://who.xinn.com/tls 来访问到应用,但是如果我们用 http 来访问的话呢就不行了,就会 404 了,因为我们根本就没有简单 80 端口这个入口点,所以要想通过 http 来访问应用的话自然我们需要监听下 web 这个入口点:

apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: ingressroutetls-http
spec:
entryPoints:
- web
routes:
- match: Host(`who.xinn.cc`) && PathPrefix(`/tls`)
kind: Rule
services:
- name: whoami
port: 80

注意这里我们创建的 IngressRoute 的 entryPoints 是 web,然后创建这个对象,这个时候我们就可以通过 http 访问到这个应用了。
但是我们如果只希望用户通过 https 来访问应用的话呢?
按照以前的知识,我们是不是可以让 http 强制跳转到 https 服务去,对的,在 Traefik 中也是可以配置强制跳转的,只是这个功能现在是通过中间件来提供的了。
如下所示,我们使用 redirectScheme 中间件来创建提供强制跳转服务:

#whoami-middleware-https.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: redirect-https
spec:
redirectScheme:
scheme: https

然后将这个中间件附加到 http 的服务上面去,因为 https 的不需要跳转:

#whoami-middleware-redirect-https.yaml
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: ingressroutetls-http
spec:
entryPoints:
- web
routes:
- match: Host(`who.xinn.cc`) && PathPrefix(`/tls`)
kind: Rule
services:
- name: whoami
port: 80
middlewares:
- name: redirect-https

这个时候我们再去访问 http 服务可以发现就会自动跳转到 https 去了。

Basic Auth 认证

为了给 Traefik Dashboard 添加密码保护,可以使用 Traefik 的中间件(Middleware) 功能,通过 IngressRoute 结合 BasicAuth 中间件实现基本身份验证。

创建 BasicAuth Secret

首先,需要为基本身份验证创建一个包含用户名和密码的 Kubernetes Secret。可以使用 htpasswd 工具生成密码文件:

# 安装 htpasswd 工具
sudo apt-get install apache2-utils

# 生成带有用户名和密码的 htpasswd 文件
root@master01:/k8s-traefik# htpasswd -nb xin "N47j#u4[£dss" > auth

这个命令会输出类似于:

admin:$apr1$eI8D7f3v$u1JiMd7J4j4OUb9BrK13K1

然后将此加密后的密码保存到 Kubernetes Secret 中:

root@master01:/k8s-traefik# kubectl create secret generic traefik-dashboard-auth   --from-file=users=auth   -n kube-system
secret/traefik-dashboard-auth created

这个命令会在 kube-system 命名空间中创建一个名为 traefik-dashboard-auth 的 Secret。

定义 BasicAuth 中间件

接下来,需要创建一个 Traefik 中间件来使用 BasicAuth 进行身份验证。以下是 BasicAuth 中间件的示例:

apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: traefik-dashboard-auth
namespace: kube-system
spec:
basicAuth:
secret: traefik-dashboard-auth
realm: TraefikDashboard

这个中间件会引用之前创建的 Secret,并为 Dashboard 添加基本身份验证。

更新 IngressRoute 以使用 BasicAuth 中间件

接下来,需要在 IngressRoute 中引用这个身份验证中间件:

apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: traefik-dashboard
namespace: kube-system
spec:
entryPoints:
- web
routes:
- match: Host(`traefik.example.com`) && (PathPrefix(`/dashboard`) || PathPrefix(`/api`))
kind: Rule
services:
- name: api@internal
kind: TraefikService
middlewares:
- name: traefik-dashboard-auth # 添加中间件进行身份验证

在这个配置中:

  • middlewares 部分引用了我们定义的 traefik-dashboard-auth 中间件,从而在访问 Traefik Dashboard 时启用了基本身份验证。

应用这些资源

现在,可以将这些资源应用到 Kubernetes 集群中:

kubectl apply -f traefik-dashboard-auth.yaml
kubectl apply -f traefik-dashboard-ingressroute.yaml

5. 访问带有密码保护的 Dashboard

之后,当访问 Traefik Dashboard 时,会提示您输入用户名和密码:

2c88480005a1


成功登录

9e5d8e03e6e5

URL Rewrite

部署Nexus 应用

接着我们再介绍如何使用 Traefik 来实现 URL Rewrite 操作,我们先部署一个 Nexus 应用,通过 IngressRoute 来暴露服务,对应的资源清单如下所示:

# nexus.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nexus
labels:
app: nexus
spec:
selector:
matchLabels:
app: nexus
template:
metadata:
labels:
app: nexus
spec:
containers:
- image: registry.cn-beijing.aliyuncs.com/xxk8s/nexus:3.20.1
imagePullPolicy: IfNotPresent
name: nexus
ports:
- containerPort: 8081
---
apiVersion: v1
kind: Service
metadata:
labels:
app: nexus
name: nexus
spec:
ports:
- name: nexusport
port: 8081
targetPort: 8081
selector:
app: nexus
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: nexus
namespace: kube-system # 和Service不在同一个命名空间
spec:
entryPoints:
- web
routes:
- kind: Rule
match: Host(`nexus.xinn.cc`)
services:
- kind: Service
name: nexus
namespace: default
port: 8081

由于我们开启了 Traefik 的跨命名空间功能(参数 --providers.kubernetescrd.allowCrossNamespace=true),所以可以引用其他命名空间中的 Service 或者中间件,直接部署上面的应用即可:

$ kubectl apply -f nexus.yaml
$ kubectl get ingressroute -n kube-system
nexus 103s
traefik-dashboard 19h

$ kubectl get pods -l app=nexus
NAME READY STATUS RESTARTS AGE
nexus-6b7649789-lnt6m 1/1 Running 0 2m3s

$ kubectl get svc -l app=nexus
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nexus ClusterIP 10.99.196.220 <none> 8081/TCP 2m19s
10.1.0.16 traefik.xinn.com who.xinn.com who.xinn.cc  nexus.xinn.cc

部署完成后,我们根据 IngressRoute 对象中的配置,只需要将域名 nexus.xinn.cc 解析到 Traefik 的节点即可访问:
8811-f100292a9ae5

到这里我们都可以很简单的来完成,同样的现在我们有一个需求是目前我们只有一个域名可以使用,但是我们有很多不同的应用需要暴露,这个时候我们就只能通过 PATH 路径来进行区分了,比如我们现在希望当我们访问 http:/nexus.xinn.com/foo 的时候就是访问的我们的 Nexus 这个应用,当路径是 /bar 开头的时候是其他应用,这种需求是很正常的,这个时候我们就需要来做 URL Rewrite 了。

StripPrefix中间件

首先我们使用 StripPrefix 这个中间件,这个中间件的功能是在转发请求之前从路径中删除前缀,在使用中间件的时候我们只需要理解中间件操作的都是我们直接的请求即可,并不是真实的应用接收到请求过后来进行修改。
现在我们添加一个如下的中间件:

#traefik-middleware-stripprefix.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: strip-foo-path
namespace: default # 注意这里的中间件我们定义在default命名空间下面
spec:
stripPrefix:
prefixes:
- /foo
root@master01:/k8s-traefik# kubectl apply -f traefik-middleware-stripprefix.yaml
middleware.traefik.containo.us/strip-foo-path created

修改IngressRoute

然后现在我们就需要从 http:/nexus.xinn.cc/foo 请求中去匹配 /foo 的请求,把这个路径下面的请求应用到上面的中间件中去,因为最终我们的 Nexus 应用接收到的请求是不会带有/foo 路径的,所以我们需要在请求到达应用之前将这个前缀删除,更新 IngressRoute 对象:

#nexus-stripprefix.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: nexus
namespace: kube-system
spec:
entryPoints:
- web
routes:
- kind: Rule
match: Host(`nexus.xinn.cc`) && PathPrefix(`/foo`) # 匹配 /foo 路径
middlewares:
- name: strip-foo-path
namespace: default # 由于我们开启了traefik的跨命名空间功能,所以可以引用其他命名空间中的中间件
services:
- kind: Service
name: nexus
namespace: default
port: 8081

创建中间件更新完成上面的 IngressRoute 对象后,这个时候我们前往浏览器中访问 http:/nexus.xinn.cc/foo,这个时候发现我们的页面任何样式都没有了:

9cd1e177abe3

我们通过 Chrome 浏览器的 Network 可以查看到 /foo 路径的请求是 200 状态码,但是其他的静态资源对象确全都是 404 了,这是为什么呢?我们仔细观察上面我们的 IngressRoute 资源对象,我们现在是不是只匹配了 /foo 的请求,而我们的静态资源是 /static 路径开头的,当然就匹配不到了,所以就出现了 404,所以我们只需要加上这个 /static 路径的匹配就可以了,同样更新 IngressRoute 对象:

apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: nexus
namespace: kube-system
spec:
entryPoints:
- web
routes:
- kind: Rule
match: Host(`nexus.xinn.cc`) && PathPrefix(`/foo`)
middlewares:
- name: strip-foo-path
namespace: default
services:
- kind: Service
name: nexus
namespace: default
port: 8081
- kind: Rule
match: Host(`nexus.xinn.cc`) && PathPrefix(`/static`) # 匹配 /static 的请求
services:
- kind: Service
name: nexus
namespace: default
port: 8081

然后更新 IngressRoute 资源对象,这个时候再次去访问应用,可以发现页面样式已经正常了,也可以正常访问应用了:

6f75d1a6dee6

但进入应用后发现还是有错误提示信息,通过 Network 分析发现还有一些 /service 开头的请求是 404,当然我们再加上这个前缀的路径即可:

apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: nexus
namespace: kube-system
spec:
entryPoints:
- web
routes:
- kind: Rule
match: Host(`nexus.xinn.cc`) && PathPrefix(`/foo`)
middlewares:
- name: strip-foo-path
namespace: default
services:
- kind: Service
name: nexus
namespace: default
port: 8081
- kind: Rule
match: Host(`nexus.xinn.cc`) && (PathPrefix(`/static`) || PathPrefix(`/service`)) # 匹配 /static 和 /service 的请求
services:
- kind: Service
name: nexus
namespace: default
port: 8081

更新后,再次访问应用就已经完全正常了:

403b20a4b03f

Traefik2.X 版本中的中间件功能非常强大,基本上官方提供的系列中间件可以满足我们大部分需求了,其他中间件的用法,可以参考文档:https://www.qikqiak.com/traefik-book/middlewares/overview/