7.2.1 Traefik配置详解

Traefik是一个用Golang开发的轻量级的Http反向代理和负载均衡器,虽然相比于Nginx,它是后起之秀,但是它天然拥抱kubernetes,直接与集群k8s的Api Server通信,反应非常迅速,实时感知集群中Ingress定义的路由规则集合和后端ServicePod的变化,自动热更新Traefik后端配置,根本不用创建Ingress controller对象,同时还提供了友好的控制面板和监控界面,不仅可以方便地查看Traefik根据Ingress生成的路由配置信息,还可以查看统计的一些性能指标数据,如:总响应时间、平均响应时间、不同的响应码返回的总次数等,不仅如此,Traefik还支持丰富的annotations配置,可配置众多出色的特性,例如:自动熔断负载均衡策略白名单;还支持许多后端存储,如:zookeeper、eureka、consul、rancher、docker等,它会自动感知这些统一配置中心的变化,热更新自己的路由配置。

  • Traefik 还有很多特性如下:

    • 速度快

    • 不需要安装其他依赖,使用 GO 语言编译可执行文件

    • 支持最小化官方 Docker 镜像

    • 支持多种后台,如 Docker, Swarm mode, Kubernetes, Marathon, Consul, Etcd, Rancher, Amazon ECS 等等

    • 支持 REST API

    • 配置文件热重载,不需要重启进程

    • 支持自动熔断功能

    • 支持轮训、负载均衡

    • 提供简洁的 UI 界面

    • 支持 Websocket, HTTP/2, GRPC

    • 自动更新 HTTPS 证书

    • 支持高可用集群模式

官网所用示例图:

  • entrypoint: 这是流量的入口,即是http还是https

  • frontend: 这相当于nginx配置中的location, server_name等,即当流量还在nginx上,没有proxy_pass之前, traefik拦截请求后,会转发给FrontEnd。前端定义EntryPoint映射到BackEnd的路由规则集,字段包括Host, Path, Headers 等,匹配请求后,默认通过加权轮询负载算法路由到一个可用的BackEnd,然后进入指定的微服务。

  • backend: 这相当于nginx中,proxy_pass后接的配置

我们将Træfɪk放大,一起看看它内部的结构:

  • 请求在入口点处结束, 顾名思义, 它们是Træfɪk的网络入口(监听端口, SSL, 流量重定向...)。

  • 之后流量会导向一个匹配的前端。 前端是定义入口点后端之间的路由的地方。 路由是通过请求字段(Host, Path, Headers...) 来定义的,它可以匹配或否定一个请求。

  • 前端 将会把请求发送到 后端。后端可以由一台或一个通过负载均衡策略配置后的多台服务器组成。

  • 最后, 服务器 将转发请求到对应私有网络的微服务当中去。

入口点

入口点是是Træfɪk的网络入口。 它们可以通过以下方式来定义:

  • 一个端口 (80, 443...)

  • SSL (证书, 密钥, 由受信任的CA签名的客户端证书的身份验证...)

  • 重定向到其他的入口点 (重定向 HTTPHTTPS)

这里有一个入口点定义的例子:

[entryPoints]
  [entryPoints.http]
  address = ":80"
    [entryPoints.http.redirect]
    entryPoint = "https"
  [entryPoints.https]
  address = ":443"
    [entryPoints.https.tls]
      [[entryPoints.https.tls.certificates]]
      certFile = "tests/traefik.crt"
      keyFile = "tests/traefik.key"
  • 这里定义了两个入口点,httphttps.

  • http 监听 80 端口, https 监听 443端口

  • 我们通过提供一个证书和一个密钥在 https 中开启SSL。

  • 并且我们转发所有的 http 入口点请求到 https入口点。

前端

