☰
Traefik 与 Docker:基于容器标签的动态路由与负载均衡实战指南
2026/11/2 2:49:43 网站建设 项目流程

Traefik 与 Docker:基于容器标签的动态路由与负载均衡实战指南

【免费下载链接】traefikThe Cloud Native Application Proxy项目地址: https://gitcode.com/GitHub_Trending/tr/traefik

Traefik(Cloud Native Application Proxy)最具代表性的特性之一,就是把路由与负载均衡的配置权下放到应用层:借助 Docker 容器的 labels,开发者在部署应用的同时即可声明域名规则、端口、中间件、TLS 等完整的动态配置。本文以 Docker 路由参考文档 为主线,完整梳理 Docker Provider 的配置模型、每一类标签的语义与取值,并结合本仓库中 Docker Provider 实现 的源码说明其底层行为。读完本文,你将能够熟练使用 compose labels 为容器声明 Router/Service/Middleware,正确解决端口探测、多端口暴露、健康检查、会话保持以及 TCP/UDP 代理等真实场景问题。

阅读前说明:本文介绍的"标签路由"是动态配置手段;启用 Docker Provider 本身的连接参数、watch、exposedByDefault、defaultRule等静态配置项不在本文逐条展开,可参考 Docker Provider 配置页。


一、核心思路:把路由配置下沉到容器本身

常规反向代理需要运维在一个集中式配置文件中维护"域名 ↔ 后端"的映射关系;而 Traefik 将这条映射关系声明式地写在了每个容器的标签里:

  • Traefik 通过 Docker API 持续感知宿主机上的容器;
  • 读取容器上以traefik.开头的标签;
  • 依据标签为每个容器自动生成对应的Service(负载均衡器,每个容器实例对应一个 server)与Router(入口规则),详见 Service 文档 与 规则与优先级文档。

从源码结构看,这条"标签 → 动态配置"的流水线集中在 pkg/provider/docker/config.go 的DynConfBuilder.build方法中:逐个inspect容器 → 用label.DecodeConfiguration解析标签 → 分别构建 HTTP/TCP/UDP 三类配置 → 最后provider.Merge合并为全局动态配置。

安全第一:标签不是存放机密的地方

!!! warning "Labels 与敏感数据" 官方明确建议:不要在标签中存放敏感数据(证书、凭据等)。标签对具备 Docker 读取权限的进程可见,应把这类信息放到 Secret、独立文件等更安全的存储中。

几条通用约定

  • 标签名不区分大小写(读取与匹配时统一处理);
  • Router 名(<router_name>)、Service 名(<service_name>)、Middleware 名中不允许出现@字符——@是 Traefik 中标识 Provider 来源(如foo@file)的保留分隔符;
  • 若定义了 TCP/UDP Router 或 Service,容器将不再被自动创建 HTTP Router/Service(详见后文)。

二、快速上手:最简单的"域名 → 容器"映射

2.1 启用 Docker Provider

在静态配置中开启 Docker Provider(三种写法等价):

providers: docker: {}
[providers.docker]
--providers.docker=true

2.2 给容器打上路由标签

在 docker compose 文件中为服务声明标签,即可完成"域名 → 容器"的暴露:

services: my-container: # ... labels: - traefik.http.routers.my-container.rule=Host(`example.com`)

启动后,访问http://example.com的流量会被路由到my-container。


三、Service 的自动创建与自动绑定规则

在基于标签的配置中有两条"偷懒"规则(源自 docs/content/includes/service-by-label.md,被本文档直接引用),理解它们能解释后续所有示例为何"只写一半"也能跑通:

  1. 规则一(自动绑定):标签声明了 Router(例如通过rule定义),也声明了 Service(例如通过loadbalancer.server.port隐式创建),但 Router 没有通过service=指定使用哪个 Service —— 此时该 Service 会被自动绑定到这个 Router;
  2. 规则二(自动创建):标签声明了 Router,却完全没有定义任何 Service —— Traefik 会自动创建一个 Service并绑定给该 Router。

而一旦 Router 通过service=显式指定了 Service,则无论是否还有其它 Service 被定义,都只会使用指定的那一个。

这三条规则在 config.go 中都能找到对应实现:buildServiceConfiguration在configuration.Services为空时会自动以"服务名"创建默认ServersLoadBalancer;provider.BuildRouterConfiguration负责把 rule 模板、默认 rule 与模型渲染进 Router。

