跳转至

路由器(Routers)

将请求连接到服务

routers

一个路由器负责将传入请求连接到可以处理这些请求的服务上去。

在这个过程中,路由器可能会使用一些 中间件 来更新请求,或者将请求转发到服务之前进行一些操作。

配置示例

/foo 请求被 service-foo 服务来处理 -- 使用 File Provider
## 动态配置
[http.routers]
  [http.routers.my-router]
    rule = "Path(`/foo`)"
    service = "service-foo"
## 动态配置
http:
  routers:
    my-router:
      rule: "Path(`/foo`)"
      service: service-foo
将端口3306上所有的(非TLS)请求转发到 database 这个服务

动态配置

## 动态配置
[tcp]
  [tcp.routers]
    [tcp.routers.to-database]
      entryPoints = ["mysql"]
      # 捕获所有请求(仅适用于非tls路由器的规则,请参考下文。)
      rule = "HostSNI(`*`)"
      service = "database"
## 动态配置
tcp:
  routers:
    to-database:
      entryPoints:
        - "mysql"
      # 捕获所有请求(仅适用于非tls路由器的规则,请参考下文。)
      rule: "HostSNI(`*`)"
      service: database

静态配置

## 静态配置
[entryPoints]
  [entryPoints.web]
    address = ":80"
  [entryPoints.mysql]
    address = ":3306"   
## 静态配置
entryPoints:
  web:
    address: ":80"
  mysql:
    address: ":3306"   
## 静态配置
--entryPoints.web.address=":80"
--entryPoints.mysql.address=":3306"   

配置 HTTP 路由器

入口点(EntryPoints)

如果没有指定,则 HTTP 路由器将接受来自所有定义的入口点的请求。如果要将路由范围限制为一组入口点,则需要设置entryPoints选项。

监听所有 EntryPoint

动态配置

## 动态配置
[http.routers]
  [http.routers.Router-1]
    # 默认,路由器监听所有的入口点
    rule = "Host(`traefik.io`)"
    service = "service-1"
## 动态配置
http:
  routers:
    Router-1:
      # 默认,路由器监听所有的入口点
      rule: "Host(`traefik.io`)"
      service: "service-1"

静态配置

## 静态配置
[entryPoints]
  [entryPoints.web]
    address = ":80"
  [entryPoints.websecure]
    address = ":443"
  [entryPoints.other]
    address = ":9090"
## 静态配置
entryPoints:
  web:
    address: ":80"
  websecure:
    address: ":443"
  other:
    address: ":9090"
## 静态配置
--entrypoints.web.address=":80"
--entrypoints.websecure.address=":443"
--entrypoints.other.address=":9090"
监听指定的 EntryPoints

动态配置

## 动态配置
[http.routers]
  [http.routers.Router-1]
    # 不会监听 web 这个入口点
    entryPoints = ["websecure", "other"]
    rule = "Host(`traefik.io`)"
    service = "service-1"
## 动态配置
http:
  routers:
    Router-1:
      # 不会监听 web 这个入口点
      entryPoints:
        - "websecure"
        - "other"
      rule: "Host(`traefik.io`)"
      service: "service-1"

静态配置

## 静态配置
[entryPoints]
  [entryPoints.web]
    address = ":80"
  [entryPoints.websecure]
    address = ":443"
  [entryPoints.other]
    address = ":9090"
## 静态配置
entryPoints:
  web:
    address: ":80"
  websecure:
    address: ":443"
  other:
    address: ":9090"
## 静态配置
--entrypoints.web.address=":80"
--entrypoints.websecure.address=":443"
--entrypoints.other.address=":9090"

规则

规则(Rules)是一组用于确定特定请求是否符合特定条件的匹配器。如果该规则得到验证,则路由器将变为可用状态,调用中间件,然后将请求转发到服务上去。

Host 地址是 traefik.io

rule = "Host(`traefik.io`)"

Host 地址是 traefik.io 或者 Host 地址是 containo.us 并且 path 路径是 /traefik

rule = "Host(`traefik.io`) || (Host(`containo.us`) && Path(`/traefik`))"

