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(兜底)路由器 —— 使用最低的优先级并路由到一个没有服务器的服务 —— 这样当没有任何其它路由器匹配时,它就会处理所有请求。
下面是一个仅使用文件 Provider 的 yaml 示例,展示了这种配置可能的样子:
静态配置:
# traefik.yml
entryPoints:
web:
address: :80
providers:
file:
filename: dynamic.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 头 |
|---|---|
| 客户端 IP | X-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),作为存储它的键。该标识符是按字母顺序连接证书的 DNSNames 和 IPAddresses(SAN)得到的。
示例:
| X509v3 Subject Alternative Name | TLS 证书标识符 |
|---|---|
DNS:example.com, IP Address:127.0.0.1 | 127.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.com | 127.0.0.1,example.com |
*.example.com,example.com,example.com | *.example.com,example.com,example.com | example.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 的商业支持。