Skip to content

Traefik 入门常见问题(FAQ)

为什么 Traefik 返回某个特定的 HTTP 响应码?

Traefik 是一个动态反向代理 —— 虽然文档中经常通过文件配置示例来演示配置选项,但 Traefik 的核心特性是它的动态可配置性,能够随着时间直接响应来自 Provider 的变化。

具体来说,安装配置 是静态的,可以由启动时的文件提供,而像文件 Provider 这样的多种 Provider 则在 Traefik 实例的整个生命周期内持续为 路由配置 的变化做出动态贡献

此外,配置还包含一些概念,例如入口点(EntryPoint) —— 可以将其视为传输层(TCP)上的一个监听器;而路由器(Router) 则更偏向表示层(TLS)和应用层(HTTP)。对于给定的入口点,可以存在任意数量的路由器。

换句话说,对于一个给定的入口点,任意时刻观察到的流量不一定只与一种协议相关。可能是 HTTP,也可能是其他;可能使用 TLS,也可能没有。更不用说动态配置的改变可能会让这种流量随时间变化。

因此,在这种动态上下文中,entryPoint 的静态配置并不能提供任何关于通过该 entryPoint 的流量将如何路由的线索。也不能确定它是否会被路由 —— 也就是说,是否存在与通过该入口点的流量类型相匹配的路由器。

404 Not Found(未找到)

Traefik 在以下情况下返回 404 响应码:

  • 请求到达的入口点没有路由器
  • 请求到达的入口点没有 HTTP 路由器(且请求是 HTTP)
  • 请求到达的入口点没有 HTTPS 路由器(且请求是 HTTPS)
  • 请求到达的入口点上有 HTTP/HTTPS 路由器,但没有任何匹配上

从 Traefik 的角度来看,每次请求无法与路由器匹配时,正确的响应码就是 404 Not Found

在这种情况下,响应码不是 503 Service Unavailable,因为 Traefik 无法确认"缺少匹配的路由器"只是暂时性的。Traefik 的路由配置是动态的、并从不同的 Provider 聚合而来,因此它无法在任意时刻假定某个特定路由"应该"被处理或"不应该"被处理。

此行为与 RFC 7231 一致:

服务器当前由于临时过载或维护而无法处理该请求。含义是:这是一个临时状态,在经过一定延迟后会缓解。如果已知延迟时长,可以在 Retry-After 响应头中给出。如果未给出 Retry-After,客户端应按照 500 响应的方式处理。

注意:503 状态码的存在并不意味着服务器在过载时必须使用它 —— 一些服务器可能选择直接拒绝连接。

—— 摘自 rfc7231#section-6.6.4

502 Bad Gateway(网关错误)

当联系上游服务时发生错误,Traefik 返回 502 响应码。

503 Service Unavailable(服务不可用)

当请求匹配了某个路由器,但没有就绪的服务器可以处理该请求时,Traefik 返回 503 响应码。

这种情况出现在:服务被显式配置为不包含任何服务器;或者服务启用了健康检查,且所有服务器都不健康时。

不使用 404 而使用其他状态码

有时 404 响应码与其它组件或服务(如 CDN)配合得并不好。

在这些情况下,你可能希望 Traefik 始终回复 503 响应码,而不是 404

要实现这种行为,可以创建一个 catchall(兜底)路由器 —— 使用最低的优先级并路由到一个没有服务器的服务 —— 这样当没有任何其它路由器匹配时,它就会处理所有请求。

下面是一个仅使用文件 Provideryaml 示例,展示了这种配置可能的样子:

静态配置:

yaml
# traefik.yml
entryPoints:
  web:
    address: :80

providers:
  file:
    filename: dynamic.yaml

动态配置:

yaml
# dynamic.yaml
http:
  routers:
    catchall:
      # 仅绑定到 web 入口点
      entryPoints:
        - "web"
      # 兜底规则
      rule: "PathPrefix(`/`)"
      service: unavailable
      # 最低的优先级
      # 在没有其它路由器匹配时才会被评估
      priority: 1

  services:
    # 该服务将始终返回 503 Service Unavailable
    unavailable:
      loadBalancer:
        servers: {}

专用服务

如果你需要返回除 503 之外的状态码,或返回自定义消息,可以沿用上面示例的原则(catchall 路由器),但 unavailable 服务应根据需要进行调整。


为什么 TLS 证书的内容更新时不会重新加载?