下表列出了所有可用的匹配器

规则 描述
Headers(`key`, `value`) 检查 headers 中是否有一个键为key 值为 value 的键值对
HeadersRegexp(`key`, `regexp`) 检查 headers 中是否有一个键为key,值匹配正则表达式regexp的键值对
Host(`domain-1`, ...) 检查请求的域名是否包含在给定的domains域名中
HostRegexp(`traefik.io`, `{subdomain:[a-z]+}.traefik.io`, ...) 检查请求的域名是否匹配给定的regexp正则表达式。
Method(`GET`, ...) 检查请求的方法是否包含在给定的methods (GET, POST, PUT, DELETE, PATCH) 中
Path(`/path`, `/articles/{category}/{id:[0-9]+}`, ...) 匹配确定的请求路径,它接受一系列文字和正则表达式路径。
PathPrefix(`/products/`, `/articles/{category}/{id:[0-9]+}`) 匹配请求前缀路径,它接受一系列文字和正则表达式前缀路径。
Query(`foo=bar`, `bar=baz`) 匹配查询字符串参数,接受 key=value 的键值对序列。

正则表达式语法

为了对 HostPath 使用正则表达式,需要声明一个任意命名的变量,然后跟上用冒号分隔的正则表达式,所有这些都用花括号括起来。 可以使用 Golang 的 regexp 包 支持的任何模式(例如:/posts/{id:[0-9]+})。

使用运算符和括号来组合匹配器

你可以使用 AND(&&)和 OR(||)运算符来结合多个匹配器,也可以使用括号。

Path Vs PathPrefix

如果你的服务只监听确定的路径,则使用 Path。例如,Path: /products 将匹配 /products 但不会匹配 /products/shoes

如果你的服务在特定的路径上监听,但是在子路径上也提供了请求,则使用 *Prefix* 匹配器。例如,PathPrefix: /products 将匹配 /products,但是也会匹配 /products/shoes/products/shirts。由于 PATH 路径是按原样进行转发的,因此你的服务应该在 /products 上进行监听。

中间件

你可以将 中间件 列表附件到每个 HTTP 路由器上去。仅当规则匹配时,并且在请请求转发到服务之前,中间件才会生效。

中间件顺序

中间件的应用顺序和其在 router 中声明的顺序是相同的。

使用一个 中间件 -- 使用 File Provider
## 动态配置
[http.routers]
  [http.routers.my-router]
    rule = "Path(`/foo`)"
    # 在其他地方声明
    middlewares = ["authentication"]
    service = "service-foo"
## 动态配置
http:
  routers:
    my-router:
      rule: "Path(`/foo`)"
      # 在其他地方声明
      middlewares:
      - authentication
      service: service-foo

服务

你必须为每个路由器添加一个 服务,服务是路由器的目标。

HTTP 路由器只能转发到 HTTP 服务(不能转发到 TCP 服务)。

TLS

常规配置

当指定 TLS 模块时,它表示 Traefik 当前的路由器仅用于 HTTPS 请求(并且该路由器应忽略 HTTP(非 TLS)请求)。Traefik 将接管 SSL 连接(意味着它将发送解密数据到服务去)。

配置路由器仅接受 HTTPS 请求
## 动态配置
[http.routers]
  [http.routers.Router-1]
    rule = "Host(`foo-domain`) && Path(`/foo-path/`)"
    service = "service-id"
    # 将接管 TLS 请求
    [http.routers.Router-1.tls]
## 动态配置
http:
  routers:
    Router-1:
      rule: "Host(`foo-domain`) && Path(`/foo-path/`)"
      service: service-id
      tls: {}

HTTPS & ACME

在现在版本中,启用 ACME 时, 将会自动生成证书应用于 TLS 模块声明的某个路由器。

HTTP & HTTPS 的路由器

如果你需要为 HTTP 和 HTTPS 请求定义相同的路由,则需要定义两个不同的路由器:

一个带 TLS 模块,一个不带。

