Traefik 与 Docker¶
快速入门使用 Docker
如果尚未阅读,可以先查看使用 Docker 提供者的快速入门指南。
配置示例¶
可以按照下面的方式启用 Docker 提供者:
providers:
docker: {}[providers.docker]--providers.docker=true为容器附加标签(在 Docker compose 文件中)
services:
my-container:
# ...
labels:
- traefik.http.routers.my-container.rule=Host(`example.com`)
配置选项¶
| 字段 | 说明 | 默认值 | 必填 |
|---|---|---|---|
providers.providersThrottleDuration |
配置重新加载后,在处理新的配置刷新事件之前至少等待的时间。 如果这段时间内发生多个事件,只处理最近的一个,其他事件全部丢弃。 此选项不能按提供者分别设置,但节流算法会独立应用于每个提供者。 |
2s | 否 |
providers.docker.endpoint |
指定 Docker API 端点。更多信息请参阅此处。 | "unix:///var/run/docker.sock" | 否 |
providers.docker.username |
定义 HTTP 基本认证的用户名。当 Docker 守护进程套接字通过需要 HTTP 基本认证的 HTTP 代理暴露时,应使用此选项。 | "" | 否 |
providers.docker.password |
定义 HTTP 基本认证的密码。当 Docker 守护进程套接字通过需要 HTTP 基本认证的 HTTP 代理暴露时,应使用此选项。 | "" | 否 |
providers.docker.useBindPortIP |
指示 Traefik 使用容器绑定的 IP/端口,而不是容器内部网络的 IP/端口。更多信息请参阅此处。 | false | 否 |
providers.docker.exposedByDefault |
默认通过 Traefik 暴露容器。如果设置为 false,最终路由配置会忽略没有 traefik.enable=true 标签的容器。更多信息请参阅此处。 |
true | 否 |
providers.docker.network |
定义连接所有容器时使用的默认 Docker 网络。可以通过 traefik.docker.network 标签为单个容器覆盖此选项。 |
"" | 否 |
providers.docker.defaultRule |
定义容器未通过标签设置规则时应用的路由规则。更多信息请参阅此处。 | "Host(`{{ normalize .Name }}`)" |
否 |
providers.docker.httpClientTimeout |
定义 HTTP 连接的客户端超时。接受时长字符串(例如 30s、1m30s)或以秒为单位的整数值。如果值为 0,则不设置超时。 |
0 | 否 |
providers.docker.watch |
指示 Traefik 是否监视 Docker 事件。 | true | 否 |
providers.docker.constraints |
定义一个表达式,Traefik 将用它匹配容器标签,以确定是否为该容器创建路由。更多信息请参阅此处。 | "" | 否 |
providers.docker.allowEmptyServices |
指示提供者为 Docker 容器创建所有已定义的服务器负载均衡器,而不考虑相应容器的健康状态。 | false | 否 |
providers.docker.tls.ca |
定义与 Docker 安全连接时使用的证书颁发机构证书路径,默认使用系统证书包。 | "" | 否 |
providers.docker.tls.cert |
定义与 Docker 安全连接时使用的公钥证书路径。使用此选项时,必须设置 key 选项。 |
"" | 是 |
providers.docker.tls.key |
定义与 Docker 安全连接时使用的私钥路径。使用此选项时,必须设置 cert 选项。 |
"" | 是 |
providers.docker.tls.insecureSkipVerify |
指示提供者在建立 TLS 连接时接受 Docker 服务器提供的任何证书,而不考虑证书所涵盖的主机名。 | false | 否 |
endpoint¶
更多信息请参阅 Docker API 访问章节。
使用 docker.sock
docker-compose 文件将 Docker 套接字共享给 Traefik 容器。
services:
traefik:
image: traefik:v3.1 # The official v3 Traefik docker image
ports:
- "80:80"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
在 Traefik 配置文件中指定 docker.sock。
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
# ...[providers.docker]
endpoint = "unix:///var/run/docker.sock"
# ...--providers.docker.endpoint=unix:///var/run/docker.sock
# ...使用 SSH
使用 Docker 18.09+ 时,可以通过 SSH 将 Traefik 连接到守护进程。 在 Traefik 配置文件中指定 SSH 主机和用户。 请注意,如果服务器要求使用公钥认证,运行 Traefik 的用户必须能够访问这些公钥。
providers:
docker:
endpoint: "ssh://[email protected]:2022"
# ...[providers.docker]
endpoint = "ssh://[email protected]:2022"
# ...--providers.docker.endpoint=ssh://[email protected]:2022
# ...使用 HTTP
使用 Docker Engine API,可以通过 HTTP 将 Traefik 连接到远程守护进程。
providers:
docker:
endpoint: "http://127.0.0.1:2375"
# ...[providers.docker]
endpoint = "http://127.0.0.1:2375"
# ...--providers.docker.endpoint=http://127.0.0.1:2375
# ...使用 TCP
使用 Docker Engine API,可以通过 TCP 将 Traefik 连接到远程守护进程。
providers:
docker:
endpoint: "tcp://127.0.0.1:2375"
# ...[providers.docker]
endpoint = "tcp://127.0.0.1:2375"
# ...--providers.docker.endpoint=tcp://127.0.0.1:2375
# ...providers:
docker:
endpoint: "unix:///var/run/docker.sock"[providers.docker]
endpoint = "unix:///var/run/docker.sock"--providers.docker.endpoint=unix:///var/run/docker.sockuseBindPortIP¶
Traefik 将请求路由到匹配容器的 IP/端口。
设置 useBindPortIP=true 时,就是让 Traefik 使用容器_绑定_的 IP/端口,而不是容器内部网络的 IP/端口。
与 traefik.http.services.<name>.loadbalancer.server.port 标签一起使用时(该标签指示 Traefik 将请求路由到特定端口),
Traefik 会尝试查找端口为 traefik.http.services.<name>.loadbalancer.server.port 的绑定。
如果找不到这样的绑定,Traefik 会回退到容器内部网络的 IP,
但仍使用标签中设置的 traefik.http.services.<name>.loadbalancer.server.port 端口。
不同情况下 usebindportip 的示例
| port 标签 | 容器绑定 | 路由目标 |
|---|---|---|
| - | - | IntIP:IntPort |
| - | ExtPort:IntPort | IntIP:IntPort |
| - | ExtIp:ExtPort:IntPort | ExtIp:ExtPort |
| LblPort | - | IntIp:LblPort |
| LblPort | ExtIp:ExtPort:LblPort | ExtIp:ExtPort |
| LblPort | ExtIp:ExtPort:OtherPort | IntIp:LblPort |
| LblPort | ExtIp1:ExtPort1:IntPort1 & ExtIp2:LblPort:IntPort2 | ExtIp2:LblPort |
上表中:
ExtIp表示“绑定中的外部 IP”IntIp表示“容器内部网络的 IP”,ExtPort表示“绑定中的外部端口”IntPort表示“容器内部网络的端口”。
providers:
docker:
useBindPortIP: true
# ...[providers.docker]
useBindPortIP = true
# ...--providers.docker.useBindPortIP=true
# ...defaultRule¶
defaultRule 选项定义容器未通过标签设置规则时应用的路由规则。
它必须是有效的 Go 模板,
并且可以使用 sprig 模板函数。
可以通过 ContainerName 标识符访问容器名称。
可以通过 Name 标识符访问服务名称。
模板可以通过 Labels 标识符访问此容器上定义的所有标签。
providers:
docker:
defaultRule: "Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)"
# ...[providers.docker]
defaultRule = "Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)"
# ...--providers.docker.defaultRule='Host(`{{ .Name }}.{{ index .Labels "customLabel"}}`)'
# ...默认规则与 Traefik 服务
暴露 Traefik 容器时,如果同时使用默认规则机制, 可能会创建一个指向自身的路由器,形成循环。 在这种情况下,为防止无限循环, Traefik 会添加一个内部中间件,拒绝来自同一路由器的请求。
constraints¶
constraints 选项可以设置为一个表达式,Traefik 将用它匹配容器标签,
以确定是否为该容器创建路由。如果没有任何容器标签匹配该表达式,
就不会为该容器创建路由。如果表达式为空,则包含所有发现的容器。
表达式语法基于 Label("key", "value") 和 LabelRegex("key", "value") 函数,
并支持常规布尔逻辑,如下例所示。
约束键的限制
请注意,traefik.* 是保留给配置使用的标签命名空间,不能用作自定义约束的键。
约束表达式示例
# Includes only containers having a label with key `a.label.name` and value `foo`
constraints = "Label(`a.label.name`, `foo`)"
# Excludes containers having any label with key `a.label.name` and value `foo`
constraints = "!Label(`a.label.name`, `value`)"
# With logical AND.
constraints = "Label(`a.label.name`, `valueA`) && Label(`another.label.name`, `valueB`)"
# With logical OR.
constraints = "Label(`a.label.name`, `valueA`) || Label(`another.label.name`, `valueB`)"
# With logical AND and OR, with precedence set by parentheses.
constraints = "Label(`a.label.name`, `valueA`) && (Label(`another.label.name`, `valueB`) || Label(`yet.another.label.name`, `valueC`))"
# Includes only containers having a label with key `a.label.name` and a value matching the `a.+` regular expression.
constraints = "LabelRegex(`a.label.name`, `a.+`)"
更多信息请参阅限制服务发现的范围。
providers:
docker:
constraints: "Label(`a.label.name`,`foo`)"
# ...[providers.docker]
constraints = "Label(`a.label.name`,`foo`)"
# ...--providers.docker.constraints=Label(`a.label.name`,`foo`)
# ...路由配置¶
使用 Docker 作为提供者时, Traefik 使用容器标签获取路由配置。
标签列表请参阅专门的路由章节。
使用标签配置路由¶
默认情况下,Traefik 会监视独立 Docker Engine 上的容器级标签。
使用 Docker Compose 时,标签通过
“services” 对象中的
labels 指令指定。
不仅限于 Docker
请注意,Nomad、Terraform、Ansible 等任何能够定义 带标签的 Docker 容器的工具,都可以与 Traefik 和 Docker 提供者一起使用。
端口检测¶
Traefik 从 Docker API 获取容器的私有 IP 和端口。
私有通信的端口检测方式如下:
- 如果容器暴露了一个端口, Traefik 就使用该端口。
- 如果容器暴露了多个端口,
Traefik 就使用最小的端口。例如,同时暴露
80和8080时,Traefik 会使用80。 - 如果容器没有暴露任何端口,或从多个端口中选择的端口不合适,
则必须使用
traefik.http.services.<service_name>.loadbalancer.server.port标签 手动指定 Traefik 通信时应使用的端口 (此标签的详细说明见路由中的专门章节)。
主机网络¶
暴露配置了主机网络的容器时, 主机 IP 地址按以下方式解析:
- 尝试查询
host.docker.internal - 如果查询失败,则尝试查询
host.containers.internal(Podman 中与host.docker.internal等效的名称) - 如果该查询也失败,则回退到
127.0.0.1
在 Linux 上,对于早于 20.10.0 的 Docker 版本,要定义 host.docker.internal,
应使用 --add-host 标志将其作为 extra_host 提供给 Traefik 容器。例如,将其设置为
网桥接口(默认为 docker0)的 IP 地址:--add-host=host.docker.internal:172.17.0.1。
IPv4 与 IPv6¶
使用启用 IPv6 的 Docker stack 时, Traefik 会优先使用容器的 IPv4 IP,而不是对应的 IPv6 IP。 因此,在 IPv6 Docker stack 上, Traefik 会使用容器的 IPv6 IP。
Docker API 访问¶
Traefik 需要访问 Docker 套接字才能获取动态配置。
可以使用 endpoint 指令指定要使用的 Docker API 端点。
安全说明
不受任何限制地访问 Docker API 存在安全风险: 如果 Traefik 遭到攻击,攻击者就可能获取底层主机的访问权限。
正如 Docker 守护进程攻击面文档中所述:
引用
[...] 只应允许受信任的用户控制 Docker 守护进程 [...]
解决方案
通过 TCP 或 SSH 暴露 Docker 套接字,而不是使用默认的 Unix 套接字文件。 根据安全评估结果,可以在不同层面实现 AAA(认证、授权、审计)概念:
- 使用客户端证书进行认证,具体见“保护 Docker 守护进程套接字”。
- 使用 Tecnativa Docker Socket Proxy 对请求授权并过滤,以限制可执行的操作。
- 使用 Docker 授权插件机制进行授权。
- 在网络层进行审计:仅在 Docker 私有网络内暴露套接字,使其只能由 Traefik 访问。
- 在容器层进行审计:由 Traefik 以外的另一个容器暴露套接字。
- 在内核层进行审计:利用 SELinux 等机制约束内核调用,只允许 Traefik 进程(或“套接字暴露”进程)执行一组明确的操作。
- SSH 公钥认证(Docker > 18.09 支持 SSH)。
- 通过暴露 Docker 守护进程套接字的 HTTP 代理使用 HTTP 基本认证。
更多资源和示例
- “挂载 /var/run/docker.sock 让你不放心?”
- Traefik 与 Docker:与 Docker Captain Bret Fisher 的对话
- Liz Rice 在 KubeCon EU 2018 上的主题演讲:拿着剪刀奔跑
- 不要暴露 Docker 套接字(即使对容器也不行)
- Hacker News 上关于共享
/var/run/docker.sock文件的讨论 - 是否采用 DinD
- Traefik 问题 GH-4174:Docker 套接字的安全性
- 使用 Socat 检查 Docker 活动
- 让 Traefik 在工作节点上运行
- Tecnativa 的 Docker Socket Proxy
在生产环境使用 Traefik OSS?
如果你在工作中使用 Traefik,可以考虑为 Traefik OSS 增加企业级 API 网关能力或商业支持。
为 Traefik OSS 增加 API 网关能力快捷且平滑,无需推倒重来,现有配置也会完整保留。请通过这个简短视频了解实际效果。