前端是入口流量从入口点转发到后端的一组规则。 前端可以通过以下规则定义:

  • Headers: Content-Type, application/json: 通过 Headers 可以添加一个匹配规则来匹配请求头部包含的值。它接受要匹配的键/值对序列。

  • HeadersRegexp: Content-Type, application/(text|json): 也可以在 Headers 中使用正则表达式。它接受要匹配的键/值对序列,序列内容解析是通过正则匹配的。

  • Host: traefik.io, www.traefik.io: 匹配请求 Host 必需在给定域名列表内。

  • HostRegexp: traefik.io, {subdomain:[a-z]+}.traefik.io: 添加匹配请求 Host 的正则表达式。 它接受一个以{}包括起来的为空或更多url变量的模版。变量的值可以以一个可选的正则表达式来匹配。

  • Method: GET, POST, PUT: Method 可以添加一个HTTP请求方法的匹配。它接受要匹配的一个或多个请求方法序列。

  • Path: /products/, /articles/{category}/{id:[0-9]+}: Path 可以添加一个URL路径的匹配。它接受一个以{}包括起来的为空或更多url变量的模版。

  • PathStrip: 和 Path 相同,但从请求的URL路径中去掉的给定的前缀。

  • PathPrefix: PathPrefix 可以添加一个URL路径前缀的匹配。它匹配给定模版中的完整URL路径前缀。

  • PathPrefixStrip: 和 PathPrefix 相同,但从请求的URL路径中去掉的给定的前缀。

  • AddPrefix : 为请求URL路径天假前缀。

你可以为一条规则添加多个值,通过用 , 分隔开。 你可以使用多条规则,通过用 ; 分隔开。

你可以选择启用 passHostHeader 来转发客户端请求Header中的 Host 字段到后端

这里有一个前端定义的例子:

[frontends]
  [frontends.frontend1]
  backend = "backend2"
    [frontends.frontend1.routes.test_1]
    rule = "Host:test.localhost,test2.localhost"
  [frontends.frontend2]
  backend = "backend1"
  passHostHeader = true
  priority = 10
  entrypoints = ["https"] # overrides defaultEntryPoints
    [frontends.frontend2.routes.test_1]
    rule = "HostRegexp:localhost,{subdomain:[a-z]+}.localhost"
  [frontends.frontend3]
  backend = "backend2"
    [frontends.frontend3.routes.test_1]
    rule = "Host:test3.localhost;Path:/test"
  • 这里定义了3个前端: frontend1, frontend2frontend3

  • 当规则Host:test.localhost,test2.localhost匹配时frontend1 将把流量转发到 backend2

  • 当规则Host:localhost,{subdomain:[a-z]+}.localhost匹配时frontend2 将把流量转发到 backend1 (转发客户端Header中的 Host 字段到后端)

  • 当规则Host:test3.localhost Path:/test同时匹配时frontend3 将把流量转发到 backend2

合并多条规则

正如上面例子中所展示的,你可以合并多条规则。 在TOML文件中,你可以使用多条路由:

  [frontends.frontend3]
  backend = "backend2"
    [frontends.frontend3.routes.test_1]
    rule = "Host:test3.localhost"
    [frontends.frontend3.routes.test_2]
    rule = "Path:/test"

这里,当规则Host:test3.localhost Path:/test同时匹配时 frontend3 将把流量转发到 backend2。 或者你也可以使用 ;符号来分隔,结果是相同的:

  [frontends.frontend3]
  backend = "backend2"
    [frontends.frontend3.routes.test_1]
    rule = "Host:test3.localhost;Path:/test"

最后,你可以使用 , 符号分隔规则,为一个前端创建一个规则来绑定多个域名或路径:

 [frontends.frontend2]
    [frontends.frontend2.routes.test_1]
    rule = "Host:test1.localhost,test2.localhost"
  [frontends.frontend3]
  backend = "backend2"
    [frontends.frontend3.routes.test_1]
    rule = "Path:/test1,/test2"

优先级

默认情况下,路由会以规则长度(为了防止部分重叠情况)被排序(倒序): PathPrefix:/12345 将会比 PathPrefix:/1234 优先被匹配到,最后才会匹配到 PathPrefix:/1

你可以在前端自定义优先级:

  [frontends]
    [frontends.frontend1]
    backend = "backend1"
    priority = 10
    passHostHeader = true
      [frontends.frontend1.routes.test_1]
      rule = "PathPrefix:/to"
    [frontends.frontend2]
    priority = 5
    backend = "backend2"
    passHostHeader = true
      [frontends.frontend2.routes.test_1]
      rule = "PathPrefix:/toto"

这里,frontend1 将会比 frontend2 优先被匹配(因为10 > 5).

后端

后端用来负责将来自一个或多个前端的流量负载均衡到一组http服务器上。 这里支持多种负载均衡方法:

  • wrr: 加权轮询

  • drr: 动态轮询: 这会为表现比其他服务器好的服务器增加权重。当服务器表现有变化的时,它也会会退到正常权重。