HTTP & HTTPS 路由
## 动态配置
[http.routers]
  [http.routers.my-https-router]
    rule = "Host(`foo-domain`) && Path(`/foo-path/`)"
    service = "service-id"
    # 将接管 TLS 请求
    [http.routers.my-https-router.tls]

  [http.routers.my-http-router]
    rule = "Host(`foo-domain`) && Path(`/foo-path/`)"
    service = "service-id"
## 动态配置
http:
  routers:
    my-https-router:
      rule: "Host(`foo-domain`) && Path(`/foo-path/`)"
      service: service-id
      # 将接管 TLS 请求
      tls: {}

    my-http-router:
      rule: "Host(`foo-domain`) && Path(`/foo-path/`)"
      service: service-id

options

options 字段用于对 TLS 参数的细粒度控制。它引用 TLS 选项 并仅在定义了 Host 规则时才适用。

关联服务名

即使可能会给人一种 TLS 选项引用被映射到一个路由或者一个路由规则的印象,但是实际上仅映射到规则 Host 部分找到的主机名。

当然,规则中也可能包含多个 Host 部分,在这种情况下,TLS options 会引用尽可能多的主机名。

需要记住的另一件事是: 从上述映射中选择 TLS 选项,并基于 TLS 握手过程中提供的服务器名称,这一切都在路由实际发生之前进行。

配置 TLS 选项
## 动态配置
[http.routers]
  [http.routers.Router-1]
    rule = "Host(`foo-domain`) && Path(`/foo-path/`)"
    service = "service-id"
    # 将接管 TLS 请求
    [http.routers.Router-1.tls]
      options = "foo"

[tls.options]
  [tls.options.foo]
    minVersion = "VersionTLS12"
    cipherSuites = [
      "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
      "TLS_RSA_WITH_AES_256_GCM_SHA384"
    ]
## 动态配置
http:
  routers:
    Router-1:
      rule: "Host(`foo-domain`) && Path(`/foo-path/`)"
      service: service-id
      # 将接管 TLS 请求
      tls:
        options: foo

tls:
  options:
    foo:
      minVersion: VersionTLS12
      cipherSuites:
        - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
        - TLS_RSA_WITH_AES_256_GCM_SHA384

TLS 选项冲突

由于 TLS 选项引用已映射到主机名了,如果配置导致相同的主机名(从Host规则)与两个 TLS 选项引用匹配的情况发生冲突,例如下面的示例:

## 动态配置
[http.routers]
  [http.routers.routerfoo]
    rule = "Host(`snitest.com`) && Path(`/foo`)"
    [http.routers.routerfoo.tls]
      options = "foo"

[http.routers]
  [http.routers.routerbar]
    rule = "Host(`snitest.com`) && Path(`/bar`)"
    [http.routers.routerbar.tls]
      options = "bar"
## 动态配置
http:
  routers:
    routerfoo:
      rule: "Host(`snitest.com`) && Path(`/foo`)"
      tls:
        options: foo

    routerbar:
      rule: "Host(`snitest.com`) && Path(`/bar`)"
      tls:
        options: bar

如果发生这种情况,两个映射都将被丢弃掉,这些路由器的主机名(这里的 snitest.com)将与默认的 TLS 选项关联。

certResolver

如果 certResolver 被定义, Traefik 将尝试根据路由的 HostHostSNI 规则生成证书。

## 动态配置
[http.routers]
  [http.routers.routerfoo]
    rule = "Host(`snitest.com`) && Path(`/foo`)"
    [http.routers.routerfoo.tls]
      certResolver = "foo"
## 动态配置
http:
  routers:
    routerfoo:
      rule: "Host(`snitest.com`) && Path(`/foo`)"
      tls:
        certResolver: foo

一个规则中的多个主机

比如这个规则 Host(`test1.traefik.io`,`test2.traefik.io`) 将请求具有主域名 test1.traefik.io 和 SAN(备用域)test2.traefik.io 的证书。

domains

你可以为每个主域设置 SANs(备用域)。每个域都必须具有指向 Traefik 的 A/AAAA 记录。每个域和 SAN 都会有一个证书请求。

