首页
/ Traefik + Docker Swarm 进阶实战:中间件、Let's Encrypt、Sticky Sessions 与多层路由

Traefik + Docker Swarm 进阶实战:中间件、Let's Encrypt、Sticky Sessions 与多层路由

2026-09-05 19:25:49作者:温玫谨Lighthearted

本文基于 Traefik 官方文档 docs/content/expose/swarm/advanced.md 展开,是 Docker Swarm 基础指南(docs/content/expose/swarm/basic.md)的进阶篇。假设你已完成基础指南,拥有一个可运行的 Traefik + Swarm 环境(whoami 服务跑在 3 副本上,whoami-api 服务跑在 2 副本上)。读完后你将掌握五类生产级能力:用 Headers 与 IP Allowlist 中间件加固响应与访问控制、用 Let's Encrypt 自动化证书管理、用 Sticky Sessions 支撑有状态应用、用 Multi-Layer Routing 实现基于认证的分级路由、以及用 Service Middlewares 把中间件配置集中到服务层面。

一、前置条件

  • 已完成基础指南(Basic Guide)中的部署
  • Docker Swarm 集群已初始化
  • Traefik 通过 Swarm 正常发现并路由 whoami / whoami-api 服务

二、添加中间件(Middlewares)

中间件允许请求/响应在穿过 Traefik 时被修改。这里添加两个最常用的中间件:用于安全响应头的 Headers 与用于访问控制的 IP Allowlist

docker-compose.yml 的 whoami 服务的 deploy 部分追加以下 labels(Swarm 模式下 labels 必须写在 deploy 段,而不是 container 级):

deploy:
  # ... existing configuration ...
  labels:
    # ... existing labels ...

    # Secure Headers Middleware
    - "traefik.http.middlewares.secure-headers.headers.frameDeny=true"
    - "traefik.http.middlewares.secure-headers.headers.sslRedirect=true"
    - "traefik.http.middlewares.secure-headers.headers.browserXssFilter=true"
    - "traefik.http.middlewares.secure-headers.headers.contentTypeNosniff=true"
    - "traefik.http.middlewares.secure-headers.headers.stsIncludeSubdomains=true"
    - "traefik.http.middlewares.secure-headers.headers.stsPreload=true"
    - "traefik.http.middlewares.secure-headers.headers.stsSeconds=31536000"

    # IP Allowlist Middleware
    - "traefik.http.middlewares.ip-allowlist.ipallowlist.sourceRange=127.0.0.1/32,192.168.0.0/16,10.0.0.0/8"

    # Apply the middlewares
    - "traefik.http.routers.whoami.middlewares=secure-headers,ip-allowlist"

各参数含义(详见 headers 参考文档):

Label 参数 效果
frameDeny=true 响应加 X-Frame-Options: DENY,防止页面被 iframe 嵌套(点击劫持)
sslRedirect=true HTTP 请求 301 跳转到 HTTPS
browserXssFilter=true X-XSS-Protection: 1; mode=block
contentTypeNosniff=true X-Content-Type-Options: nosniff,禁止浏览器嗅探 MIME 类型
stsIncludeSubdomains / stsPreload / stsSeconds=31536000 下发 Strict-Transport-Security(HSTS),有效期 1 年,覆盖子域并声明可预加载
ipallowlist.sourceRange=... 仅放行 CIDR 列表内的来源 IP(这里覆盖本机与常见内网段),其余返回 403

whoami-api 服务做同样的挂载:

deploy:
  # ... existing configuration ...
  labels:
    # ... existing labels ...
    - "traefik.http.routers.whoami-api.middlewares=secure-headers,ip-allowlist"

应用变更:

docker stack deploy -c docker-compose.yml traefik

验证中间件生效

验证 Secure Headers:

curl -k -I -H "Host: whoami.swarm.localhost" https://localhost/