断路器也可以应用到后端,用于防止故障服务器上的高负载。 初始化状态是Standby。断路器只观察统计信息但并不修改请求。 当断路条件匹配时,断路器进入Tripped状态,它会返回与定义的http状态码或转发到其他前端。 一旦Tripped状态计时器超时,断路器会进入Recovering状态并重置所有统计数据。 当短路条件不匹配并且Recovery状态计时器超时时,断路器进入Standby状态。

它可以通过配置:

  • 方法: LatencyAtQuantileMS, NetworkErrorRatio, ResponseCodeRatio

  • 操作符: AND, OR, EQ, NEQ, LT, LE, GT, GE

举个例子:

  • NetworkErrorRatio() > 0.5: 监控网络故障率大于0.5超过10秒后,为这个前端平滑切换,断路条件匹配

  • LatencyAtQuantileMS(50.0) > 50: 监控延迟超过50ms时断路条件匹配

  • ResponseCodeRatio(500, 600, 0, 600) > 0.5: 监控返回 HTTP状态码在[500-600]之间的数量/HTTP状态码在[0-600]之间的数量 的比例大于0.5时,断路条件匹配

为了主动防治后端被高负载压垮,可以为每个后端设置最大连接数限制。

最大连接数限制可以通过为maxconn.amount配置一个整型值,同时 maxconn.extractorfunc 是用来配置通过什么样的维度来统计最大连接数。"例如下面例子中通过请求中host来统计连接数"

例如:

[backends]
  [backends.backend1]
    [backends.backend1.maxconn]
       amount = 10
       extractorfunc = "request.host"
  • 当已经有10个请求是同一个Host Header时,下一个请求backend1 将返回 HTTP code 429 Too Many Requests

  • 另外一个可可行的 extractorfunc 维度配置是 client.ip,它将通过统计客户端请求IP来统计连接数。

  • 其实 extractorfunc 可以设置 request.header.ANY_HEADER 中的任意维度,它将以你提供的 ANY_HEADER 任意维度来统计连接数。

例如:

[backends]
  [backends.backend1]
    [backends.backend1.loadbalancer]
      sticky = true

服务器健康检查也是可配置的,当Traefik定期执行HTTP GET请求到后端时,后端返回的HTTP状态码不是200 OK,那么这个后端将被从负载均衡轮询列表中移除。健康检查可以以一个在后端URL后附加路径的路径地址与一个时间间隔 (以 time.ParseDuration 所识别的格式给出) specifying how 配置多久健康检查应该执行一次 (默认30秒). 每个后端必需在5秒内回应健康检查。

当一个后端重新返回HTTP状态码200 OK时,将被重新添加回负载均衡轮询列表。

例如:

[backends]
  [backends.backend1]
    [backends.backend1.healthcheck]
      path = "/health"
      interval = "10s"

服务器

服务器可以简单的被定义为URL。你也可以设置一个自定义的 weight 给每个服务器(这个weight将会被用于负载均衡)。

这里有一个关于后端和服务器的定义的例子:

[backends]
  [backends.backend1]
    [backends.backend1.circuitbreaker]
      expression = "NetworkErrorRatio() > 0.5"
    [backends.backend1.servers.server1]
    url = "http://172.17.0.2:80"
    weight = 10
    [backends.backend1.servers.server2]
    url = "http://172.17.0.3:80"
    weight = 1
  [backends.backend2]
    [backends.backend2.LoadBalancer]
      method = "drr"
    [backends.backend2.servers.server1]
    url = "http://172.17.0.4:80"
    weight = 1
    [backends.backend2.servers.server2]
    url = "http://172.17.0.5:80"
    weight = 2
  • 定义了两个后端: backend1 and backend2

  • backend1 将把流量转发到两台服务器: http://172.17.0.2:80" 权重 10http://172.17.0.3:80 权重 1 并使用默认的 wrr 负载均衡策略。

  • backend2 将把流量转发到两台服务器: http://172.17.0.4:80" 权重 1http://172.17.0.5:80 权重 2 并使用 drr负载均衡策略。

  • 一个断路器被添加到 backend1 使用 NetworkErrorRatio() > 0.5 表达式:监控网络故障率大于0.5超过10秒为这个前端平滑切换,断路条件匹配

最后更新于