## 动态配置
[http.routers]
  [http.routers.routerbar]
    rule = "Host(`snitest.com`) && Path(`/bar`)"
    [http.routers.routerbar.tls]
      certResolver = "bar"
      [[http.routers.routerbar.tls.domains]]
        main = "snitest.com"
        sans = ["*.snitest.com"]
## 动态配置
http:
  routers:
    routerbar:
      rule: "Host(`snitest.com`) && Path(`/bar`)"
      tls:
        certResolver: "bar"
        domains:
          - main: "snitest.com"
            sans: "*.snitest.com"

ACME v2 支持通配符证书。

Let's Encrypt 的文档 中提到,通配符证书只能通过 DNS-01 challenge 来生成。

根域名也应该接收一个证书,因此需要将其指定为 SAN 并执行 2个 DNS-01 challenges。在这种情况下,两个域生成的 DNS TXT 记录是相同的。即使该行为符合 DNS RFC 的规定,但是由于所有 DNS 提供商都会在给定的时间(TTL)中保留记录的缓存,因此可能会导致一些问题,并且该 TTL 可能大于 challenge 时间而超时,从而使得 DNS-01 challenge 失败。

Traefik 的 ACME 客户端库 lego 支持某些(不是全部)DNS 提供商来解决该问题。支持的 提供商表格 指示它们是否允许为通配符域及其根域生成证书。

通配符证书只能通过 DNS-01 challenge 进行校验。

双通配符证书

是无法为一个域名请求双通配符证书的。(比如 *.*.local.com)

配置 TCP 路由

常规配置

如果 HTTP 路由和 TCP 路由都监听相同的入口点,则 TCP 路由将在 HTTP 路由之前应用。

如果找不到与 TCP 路由器匹配的路由,则 HTTP 路由器将接管。

入口点

如果未指定,TCP 路由将接受来自所有已定义入口点的请求。如果要将路由范围限制为一组入口点,则设置入口点选项即可。

监听所有入口点

动态配置

## 动态配置

[tcp.routers]
  [tcp.routers.Router-1]
    # 默认,路由监听所有的入口点
    rule = "HostSNI(`traefik.io`)"
    service = "service-1"
    # 将路由 TLS 请求(忽略非 tls 请求)
    [tcp.routers.Router-1.tls]
## 动态配置

tcp:
  routers:
    Router-1:
      # 默认,路由监听所有的入口点
      rule: "HostSNI(`traefik.io`)"
      service: "service-1"
      # 将路由 TLS 请求(忽略非 tls 请求)
      tls: {}

静态配置

## 静态配置

[entryPoints]
  [entryPoints.web]
    address = ":80"
  [entryPoints.websecure]
    address = ":443"
  [entryPoints.other]
    address = ":9090"
## 静态配置

entryPoints:
  web:
    address: ":80"
  websecure:
    address: ":443"
  other:
    address: ":9090"
## 静态配置
--entrypoints.web.address=":80"
--entrypoints.websecure.address=":443"
--entrypoints.other.address=":9090"
监听指定的入口点

动态配置

## 动态配置
[tcp.routers]
  [tcp.routers.Router-1]
    # 不会监听 web 入口点
    entryPoints = ["websecure", "other"]
    rule = "HostSNI(`traefik.io`)"
    service = "service-1"
    # 将路由 TLS 请求(忽略非 tls 请求)
    [tcp.routers.Router-1.tls]
## 动态配置
tcp:
  routers:
    Router-1:
      # 不会监听 web 入口点
      entryPoints:
        - "websecure"
        - "other"
      rule: "HostSNI(`traefik.io`)"
      service: "service-1"
      # 将路由 TLS 请求(忽略非 tls 请求)
      tls: {}

静态配置

## 静态配置

[entryPoints]
  [entryPoints.web]
    address = ":80"
  [entryPoints.websecure]
    address = ":443"
  [entryPoints.other]
    address = ":9090"
## 静态配置

entryPoints:
  web:
    address: ":80"
  websecure:
    address: ":443"
  other:
    address: ":9090"