响应头中应看到中间件设置的:

  • X-Frame-Options: DENY
  • X-Content-Type-Options: nosniff
  • X-XSS-Protection: 1; mode=block
  • 带相应设置的 Strict-Transport-Security

验证 IP Allowlist:请求来源 IP 在允许列表内(如 127.0.0.1)时请求成功:

curl -k -I -H "Host: whoami.swarm.localhost" https://localhost/

若来源 IP 不在列表内,请求会被拒绝并返回 403 Forbidden。本地环境可以临时修改 sourceRange 把自己排除掉来模拟被拒绝的场景,测完再改回来。

源码视角:中间件如何被执行

Swarm provider 在 SwarmProvider.Provide 中周期性(默认 RefreshSeconds 15 秒,见 SetDefaults)调用 listServices 拉取 Docker Swarm 的 service 列表与 overlay 网络,把每个 service 的 deploy.labels 解析成动态配置。labels 中 traefik.http.middlewares.* 定义中间件、traefik.http.routers.<name>.middlewares 把它们按顺序挂到路由链上,最终由 http.Router 的 Middlewares 字段 承载。

三、用 Let's Encrypt 自动生成证书

Let's Encrypt 提供免费且自动续期的 TLS 证书。不再使用基础指南中的自签证书,把配置改为自动签发。

在 Traefik 服务的 command 段加入证书解析器配置:

command:
  # ... existing commands ...
  # Let's Encrypt configuration
  - "--certificatesresolvers.le.acme.email=your-email@example.com" # replace with your actual email
  - "--certificatesresolvers.le.acme.storage=/letsencrypt/acme.json"
  - "--certificatesresolvers.le.acme.httpchallenge.entrypoint=web"

三项参数的作用:

  • email:ACME 账户注册邮箱(替换为你的真实邮箱);
  • storage=/letsencrypt/acme.json:证书与账户状态持久化位置,需要挂卷保证容器重建后不丢;
  • httpchallenge.entrypoint=web:HTTP-01 验证走 web(80 端口)入口点,Traefik 会临时监听 /.well-known/acme-challenge/ 路径回应 Let's Encrypt 的验证请求。

添加 Let's Encrypt 证书卷(服务引用 + 顶层命名卷):

volumes:
  # ...Existing volumes...
  - letsencrypt:/letsencrypt
volumes:
  # ... existing volumes ...
  letsencrypt:
    driver: local

更新路由 labels 指定使用 le 证书解析器:

labels:
  # ... existing labels ...
  - "traefik.http.routers.whoami.tls.certresolver=le"

其他需要 HTTPS 的服务同理:

labels:
  # ... existing labels ...
  - "traefik.http.routers.whoami-api.tls.certresolver=le"

应用变更:

docker stack deploy -c docker-compose.yml traefik

重要:需要公网 DNS。 Let's Encrypt 验证域名所有权要求域名可公网访问。对于 whoami.swarm.localhost 这类本地域名,证书仍会保持自签状态。生产环境请替换为真实域名,并让 DNS A 记录指向你的 Traefik 实例。

证书签发后可以验证:

# Verify the certificate chain
curl -v https://whoami.swarm.localhost/ 2>&1 | grep -i "server certificate"

应能看到证书签发方为 Let's Encrypt(本地测试域则仍是自签证书)。

四、配置 Sticky Sessions(会话粘滞)

Sticky sessions 保证同一用户的请求始终落到同一个后端容器,对有状态应用必不可少。Swarm 中 whoami 已跑多个副本,现在给它加粘滞配置:

deploy:
  # ... existing configuration ...
  labels:
    # ... existing labels ...

    # Sticky Sessions Configuration
    - "traefik.http.services.whoami.loadbalancer.sticky.cookie=true"
    - "traefik.http.services.whoami.loadbalancer.sticky.cookie.name=sticky_cookie"
    - "traefik.http.services.whoami.loadbalancer.sticky.cookie.secure=true"
    - "traefik.http.services.whoami.loadbalancer.sticky.cookie.httpOnly=true"