3.1 三种声明的完整示例

示例 A:只声明 Service,Router 自动绑定(对应规则一)

labels: - "traefik.http.routers.myproxy.rule=Host(`example.net`)" # 下面的 service myservice 会自动绑定到 router myproxy - "traefik.http.services.myservice.loadbalancer.server.port=80"

示例 B:只声明 Router,Service 自动创建(对应规则二)

labels: # 没有声明任何 service,Traefik 会自动创建并绑定到 router myproxy - "traefik.http.routers.myproxy.rule=Host(`example.net`)"

示例 C:显式绑定 Router 与 Service

labels: - traefik.http.routers.www-router.rule=Host(`example-a.com`) # 显式建立 router 与 service 的关联 - traefik.http.routers.www-router.service=www-service - traefik.http.services.www-service.loadbalancer.server.port=8000

关于"默认自动创建的 Service 指向哪个端口/地址",见下一节loadbalancer.server.port的说明——不指定端口时 Traefik 默认取容器第一个暴露的端口。


四、HTTP 层标签详解

4.1 Routers(HTTP 路由器)

要为某个容器上自动生成的 Router 改配置,添加以traefik.http.routers.<name-of-your-choice>.开头、后跟想修改的选项名的标签即可。例如修改规则:

labels: - traefik.http.routers.my-container.rule=Host(`example.com`)
LabelDescriptionValue
traefik.http.routers.<router_name>.rule路由匹配规则,详见 RulesHost(`example.com`)
traefik.http.routers.<router_name>.ruleSyntax按 Router 指定规则解析语法。⚠️ruleSyntax已弃用,将在下一个大版本移除,请改用 v3 语法书写规则v3
traefik.http.routers.<router_name>.entrypoints该 Router 绑定的入口点,详见 EntryPointsep1,ep2
traefik.http.routers.<router_name>.middlewares该 Router 挂载的中间件(引用链),详见 middlewares overviewauth,prefix,cb
traefik.http.routers.<router_name>.service该 Router 使用的 Service,详见 Servicemyservice
traefik.http.routers.<router_name>.tls是否启用 TLS(HTTPS 入口),详见 TLS overviewtrue
traefik.http.routers.<router_name>.tls.certresolver为 Router 指定 ACME 证书解析器,详见 certResolvermyresolver
traefik.http.routers.<router_name>.tls.domains[n].main主域名(SAN 主体),详见 ACME 域定义example.org
traefik.http.routers.<router_name>.tls.domains[n].sansSAN 备用域名,同上test.example.org,dev.example.org
traefik.http.routers.<router_name>.tls.options引用的 TLS Options(由 File/K8s CRD Provider 定义),详见 TLS optionsfoobar
traefik.http.routers.<router_name>.observability.accesslogs控制该 Router 是否产生访问日志true
traefik.http.routers.<router_name>.observability.metrics控制该 Router 是否产生指标true
traefik.http.routers.<router_name>.observability.tracing控制该 Router 是否产生链路追踪true
traefik.http.routers.<router_name>.priority路由优先级,详见 优先级计算42
4.1.1 同一容器暴露两个域名(两个 Router)
services: my-container: # ... labels: - traefik.http.routers.www-router.rule=Host(`example-a.com`) - traefik.http.routers.www-router.service=www-service - traefik.http.services.www-service.loadbalancer.server.port=8000 - traefik.http.routers.admin-router.rule=Host(`example-b.com`) - traefik.http.routers.admin-router.service=admin-service - traefik.http.services.admin-service.loadbalancer.server.port=9000

4.2 Services(HTTP 负载均衡服务)

修改容器自动生成的 Service 时,添加traefik.http.services.<name-of-your-choice>.前缀的标签。例如想关闭passHostHeader(默认把请求 Host 原样转发给后端):