## 静态配置
--entrypoints.web.address=":80"
--entrypoints.websecure.address=":443"
--entrypoints.other.address=":9090"

规则

规则 描述
HostSNI(`domain-1`, ...) 检查服务名标识是否和给定的 domains 对应。

HostSNI & TLS

需要注意的是服务名标识(SNI)是 TLS 协议的扩展。因此,只有 TLS 路由才能使用该规则指定域名。但是,非 TLS 路由必须使用带有 * 的规则(每个域)来声明每个非 TLS 请求都将由路由进行处理。

服务

你必须为每个 TCP 路由附加一个 TCP 服务。服务是路由器的目标。

TCP 路由只能被路由到 TCP 服务(不能到 HTTP 服务)。

TLS

常规配置

当指定 TLS 模块时,它指示 Traefik 当前路由仅专用于 TLS 请求(并且路由应忽略非 TLS 请求)。默认情况下,Traefik 将处理 SSL 连接,但是可以配置 Traefik 以便让请求通过(保持数据加密),然后转发给对应的服务。

配置 TLS 处理
## 动态配置
[tcp.routers]
  [tcp.routers.Router-1]
    rule = "HostSNI(`foo-domain`)"
    service = "service-id"
    # 将默认处理 TLS 请求
    [tcp.routers.Router-1.tls]
## 动态配置
tcp:
  routers:
    Router-1:
      rule: "HostSNI(`foo-domain`)"
      service: service-id
      # 将默认处理 TLS 请求
      tls: {}
配置直通
## 动态配置
[tcp.routers]
  [tcp.routers.Router-1]
    rule = "HostSNI(`foo-domain`)"
    service = "service-id"
    [tcp.routers.Router-1.tls]
      passthrough = true
## 动态配置
tcp:
  routers:
    Router-1:
      rule: "HostSNI(`foo-domain`)"
      service: service-id
      tls:
        passthrough: true

TLS & ACME

在现在的版本中,开启 ACME,将自动生成证书用于每个声明 TLS 部分的路由器。

options

options 用于对 TLS 参数的更细粒度的控制,它引用一个 TLS Options,并且仅在定义了 HostSNI 规则时才适用。

配置 tls options

## 动态配置
[tcp.routers]
  [tcp.routers.Router-1]
    rule = "HostSNI(`foo-domain`)"
    service = "service-id"
    # 处理 TLS 请求
    [tcp.routers.Router-1.tls]
      options = "foo"

[tls.options]
  [tls.options.foo]
    minVersion = "VersionTLS12"
    cipherSuites = [
      "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
      "TLS_RSA_WITH_AES_256_GCM_SHA384"
    ]
## 动态配置
tcp:
  routers:
    Router-1:
      rule: "HostSNI(`foo-domain`)"
      service: service-id
      # 将处理 TLS 请求
      tls:
        options: foo

tls:
  options:
    foo:
      minVersion: VersionTLS12
      cipherSuites:
        - "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
        - "TLS_RSA_WITH_AES_256_GCM_SHA384"

certResolver

可以查看 certResolver 在 HTTP 路由 中的信息了解。

## 动态配置
[tcp.routers]
  [tcp.routers.routerfoo]
    rule = "HostSNI(`snitest.com`)"
    [tcp.routers.routerfoo.tls]
      certResolver = "foo"
## 动态配置
tcp:
  routers:
    routerfoo:
      rule: "HostSNI(`snitest.com`)"
      tls:
        certResolver: foo

domains

同样可以查看 HTTP 路由部分的 domains 了解更多信息。

## 动态配置
[tcp.routers]
  [tcp.routers.routerbar]
    rule = "HostSNI(`snitest.com`)"
    [tcp.routers.routerbar.tls]
      certResolver = "bar"
      [[tcp.routers.routerbar.tls.domains]]
        main = "snitest.com"
        sans = ["*.snitest.com"]
## 动态配置
tcp:
  routers:
    routerbar:
      rule: "HostSNI(`snitest.com`)"
      tls:
        certResolver: "bar"
        domains:
          - main: "snitest.com"
            sans: "*.snitest.com"