Traefik Middlewares简介
官方文档
Traefik Middlewares
是一个处于路由和后端服务之前的中间件,在外部流量进入 Traefik
,且路由规则匹配成功后,将流量发送到对应的后端服务前,先将其发给中间件进行一系列处理(类似于过滤器链 Filter,进行一系列处理),例如,添加 Header 头信息、鉴权、流量转发、处理访问路径前缀、IP 白名单等等,经过一个或者多个中间件处理完成后,再发送给后端服务,这个就是中间件的作用。 Traefik内置了很多不同功能的Middleware,主要是针对HTTP和TCP,这里挑选几个比较常用的进行演示。
重定向-redirectScheme
官方文档
我们定义的 whoami 这个应用,我们可以通过 https://whoami.od.com/tls
来访问到应用,但是如果我们用 http
来访问的话呢就不行了,就会 404 了,因为我们根本就没有简单 80 端口这个入口点,所以要想通过 http
来访问应用的话自然我们需要监听下 web
这个入口点:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| cat > tls-https.yml <<EOF apiVersion: traefik.containo.us/v1alpha1 kind: IngressRoute metadata: name: ingressroutetls-http spec: entryPoints: - web routes: - match: Host(\`whoami.od.com\`) && PathPrefix(\`/tls\`) kind: Rule services: - name: whoami port: 80 EOF
|
这里我们创建的 IngressRoute 的 entryPoints 是 web
,然后创建这个对象,这个时候我们就可以通过 http 访问到这个应用了。
但是我们如果只希望用户通过 https 来访问应用的话呢?按照以前的知识,我们是不是可以让 http 强制跳转到 https 服务去,对的,在 Traefik 中也是可以配置强制跳转的,只是这个功能现在是通过中间件来提供的了。如下所示,我们使用 redirectScheme
中间件来创建提供强制跳转服务:
1 2 3 4 5 6 7 8 9 10
| cat >> tls-https.yml <<EOF --- apiVersion: traefik.containo.us/v1alpha1 kind: Middleware metadata: name: redirect-https spec: redirectScheme: scheme: https EOF
|
然后将这个中间件附加到 http 的服务上面去,因为 https 的不需要跳转
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| cat >> tls-https.yml <<EOF --- apiVersion: traefik.containo.us/v1alpha1 kind: IngressRoute metadata: name: ingressroutetls-http spec: entryPoints: - web routes: - match: Host(\`whoami.od.com\`) && PathPrefix(\`/tls\`) kind: Rule services: - name: whoami port: 80 middlewares: - name: redirect-https EOF
|
这个时候我们再去访问 http 服务可以发现自动 307 重定向到了 https。
1 2 3 4 5 6 7 8
| $ curl -I http://whoami.od.com/tls HTTP/1.1 307 Temporary Redirect Server: nginx/1.18.0 Date: Thu, 24 Aug 2023 06:14:23 GMT Content-Type: text/plain; charset=utf-8 Content-Length: 18 Connection: keep-alive Location: https://whoami.od.com/tls
|
去除请求路径前缀-stripPrefix
官方文档
假设现在有这样一个需求,当访问 http://myapp.test.com/v1
时,流量调度至 myapp1。当访问 http://myapp.test.com/v2
时,流量调度至 myapp2。这种需求是非常常见的,在 NGINX 中,我们可以配置多个 Location 来定制规则,使用 Traefik 也可以这么做。但是定制不同的前缀后,由于应用本身并没有这些前缀,导致请求返回 404,这时候我们就需要对请求的 path 进行处理。
创建一个 IngressRoute,并设置两条规则,根据不同的访问路径代理至相对应的 service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| apiVersion: traefik.containo.us/v1alpha1 kind: IngressRoute metadata: name: myapp spec: entryPoints: - web routes: - match: Host(`myapp.test.com`) && PathPrefix(`/v1`) kind: Rule services: - name: myapp1 port: 80 middlewares: - name: prefix-url-middleware - match: Host(`myapp.test.com`) && PathPrefix(`/v2`) kind: Rule services: - name: myapp2 port: 80 middlewares: - name: prefix-url-middleware --- apiVersion: traefik.containo.us/v1alpha1 kind: Middleware metadata: name: prefix-url-middleware spec: stripPrefix: prefixes: - /v1 - /v2
|
部署测试
1 2 3 4 5 6 7 8 9 10
| [root@k8s-node1 ~]# curl http://myapp.test.com/v1 Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a> [root@k8s-node1 ~]# curl http://myapp.test.com/v2 Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
[root@k8s-node1 ~]# kubectl logs -l app=myapp1 | tail -2 # 未添加插件的访问路径为 /v1/ 10.244.36.64 - - [19/Apr/2023:08:02:03 +0000] "GET /v1/ HTTP/1.1" 404 169 "-" "curl/7.29.0" "1.1.1.1" # 添加插件后的访问路径为 / 10.244.36.64 - - [19/Apr/2023:08:04:31 +0000] "GET / HTTP/1.1" 200 65 "-" "curl/7.29.0" "1.1.1.1"
|
白名单-IPWhiteList
官方文档
为提高安全性,通常情况下一些管理员界面会设置 ip 访问白名单,只希望个别用户可以访问。
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| cat > ip-white.yml <<EOF apiVersion: traefik.containo.us/v1alpha1 kind: IngressRoute metadata: name: myapp spec: entryPoints: - web routes: - match: Host(\`whoami.od.com\`) && PathPrefix(\`/notls\`) kind: Rule services: - name: whoami port: 80 middlewares: - name: ip-white-list-middleware --- apiVersion: traefik.containo.us/v1alpha1 kind: Middleware metadata: name: ip-white-list-middleware spec: ipWhiteList: sourceRange: - 127.0.0.1/32 - 10.1.1.11/32 ipStrategy: depth: 1 EOF
|
非白名单中IP访问则会出现403。
基础用户认证-basicAuth
官方文档
通常企业安全要求规范除了要对管理员页面限制访问ip外,还需要添加账号密码认证,而 traefik 默认没有提供账号密码认证功能,此时就可以通过BasicAuth 中间件完成用户认证,只有认证通过的授权用户才可以访问页面。
安装 htpasswd 工具生成密码文件
1 2 3 4 5 6
| yum -y install httpd-tools.x86_64
# 密码文件名称为htpasswd htpasswd -bc htpasswd wangxiansen 123456
kubectl create secret generic basic-auth --from-file=htpasswd
|
创建 ingressroute,使用 basicAuth 中间件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| cat > basic-auth.yml <<EOF apiVersion: traefik.containo.us/v1alpha1 kind: IngressRoute metadata: name: myapp spec: entryPoints: - web routes: - match: Host(\`whoami.od.com\`) && PathPrefix(\`/notls\`) kind: Rule services: - name: whoami port: 80 middlewares: - name: basic-auth-middleware --- apiVersion: traefik.containo.us/v1alpha1 kind: Middleware metadata: name: basic-auth-middleware spec: basicAuth: secret: basic-auth EOF
|
访问测试,可以看到弹出界面提示需要输入用户名和密码,输入后回车显示正常页面
官方文档
为了提高业务的安全性,安全团队会定期进行漏洞扫描,其中有些 web 漏洞就需要通过修改响应头处理,traefik 的 Headers 中间件不仅可以修改返回客户端的响应头信息,还能修改反向代理后端 service 服务的请求头信息。
例如对 https://whoami.od.com/tls
提高安全策略,强制启用HSTS HSTS:即 HTTP 严格传输安全响应头,收到该响应头的浏览器会在 63072000s(约 2 年)的时间内,只要访问该网站,即使输入的是 http,浏览器会自动跳转到 https。(HSTS 是浏览器端的跳转,之前的HTTP 重定向到 HTTPS是服务器端的跳转)
创建 ingressRoute 和 headers 中间件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| cat > headers.yml <<EOF apiVersion: traefik.containo.us/v1alpha1 kind: IngressRoute metadata: name: headers-tls-demo spec: entryPoints: - web - websecure routes: - match: Host(\`whoami.od.com\`) && PathPrefix(\`/tls\`) kind: Rule services: - name: whoami port: 80 middlewares: - name: hsts-header-middleware tls: secretName: who-tls --- apiVersion: traefik.containo.us/v1alpha1 kind: Middleware metadata: name: hsts-header-middleware spec: headers: customResponseHeaders: Strict-Transport-Security: 'max-age=63072000' EOF
|
访问测试
1 2 3 4 5 6
| $ curl -Ik https://whoami.od.com/tls HTTP/1.1 200 OK Content-Length: 363 Content-Type: text/plain; charset=utf-8 Date: Thu, 24 Aug 2023 06:52:41 GMT Strict-Transport-Security: max-age=63072000 # headers 插件添加的响应头
|
限流-rateLimit
官方文档
在实际生产环境中,流量限制也是经常用到的,它可以用作安全目的,比如可以减慢暴力密码破解的速率。通过将传入请求的速率限制为真实用户的典型值,并标识目标URL地址(通过日志),还可以用来抵御 DDOS 攻击。更常见的情况,该功能被用来保护下游应用服务器不被同时太多用户请求所压垮。
创建 ingressRoute 和 rateLimit 中间件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| cat > rate-limit.yml <<EOF apiVersion: traefik.containo.us/v1alpha1 kind: IngressRoute metadata: name: rate-limit spec: entryPoints: - web routes: - match: Host(\`whoami.od.com\`) && PathPrefix(\`/notls\`) kind: Rule services: - name: whoami port: 80 middlewares: - name: rate-limit-middleware --- apiVersion: traefik.containo.us/v1alpha1 kind: Middleware metadata: name: rate-limit-middleware spec: rateLimit: burst: 10 average: 50 EOF
|
压力测试,使用ab工具进行压力测试,一共请求 100 次,每次并发 10。测试结果失败的请求为
63 次,总耗时 0.110 秒
1 2 3 4 5 6 7
| $ ab -n 100 -c 10 "http://whoami.od.com/notls" Document Path: /notls Concurrency Level: 10 Time taken for tests: 0.110 seconds Complete requests: 100 Failed requests: 63 Non-2xx responses: 63
|
熔断-circuitBreaker
官方文档
服务熔断的作用类似于保险丝,当某服务出现不可用或响应超时的情况时,为了防止整个系统出现雪崩,暂时停止对该服务的调用。
熔断器三种状态
- Closed:关闭状态,所有请求都正常访问。
- Open:打开状态,所有请求都会被降级。traefik 会对请求情况计数,当一定时间内失败请求百分比达到阈值,则触发熔断,断路器会完全打开。
- Recovering:半开恢复状态,open 状态不是永久的,打开后会进入休眠时间。随后断路器会自动进入半开状态。此时会释放部分请求通过,若这些请求都是健康的,则会完全关闭断路器,否则继续保持打开,再次进行休眠计时
服务熔断原理(断路器的原理) 统计用户在指定的时间范围(默认10s)之内的请求总数达到指定的数量之后,如果不健康的请求(超时、异常)占总请求数量的百分比(50%)达到了指定的阈值之后,就会触发熔断。触发熔断,断路器就会打开(open),此时所有请求都不能通过。在5s之后,断路器会恢复到半开状态(half open),会允许少量请求通过,如果这些请求都是健康的,那么断路器会回到关闭状态(close).如果这些请求还是失败的请求,断路器还是恢复到打开的状态(open).
traefik支持的触发器
- NetworkErrorRatio:网络错误率
- ResponseCodeRatio:状态代码比率
- LatencyAtQuantileMS:分位数的延迟(以毫秒为单位)
创建 ingressRoute ,添加 circuitBreaker 中间件,指定 50% 的请求比例响应时间大于 1MS 时熔断。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| cat > circuit.yml <<EOF apiVersion: traefik.containo.us/v1alpha1 kind: IngressRoute metadata: name: rate-limit spec: entryPoints: - web routes: - match: Host(\`whoami.od.com\`) && PathPrefix(\`/notls\`) kind: Rule services: - name: whoami port: 80 middlewares: - name: circuit-breaker-middleware --- apiVersion: traefik.containo.us/v1alpha1 kind: Middleware metadata: name: circuit-breaker-middleware spec: circuitBreaker: expression: LatencyAtQuantileMS(50.0) > 1 EOF
|
压力测试,一共请求 1000 次,每次并发 100 次。触发熔断机制,测试结果失败的请求为 995次,总耗时 0.378 秒。
1 2 3 4 5 6 7
| $ ab -n 1000 -c 100 "http://whoami.od.com/notls" Document Path: /notls Concurrency Level: 100 Time taken for tests: 0.378 seconds Complete requests: 1000 Failed requests: 995 Non-2xx responses: 995
|
自定义错误页-errorPages
官方文档
在实际的业务中,肯定会存在 4XX
5XX
相关的错误异常,如果每个应用都开发一个单独的错误页,无疑大大增加了开发成本,traefik 同样也支持自定义错误页,但是需要注意的是,错误页面不是由 traefik 存储处理,而是通过定义中间件,将错误的请求重定向到其他的页面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| cat > error.yml <<EOF apiVersion: traefik.containo.us/v1alpha1 kind: Middleware metadata: name: errors4 spec: errors: status: - "400-499" query : /notls service: name: whoami port: 80 --- apiVersion: traefik.containo.us/v1alpha1 kind: IngressRoute metadata: name: errors-ing spec: entryPoints: - web routes: - match: Host(\`whoami.od.com\`) && PathPrefix(\`/notls\`) kind: Rule services: - name: whoami port: 80 middlewares: - name: errors4 EOF
|
数据压缩-compress
官方文档
有时候客户端和服务器之间会传输比较大的报文数据,这时候就占用较大的网络带宽和时长。为了节省带宽,加速报文的响应速速,可以将传输的报文数据先进行压缩,然后再进行传输,traefik也同样支持数据压缩。
traefik 默认只对大于 1024 字节,且请求标头包含 Accept-Encoding gzip
的资源进行压缩。可以指定排除特定类型不启用压缩或者根据内容大小来决定是否压缩。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| cat > compress.yml <<EOF apiVersion: traefik.containo.us/v1alpha1 kind: Middleware metadata: name: compress spec: compress: {} --- apiVersion: traefik.containo.us/v1alpha1 kind: IngressRoute metadata: name: compress-ing spec: entryPoints: - web routes: - match: Host(\`whoami.od.com\`) && PathPrefix(\`/notls\`) kind: Rule services: - name: whoami port: 80 middlewares: - name: compress EOF
|
html 文件小于 1024 字节,未开启压缩,图片资源大于 1024 字节,开启了压缩