labels: - traefik.http.services.<name-of-your-choice>.loadbalancer.passhostheader=false
LabelDescriptionValue
traefik.http.services.<service_name>.loadbalancer.server.port注册端口。容器暴露多个端口时非常有用8080
traefik.http.services.<service_name>.loadbalancer.server.scheme覆盖默认转发协议(http/https/h2c等)http
traefik.http.services.<service_name>.loadbalancer.server.url直接指定完整后端 URL。不能与port/scheme同时使用http://foobar:8080
traefik.http.services.<service_name>.loadbalancer.serverstransport引用一个 ServersTransport(由 File 或 Kubernetes CRD Provider 定义),详见 serverstransportfoobar@file
traefik.http.services.<service_name>.loadbalancer.passhostheader转发请求时是否保留原始 Host 头true
traefik.http.services.<service_name>.loadbalancer.healthcheck.headers.<header_name>健康检查请求附加头,详见 health checkfoobar
traefik.http.services.<service_name>.loadbalancer.healthcheck.hostname健康检查 Host 头example.org
traefik.http.services.<service_name>.loadbalancer.healthcheck.interval健康检查间隔10s
traefik.http.services.<service_name>.loadbalancer.healthcheck.unhealthyinterval后端不健康时使用的检查间隔10s
traefik.http.services.<service_name>.loadbalancer.healthcheck.path健康检查请求路径/foo
traefik.http.services.<service_name>.loadbalancer.healthcheck.method健康检查请求方法foobar
traefik.http.services.<service_name>.loadbalancer.healthcheck.status判定健康所需的状态码/范围42
traefik.http.services.<service_name>.loadbalancer.healthcheck.port健康检查使用的端口42
traefik.http.services.<service_name>.loadbalancer.healthcheck.scheme健康检查协议http
traefik.http.services.<service_name>.loadbalancer.healthcheck.timeout健康检查超时10s
traefik.http.services.<service_name>.loadbalancer.healthcheck.followredirects健康检查是否跟随重定向true
traefik.http.services.<service_name>.loadbalancer.sticky.cookie是否开启基于 Cookie 的会话保持true
traefik.http.services.<service_name>.loadbalancer.sticky.cookie.httponlyCookie 是否带 HttpOnly 属性true
traefik.http.services.<service_name>.loadbalancer.sticky.cookie.nameCookie 名称foobar
traefik.http.services.<service_name>.loadbalancer.sticky.cookie.pathCookie 的 Path 属性/foobar
traefik.http.services.<service_name>.loadbalancer.sticky.cookie.secureCookie 是否带 Secure 属性true
traefik.http.services.<service_name>.loadbalancer.sticky.cookie.samesiteCookie 的 SameSite 属性none
traefik.http.services.<service_name>.loadbalancer.sticky.cookie.maxageCookie 的 Max-Age(秒)42
traefik.http.services.<service_name>.loadbalancer.responseforwarding.flushinterval反向代理刷新间隔(秒),常用于流式/SSE 场景10
4.2.1 指定自定义转发端口:避免 502 的关键

请求从http://example.com转发到http://<容器私网IP>:12345:

services: my-container: # ... labels: - traefik.http.routers.my-container.rule=Host(`example.com`) # 告诉 Traefik 使用 12345 端口连接 my-container - traefik.http.services.my-service.loadbalancer.server.port=12345

!!! important "Traefik 连错端口导致HTTP/502 Gateway Error" 默认情况下,Traefik 使用容器第一个被暴露的端口来建立后端连接。设置traefik.http.services.xxx.loadbalancer.server.port可以覆盖这一行为,从而规避"连到错误端口"引发的 502。

从实现细节看,getPort(shared.go)会遍历NetworkSettings.Ports,把端口按数值升序排序后取第一个;而当显式指定port时,addServer(config.go)会把该端口与容器 IP 拼成scheme://ip:port形式的 server URL——若同时给出了url而还写了scheme/port,则直接报错拒绝(与表格中"url 不能与 port/scheme 组合"的约束一一对应)。

4.3 Middleware(HTTP 中间件)

中间件同样可以在容器标签中声明,再用middlewares=引用:

  • 声明以traefik.http.middlewares.<name-of-your-choice>.开头,后跟中间件类型与选项;
  • 例如声明一个名为my-redirect的 redirectscheme 中间件:traefik.http.middlewares.my-redirect.redirectscheme.scheme=https;
  • 更多中间件清单见 middlewares 总览。

示例:声明并引用一个中间件

services: my-container: # ... labels: # 声明一个中间件 - traefik.http.middlewares.my-redirect.redirectscheme.scheme=https # 在 Router 上引用它 - traefik.http.routers.my-container.middlewares=my-redirect

!!! warning "同名中间件冲突" 如果以同名声明了多个参数不同的中间件,声明将失败(配置被丢弃)。这与动态配置合并时的资源冲突策略有关:DynConfBuilder.build会对每个容器生成独立配置再执行provider.Merge,同名冲突会触发合并失败。完整的中间件配置解析与合法性检查可结合 config_test.go 与 builder_test.go 中的用例验证。


