Traefik on Docker Swarm 基础实战:使用服务标签暴露 HTTP 服务、路径路由与自签名 TLS 全流程
导读
本文以 Traefik 官方文档 docs/content/expose/swarm/basic.md 为主线,完整演示如何在 Docker Swarm 集群中通过 Traefik Proxy 暴露后端服务:先用 traefik/whoami 打通首条 HTTP 路由并验证请求流转,再基于 URL 路径实现不同服务间的分流,最后用自签名证书为服务启用 HTTPS。读完本文,你将掌握 Swarm 模式下“以服务(service)为单位、以 deploy.labels 为载体”的 Traefik 配置方式,并能独立搭建一套可本地联调的多服务 Swarm 网关。相关源码与参考文档可继续深入 swarm provider 参考、Docker Swarm 部署指南 与本系列进阶篇 Traefik on Docker Swarm 进阶指南。
前置条件
在开始之前,请确保环境满足:
- 已初始化 Docker Swarm 集群(
docker swarm init); - 具备基本的 Docker Swarm 概念(服务、任务、overlay 网络);
- 已按 Traefik Docker Swarm 部署指南 将 Traefik 部署为 Swarm 服务,且静态配置中已定义
web(:80)与websecure(:443)两个 entrypoint。
Swarm 模式与 Docker 模式的配置差异(建议先理解)
Traefik 通过 provider 发现后端并读取其路由配置。需要特别注意的是:在 Swarm 模式下,Traefik 读取的是“服务”上的标签,而不是单个容器上的标签。因此,使用 compose 文件部署时,标签必须写在服务的 deploy.labels 段中,而不能写在普通的 labels 段(那是给容器用的)。
从源码看,Traefik 的 Swarm 集成实现在 pkg/provider/docker/pswarm.go,其与共享标签逻辑共用 pkg/provider/docker/shared_labels.go 中的标签解码函数,分别识别 traefik.enable、traefik.docker.* 与 traefik.swarm.* 前缀的标签,其中 traefik.docker.* 在 Swarm provider 下已标记为废弃,应改用 traefik.swarm.*。这也解释了为何本文与部署指南中的标签都形如 traefik.enable=true、traefik.http.routers.whoami.rule=...——前者是总开关,后者属于 traefik.http.* 动态路由命名空间,与具体 provider 无关。
此外,Swarm API 只暴露在 manager 节点上,Traefik 通常应通过 deploy.placement.constraints 限定运行在 manager 节点,并以只读方式挂载 /var/run/docker.sock,具体做法见 部署指南。
暴露第一个 HTTP 服务
我们使用 traefik/whoami 作为演示后端。它会将收到的 HTTP 请求原样回显(主机名、IP、请求头等),非常适合验证 Traefik 的路由结果。
如果你还没有 compose 文件,请新建 docker-compose.yml;如果已有(例如部署指南中创建的 docker-compose-swarm.yaml),则在其基础上补充服务:
services:
whoami:
image: traefik/whoami
networks:
- traefik_proxy
deploy:
replicas: 3
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`whoami.swarm.localhost`)"
- "traefik.http.routers.whoami.entrypoints=web,websecure"
这里每一条标签的作用:
| 标签 | 含义 |
|---|---|
traefik.enable=true |
显式允许该服务被 Traefik 发现。建议在 Traefik 静态配置中同时设置 exposedbydefault=false,让“只有打了 traefik.enable=true 的服务”才会被纳入路由,见 exposedByDefault 说明 |
traefik.http.routers.whoami.rule=Host(\whoami.swarm.localhost`)` |
定义路由器 whoami 的匹配规则:仅当请求 Host 为 whoami.swarm.localhost 时命中 |
traefik.http.routers.whoami.entrypoints=web,websecure |
将该路由器挂到已定义的 HTTP(web) 与 HTTPS(websecure) 两个入口点上 |
其中 rule 中反引号包裹的是 Traefik 规则表达式(与 Go template、正则均无关),Host(...) 是内建匹配函数之一。Swarm 模式下服务可多副本(这里 replicas: 3),Traefik 会自动把该服务的多个任务放入同一个负载均衡集合中分发流量。
部署栈:
docker stack deploy -c docker-compose.yml traefik
若使用的是部署指南中的
docker-compose-swarm.yaml,执行docker stack deploy -c docker-compose-swarm.yaml traefik,并把本文示例合入对应文件。首次部署前确保 overlay 网络traefik_proxy已创建(docker network create --driver overlay --attachable traefik_proxy),并且 Traefik 服务与业务服务都挂在该网络上,Traefik 才能解析到各服务的地址。
关于“路由器没有显式绑定 service”这一点:当 Swarm 中该服务只对应一个服务(即 Traefik 为该服务自动生成的服务)且路由器未指定 service 时,Traefik 会把唯一的服务自动关联到该路由器,规则细节参见 service-by-label 说明。如果希望显式且可控,可在标签中补充 traefik.http.services.whoami.loadbalancer.server.port=80(Swarm provider 不提供端口自动探测,明确端口是官方推荐做法,见 Port Detection)。
验证服务是否可达
服务暴露后即可通过 whoami.swarm.localhost 访问。由于本地没有对应 DNS 解析,直接让 curl 携带 Host 头打到 localhost 即可:
curl -H "Host: whoami.swarm.localhost" http://localhost/
正常响应类似:
Hostname: whoami.1.7c8f7tr56q3p949rscxrkp80e
IP: 127.0.0.1
IP: ::1
IP: 10.0.1.8
IP: fe80::215:5dff:fe00:c9e
RemoteAddr: 10.0.1.2:45098
GET / HTTP/1.1
Host: whoami.swarm.localhost
User-Agent: curl/7.68.0
Accept: */*
Accept-Encoding: gzip
X-Forwarded-For: 10.0.1.1
X-Forwarded-Host: whoami.swarm.localhost
X-Forwarded-Port: 80
X-Forwarded-Proto: http
X-Forwarded-Server: 5789f594e7d5
X-Real-Ip: 10.0.1.1
这份回显正是“Traefik 已成功把请求路由到 whoami 后端”的直接证据:
Hostname形如whoami.1.<task-id>,说明请求落到了 Swarm 中的某个 whoami 任务(副本);X-Forwarded-For/X-Real-Ip表明 Traefik 在转发前按约定补充了代理头;X-Forwarded-Proto: http说明请求确实经由 HTTP entrypoint 进入(后续启用 TLS 后可对比看到https);- 多次请求应看到不同
Hostname(不同副本被轮询),直观感受 Swarm 的负载均衡。
添加基于路径的路由规则
单个 Host 只能服务一个入口,实际部署常需要把同一域名下的流量按 URL 路径分发到不同后端,典型场景包括:API 版本化、前后端分离、微服务按路径拆分等。Traefik 通过 PathPrefix / Path / PathRegexp 等匹配函数支持这一能力,完整语法参见 规则文档中的 Path、PathPrefix 与 PathRegexp 一节。
继续编辑 docker-compose.yml,追加一个带路径规则的服务:
# ...
# New service
whoami-api:
image: traefik/whoami
networks:
- traefik_proxy
environment:
- WHOAMI_NAME=API Service
deploy:
replicas: 2
labels:
- "traefik.enable=true"
# Path-based routing
- "traefik.http.routers.whoami-api.rule=Host(`whoami.swarm.localhost`) && PathPrefix(`/api`)"
- "traefik.http.routers.whoami-api.entrypoints=web,websecure"
- "traefik.http.routers.whoami-api.service=whoami-api-svc"
- "traefik.http.services.whoami-api-svc.loadbalancer.server.port=80"
各标签要点:
- 路由规则升级为复合规则:
Host(\whoami.swarm.localhost`) && PathPrefix(`/api`),即“域名匹配且路径以/api` 开头”两个条件同时满足才命中; traefik.http.routers.whoami-api.service=whoami-api-svc显式把路由器绑定到一个自定义命名的 service;traefik.http.services.whoami-api-svc.loadbalancer.server.port=80声明后端端口。这里为whoami-api显式声明 service 与端口,与首例依赖“自动关联”的方式互补,也演示了命名必须两端一致的规则;- 通过环境变量
WHOAMI_NAME=API Service给该后端打上业务标识,whoami 会把它回显在响应里,便于区分后端来自哪个服务。
应用变更(Swarm 会对既有服务做滚动更新,而不是重建整个栈):
docker stack deploy -c docker-compose.yml traefik
测试路径路由是否生效
# Root path should go to the main whoami service
curl -H "Host: whoami.swarm.localhost" http://localhost/
# /api path should go to the whoami-api service
curl -H "Host: whoami.swarm.localhost" http://localhost/api
访问 /api 时,响应中应能看到与 WHOAMI_NAME=API Service 相关的环境变量信息(API Service 字样),以此确认请求确实被分发到了 whoami-api,路径路由生效。
补充说明路由优先级:当 Host(...) 规则相同而 Path 规则不同时,Traefik 会按规则长度自动计算优先级(Path 规则更长、更具体则优先),因此 /api 前缀请求会命中 whoami-api,其余路径回落到 whoami,具体计算规则见 Priority Calculation。
启用 TLS(自签名证书)
加密是网关的基本能力。本文先介绍面向本地开发的自签名证书方案,下一进阶篇再介绍 Let's Encrypt 自动化签发。
生成自签名证书与动态配置文件
首先生成有效期一年的自签名证书(覆盖 *.swarm.localhost 通配域名):
mkdir -p certs
# key + cert (valid for one year)
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout certs/local.key -out certs/local.crt \
-subj "/CN=*.swarm.localhost"
然后生成一段动态配置(File provider),告诉 Traefik 证书文件存放位置。这里把证书路径写为容器内的 /certificates/:
# dynamic config that tells Traefik where the cert lives
cat > certs/tls.yml <<'EOF'
tls:
certificates:
- certFile: /certificates/local.crt
keyFile: /certificates/local.key
EOF
Swarm 中多个节点都要能读到证书文件,比较干净的做法是使用 Docker config(Swarm 会把它作为只读文件分发给相应节点),而非依赖每个节点本地都存在 certs/ 目录:
docker config create swarm-cert.crt certs/local.crt
docker config create swarm-cert.key certs/local.key
docker config create swarm-tls.yml certs/tls.yml
修改 Traefik 服务与 compose 根配置
让 Traefik 把上面创建的 config 挂载为文件,并开启 File provider 动态目录。在 docker-compose.yml 中做两处修改:
① Traefik 服务的 command 段追加(开启 websecure entrypoint 的 TLS,并声明动态配置文件目录):
command:
# ... existing commands ...
- "--entryPoints.websecure.address=:443"
- "--entryPoints.websecure.http.tls=true"
- "--providers.file.directory=/etc/traefik/dynamic"
说明:
--providers.file.directory让 Traefik 监听该目录下的动态配置文件(支持.yml/.yaml/.toml)。部署指南中的 Traefik 服务使用的是.yaml后缀,因此这里命名为tls.yml亦会被识别。若部署指南已通过--providers.file.filename=/dynamic/tls.yaml挂载了证书动态配置,则该段可与之等价,不必重复。
② compose 根层级声明 configs 来源:
configs:
swarm-cert.crt:
file: ./certs/local.crt
swarm-cert.key:
file: ./certs/local.key
swarm-tls.yml:
file: ./certs/tls.yml
③ Traefik 服务的 configs 段把来源映射为容器内目标路径(对应动态配置中写明的 /certificates/ 路径):
configs:
- source: swarm-cert.crt
target: /certificates/local.crt
- source: swarm-cert.key
target: /certificates/local.key
- source: swarm-tls.yml
target: /etc/traefik/dynamic/tls.yml
综合来看,你的 Traefik 服务应同时具备:只读的 Docker socket 挂载、web/websecure 两个 entrypoint、上文的 configs 映射,以及 manager 节点调度约束(可参考 部署指南中的完整清单)。
重新部署并验证 HTTPS
docker stack deploy -c docker-compose.yml traefik
部署完成后,浏览器访问 https://whoami.swarm.localhost/ 即可看到 whoami 的回显。因为是自签名证书,浏览器会弹出安全警告,本地联调时接受该警告即可;用 curl 验证时可加 -k 跳过证书校验:
curl -k https://whoami.swarm.localhost/
对比 HTTP 与 HTTPS 两个入口,观察回显中 X-Forwarded-Proto 从 http 变为 https,即证明请求确实经由 TLS 终止后转发到后端。
小结与下一步
至此你已完成 Swarm 下 Traefik 的三项基础能力:
- 服务发现与暴露:通过
deploy.labels让 Swarm 中的服务被 Traefik 识别并按Host规则路由; - 基于路径的路由:用
&& PathPrefix(...)复合规则把同一域名流量分流到不同后端; - TLS 加密:以自签名证书 + File provider 动态配置 + Docker config 分发,为本地 Swarm 提供 HTTPS 能力。
在此基础上,建议继续研读 Traefik on Docker Swarm 进阶指南,那里将展开:通过中间件(Middleware)注入安全响应头与 IP 白名单访问控制、用 Let's Encrypt 自动化签发与管理证书、为有状态应用配置会话粘滞(sticky sessions)、基于认证的父子路由器多层路由,以及在服务层统一应用中间件等生产级话题。若需查阅 Swarm provider 的全部配置项(轮询间隔、constraints、defaultRule、TLS 连接 Docker 等),可直接对照 Swarm provider 官方参考文档。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00