参数说明(结构定义见 Sticky / Cookie 类型,完整参考见 load balancer 文档):

  • sticky.cookie=true:启用基于 Cookie 的粘滞;
  • name=sticky_cookie:自定义 Cookie 名,Traefik 在其中写入所选后端标识;
  • secure=true:Cookie 仅通过 HTTPS 传输;
  • httpOnly=true:JavaScript 无法读取该 Cookie,降低被脚本篡改的风险。

应用变更:

docker stack deploy -c docker-compose.yml traefik

测试粘滞效果

# First request - save cookies to a file
curl -k -c cookies.txt -H "Host: whoami.swarm.localhost" https://localhost/

# Subsequent requests - use the cookies
curl -k -b cookies.txt -H "Host: whoami.swarm.localhost" https://localhost/
curl -k -b cookies.txt -H "Host: whoami.swarm.localhost" https://localhost/

观察每次响应中的 Hostname 字段——带 cookie 时它应保持相同,说明粘滞生效。作为对照,不带 cookie 的请求会在不同容器间做负载均衡:

# Requests without cookies should be load-balanced across different containers
curl -k -H "Host: whoami.swarm.localhost" https://localhost/
curl -k -H "Host: whoami.swarm.localhost" https://localhost/

这两次响应应出现不同的 Hostname 值。

提示:浏览器测试。 浏览器中需用同一浏览器会话维持 Cookie。由于 Cookie 带 httpOnlysecure 标志,它只会在 HTTPS 连接中发送,且无法被 JavaScript 访问。

源码层面可以印证粘滞的实现位置:sticky 字段同时存在于 LoadBalancer(普通服务)与 WeightedRoundRobin 等加权负载均衡器 结构中,Traefik 在服务选择后端 server 时才评估 sticky cookie,因此它与 loadbalancer.server.port 等负载均衡配置处于同一层级(traefik.http.services.<name>.loadbalancer.*)。

五、Multi-Layer Routing(多层路由)

多层路由允许路由器之间建立层级关系:父路由器先通过中间件处理请求,子路由器再做最终路由决策。特别适合基于认证的分级路由或分阶段中间件应用。

前提约束: 多层路由依赖 File provider——Docker Swarm 的 labels 不支持 parentRefs 字段(在 http.Router 定义 中该字段标注 label:"-",即禁止通过 label 写入)。但可以同时启用 Docker Swarm 和 File 两个 provider:用 Swarm labels 做服务发现,用 File 配置做多层路由编排。

启用 File provider

docker-compose.yml 中更新 Traefik 服务,把 File provider 与 Swarm provider 一起打开,并通过 Swarm config 注入动态配置文件:

services:
  traefik:
    image: traefik:v3.4
    command:
      - "--api.dashboard=true"
      - "--providers.docker.swarmMode=true"
      - "--providers.docker.exposedbydefault=false"
      - "--providers.docker.network=traefik_proxy"
      - "--providers.file.directory=/etc/traefik/dynamic"  # Enable File provider
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--entrypoints.websecure.http.tls=true"
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    configs:
      - source: mlr-config
        target: /etc/traefik/dynamic/mlr.yml
    networks:
      - traefik_proxy
    deploy:
      placement:
        constraints:
          - node.role == manager

configs:
  mlr-config:
    file: ./dynamic/mlr.yml

networks:
  traefik_proxy:
    external: true

注意 v3 的 provider 命名: 从当前仓库的 deprecation 检查 看,v3 已把 --providers.docker.swarmMode=true 标记为移除项,提示改用独立的 Swarm provider(providers.swarm 配置段,即 --providers.swarm=true)。原文示例为教学场景写作;在 v3 正式部署中建议按 Swarm provider 参考文档 使用 providers.swarm.* 配置项。node.role == manager 约束是因为 Swarm API 只在 manager 节点暴露(见 Swarm provider 文档的 Docker API Access 一节)。

基于认证的父/子路由示例