五、TCP 层标签详解

5.1 TCP 示例

services: my-container: # ... labels: - "traefik.tcp.routers.my-router.rule=HostSNI(`example.com`)" - "traefik.tcp.routers.my-router.tls=true" - "traefik.tcp.services.my-service.loadbalancer.server.port=4123"

!!! warning "TCP 与 HTTP 的互斥" 一旦为容器声明了 TCP Router/Service,Traefik 就不会再像默认那样自动创建 HTTP Router/Service。若同一个容器两种协议都需要,必须同时手动声明 TCP 与 HTTP 两套 Router/Service。

5.2 TCP Routers 配置选项

LabelDescriptionValue
traefik.tcp.routers.<router_name>.entrypoints绑定入口点,详见 EntryPointsep1,ep2
traefik.tcp.routers.<router_name>.ruleTCP 匹配规则,详见 TCP rulesHostSNI(`example.com`)
traefik.tcp.routers.<router_name>.ruleSyntax按 Router 指定规则解析语法。⚠️ 已弃用,请改用 v3 语法v3
traefik.tcp.routers.<router_name>.service使用的 TCP Service,详见 TCP servicemyservice
traefik.tcp.routers.<router_name>.tls是否启用 TLS,详见 TCP TLStrue
traefik.tcp.routers.<router_name>.tls.certresolver为 TCP Router 指定证书解析器,详见 TCP TLS 配置选项myresolver
traefik.tcp.routers.<router_name>.tls.domains[n].main证书主域名example.org
traefik.tcp.routers.<router_name>.tls.domains[n].sans证书 SAN 域名test.example.org,dev.example.org
traefik.tcp.routers.<router_name>.tls.options引用的 TLS Optionsmysoptions
traefik.tcp.routers.<router_name>.tls.passthroughTLS 透传(不解密、直接转发加密流量),详见 Passthroughtrue
traefik.tcp.routers.<router_name>.priorityTCP 路由优先级,详见 优先级计算42

5.3 TCP Services 配置选项

LabelDescriptionValue
traefik.tcp.services.<service_name>.loadbalancer.server.port注册应用端口423
traefik.tcp.services.<service_name>.loadbalancer.server.tls与后端建连时是否启用 TLStrue
traefik.tcp.services.<service_name>.loadbalancer.serverstransport引用 ServersTransport 资源,详见 TCP serverstransportfoobar@file

5.4 TCP Middleware

TCP 中间件用traefik.tcp.middlewares.<name-of-your-choice>.前缀声明。例如声明一个名为test-inflightconn的 InFlightConn 限流中间件:

# ... # 声明一个中间件:限制并发连接数为 10 traefik.tcp.middlewares.test-inflightconn.inflightconn.amount=10 # 在 Router 上引用 traefik.tcp.routers.my-service.middlewares=test-inflightconn

更完整的 TCP 中间件目录见 TCP middlewares overview。同名参数不同的声明同样会失败。


六、UDP 层标签详解

6.1 UDP 示例

services: my-container: # ... labels: - "traefik.udp.routers.my-router.entrypoints=udp" - "traefik.udp.services.my-service.loadbalancer.server.port=4123"

!!! warning "UDP 与 HTTP 的互斥" 与 TCP 相同:声明了 UDP Router/Service 后,容器不会自动生成 HTTP Router/Service;需要 HTTP+UDP 同时暴露时请手动声明两套。

6.2 UDP Routers 配置选项

LabelDescriptionValue
traefik.udp.routers.<router_name>.entrypoints绑定入口点ep1,ep2
traefik.udp.routers.<router_name>.service使用的 UDP Service,详见 UDP servicemyservice

6.3 UDP Services 配置选项

LabelDescriptionValue
traefik.udp.services.<service_name>.loadbalancer.server.port注册应用端口423

七、Docker Provider 专属标签

以下标签不改变"动态路由",而是控制Docker Provider 的服务发现行为本身,定义于 shared_labels.go 的labelConfiguration/dockerSpecificConfiguration:

LabelDescriptionValue
traefik.enable显式告知 Traefik 是否纳入该容器(true/false)。该标签优先于静态配置的exposedByDefaulttrue
traefik.docker.allownonrunning默认 Traefik 只关注running状态的容器。设为true后,非 running(stopped/paused/exited)容器仍参与服务发现:保留 Router/Service 配置、Service 后端列表为空、对停机容器的请求返回503 Service Unavailable(而非404 Not Found),且会完整执行中间件链让中间件有机会拦截请求。⚠️ 注意:若有多个已停止容器暴露同一 Router 但配置相互分歧,这些 Router 会被整体丢弃true
traefik.docker.network覆盖默认用于连接容器的 Docker 网络。容器挂了多个网络时务必指定正确网络名(可用docker inspect <container_id>查询),否则 Traefik 会随机选择(取决于 Docker 返回顺序)。用 compose 文件stack部署时,定义在 compose 中的网络会自动加上stack前缀mynetwork

上述标签在代码中落实于 config.go 的keepContainer判定逻辑,其判定顺序与优先级值得注意:

  1. 容器标签里的traefik.enable=false直接过滤(可覆盖静态默认值ExposedByDefault);
  2. 通过constraints约束表达式过滤;
  3. AllowNonRunning的优先级高于健康/状态检查——一旦命中traefik.docker.allownonrunning=true,不再关心容器 health/status,直接保留配置(对应表格中"创建空后端列表、请求返回 503"的描述);
  4. 否则,非 running 容器、以及(默认不允许空 Service 时)不健康或 starting 的容器都会被过滤掉。

traefik.docker.network则对应getIPAddress:先精确查找指定网络;网络名不存在时告警并回退到第一个可用网络。代码对host网络模式与container:共享网络模式也分别做了处理。


八、结合源码看"容器实例 → 后端 Server"的生命周期

把标签模型与运行态对应起来,有几个值得记住的实现事实(均可在 config.go 中追溯):

  • 每实例一 Server:一个容器就是 Service 里的一个 server。addServer/addServerTCP/addServerUDP会把"空占位 server"填充为真实ip:port(HTTP 再拼接scheme://),见loadBalancer.Servers[0].URL/.Address的赋值逻辑;
  • 非 running / 不健康容器 → 空后端:在build*ServiceConfiguration中,若容器状态非 running 或 health 非 healthy,会直接返回并保留一个空 server 列表的负载均衡器,而非报错——这与traefik.docker.allownonrunning描述的行为一致;
  • Service 名取自 compose 服务名:getServiceName(shared.go)会优先使用 compose 的com.docker.compose.service+com.docker.compose.project组合(形如service_project)并经Normalize清洗,因此在 compose 项目内即使不同文件、不同 host 也可以形成稳定的配置命名;
  • 默认 Rule 模板:容器未在标签里声明 rule 时,Router 的规则来自静态配置的defaultRule,其默认值是Host(\{{ normalize .Name }}`)(见DefaultTemplateRule`),即"用规范化后的服务名作为主机名"。

仓库中 config_test.go 与 builder_test.go 覆盖了大量标签解码、默认端口、多网络、自动绑定等场景的单元测试;集成层面可参考 integration/docker_test.go 及配套 fixture(如 integration/fixtures/docker/simple.toml),它们验证了真实 Docker 环境下的路由暴露与转发行为。


九、常见陷阱速查

  • 502 Gateway Error:容器暴露多个端口、或应用监听端口并非暴露端口时,Traefik 默认取的"第一个暴露端口"会连错——用traefik.http.services.<name>.loadbalancer.server.port显式指定;
  • 端口暴露了但连不上:容器挂在多个网络时,务必用traefik.docker.network锁定 Traefik 所在的那个网络,否则可能随机选中一个网络导致 IP 不通;
  • 404 而非 502:想保留"已停止但注册过的容器"的路由返回 503 而非 404,需开启traefik.docker.allownonrunning=true;
  • 自动生成与你预期不符:一旦写 TCP/UDP Router/Service 标签,HTTP 的自动 Router/Service 就不会再生成;
  • 名称冲突:Router/Service/Middleware 名中不要用@;同名不同参数的中间件声明会导致整个声明失败。

如需继续深入,本文相关的上游页面还包括:Docker Provider 静态配置、HTTP Service 与健康检查、规则与优先级、TLS 默认证书,以及 Swarm/File/K8s 等其它 Provider 的标签文档(位于 docs/content/reference/routing-configuration/other-providers)。

【免费下载链接】traefikThe Cloud Native Application Proxy项目地址: https://gitcode.com/GitHub_Trending/tr/traefik

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询