使用 文件 Provider 时,只有被监听(watch)的配置文件发生修改时,才会触发配置更新。

这就是为什么:当证书以路径方式定义时,即使该证书的实际内容发生了变化,也不会触发配置更新。

要使新的证书内容生效,必须强制更新动态配置。一种可行的方法是:触发文件通知事件 —— 例如在配置文件上使用 touch 命令。


代理 HTTP 请求时会添加哪些转发的头?

默认情况下,代理请求时会自动添加以下头:

属性HTTP 头
客户端 IPX-Forwarded-For, X-Real-Ip
主机X-Forwarded-Host
端口X-Forwarded-Port
协议X-Forwarded-Proto
代理服务器的主机名X-Forwarded-Server

更多详情,请参阅 Forwarded Header 文档。


Traefik 是如何存储和提供 TLS 证书的?

存储 TLS 证书

TLS 证书既可以由 路由配置 直接提供,也可以由 证书解析器(Certificate resolvers) 提供。

对于每个 TLS 证书,Traefik 会生成一个标识符(identifier),作为存储它的键。该标识符是按字母顺序连接证书的 DNSNamesIPAddresses(SAN)得到的。

示例:

X509v3 Subject Alternative NameTLS 证书标识符
DNS:example.com, IP Address:127.0.0.1127.0.0.1,example.com
DNS:example.com, DNS:*.example.com*.example.com,example.com

该标识符用于存储 TLS 证书,以便之后用于处理 TLS 连接。每次配置发生变化时都会执行此操作。

如果多个 TLS 证书提供了相同的 SAN 定义(即相同的标识符),则只保留最先被处理的那一个。由于动态配置是从所有 Provider 聚合而来,在处理时无法保证它们的处理顺序。这意味着 —— 随着应用的配置变化 —— 给定标识符所保留的 TLS 证书也可能不同。

提供 TLS 证书

对于每个传入连接,Traefik 都会为提供的服务器名提供"最佳"匹配的 TLS 证书。

TLS 证书的选择过程会先缩小与服务器名匹配的证书列表,然后按标识符字母顺序排序,最后选择该排序列表中的最后一个证书。

示例:

选中的 TLS 证书标识符排序后的 TLS 证书标识符提供的证书标识符
127.0.0.1,example.com,*.example.com,example.com*.example.com,example.com,127.0.0.1,example.com127.0.0.1,example.com
*.example.com,example.com,example.com*.example.com,example.com,example.comexample.com

缓存 TLS 证书

虽然 Traefik 会为每个传入连接提供"最佳"匹配的 TLS 证书,但通过缓存机制避免了对每个传入连接都执行选择过程的开销。

一旦某个 TLS 证书被选定为服务器名的"最佳"证书,它就会被缓存 1 小时,从而避免后续连接再次执行选择过程。

不过,当应用新配置时,缓存会被重置


"field not found" 错误是什么意思?

error: field not found, node: -badField-

当动态或静态配置中出现了未知属性时,就会发生 "field not found" 错误。

要检查配置文件是否格式正确,可以采用以下方法进行验证:


为什么某些资源(router、middleware、service...)未被创建/应用?

作为一个通用提示:如果在评估动态配置后,某个资源被 Traefik 丢弃/未创建,你应该首先查看日志中是否有错误

如果发现错误,则该错误可以确认在创建资源时出现了问题,错误消息应能帮助你弄清楚配置中的错误并修复它。

使用文件 Provider 时,验证动态配置格式是否正确的一种方式是使用 动态配置的 JSON Schema


为什么 Let's Encrypt 通配符证书的 DNS challenge 更新/生成会失败?

如果你尝试使用 DNS challenge 更新通配符证书,并出现如下错误:

msg="Error renewing certificate from LE: {example.com [*.example.com]}"
providerName=letsencrypt.acme error="error: one or more domains had a problem:
[example.com] acme: error presenting token: gandiv5: unexpected authZone example.com. for fqdn example.com."

那么问题可能与 CNAME 支持有关。

在这种情况下,请确保你的基础设施已正确设置为不依赖 CNAME 的 DNS challenge,并尝试通过以下环境变量禁用 CNAME 支持

LEGO_DISABLE_CNAME_SUPPORT=true

在生产环境使用 Traefik OSS?

如果你在工作中使用 Traefik,可以考虑为其添加企业级 API 网关能力或获取 Traefik OSS 的商业支持。

基于 MIT 协议发布