场景:父路由器先做 BasicAuth 认证,子路由器再依据认证出的用户角色把流量导向不同服务。

第一步,Swarm 侧照常定义两个后端服务(labels 声明服务发现信息):

# In docker-compose.yml
services:
  # ... traefik service from above ...

  # Admin backend service
  admin-backend:
    image: traefik/whoami
    networks:
      - traefik_proxy
    environment:
      - WHOAMI_NAME=Admin Backend
    deploy:
      replicas: 2
      labels:
        - "traefik.enable=true"
        - "traefik.http.services.admin-backend.loadbalancer.server.port=80"

  # User backend service
  user-backend:
    image: traefik/whoami
    networks:
      - traefik_proxy
    environment:
      - WHOAMI_NAME=User Backend
    deploy:
      replicas: 2
      labels:
        - "traefik.enable=true"
        - "traefik.http.services.user-backend.loadbalancer.server.port=80"

第二步,创建多层路由配置文件 dynamic/mlr.yml(通过上面的 Swarm config 挂载到 /etc/traefik/dynamic/mlr.yml):

http:
  routers:
    # Parent router with authentication middleware
    api-parent:
      rule: "Host(`api.swarm.localhost`) && PathPrefix(`/api`)"
      middlewares:
        - auth-middleware
      entryPoints:
        - websecure
      # Note: No service and no TLS config - this is a parent router

    # Child router for admin users
    api-admin:
      rule: "HeadersRegexp(`X-Auth-User`, `admin`)"
      service: admin-backend@swarm  # Reference Swarm service
      parentRefs:
        - api-parent@file  # Explicit reference to parent in file provider

    # Child router for regular users
    api-user:
      rule: "HeadersRegexp(`X-Auth-User`, `user`)"
      service: user-backend@swarm  # Reference Swarm service
      parentRefs:
        - api-parent@file  # Explicit reference to parent in file provider

  middlewares:
    auth-middleware:
      basicAuth:
        users:
          - "admin:$apr1$DmXR3Add$wfdbGw6RWIhFb0ffXMM4d0"
          - "user:$apr1$GJtcIY1o$mSLdsWYeXpPHVsxGDqadI."
        headerField: X-Auth-User

要点解读:

  • 父路由器 api-parent:只有 rule、middlewares 和 entryPoints,没有 service 也没有 TLS——这是父路由器的特征,它不直接转发流量,只负责"认证 + 派发给子路由";
  • headerField: X-Auth-User:BasicAuth 中间件(见 basicAuth 参考)认证成功后,把用户名写入 X-Auth-User 请求头,子路由的规则正是基于这个头做匹配;
  • 子路由器 通过 parentRefs 声明父引用,流量必须先从父路由器"流下来"才进入子路由的规则评估。

密码哈希: 上文的 $apr1$... 是 MD5-crypt 格式哈希,可用 Apache 工具生成:

htpasswd -nb admin yourpassword

跨 Provider 引用的 @ 后缀: 注意服务名上的 @swarmparentRefs 里的 @file@provider 后缀告诉 Traefik 去哪个 provider 命名空间查找资源:引用 Swarm 服务发现的服务写 service-name@swarm;在 File provider 里引用 File provider 定义的父路由器写 parent-name@file

部署 stack:

docker stack deploy -c docker-compose.yml traefik

测试多层路由

# Request goes through parent router → auth middleware → admin child router
curl -k -u admin:test -H "Host: api.swarm.localhost" https://localhost/api

admin:test 认证时应看到 admin-backend(whoami 输出中标注 Admin Backend)的响应;改用 user:test 则会命中 user-backend。

工作原理拆解

  1. 请求到达 api.swarm.localhost/api
  2. 父路由器 api-parent 按 Host + PathPrefix 规则匹配
  3. auth-middleware(BasicAuth)完成认证,把用户名写入 X-Auth-User 请求头
  4. 子路由器 api-adminapi-user 按该头的值匹配
  5. 请求被转发到对应的 Swarm 服务

