在 Docker Swarm 中部署 Traefik v3 代理:完整配置指南(HTTP/HTTPS 入口、Dashboard 鉴权与可观测性)
本指南以官方 Swarm 教程为主体,逐步讲解如何把 Traefik v3 作为 Swarm Service 通过 docker stack deploy 部署到 Docker Swarm 集群:启用 Swarm Provider 完成服务发现、暴露 web/websecure 两个入口点、强制 HTTP→HTTPS 跳转、用 basic-auth 保护 Dashboard、以自签名证书终结 TLS,并部署 whoami 演示服务验证全链路。读完你将从零搭建一套可用于本地多节点 Swarm 的、带 HTTPS 与可观测性能力的反向代理栈,并掌握后续扩展 Let's Encrypt、Prometheus 指标、OTel 链路追踪和访问日志的配置方法。
该指南的结构与单机 Docker 教程保持一致,可对照阅读;其核心差异在于:Swarm 模式下配置发现由 Swarm Provider(而非 Docker Provider)完成,服务标签需要写在
deploy.labels中,端口发布必须使用长语法,且服务发现的对象从"容器"变为"服务"。
准备工作(Prerequisites)
开始之前,请确认环境满足以下条件:
- Docker Engine 已初始化 Swarm 模式(
docker swarm init或已加入一个现有 Swarm 集群); docker compose命令可用(仅用于校验 compose 文件);- 主机上安装了
openssl(生成自签名证书); - 主机上安装了
htpasswd(生成 basic-auth 口令哈希,通常随apache2-utils/httpd-tools提供)。
本教程的典型目标环境是本地多节点开发 Swarm:没有可信 CA,因此使用 *.swarm.localhost 的自签名证书即可满足验证需求。生产环境请改用受信任 CA 签发的证书,或启用 Let's Encrypt(见后文"扩展配置")。
第一步:生成自签名证书
要让 Traefik 在本地提供 HTTPS 服务,首先需要一份证书。生产环境应使用受信任 CA 签发的证书,而对于多节点开发 Swarm,用 openssl 快速生成一张自签名证书即可:
mkdir -p certs
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout certs/local.key -out certs/local.crt \
-subj "/CN=*.swarm.localhost"
参数说明:
-x509:直接输出自签名 X.509 证书而非 CSR;-nodes:私钥不加密(便于容器内直接读取);-days 365:证书有效期一年;-newkey rsa:2048:同时生成 2048 位 RSA 私钥;-subj "/CN=*.swarm.localhost":主题通用名为通配符域名,覆盖后面用到的dashboard.swarm.localhost与whoami.swarm.localhost。
生成的 certs/local.crt 与 certs/local.key 将以只读方式挂载进 Traefik 容器(见 compose 文件中的 volumes),并由动态配置文件 dynamic/tls.yaml 引用。
第二步:生成 Dashboard 的 basic-auth 凭证
Traefik 的 basic-auth 中间件会校验一组"用户名:口令哈希"。使用 htpasswd 生成 admin 用户的口令,并用 sed 把 $ 转义为 $$(Compose / 环境变量的取值语法需要双重转义):
htpasswd -nb admin "P@ssw0rd" | sed -e 's/\$/\$\$/g'
复制命令的完整输出(形如 admin:$$apr1$$…),稍后粘贴到中间件的 label 中。
安全提示:口令哈希直接以明文形式存放在 service label 中。这对本地开发没问题,但任何能访问 Docker API 的人都可以用
docker service inspect查看到它。生产环境请改用更安全的密钥存储方式(如 Docker Secrets + 文件提供者加载),本仓库的 Swarm provider 参考文档中也强调了标签方式的能力边界。
第三步:编写动态 TLS 配置与 docker-compose-swarm.yaml
Swarm 使用 docker stack deploy 发布服务,compose 文件可以任意命名(本文沿用原教程的 docker-compose-swarm.yaml)。
首先在项目目录下创建名为 dynamic 的文件夹并添加 tls.yaml,用于承载动态 TLS 配置(证书文件的声明属于动态配置,Swarm Provider 无法通过标签描述静态入口点,因此以文件方式挂载):
# dynamic/tls.yaml
tls:
certificates:
- certFile: /certs/local.crt
keyFile: /certs/local.key
在同一目录创建 docker-compose-swarm.yaml:
services:
traefik:
image: traefik:v3.7
networks:
# 连接到 'traefik_proxy' 覆盖网络,实现跨节点容器通信
- traefik_proxy
ports:
# 将 Traefik 的入口点暴露给 Swarm
# Swarm 要求端口使用长语法声明。
- target: 80 # 容器端口(Traefik web 入口点)
published: 80 # 节点上对外暴露的主机端口
protocol: tcp
# 'host' 模式直接绑定到任务所在节点的 IP。
# 'ingress' 模式使用 Swarm 路由网格(跨节点负载均衡)。
# 按你的负载均衡策略选择;若外部已有 LB,'host' 模式通常更简单。
mode: host
- target: 443 # 容器端口(Traefik websecure 入口点)
published: 443 # 主机端口
protocol: tcp
mode: host
volumes:
# 挂载 Docker socket 供 Swarm provider 使用
# 必须在 manager 节点上运行,才能通过 socket 访问 Swarm API
- /var/run/docker.sock:/var/run/docker.sock:ro # Swarm API socket
- ./certs:/certs:ro
- ./dynamic:/dynamic:ro
# Traefik 静态配置:通过命令行参数传入
command:
# HTTP 入口点
- "--entrypoints.web.address=:80"
# 配置 HTTP 到 HTTPS 的重定向
- "--entrypoints.web.http.redirections.entrypoint.to=websecure"
- "--entrypoints.web.http.redirections.entrypoint.scheme=https"
- "--entrypoints.web.http.redirections.entrypoint.permanent=true"
# HTTPS 入口点
- "--entrypoints.websecure.address=:443"
- "--entrypoints.websecure.http.tls=true"
# 挂载动态 TLS 文件
- "--providers.file.filename=/dynamic/tls.yaml"
# 提供者
# 启用 Docker Swarm provider(而非 Docker provider)
- "--providers.swarm.endpoint=unix:///var/run/docker.sock"
# 监听 Swarm 服务变化(需要 socket 访问权限)
- "--providers.swarm.watch=true"
# 推荐:默认不暴露任何服务,要求显式添加标签
- "--providers.swarm.exposedbydefault=false"
# 指定 Traefik 用于连接服务的默认网络
- "--providers.swarm.network=traefik_traefik_proxy"
# API 与 Dashboard
- "--api.dashboard=true" # 启用 Dashboard
- "--api.insecure=false" # 显式关闭非安全 API 模式
# 可观测性
- "--log.level=INFO" # 设置日志级别,如 INFO、DEBUG
- "--accesslog=true" # 启用访问日志
- "--metrics.prometheus=true" # 启用 Prometheus 指标
deploy:
mode: replicated
replicas: 1
placement:
# 放置约束限制 Traefik 任务可以运行的节点。
# 通常放在 manager 节点上,以便通过 socket 访问 Swarm API。
constraints:
- node.role == manager
# Traefik 动态配置:通过标签定义
# 在 Swarm 中,service 定义上的标签用于配置该服务的 Traefik 路由。
labels:
- "traefik.enable=true"
# Dashboard 路由器
- "traefik.http.routers.dashboard.rule=Host(`dashboard.swarm.localhost`)"
- "traefik.http.routers.dashboard.entrypoints=websecure"
- "traefik.http.routers.dashboard.service=api@internal"
- "traefik.http.routers.dashboard.tls=true"
# basic-auth 中间件
- "traefik.http.middlewares.dashboard-auth.basicauth.users=<PASTE_HASH_HERE>"
- "traefik.http.routers.dashboard.middlewares=dashboard-auth@swarm"
# 服务提示
- "traefik.http.services.traefik.loadbalancer.server.port=8080"
# 部署 Whoami 演示应用
whoami:
image: traefik/whoami
networks:
- traefik_proxy
deploy:
labels:
# 为 Traefik 启用服务发现
- "traefik.enable=true"
# 定义 Whoami 的路由规则
- "traefik.http.routers.whoami.rule=Host(`whoami.swarm.localhost`)"
# 在 HTTPS 入口点上暴露 Whoami
- "traefik.http.routers.whoami.entrypoints=websecure"
# 启用 TLS
- "traefik.http.routers.whoami.tls=true"
# 向 Traefik 暴露 whoami 的端口号
- traefik.http.services.whoami.loadbalancer.server.port=80
# 为 Swarm 定义覆盖网络
networks:
traefik_proxy:
driver: overlay
attachable: true
关键点提醒:
- 把
<PASTE_HASH_HERE>替换为第二步生成的转义哈希; - Swarm 对端口必须使用长语法(
target/published/protocol/mode),短语法会被忽略;mode: host直接绑定节点 IP,mode: ingress则走 Swarm 路由网格做跨节点分发; - 标签必须放在
deploy.labels,services.traefik.labels(容器标签)在 Swarm 模式下不会被读取——这是与单机 Docker 教程最大的差异,参考实现见 pkg/provider/docker/pswarm.go; --providers.swarm.network=traefik_traefik_proxy中的前缀:以docker stack deploy traefik发布且网络在 compose 内声明时,Swarm 会为网络加上栈名前缀,traefik_proxy即变为traefik_traefik_proxy;若网络是预先手动创建的外部网络,则保持原名(两种方式见下节);Traefik 需要明确它应通过哪个 overlay 网络解析服务 IP,跨节点请求才能正确转发;- dashboard 中间件引用写作
dashboard-auth@swarm:@swarm后缀显式声明中间件来源提供者是 Swarm Provider(动态元素名带 provider 限定符); - Dashboard 路由将请求转发给内置服务
api@internal(Traefik 自身 API,默认监听 8080),随后的 service 提示端口 8080 只是帮助明确负载均衡指向的端口。
第四步:发布 Stack
先创建一次 overlay 网络(若尚不存在),然后发布栈:
docker network create --driver overlay --attachable traefik_proxy || true
docker stack deploy -c docker-compose-swarm.yaml traefik
Swarm 会依据放置约束把服务调度到 manager 节点,并在节点上绑定 80/443 端口。docker stack services traefik、docker service ps traefik_traefik 可以观察服务的滚动调度与运行状态;若此前已手动创建了同名网络,栈会直接复用该外部网络。
第五步:访问 Dashboard
在浏览器打开 https://dashboard.swarm.localhost/,浏览器会提示证书不受信任(自签名证书,点选继续即可),随后页面要求输入你在 htpasswd 步骤中配置的 basic-auth 凭据(用户名 admin、密码 P@ssw0rd)。成功登录后即可看到路由、服务、中间件与入口点的实时概览:
若该页面正常展示,说明证书加载、TLS 终结、Swarm Provider 服务发现、basic-auth 中间件与 api@internal 转发整条链路都已打通。Dashboard 前端源码位于本仓库 webui,通过内置 HTTP API(api@internal,默认 :8080)取数渲染。
第六步:测试 whoami 应用
使用 curl 向 HTTPS 入口发起请求(-k 表示忽略自签名证书校验):
curl -k https://whoami.swarm.localhost/
whoami 服务会原样回显收到的请求与头部信息:
Hostname: whoami-76c9859cfc-k7jzs
IP: 127.0.0.1
IP: ::1
IP: 10.42.0.59
IP: fe80::50d7:a2ff:fed5:2530
RemoteAddr: 10.42.0.60:54148
GET / HTTP/1.1
Host: whoami.swarm.localhost
User-Agent: curl/8.7.1
Accept: */*
Accept-Encoding: gzip
X-Forwarded-For: 10.42.0.1
X-Forwarded-Host: whoami.swarm.localhost
X-Forwarded-Port: 443
X-Forwarded-Proto: https
X-Forwarded-Server: traefik-644b7c67d9-f2tn9
X-Real-Ip: 10.42.0.1
注意 X-Forwarded-Proto: https 与 X-Forwarded-Port: 443:这些是 Traefik 在 TLS 终结后附加到上游请求的转发头(由 forwarded headers 机制注入),证明请求确实经过了 websecure 入口点的 HTTPS 处理。
再用 HTTP 入口请求同一域名,验证重定向:
curl -k http://whoami.swarm.localhost
Moved Permanently
HTTP 请求被 301 永久重定向到 HTTPS,说明 HTTP→HTTPS 跳转配置生效。也可以在浏览器直接访问 https://whoami.swarm.localhost 查看服务回显的 JSON 数据:
Swarm Provider 的底层实现与配置项
为了用好这套配置,有必要理解 Swarm Provider 的工作原理。从源码看,Traefik 的 Swarm Provider 与 Docker Provider 共用同一实现目录 pkg/provider/docker,其中 pswarm.go 声明了 SwarmProvider:
- provider 注册名固定为
swarm(源码中的SwarmName = "swarm"); SetDefaults()(pkg/provider/docker/pswarm.go)给出的默认值为:watch=true、exposedByDefault=true、endpoint=unix:///var/run/docker.sock、refreshSeconds=15s、默认规则模板Host(\{{ normalize .Name }}`)`——这正是官方参考文档 Swarm Provider 参考 中默认值的来源;- 启动后通过
listServices枚举 Swarm 服务,再用builder.build把服务标签翻译成动态配置;watch=true时以 15 秒间隔轮询服务列表(Swarm 事件机制受限,见源码注释对 docker/docker#23827 的说明),并把新的动态配置推送给 Traefik 动态配置通道。
由于 默认暴露所有服务,原教程刻意设置 exposedbydefault=false 并配合 traefik.enable=true 显式开关,这是生产环境的推荐姿势。
常用配置项的默认值与说明可归纳如下(完整表格见 providers/swarm.md):
| 字段 | 默认值 | 作用 |
|---|---|---|
providers.swarm.endpoint |
unix:///var/run/docker.sock |
Docker/Swarm API 端点,也支持 ssh:// 远程端点 |
providers.swarm.watch |
true |
是否轮询监听 Swarm 服务变化 |
providers.swarm.exposedByDefault |
true |
默认是否暴露所有服务(建议显式设为 false) |
providers.swarm.network |
"" |
连接所有服务的默认 overlay 网络,可被 traefik.swarm.network 标签按服务覆盖 |
providers.swarm.refreshSeconds |
15s |
Swarm 模式的轮询间隔 |
providers.swarm.defaultRule |
Host(\{{ normalize .Name }}`)` |
未通过标签指定规则时的默认路由规则模板 |
providers.swarm.constraints |
"" |
依据服务标签过滤的约束表达式 |
providers.swarm.useBindPortIP |
false |
使用绑定端口的 IP/Port 而非容器内部网络地址 |
标签的命名空间也遵循统一约定:路由 traefik.http.routers.<name>.*、中间件 traefik.http.middlewares.<name>.*、服务 traefik.http.services.<name>.*。Swarm 模式下如果某个服务只有一个且路由器未显式指定 service,该服务会自动分配给该路由器。
更多关键配置方向
基础栈已可用,下面补充几条最常见的高频扩展配置。所有示例都沿用 compose 文件的 command 参数或服务 deploy.labels 写法。
证书自动管理(Let's Encrypt / ACME)
要让 websecure 入口点自动签发可信的 HTTPS 证书,启用 Let's Encrypt(ACME)证书解析器:
command:
# ...
- "--certificatesresolvers.le.acme.email=you@example.com"
- "--certificatesresolvers.le.acme.storage=/letsencrypt/acme.json"
- "--certificatesresolvers.le.acme.httpchallenge.entrypoint=web"
- "--entrypoints.websecure.http.tls.certresolver=le"
这里定义了一个名为 le 的证书解析器:email 用于注册 ACME 账户,storage 指向 /letsencrypt/acme.json(存放已签发证书),httpchallenge.entrypoint=web 使用 HTTP-01 质询方式验证域名,最后把 websecure 入口点的证书解析器指定为 le。
在 Swarm 中启用 ACME 有两点必须注意(参考文档 certificate-resolvers/overview 与 acme):
/letsencrypt路径必须放在共享卷或 NFS 上,让所有节点都能读取证书(Traefik 副本分布在多节点时,证书存储必须全局一致);- 记得在
docker-compose-swarm.yaml的traefik服务中挂载/letsencrypt卷,例如- letsencrypt:/letsencrypt。
质询方式与各家 DNS 提供商的详细配置请查阅上述参考文档。
Prometheus 指标
Traefik 内部指标可暴露给 Prometheus 抓取。基础栈中已开启 --metrics.prometheus=true,这里给出进一步定制:
command:
# 若使用独立指标入口点,先定义它:
- "--entrypoints.metrics.address=:8082"
- "--metrics.prometheus=true"
# 可选:更换指标暴露所在的入口点(默认是 'traefik')
- "--metrics.prometheus.entrypoint=metrics"
# 为路由/服务附加标签到指标(会提高基数)
- "--metrics.prometheus.addrouterslabels=true"
指标端点默认位于 traefik 入口点(即内部 API 端口 8080),也可用专用入口点在独立的 :8082 上暴露 /metrics,从而与业务流量隔离。全部选项见 observability/metrics.md;Traefik 自带的 Grafana 仪表盘模板位于本仓库 contrib/grafana,可直接导入使用。
分布式追踪(OpenTelemetry / OTel)
可以开启分布式追踪来跟踪请求在 Traefik 中的处理路径:
command:
# ...
- "--tracing.otel=true"
- "--tracing.otel.grpcendpoint=otel-collector:4317"
前提是 Swarm 中存在一个 Traefik 可访问的 OTel Collector,并监听上述 gRPC 端点。完整的端点与协议配置见 observability/tracing.md;仓库中 integration/fixtures/tracing/otel-collector-config.yaml 提供了一份可参考的 Collector 配置样例。
访问日志
Traefik 可以把进入的每个请求记录成访问日志,便于排障与分析:
command:
# ... 其他命令行参数 ...
- "--accesslog=true" # 启用访问日志到 stdout
# 可选:修改格式或输出文件(输出文件需要挂载卷)
- "--accesslog.format=json"
- "--accesslog.filepath=/path/to/access.log"
# 可选:过滤日志
- "--accesslog.filters.statuscodes=400-599"
format 支持默认(CLF)与 json 两种格式;filters.statuscodes 只保留指定状态码范围(如 400-599)的记录,用于聚焦错误请求。字段定义与过滤规则详见 logs-and-access-logs.md。
验证与排障思路
- 端口未绑定 / 无法访问:确认 Traefik 任务确实调度到了
node.role == manager的节点,且该节点上 80/443 未被占用; - 路由 404:检查目标服务的
traefik.enable=true与deploy.labels是否书写正确,并确认traefik.http.services.<name>.loadbalancer.server.port指向了真实容器端口; - 标签不生效:Swarm 模式只读取
deploy.labels;容器级labels不会参与服务发现; - 基础认证失败:确认已用
sed -e 's/\$/\$\$/g'转义哈希,并且中间件名与路由器引用一致(dashboard-auth@swarm); - 跨节点转发失败:确认
--providers.swarm.network指向的 overlay 网络名正确(栈内声明的网络会带栈名前缀),且该网络对相关服务均attachable。
结论
至此,你已经在 Docker Swarm 上运行起一套完整可用的 Traefik v3:HTTPS 与 HTTP→HTTPS 自动跳转、basic-auth 保护的 Dashboard、自签名证书 TLS 终结、whoami 演示服务,以及日志与 Prometheus 指标等基础可观测能力。随着 Swarm 规模增长,你可以在此基础上叠加 Let's Encrypt 自动证书、更多中间件(限流、重试、鉴权等)或运行多副本 Traefik;关于更复杂的生产部署考虑,还可参考仓库内的企业级部署讨论 traefik-for-business-applications。对应源码与示例均可在当前仓库中找到,便于对照深入学习。
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