更多机制细节见 Multi-Layer Routing 参考文档

六、Service Middlewares(服务级中间件)

Service middlewares 把中间件挂在服务而不是路由上,效果是该服务处理的所有请求都会经过它,不管流量是从哪个路由进来的。

适用场景:

  • 多个路由转发到同一个服务,且都应应用同一套中间件;
  • 希望确保无论流量从哪条路径到达,中间件一定生效;
  • 把中间件配置集中到服务层面,便于统一维护。

docker-compose.yml 的 whoami 服务上追加 labels:

services:
  whoami:
    image: traefik/whoami
    networks:
      - traefik_proxy
    deploy:
      replicas: 2
      labels:
        - "traefik.enable=true"
        - "traefik.http.routers.whoami.rule=Host(`whoami.swarm.localhost`)"
        - "traefik.http.routers.whoami.entrypoints=websecure"
        - "traefik.http.routers.whoami.tls=true"
        # Define the middleware
        - "traefik.http.middlewares.service-headers.headers.customRequestHeaders.X-Service-Middleware=applied"
        # Attach middleware at the SERVICE level (not the router level)
        - "traefik.http.services.whoami.middlewares=service-headers"
        - "traefik.http.services.whoami.loadbalancer.server.port=80"

注意两条 labels 的区别:

  • 路由级 traefik.http.routers.<name>.middlewares:仅当流量匹配该路由的规则时才生效;
  • 服务级 traefik.http.services.<name>.middlewares:对该服务的所有入站流量生效,与经由哪个路由转发无关。

两者同时配置时,路由级中间件先执行,然后才执行服务级中间件

部署:

docker stack deploy -c docker-compose.yml traefik

验证:

curl -k -H "Host: whoami.swarm.localhost" https://localhost/

whoami 会回显收到的请求头,其中应能看到服务级中间件注入的自定义请求头:

X-Service-Middleware: applied

更多 service 中间件细节见 service 参考文档的 Middlewares 小节

七、Swarm Provider 底层机制速览

结合仓库源码,理解上面这些配置为什么在 Swarm 环境下可行:

  • 标签作用域:Swarm 模式下 Traefik 读取的是 service 级 labelsdeploy.labels),而非单个容器标签——这正是所有示例把 traefik.* labels 放在 deploy: 下的原因(见 Swarm provider 文档);
  • 端口必须显式声明:Swarm API 不向 Traefik 暴露端口探测信息,因此 traefik.http.services.<name>.loadbalancer.server.port 是必填的,这也是为什么每个示例服务都写了 loadbalancer.server.port=80
  • 任务到后端服务器的映射listTasks / parseTasks 通过 Docker API 枚举每个 service 下处于 running 状态的 task,再依据 task 的网络附加信息拿到各副本的内网 IP,最终组成负载均衡池——这解释了 sticky sessions 中 Hostname 会在不同任务/节点间变化的现象;
  • 配置刷新节奏:provider 以 RefreshSeconds(默认 15s)轮询 service 列表(pswarm.go 中的 ticker 逻辑),因此 docker stack deploy 后配置生效通常有一个小的轮询延迟;
  • 部署位置约束:Swarm API 只在 manager 节点暴露,所以 Traefik 服务要加 node.role == manager 约束,或通过 TCP/SSH endpoint 将 API 暴露到任意节点(安全影响见 provider 文档的 Security Note)。

八、小结与延伸

本文覆盖了 Swarm 进阶部署的五个关键能力:

  • 用 secure headers 与 IP allowlisting 中间件加固服务;
  • 用 Let's Encrypt 自动管理证书(需公网可达域名);
  • 用 sticky sessions 支撑有状态应用;
  • 用 Swarm + File 双 provider 组合实现基于认证的 multi-layer routing;
  • 用 service middlewares 把中间件配置集中到服务层面。

如需进一步深入,可继续参考仓库内的这些文档:

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.83 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.79 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
988
506
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384