最后更新于:2026年08月

⚠️ 重要声明:本文仅从技术研究角度介绍 Traefik 反向代理的工程实现。请在遵守当地法律法规的前提下阅读和应用本文内容。

Traefik 反向代理与 Docker 自动发现完全指南
Traefik:Docker 原生的云原生反向代理

前面几篇文章里,Caddy 帮你搞定 HTTPS 反代(零配置证书),Nginx 帮你搞定负载均衡和高性能静态文件。但你有没有遇到这样的痛点:每加一个 Docker 容器,就要手动改 Nginx/Caddy 配置文件、reload 服务、祈祷别打错字?10 个容器还好,30 个容器时配置文件已经几百行,改一个域名就怕把别的搞崩。

Traefik(读音 “traffic”) 就是为解决这个问题而生的——它是 Docker 原生的云原生反向代理,通过读取容器 Label 自动生成路由规则,容器启动即注册、容器停止即摘除,全程零重启、零手写配置。再加上内置 ACME 自动 HTTPS、100+ 中间件链式编排、实时 Dashboard、Prometheus 指标导出,2026 年的 Docker 重度用户几乎人手一个。

本文从三大反代对比、核心概念、Docker Compose 部署、Label 路由、Let’s Encrypt 自动化、中间件链实战、File 动态配置、TLS 加固、可观测性、生产安全、Caddy/Nginx 迁移,到与 CF Tunnel/FRP/Uptime Kuma/acme.sh 联动,12 个章节全面覆盖 Traefik v3.3 的生产级用法。


🧭 第一部分:Traefik 是什么?三大反代对比

1.1 Nginx vs Caddy vs Traefik 全景对比(2026)

PLAINTEXT
┌────────────────────────────────────────────────────────────────────────────┐
│              三大反向代理全景对比(2026)                                     │
├──────────────┬──────────────────┬──────────────────┬──────────────────────┤
│ 维度          │ Nginx 1.27       │ Caddy 2.8        │ Traefik v3.3         │
├──────────────┼──────────────────┼──────────────────┼──────────────────────┤
│ 定位          │ 高性能 Web 服务器 │ 极简反代+自动TLS  │ 云原生动态反代       │
│ 配置方式      │ 手写 nginx.conf  │ Caddyfile (极简)  │ Label + YAML + 动态  │
│ Docker 发现   │ ❌ 手动改+reload  │ ❌ 手动改+reload  │ ✅ Label 自动发现    │
│ 配置重载      │ nginx -s reload  │ 自动(文件变更)    │ ✅ 零重载(动态生效)  │
│ ACME 证书     │ ❌ 需 acme.sh     │ ✅ 内置自动       │ ✅ 内置自动          │
│ HTTP/3 (QUIC)│ ✅ (1.27+)        │ ✅                │ ✅ (v3+)             │
│ 中间件系统    │ 模块化(编译时)    │ 简单指令          │ ✅ 100+ 可链式编排   │
│ Dashboard     │ ❌ 需第三方       │ ❌ 无             │ ✅ 内置实时 Web UI   │
│ Prometheus    │ ❌ 需 exporter    │ ❌ 需 exporter    │ ✅ 内置 /metrics     │
│ 负载均衡算法  │ round-robin      │ round-robin       │ ✅ WRR/PRR/最少连接  │
│ 性能(QPS)     │ ⭐⭐⭐⭐⭐ 最高   │ ⭐⭐⭐⭐ 高       │ ⭐⭐⭐ 中高(Go实现)  │
│ 学习曲线      │ ⭐⭐⭐ 中等       │ ⭐⭐⭐⭐⭐ 极简   │ ⭐⭐⭐ 中等           │
├──────────────┼──────────────────┼──────────────────┼──────────────────────┤
│ 最佳场景      │ 高并发/静态文件   │ 小团队/极简部署   │ Docker/K8s 容器环境  │
│              │ CDN/大流量入口    │ 自托管 Homelab    │ 微服务/多容器编排    │
└──────────────┴──────────────────┴──────────────────┴──────────────────────┘

一句话选型:

  • 容器多(10+)、经常增删服务、不想每次改配置 → Traefik ✅
  • 就几个服务、想要最简单的 HTTPS 反代 → Caddy ✅
  • 超高并发、CDN 入口、需要极限性能 → Nginx ✅
  • 三者可以共存:Nginx 做最外层 CDN 入口,Traefik 做容器内网路由,Caddy 做特定服务的简单反代。

1.2 Traefik 核心架构

PLAINTEXT
┌──────────────────────────────────────────────────────────────────────────┐
│                    Traefik v3 架构原理                                     │
│                                                                          │
│  ┌─────────────────────────────────────────────────────────────────┐    │
│  │                      Traefik 进程                                │    │
│  │                                                                 │    │
│  │  ┌──────────┐    ┌──────────────┐    ┌──────────┐              │    │
│  │  │EntryPoint│───→│   Router     │───→│ Service  │              │    │
│  │  │ (:443)   │    │ (匹配规则)    │    │ (后端)   │              │    │
│  │  │ (:80)    │    │              │    │          │              │    │
│  │  └──────────┘    └──────┬───────┘    └──────────┘              │    │
│  │                         │                                       │    │
│  │                   ┌─────▼──────┐                                │    │
│  │                   │ Middleware  │  ← 链式处理                    │    │
│  │                   │ (认证/限流/  │    (压缩→重定向→Header→转发)   │    │
│  │                   │  压缩/头...) │                                │    │
│  │                   └────────────┘                                │    │
│  │                                                                 │    │
│  │  ┌────────────────────────────────────────────────────────┐    │    │
│  │  │              Providers(配置来源)                       │    │    │
│  │  │  ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌──────────────┐ │    │    │
│  │  │  │ Docker  │ │  File   │ │   K8s   │ │ Cloudflare/  │ │    │    │
│  │  │  │ (Label) │ │ (YAML)  │ │ Ingress │ │ Consul/Redis │ │    │    │
│  │  │  └─────────┘ └─────────┘ └─────────┘ └──────────────┘ │    │    │
│  │  │     ↓ 实时监听变化,自动更新路由表,零重载               │    │    │
│  │  └────────────────────────────────────────────────────────┘    │    │
│  │                                                                 │    │
│  │  ┌──────────┐  ┌──────────┐  ┌──────────┐                     │    │
│  │  │ ACME     │  │Dashboard │  │ Metrics  │                     │    │
│  │  │ (自动证书)│  │ (:8080)  │  │(/metrics)│                     │    │
│  │  └──────────┘  └──────────┘  └──────────┘                     │    │
│  └─────────────────────────────────────────────────────────────────┘    │
│                                                                          │
│  ┌─────────┐         ┌─────────┐         ┌─────────┐                   │
│  │ whoami  │         │nextcloud│         │ jellyfin│  ← Docker 容器    │
│  │ Label:  │         │ Label:  │         │ Label:  │    自动注册       │
│  │ Host=   │         │ Host=   │         │ Host=   │                   │
│  │api.x.com│         │cloud.x  │         │media.x  │                   │
│  └─────────┘         └─────────┘         └─────────┘                   │
│      ↑ 容器启动 → Traefik 自动发现 Label → 生成路由 → 立刻可访问          │
└──────────────────────────────────────────────────────────────────────────┘

1.3 Traefik v3.3 能力矩阵

PLAINTEXT
┌──────────────────────────────────────────────────────────────────┐
│              Traefik v3.3 能力矩阵                                 │
├──────────────────────────────────────────────────────────────────┤
│  🔗 Providers(配置来源,可同时启用多个)                           │
│  ├── Docker / Docker Swarm  ← Label 驱动,最常用                  │
│  ├── Kubernetes Ingress / CRD  ← K8s 原生                        │
│  ├── File (YAML/TOML)  ← 静态/动态文件配置                        │
│  ├── Consul / Consul Catalog  ← 服务发现                          │
│  ├── Redis  ← 动态配置存储                                        │
│  └── HashiCorp Nomad / Amazon ECS  ← 云平台                       │
│                                                                  │
│  🔐 ACME / Let's Encrypt 自动证书                                  │
│  ├── HTTP-01 challenge(需要 80 端口)                            │
│  ├── TLS-ALPN-01 challenge(需要 443 端口)                      │
│  ├── DNS-01 challenge(支持 100+ DNS 提供商)                    │
│  ├── Wildcard 证书(DNS-01 专用)                                │
│  └── 证书存储:文件 / Redis / Consul(多副本共享)                │
│                                                                  │
│  🧩 Middleware(中间件,可链式组合)                                │
│  ├── 认证:BasicAuth / ForwardAuth / DigestAuth                  │
│  ├── 限流:RateLimit / InFlightReq                               │
│  ├── 压缩:Compress (gzip/zstd)                                  │
│  ├── 重定向:RedirectScheme / RedirectRegex                       │
│  ├── Header:Headers / CustomRequestHeaders / CustomResponseHeaders│
│  ├── 路径重写:ReplacePath / ReplacePathRegex / StripPrefix       │
│  ├── 超时:ForwardingTimeouts / Buffering                         │
│  ├── IP 白名单:IPAllowList                                      │
│  ├── 重试:Retry                                                  │
│  ├── CORS:Headers(Access-Control-*)                            │
│  └── 自定义插件(Go/WASM,v3 新增 WASM 支持)                     │
│                                                                  │
│  📊 可观测性                                                      │
│  ├── 内置 Dashboard(实时路由/服务/中间件拓扑图)                  │
│  ├── Prometheus /metrics(QPS/延迟/错误率/证书状态)              │
│  ├── Access Logs(JSON/CLF 格式)                                 │
│  ├── Open Telemetry Tracing(Jaeger/Zipkin/Tempo)               │
│  └── 健康检查端点 /ping 和 /metrics                               │
│                                                                  │
│  ⚡ 协议支持                                                      │
│  ├── HTTP/1.1 / HTTP/2 / HTTP/3 (QUIC)                           │
│  ├── TCP 代理(任意 TCP 服务,非 HTTP 也能路由)                  │
│  ├── UDP 代理(v3 新增)                                          │
│  └── gRPC 原生支持(基于 HTTP/2)                                │
└──────────────────────────────────────────────────────────────────┘

⚡ 第二部分:10 分钟快速上手

2.1 核心概念速览(必须先理解)

PLAINTEXT
┌──────────────────────────────────────────────────────────────────┐
│  Traefik 四大核心概念(类比 Nginx 理解)                           │
├──────────────────────────────────────────────────────────────────┤
│                                                                  │
│  ① EntryPoint(入口点)= Nginx 的 listen                         │
│     定义 Traefik 监听哪些端口                                    │
│     例:web(:80) / websecure(:443) / traefik(:8080)              │
│                                                                  │
│  ② Router(路由器)= Nginx 的 server + location                  │
│     定义「什么请求 → 走哪个 Service」                            │
│     匹配规则:Host / Path / HostPrefix / Headers                 │
│     例:Host(`cloud.example.com`) → Service(nextcloud)          │
│                                                                  │
│  ③ Service(服务)= Nginx 的 upstream                            │
│     定义「后端服务在哪」                                         │
│     例:http://nextcloud:80  (Docker 容器名+端口)               │
│                                                                  │
│  ④ Middleware(中间件)= Nginx 的模块指令                       │
│     在 Router 和 Service 之间插入处理链                           │
│     例:compress → headers → ratelimit → forward                │
│                                                                  │
│  数据流:                                                         │
│  客户端 → EntryPoint(:443) → Router(匹配Host)                   │
│         → Middleware链(压缩/认证/限流) → Service(后端容器)        │
└──────────────────────────────────────────────────────────────────┘

2.2 Docker Compose 一键部署 Traefik + 示例服务

YAML
# ~/traefik/docker-compose.yml
services:
  # ═══════════════════════════════════════
  #  Traefik 反向代理主体
  # ═══════════════════════════════════════
  traefik:
    image: traefik:v3.3
    container_name: traefik
    restart: unless-stopped
    ports:
      - "80:80"       # HTTP 入口(用于 ACME HTTP-01 + 重定向到 HTTPS)
      - "443:443"     # HTTPS 入口(主入口)
      - "8080:8080"   # Dashboard(生产环境不要直接暴露!后面讲安全做法)
    volumes:
      # 🔑 让 Traefik 能读取其他容器的 Label 和网络状态
      - /var/run/docker.sock:/var/run/docker.sock:ro
      # 🔑 ACME 证书持久化(非常重要!丢了就要重新申请)
      - ./letsencrypt:/letsencrypt
      # (可选)静态配置文件
      - ./traefik.yml:/etc/traefik/traefik.yml:ro
      # (可选)动态配置文件目录
      - ./dynamic:/dynamic:ro
    command:
      # ── EntryPoint(监听端口) ──
      - --entrypoints.web.address=:80
      - --entrypoints.websecure.address=:443
      - --entrypoints.traefik.address=:8080
      # ── 全局 HTTP → HTTPS 重定向 ──
      - --entrypoints.web.http.redirections.entrypoint.to=websecure
      - --entrypoints.web.http.redirections.entrypoint.scheme=https
      # ── Docker Provider(自动发现) ──
      - --providers.docker=true
      - --providers.docker.exposedbydefault=false  # 🔒 默认不暴露,需 Label 显式开启
      # ── File Provider(动态配置) ──
      - --providers.file.directory=/dynamic
      - --providers.file.watch=true
      # ── ACME Let's Encrypt 自动证书 ──
      - --certificatesresolvers.letsencrypt.acme.email=you@example.com
      - --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
      - --certificatesresolvers.letsencrypt.acme.tlschallenge=true  # TLS-ALPN-01
      # ── Dashboard ──
      - --api.dashboard=true
      - --api.insecure=true  # ⚠️ 仅测试用!生产环境用 Label + BasicAuth 保护
      # ── 日志 ──
      - --log.level=INFO
      - --accesslog=true
      # ── Prometheus 指标 ──
      - --metrics.prometheus=true
      - --metrics.prometheus.addEntryPointsLabels=true
      - --metrics.prometheus.addServicesLabels=true
    networks:
      - proxy

  # ═══════════════════════════════════════
  #  示例服务 1:whoami(测试用)
  # ═══════════════════════════════════════
  whoami:
    image: traefik/whoami
    container_name: whoami
    restart: unless-stopped
    labels:
      # 🔑 启用 Traefik(因为 exposedbydefault=false)
      - traefik.enable=true
      # 🔑 路由:匹配 Host
      - traefik.http.routers.whoami.rule=Host(`whoami.example.com`)
      - traefik.http.routers.whoami.entrypoints=websecure
      - traefik.http.routers.whoami.tls=true
      - traefik.http.routers.whoami.tls.certresolver=letsencrypt
      # 🔑 后端服务端口
      - traefik.http.services.whoami.loadbalancer.server.port=80
    networks:
      - proxy

  # ═══════════════════════════════════════
  #  示例服务 2:Nextcloud
  # ═══════════════════════════════════════
  nextcloud:
    image: nextcloud:29-apache
    container_name: nextcloud
    restart: unless-stopped
    volumes:
      - ./nextcloud:/var/www/html
    labels:
      - traefik.enable=true
      - traefik.http.routers.nextcloud.rule=Host(`cloud.example.com`)
      - traefik.http.routers.nextcloud.entrypoints=websecure
      - traefik.http.routers.nextcloud.tls=true
      - traefik.http.routers.nextcloud.tls.certresolver=letsencrypt
      - traefik.http.services.nextcloud.loadbalancer.server.port=80
      # Nextcloud 特别:需要大文件上传,加中间件
      - traefik.http.middlewares.nextcloud-headers.headers.stsSeconds=15552000
      - traefik.http.middlewares.nextcloud-headers.headers.customFrameOptionsValue=SAMEORIGIN
      - traefik.http.routers.nextcloud.middlewares=nextcloud-headers
    networks:
      - proxy

networks:
  proxy:
    external: true   # 先 docker network create proxy
BASH
# 初始化
docker network create proxy
mkdir -p ~/traefik/letsencrypt ~/traefik/dynamic
chmod 600 ~/traefik/letsencrypt/acme.json  # 证书文件权限必须 600

# 启动
cd ~/traefik
docker compose up -d

# 查看 Traefik 日志
docker compose logs -f traefik

# 打开 Dashboard
# 浏览器访问 http://your-server:8080
# 看到 HTTP / TCP 路由列表 + 服务状态 = 成功

# 测试 whoami(DNS 要先解析到你的服务器)
curl -H "Host: whoami.example.com" http://localhost
# 会自动 301 重定向到 HTTPS,再访问就能看到 whoami 的输出

💡 关键魔法:你刚才没写一行 Nginx/Caddy 配置文件!whoami 和 nextcloud 容器一启动,Traefik 自动读取 Label 生成了路由规则。以后你要加新服务,只需要在 docker-compose.yml 里加几行 Label——不用动 Traefik 本身。


🏷️ 第三部分:Label 驱动路由深度实战

3.1 Label 命名规则与完整语法

Traefik 的 Label 就是 Docker Label,命名遵循层级结构:

PLAINTEXT
traefik.<资源类型>.<自定义名称>.<属性>=<值>

资源类型:
  http.routers        → HTTP 路由器
  http.services       → HTTP 后端服务
  http.middlewares    → HTTP 中间件
  tcp.routers         → TCP 路由器(非 HTTP 服务用)
  tcp.services        → TCP 后端服务
  udp.routers         → UDP 路由器(v3 新增)
  udp.services        → UDP 后端服务

3.2 路由匹配规则(Rule)大全

YAML
# 单个 Host
- traefik.http.routers.app.rule=Host(`app.example.com`)

# 多个 Host(OR)
- traefik.http.routers.app.rule=Host(`app.example.com`, `www.example.com`)

# Host + Path 前缀
- traefik.http.routers.app.rule=Host(`api.example.com`) && PathPrefix(`/v1`)

# Host + Path 精确正则
- traefik.http.routers.app.rule=Host(`api.example.com`) && Path(`/api/{category:[a-z]+}/{id:[0-9]+}`)

# 通配符子域名
- traefik.http.routers.app.rule=HostRegexp(`^.+\.example\.com$`)

# 按 Header 路由(蓝绿部署/灰度发布)
- traefik.http.routers.app-canary.rule=Host(`app.example.com`) && Headers(`X-Canary`, `true`)

# 按查询参数路由
- traefik.http.routers.app-preview.rule=Host(`app.example.com`) && Query(`preview`, `true`)

# 按 HTTP 方法路由
- traefik.http.routers.api-write.rule=Host(`api.example.com`) && Method(`POST`, `PUT`, `DELETE`)
- traefik.http.routers.api-read.rule=Host(`api.example.com`) && Method(`GET`)

3.3 实战:10 个常见服务的 Label 模板

YAML
# ════ 1. Vaultwarden(密码管理器) ════
vaultwarden:
  image: vaultwarden/server:latest
  labels:
    - traefik.enable=true
    - traefik.http.routers.vault.rule=Host(`pass.example.com`)
    - traefik.http.routers.vault.entrypoints=websecure
    - traefik.http.routers.vault.tls=true
    - traefik.http.routers.vault.tls.certresolver=letsencrypt
    - traefik.http.services.vault.loadbalancer.server.port=80
    # WebSocket 支持(实时同步)
    - traefik.http.routers.vault.middlewares=vault-headers
    - traefik.http.middlewares.vault-headers.headers.customRequestHeaders.X-Real-IP=127.0.0.1

# ════ 2. Jellyfin(媒体服务器) ════
jellyfin:
  image: lscr.io/linuxserver/jellyfin:latest
  labels:
    - traefik.enable=true
    - traefik.http.routers.jellyfin.rule=Host(`media.example.com`)
    - traefik.http.routers.jellyfin.entrypoints=websecure
    - traefik.http.routers.jellyfin.tls=true
    - traefik.http.routers.jellyfin.tls.certresolver=letsencrypt
    - traefik.http.services.jellyfin.loadbalancer.server.port=8096
    # Jellyfin 需要大 body 限制(上传媒体)
    - traefik.http.middlewares.jellyfin-buffering.buffering.maxRequestBodyBytes=50000000000
    - traefik.http.routers.jellyfin.middlewares=jellyfin-headers
    - traefik.http.middlewares.jellyfin-headers.headers.customResponseHeaders.Server=

# ════ 3. Portainer(Docker 管理面板) ════
portainer:
  image: portainer/portainer-ce:latest
  labels:
    - traefik.enable=true
    - traefik.http.routers.portainer.rule=Host(`docker.example.com`)
    - traefik.http.routers.portainer.entrypoints=websecure
    - traefik.http.routers.portainer.tls=true
    - traefik.http.routers.portainer.tls.certresolver=letsencrypt
    - traefik.http.services.portainer.loadbalancer.server.port=9000
    # Portainer Edge 用 8000 端口(TCP 隧道),需要单独配 TCP 路由
    - traefik.tcp.routers.portainer-edge.rule=HostSNI(`*`)
    - traefik.tcp.routers.portainer-edge.entrypoints=portainer-edge
    - traefik.tcp.services.portainer-edge.loadbalancer.server.port=8000

# ════ 4. Uptime Kuma(监控面板) ════
uptime-kuma:
  image: louislam/uptime-kuma:2
  labels:
    - traefik.enable=true
    - traefik.http.routers.kuma.rule=Host(`status.example.com`)
    - traefik.http.routers.kuma.entrypoints=websecure
    - traefik.http.routers.kuma.tls=true
    - traefik.http.routers.kuma.tls.certresolver=letsencrypt
    - traefik.http.services.kuma.loadbalancer.server.port=3001
    # Kuma 用 WebSocket 实时推送,Traefik 默认支持,无需额外配置

# ════ 5. Gitea(Git 服务) ════
gitea:
  image: gitea/gitea:latest
  labels:
    - traefik.enable=true
    # Web UI
    - traefik.http.routers.gitea-web.rule=Host(`git.example.com`)
    - traefik.http.routers.gitea-web.entrypoints=websecure
    - traefik.http.routers.gitea-web.tls=true
    - traefik.http.routers.gitea-web.tls.certresolver=letsencrypt
    - traefik.http.services.gitea-web.loadbalancer.server.port=3000
    # SSH(TCP 路由,非 HTTP)
    - traefik.tcp.routers.gitea-ssh.rule=HostSNI(`*`)
    - traefik.tcp.routers.gitea-ssh.entrypoints=gitea-ssh
    - traefik.tcp.services.gitea-ssh.loadbalancer.server.port=22

# ════ 6. AdGuard Home(DNS 广告拦截) ════
adguard:
  image: adguard/adguardhome:latest
  labels:
    - traefik.enable=true
    # Web 管理面板
    - traefik.http.routers.adguard.rule=Host(`dns.example.com`)
    - traefik.http.routers.adguard.entrypoints=websecure
    - traefik.http.routers.adguard.tls=true
    - traefik.http.routers.adguard.tls.certresolver=letsencrypt
    - traefik.http.services.adguard.loadbalancer.server.port=3000
    # DNS over HTTPS (DoH) — 用 TCP 路由走 443 端口的 /dns-query 路径
    # DoH 走的是 HTTPS,所以就是普通 HTTP 路由
    - traefik.http.routers.adguard-doh.rule=Host(`dns.example.com`) && PathPrefix(`/dns-query`)
    - traefik.http.routers.adguard-doh.entrypoints=websecure
    - traefik.http.routers.adguard-doh.tls=true
    - traefik.http.routers.adguard-doh.tls.certresolver=letsencrypt
    - traefik.http.services.adguard-doh.loadbalancer.server.port=443
    # 注意 DoH 后端是 HTTPS,需要指定 scheme
    - traefik.http.services.adguard-doh.loadbalancer.server.scheme=https

# ════ 7. Grafana(监控面板)+ BasicAuth 保护 ════
grafana:
  image: grafana/grafana:latest
  labels:
    - traefik.enable=true
    - traefik.http.routers.grafana.rule=Host(`grafana.example.com`)
    - traefik.http.routers.grafana.entrypoints=websecure
    - traefik.http.routers.grafana.tls=true
    - traefik.http.routers.grafana.tls.certresolver=letsencrypt
    - traefik.http.services.grafana.loadbalancer.server.port=3000
    # 🔐 在 Grafana 前面加一层 BasicAuth(双因素)
    # 密码用 htpasswd 生成:htpasswd -nbB user yourpassword
    - traefik.http.middlewares.grafana-auth.basicauth.users=user:$$2y$$05$$XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    - traefik.http.routers.grafana.middlewares=grafana-auth

# ════ 8. 多容器负载均衡(3 个 whoami 副本) ════
whoami-1:
  image: traefik/whoami
  labels:
    - traefik.enable=true
    - traefik.http.routers.lb.rule=Host(`lb.example.com`)
    - traefik.http.routers.lb.entrypoints=websecure
    - traefik.http.routers.lb.tls=true
    - traefik.http.routers.lb.tls.certresolver=letsencrypt
    - traefik.http.services.lb.loadbalancer.server.port=80
    # 加权负载均衡
    - traefik.http.services.lb.loadbalancer.server.weight=10

whoami-2:
  image: traefik/whoami
  labels:
    - traefik.enable=true
    - traefik.http.routers.lb.rule=Host(`lb.example.com`)     # 同名 Router 自动合并
    - traefik.http.routers.lb.entrypoints=websecure
    - traefik.http.routers.lb.tls=true
    - traefik.http.services.lb.loadbalancer.server.port=80
    - traefik.http.services.lb.loadbalancer.server.weight=5   # 权重 5

whoami-3:
  image: traefik/whoami
  labels:
    - traefik.enable=true
    - traefik.http.routers.lb.rule=Host(`lb.example.com`)
    - traefik.http.routers.lb.entrypoints=websecure
    - traefik.http.routers.lb.tls=true
    - traefik.http.services.lb.loadbalancer.server.port=80
    - traefik.http.services.lb.loadbalancer.server.weight=1   # 权重 1(灰度)
    # 🎯 三个容器同名 Router → Traefik 自动做加权负载均衡:10:5:1

# ════ 9. 蓝绿部署(Header 路由) ════
app-blue:
  image: myapp:v1
  labels:
    - traefik.enable=true
    # 默认路由 → blue(生产版)
    - traefik.http.routers.app.rule=Host(`app.example.com`)
    - traefik.http.routers.app.entrypoints=websecure
    - traefik.http.routers.app.tls=true
    - traefik.http.routers.app.tls.certresolver=letsencrypt
    - traefik.http.services.app.loadbalancer.server.port=3000

app-green:
  image: myapp:v2
  labels:
    - traefik.enable=true
    # 带 X-Preview 头的路由 → green(新版灰度)
    - traefik.http.routers.app-preview.rule=Host(`app.example.com`) && Headers(`X-Preview`, `true`)
    - traefik.http.routers.app-preview.entrypoints=websecure
    - traefik.http.routers.app-preview.tls=true
    - traefik.http.routers.app-preview.tls.certresolver=letsencrypt
    - traefik.http.services.app-preview.loadbalancer.server.port=3000
    # 测试方法:curl -H "X-Preview: true" https://app.example.com → 走 green
    #          curl https://app.example.com → 走 blue(生产)

# ════ 10. TCP 服务(MySQL 数据库代理) ════
mysql:
  image: mysql:8
  labels:
    - traefik.enable=true
    # TCP 路由(非 HTTP),用 HostSNI(`*`) 匹配所有
    - traefik.tcp.routers.mysql.rule=HostSNI(`*`)
    - traefik.tcp.routers.mysql.entrypoints=mysql
    - traefik.tcp.services.mysql.loadbalancer.server.port=3306
    # 需要在 Traefik 启动参数里定义 entrypoint:
    #   --entrypoints.mysql.address=:33060

🔐 第四部分:Let’s Encrypt ACME 自动 HTTPS

4.1 三种 Challenge 方式对比与选择

PLAINTEXT
┌──────────────────────────────────────────────────────────────────────┐
│              ACME Challenge 三种方式对比                                │
├──────────────┬─────────────┬─────────────┬───────────────────────────┤
│ 方式          │ HTTP-01     │ TLS-ALPN-01 │ DNS-01                    │
├──────────────┼─────────────┼─────────────┼───────────────────────────┤
│ 需要 80 端口  │ ✅ 必须      │ ❌           │ ❌                        │
│ 需要 443 端口 │ ❌           │ ✅ 必须      │ ❌                        │
│ 通配符证书    │ ❌ 不支持    │ ❌ 不支持    │ ✅ 支持 *.example.com    │
│ 内网可用      │ ❌           │ ❌           │ ✅(不需要入站连接)      │
│ 配置复杂度    │ ⭐ 最简      │ ⭐⭐          │ ⭐⭐⭐(需 DNS API Token)│
│ 适用场景      │ 有公网 80    │ 有公网 443   │ 通配符 / 内网 / 无 80 端口│
└──────────────┴─────────────┴─────────────┴───────────────────────────┘

推荐:
  · 有公网 IP + 80/443 都通 → TLS-ALPN-01(最安全,不需要开 80 端口)
  · 需要通配符证书 → DNS-01(必选)
  · 内网/CF Tunnel 后面 → DNS-01(唯一选择)

4.2 DNS-01 配置(通配符证书 + Cloudflare)

YAML
# docker-compose.yml 的 traefik 服务 command 里加:
command:
  # ... 其他配置 ...
  # DNS-01 with Cloudflare
  - --certificatesresolvers.cloudflare.acme.email=you@example.com
  - --certificatesresolvers.cloudflare.acme.storage=/letsencrypt/acme.json
  - --certificatesresolvers.cloudflare.acme.dnschallenge=true
  - --certificatesresolvers.cloudflare.acme.dnschallenge.provider=cloudflare
  # 可选:指定 DNS 服务器(加速传播验证)
  - --certificatesresolvers.cloudflare.acme.dnschallenge.resolvers=1.1.1.1:53,8.8.8.8:53
  # 可选:关闭 staging(测试时先开 staging 避免限频)
  # - --certificatesresolvers.cloudflare.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory

environment:
  # Cloudflare API Token(需要 Zone:DNS:Edit 权限)
  - CF_DNS_API_TOKEN=your_cloudflare_api_token_here

# 然后在容器的 Label 里使用通配符证书:
# - traefik.http.routers.app.tls.certresolver=cloudflare
# - traefik.http.routers.app.tls.domains[0].main=example.com
# - traefik.http.routers.app.tls.domains[0].sans=*.example.com

💡 DNS-01 的优势:不需要开放 80 端口,不需要公网入站连接。即使 Traefik 在 Cloudflare Tunnel 后面、在 FRP 后面、在内网,只要有 DNS API Token 就能申请证书。和第 5 篇 acme.sh 文章里的 DNS-01 是同一个原理,但 Traefik 把它内置了。

4.3 证书存储与多副本共享

PLAINTEXT
单实例(默认):
  证书存在 /letsencrypt/acme.json 文件里
  → 备份这个文件就行

多实例(高可用):
  证书存在 Redis / Consul 里,多个 Traefik 实例共享
  → 配置:
     - --certificatesresolvers.letsencrypt.acme.storage=traefik-acme
     需要 KV store provider:
     - --providers.redis=true
     - --providers.redis.endpoints=redis:6379
  → 第一个 Traefik 申请证书 → 存 Redis → 其他 Traefik 自动读取
  → 不用每个实例都申请一次(避免 Let's Encrypt 限频)

🧩 第五部分:中间件链式编排实战

5.1 中间件核心概念

PLAINTEXT
┌──────────────────────────────────────────────────────────────────┐
│  中间件执行顺序(链式处理,按顺序执行)                             │
│                                                                  │
│  请求 → EntryPoint → Router                                      │
│         ↓                                                        │
│         Middleware 1: RedirectScheme (HTTP→HTTPS)               │
│         ↓                                                        │
│         Middleware 2: RateLimit (100 req/min)                   │
│         ↓                                                        │
│         Middleware 3: BasicAuth (用户认证)                       │
│         ↓                                                        │
│         Middleware 4: Compress (gzip 压缩)                      │
│         ↓                                                        │
│         Service (后端容器)                                        │
│                                                                  │
│  响应 ← Compress ← Service                                       │
│                                                                  │
│  配置方式:                                                       │
│    traefik.http.routers.app.middlewares=redirect,ratelimit,auth,compress
│    (逗号分隔,按顺序执行)                                       │
└──────────────────────────────────────────────────────────────────┘

5.2 常用中间件完整配置

YAML
# ════ 1. 全局 HTTP→HTTPS 重定向 ════
# 在 Traefik 启动参数里配(所有路由生效):
#   --entrypoints.web.http.redirections.entrypoint.to=websecure
#   --entrypoints.web.http.redirections.entrypoint.scheme=https

# 或者在 Label 里单独配:
- traefik.http.middlewares.https-redirect.redirectscheme.scheme=https
- traefik.http.middlewares.https-redirect.redirectscheme.permanent=true
- traefik.http.routers.app.middlewares=https-redirect

# ════ 2. BasicAuth(管理面板保护) ════
# 先生成密码哈希:htpasswd -nbB admin 'YourStr0ngP@ss'
# 注意:Docker Label 里 $ 要写成 $$
- traefik.http.middlewares.admin-auth.basicauth.users=admin:$$2y$$05$$XXXXXXX...
- traefik.http.middlewares.admin-auth.basicauth.realm=Admin Area
- traefik.http.middlewares.admin-auth.basicauth.removeheader=true
- traefik.http.routers.portainer.middlewares=admin-auth

# ════ 3. RateLimit(限流防 CC) ════
- traefik.http.middlewares.ratelimit.ratelimit.average=100        # 每秒平均 100 请求
- traefik.http.middlewares.ratelimit.ratelimit.burst=50           # 突发 50 请求
- traefik.http.middlewares.ratelimit.ratelimit.period=1s
# 按客户端 IP 分组限流
- traefik.http.middlewares.ratelimit.ratelimit.sourcecriterion.requesthost=false
- traefik.http.middlewares.ratelimit.ratelimit.sourcecriterion.ipstrategy.depth=0

# ════ 4. Compress(压缩响应) ════
- traefik.http.middlewares.compress.compress=true
# v3 支持 zstd(比 gzip 更快更好)
- traefik.http.middlewares.compress-zstd.compress.encodings=zstd,gzip

# ════ 5. Security Headers(安全头) ════
- traefik.http.middlewares.secure-headers.headers.stsSeconds=31536000
- traefik.http.middlewares.secure-headers.headers.stsIncludeSubdomains=true
- traefik.http.middlewares.secure-headers.headers.stsPreload=true
- traefik.http.middlewares.secure-headers.headers.forceSTSHeader=true
- traefik.http.middlewares.secure-headers.headers.customFrameOptionsValue=SAMEORIGIN
- traefik.http.middlewares.secure-headers.headers.contentTypeNosniff=true
- traefik.http.middlewares.secure-headers.headers.browserXssFilter=true
- traefik.http.middlewares.secure-headers.headers.referrerPolicy=no-referrer-when-downgrade
- traefik.http.middlewares.secure-headers.headers.permissionsPolicy=camera=(), microphone=(), geolocation=()

# ════ 6. IPAllowList(IP 白名单) ════
- traefik.http.middlewares.ip-whitelist.ipallowlist.sourcerange=10.0.0.0/8,192.168.0.0/16,172.16.0.0/12
# 如果 Traefik 在反代后面,需要用 IPStrategy 拿真实 IP
- traefik.http.middlewares.ip-whitelist.ipallowlist.ipstrategy.depth=1

# ════ 7. StripPrefix(路径前缀剥离) ════
# 例:请求 /api/v1/users → 后端收到 /users
- traefik.http.middlewares.strip-api.stripprefix.prefixes=/api/v1

# ════ 8. ReplacePathRegex(路径重写) ════
# 例:请求 /old/foo → 后端收到 /new/foo
- traefik.http.middlewares.rewrite-path.replacepathregex.regex=^/old/(.*)
- traefik.http.middlewares.rewrite-path.replacepathregex.replacement=/new/$$1

# ════ 9. ForwardAuth(外部认证) ════
# 对接 Authentik / Authelia / 自建 SSO
- traefik.http.middlewares.sso-auth.forwardauth.address=http://authelia:9091/api/verify?rd=https://auth.example.com
- traefik.http.middlewares.sso-auth.forwardauth.trustForwardHeader=true
- traefik.http.middlewares.sso-auth.forwardauth.authResponseHeaders=Remote-User,Remote-Groups,Remote-Email,Remote-Name

# ════ 10. Retry(重试) ════
- traefik.http.middlewares.retry.retry.attempts=4
- traefik.http.middlewares.retry.retry.initialinterval=100ms

# ════ 11. CORS(跨域) ════
- traefik.http.middlewares.cors.headers.accessControlAllowOriginList=https://app.example.com,https://admin.example.com
- traefik.http.middlewares.cors.headers.accessControlAllowMethods=GET,POST,PUT,DELETE,OPTIONS
- traefik.http.middlewares.cors.headers.accessControlAllowHeaders=Authorization,Content-Type
- traefik.http.middlewares.cors.headers.accessControlMaxAge=86400
- traefik.http.middlewares.cors.headers.addVaryHeader=true

# ════ 12. Chain(组合多个中间件) ════
# 把多个中间件打包成一个链,复用更方便
- traefik.http.middlewares.secure-chain.chain.middlewares=https-redirect,secure-headers,ratelimit,compress
- traefik.http.routers.app.middlewares=secure-chain

5.3 全局中间件:所有路由共享

YAML
# 在 traefik.yml 静态配置文件里定义全局默认中间件
# ~/traefik/traefik.yml
entryPoints:
  web:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https
  websecure:
    address: ":443"

providers:
  docker:
    exposedByDefault: false
  file:
    directory: /dynamic
    watch: true

# 全局默认中间件(所有 Router 自动继承)
# 定义在 dynamic/defaults.yml 里:
YAML
# ~/traefik/dynamic/defaults.yml
tls:
  options:
    default:
      minVersion: VersionTLS12
      maxVersion: VersionTLS13
      cipherSuites:
        - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
        - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305
        - TLS_AES_256_GCM_SHA384
        - TLS_CHACHA20_POLY1305_SHA256
      sniStrict: true

📁 第六部分:File Provider —— 动态文件配置

6.1 什么时候用 File Provider?

PLAINTEXT
Docker Provider 的局限:
  · 只能配 Docker 容器的路由
  · 复杂的中间件链在 Label 里写很长很难维护
  · TCP/UDP 路由在 Label 里不够灵活

File Provider 的优势:
  · 用 YAML/TOML 写配置,结构清晰
  · 支持热重载(文件改了自动生效,零重启)
  · 适合配:非 Docker 服务 / 复杂中间件链 / TLS 选项 / 全局默认值
  · 和 Docker Provider 同时使用,互补

典型用法:
  · Docker 容器 → Label 配 Router + Service
  · File 配 → 全局 TLS 选项 / 复杂中间件 / 非 Docker 服务路由

6.2 动态配置文件示例

YAML
# ~/traefik/dynamic/dynamic.yml

# ════ HTTP 路由(非 Docker 服务) ════
http:
  routers:
    # 路由到宿主机上的服务(不在 Docker 里)
    host-service:
      rule: Host(`internal.example.com`)
      entryPoints:
        - websecure
      service: host-backend
      tls:
        certResolver: letsencrypt

    # 带复杂中间件链的管理面板
    admin-panel:
      rule: Host(`admin.example.com`)
      entryPoints:
        - websecure
      service: admin-backend
      middlewares:
        - https-only
        - secure-headers
        - admin-auth
        - ip-whitelist
        - rate-limit
      tls:
        certResolver: letsencrypt

  services:
    # 宿主机服务
    host-backend:
      loadBalancer:
        servers:
          - url: http://192.168.1.100:8080

    # 管理面板
    admin-backend:
      loadBalancer:
        servers:
          - url: http://portainer:9000

    # 健康检查 + 负载均衡
    app-cluster:
      loadBalancer:
        healthCheck:
          path: /health
          interval: 10s
          timeout: 3s
        servers:
          - url: http://app-1:3000
          - url: http://app-2:3000
          - url: http://app-3:3000
        # 故障转移策略
        passHostHeader: true
        responseForwarding:
          flushInterval: 100ms

  middlewares:
    https-only:
      redirectScheme:
        scheme: https
        permanent: true

    secure-headers:
      headers:
        stsSeconds: 31536000
        stsIncludeSubdomains: true
        forceSTSHeader: true
        customFrameOptionsValue: SAMEORIGIN
        contentTypeNosniff: true
        browserXssFilter: true

    admin-auth:
      basicAuth:
        users:
          - "admin:$2y$05$XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

    ip-whitelist:
      ipAllowList:
        sourceRange:
          - 10.0.0.0/8
          - 192.168.0.0/16

    rate-limit:
      rateLimit:
        average: 100
        burst: 50

# ════ TCP 路由 ════
tcp:
  routers:
    mysql-proxy:
      rule: HostSNI(`*`)
      entryPoints:
        - mysql
      service: mysql-backend

  services:
    mysql-backend:
      loadBalancer:
        servers:
          - address: 192.168.1.50:3306

# ════ TLS 全局选项 ════
tls:
  options:
    default:
      minVersion: VersionTLS12
      cipherSuites:
        - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
        - TLS_AES_256_GCM_SHA384
        - TLS_CHACHA20_POLY1305_SHA256
      sniStrict: true

    # 专门给老客户端的宽松 TLS 配置
    legacy:
      minVersion: VersionTLS10

  stores:
    # 默认证书(当 ACME 还没申请到证书时的 fallback)
    default:
      defaultCertificate:
        certFile: /certs/fallback.crt
        keyFile: /certs/fallback.key

💡 文件改了立即生效:Traefik 会 watch dynamic/ 目录,任何 YAML 文件变更自动热重载,零重启、零中断。这就是 Traefik 相比 Nginx(需要 nginx -s reload)最大的优势之一。


🛡️ 第七部分:Dashboard 安全 + 生产部署

7.1 Dashboard 安全加固(不要裸奔!)

YAML
# 生产环境绝不能 --api.insecure=true!
# 正确做法:用 Label + BasicAuth + IP 白名单保护

traefik:
  image: traefik:v3.3
  command:
    # ✅ 开 Dashboard 但不开 insecure 模式
    - --api.dashboard=true
    - --api.insecure=false    # 🔒 关掉 8080 端口的裸奔访问
    # ... 其他配置 ...
  labels:
    # 给 Traefik 自己也加路由(通过 Docker Label 暴露 Dashboard)
    - traefik.enable=true
    - traefik.http.routers.dashboard.rule=Host(`traefik.example.com`)
    - traefik.http.routers.dashboard.entrypoints=websecure
    - traefik.http.routers.dashboard.tls=true
    - traefik.http.routers.dashboard.tls.certresolver=letsencrypt
    - traefik.http.routers.dashboard.service=api@internal
    # 🔒 BasicAuth 保护
    - traefik.http.middlewares.dashboard-auth.basicauth.users=admin:$$2y$$05$$XXXXXXX...
    # 🔒 IP 白名单
    - traefik.http.middlewares.dashboard-ipfilter.ipallowlist.sourcerange=10.0.0.0/8,192.168.0.0/16
    # 组合中间件
    - traefik.http.routers.dashboard.middlewares=dashboard-auth,dashboard-ipfilter

7.2 生产级 Docker Compose 完整模板

YAML
# ~/traefik/docker-compose.yml —— 生产级配置
services:
  traefik:
    image: traefik:v3.3
    container_name: traefik
    restart: unless-stopped
    ports:
      - "0.0.0.0:80:80"
      - "0.0.0.0:443:443"
      # 注意:8080 端口不映射!Dashboard 走 websecure 入口
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./letsencrypt:/letsencrypt
      - ./traefik.yml:/etc/traefik/traefik.yml:ro
      - ./dynamic:/dynamic:ro
      - ./logs:/logs
    command:
      - --configFile=/etc/traefik/traefik.yml
    healthcheck:
      test: ["CMD", "traefik", "healthcheck", "--ping"]
      interval: 30s
      timeout: 5s
      retries: 3
    labels:
      - traefik.enable=true
      - traefik.http.routers.dashboard.rule=Host(`traefik.example.com`)
      - traefik.http.routers.dashboard.entrypoints=websecure
      - traefik.http.routers.dashboard.tls=true
      - traefik.http.routers.dashboard.tls.certresolver=letsencrypt
      - traefik.http.routers.dashboard.service=api@internal
      - traefik.http.routers.dashboard.middlewares=dashboard-auth,dashboard-ipfilter,secure-headers
      # BasicAuth(用 htpasswd 生成,$ → $$)
      - traefik.http.middlewares.dashboard-auth.basicauth.users=admin:$$2y$$05$$XXXXXXX
      # IP 白名单
      - traefik.http.middlewares.dashboard-ipfilter.ipallowlist.sourcerange=10.0.0.0/8,192.168.0.0/16
      # 安全头
      - traefik.http.middlewares.secure-headers.headers.stsSeconds=31536000
      - traefik.http.middlewares.secure-headers.headers.forceSTSHeader=true
    networks:
      - proxy
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"
    security_opt:
      - no-new-privileges:true
    mem_limit: 512m
    cpus: "2.0"

networks:
  proxy:
    external: true
YAML
# ~/traefik/traefik.yml —— 静态配置
global:
  checkNewVersion: false
  sendAnonymousUsage: false   # 🔒 关闭匿名遥测

entryPoints:
  web:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https
          permanent: true
  websecure:
    address: ":443"
    http:
      tls:
        # 全局默认 ACME resolver
        certResolver: letsencrypt
        domains:
          - main: example.com
            sans:
              - "*.example.com"
    # 连接超时
    transport:
      respondingTimeouts:
        readTimeout: 60s
        writeTimeout: 60s
        idleTimeout: 180s
  traefik:
    address: ":8080"

api:
  dashboard: true
  insecure: false

ping:
  entryPoint: traefik

providers:
  docker:
    endpoint: unix:///var/run/docker.sock
    exposedByDefault: false
    network: proxy        # 🔑 只监听 proxy 网络里的容器
    watch: true
  file:
    directory: /dynamic
    watch: true

certificatesResolvers:
  letsencrypt:
    acme:
      email: you@example.com
      storage: /letsencrypt/acme.json
      tlsChallenge: true
      # 可选:用 DNS-01 + Cloudflare 申请通配符
      # dnsChallenge:
      #   provider: cloudflare
      #   resolvers:
      #     - 1.1.1.1:53
      #     - 8.8.8.8:53

log:
  level: INFO
  filePath: /logs/traefik.log

accessLog:
  filePath: /logs/access.log
  format: json
  fields:
    defaultMode: keep
    headers:
      defaultMode: keep

metrics:
  prometheus:
    addEntryPointsLabels: true
    addServicesLabels: true
    addRoutersLabels: true
    entryPoint: traefik

📊 第八部分:可观测性 —— Dashboard + Prometheus + Access Log

8.1 Dashboard 使用指南

PLAINTEXT
访问 https://traefik.example.com(配了 BasicAuth)

Dashboard 首页:
  ├── HTTP → Routers        ← 所有路由规则 + 状态
  ├── HTTP → Services       ← 所有后端服务 + 健康状态
  ├── HTTP → Middlewares    ← 所有中间件配置
  ├── TCP → Routers/Services ← TCP 路由
  ├── UDP → Routers/Services ← UDP 路由
  └── TLS → Certificates    ← 所有证书 + 到期时间

  每个 Router 可以点进去看:
    Rule / EntryPoint / Service / Middleware 链 / TLS 状态
    实时状态:UP / DOWN / 错误数

8.2 Prometheus + Grafana 监控

YAML
# Prometheus scrape_configs 增加 Traefik
scrape_configs:
  - job_name: traefik
    static_configs:
      - targets: ['traefik:8080']   # 或 traefik.example.com:443
    metrics_path: /metrics
    scheme: http                     # 内网用 http,外网用 https
BASH
# 关键 Prometheus 指标速查
# 当前入口点 QPS
traefik_entrypoint_requests_total

# 请求延迟分布(按 entrypoint)
traefik_entrypoint_request_duration_seconds_bucket

# 后端服务健康状态(1=UP, 0=DOWN)
traefik_service_server_up

# ACME 证书剩余天数(证书过期告警用)
traefik_tls_certificate_expiration_date_gauge

# 当前活跃连接数
traefik_entrypoint_open_connections
PLAINTEXT
Grafana Dashboard 推荐导入:
  ID 17347 —— Traefik Official v3(官方维护)
  ID 11462 —— Traefik Overview(社区高赞)

  包含面板:
  · 入口 QPS / 延迟 P50/P95/P99
  · HTTP 状态码分布(2xx/3xx/4xx/5xx)
  · 后端服务 UP/DOWN 状态表
  · 证书剩余天数热力图
  · 中间件执行时间 TOP10
  · 路由命中分布

8.3 Access Log(JSON 格式,对接 ELK/Loki)

JSON
// 默认 JSON 格式 Access Log 示例
{
  "ClientHost": "203.0.113.42",
  "ClientPort": "54321",
  "ClientUsername": "-",
  "DownstreamStatus": "200",
  "DownstreamContentSize": 2048,
  "Duration": 0.045,
  "StartUTC": "2026-08-08T12:00:00Z",
  "RouterName": "nextcloud@docker",
  "ServiceName": "nextcloud@docker",
  "ServiceURL": "http://172.18.0.5:80",
  "EntryPointName": "websecure",
  "OriginStatus": 200,
  "RequestContentSize": 0,
  "RequestHost": "cloud.example.com",
  "RequestMethod": "GET",
  "RequestPath": "/index.php/apps/files/",
  "RequestProtocol": "HTTP/2.0",
  "RequestScheme": "https",
  "TLSVersion": "1.3",
  "TLSCipher": "TLS_CHACHA20_POLY1305_SHA256",
  "MiddlewareName": "secure-headers@docker"
}
YAML
# Vector / Fluent-bit 采集 Traefik 日志推到 Loki
# vector.toml
[sources.traefik_access]
type = "file"
include = ["/var/log/traefik/access.log"]
read_from = "beginning"

[sinks.loki]
type = "loki"
inputs = ["traefik_access"]
endpoint = "http://loki:3100"
labels.app = "traefik"
labels.env = "production"
encoding.codec = "json"

🔄 第九部分:从 Caddy / Nginx 迁移到 Traefik

9.1 Caddy → Traefik 配置对照

PLAINTEXT
┌──────────────────────────────────────┬──────────────────────────────────────┐
│  Caddyfile                           │  Traefik Label / YAML                │
├──────────────────────────────────────┼──────────────────────────────────────┤
│  app.example.com {                   │  labels:                             │
│      reverse_proxy localhost:3000    │    - traefik.enable=true             │
│  }                                   │    - traefik.http.routers.app.rule=  │
│                                      │      Host(`app.example.com`)         │
│                                      │    - traefik.http.services.app       │
│                                      │      .loadbalancer.server.port=3000  │
├──────────────────────────────────────┼──────────────────────────────────────┤
│  tls you@email.com                   │  --certificatesresolvers.letsencrypt │
│  (Caddy 自动申请)                    │    .acme.email=you@email.com         │
│                                      │  + tls.certresolver=letsencrypt      │
├──────────────────────────────────────┼──────────────────────────────────────┤
│  encode gzip zstd                    │  - traefik.http.middlewares.compress │
│                                      │    .compress=true                    │
├──────────────────────────────────────┼──────────────────────────────────────┤
│  header Strict-Transport-Security    │  - traefik.http.middlewares.headers  │
│    "max-age=31536000"                │    .headers.stsSeconds=31536000      │
├──────────────────────────────────────┼──────────────────────────────────────┤
│  basicauth /secret/* {               │  - traefik.http.middlewares.auth     │
│      admin <hashed-pass>             │    .basicauth.users=admin:$$hash$$   │
│  }                                   │  + Router.middlewares=auth           │
├──────────────────────────────────────┼──────────────────────────────────────┤
│  redir http://  https://  permanent  │  --entrypoints.web.http.redirections  │
│                                      │    .entrypoint.to=websecure          │
├──────────────────────────────────────┼──────────────────────────────────────┤
│  example.com/path/* {                │  rule=Host(`example.com`)            │
│      reverse_proxy svc:8080          │    && PathPrefix(`/path`)            │
│  }                                   │                                      │
└──────────────────────────────────────┴──────────────────────────────────────┘

9.2 Nginx → Traefik 配置对照

PLAINTEXT
┌──────────────────────────────────────┬──────────────────────────────────────┐
│  Nginx nginx.conf                    │  Traefik Label / YAML                │
├──────────────────────────────────────┼──────────────────────────────────────┤
│  server {                            │  traefik.http.routers.app.rule=      │
│      server_name app.example.com;    │    Host(`app.example.com`)           │
│      listen 443 ssl;                 │  + entrypoints=websecure             │
│  }                                   │  + tls=true                          │
├──────────────────────────────────────┼──────────────────────────────────────┤
│  location / {                        │  traefik.http.routers.app.rule=      │
│      proxy_pass http://app:3000;     │    Host(`app.example.com`)           │
│  }                                   │  + service=app@docker               │
│                                      │  + port=3000                         │
├──────────────────────────────────────┼──────────────────────────────────────┤
│  upstream backend {                  │  多容器同名 Router 自动合并 =         │
│      server app1:3000 weight=3;      │  负载均衡 + weight                    │
│      server app2:3000 weight=1;      │  + loadbalancer.server.weight=N      │
│  }                                   │                                      │
├──────────────────────────────────────┼──────────────────────────────────────┤
│  ssl_certificate /path/cert.pem;     │  ACME 自动申请(不用手动管证书文件)  │
│  ssl_certificate_key /path/key.pem;  │  + tls.certresolver=letsencrypt      │
├──────────────────────────────────────┼──────────────────────────────────────┤
│  limit_req zone=one burst=20;        │  ratelimit.average=100               │
│                                      │  + ratelimit.burst=20                │
├──────────────────────────────────────┼──────────────────────────────────────┤
│  gzip on;                            │  compress=true                       │
├──────────────────────────────────────┼──────────────────────────────────────┤
│  add_header X-Frame-Options          │  headers.customFrameOptionsValue=    │
│    "SAMEORIGIN";                     │    SAMEORIGIN                        │
├──────────────────────────────────────┼──────────────────────────────────────┤
│  location /api/ {                    │  rule=Host(`app.com`)                │
│      rewrite ^/api/(.*) /$1 break;   │    && PathPrefix(`/api`)             │
│      proxy_pass http://api:8080;     │  + middleware: stripprefix=/api      │
│  }                                   │  + service=api@docker, port=8080     │
└──────────────────────────────────────┴──────────────────────────────────────┘

9.3 渐进式迁移策略

PLAINTEXT
阶段 1:Traefik 和 Nginx/Caddy 并存
  · Nginx 继续做主入口(80/443)
  · Traefik 监听 8080/8443(内部端口)
  · Nginx 把新服务 proxy_pass 到 Traefik
  · 逐个验证新路由

阶段 2:切流量
  · 新服务直接走 Traefik Label
  · 老服务逐步从 Nginx 迁移到 Traefik
  · 监控 Uptime Kuma 确认无中断

阶段 3:Nginx 退役
  · Traefik 接管 80/443
  · Nginx 降级为特定服务的静态文件服务器(可选保留)
  · 或完全退役

⚠️ 注意事项:
  · ACME 证书不能同时两个工具申请同一域名(会冲突)
  · 迁移前先备份 Nginx/Caddy 配置和证书
  · DNS TTL 调短到 60s,方便回滚

🏗️ 第十部分:高可用与多副本部署

10.1 单机 vs Docker Swarm vs K8s

PLAINTEXT
┌──────────────────────────────────────────────────────────────────────┐
│              Traefik 部署模式选择                                       │
├──────────────┬──────────────────┬──────────────────┬─────────────────┤
│ 维度          │ 单机 Docker       │ Docker Swarm     │ Kubernetes      │
├──────────────┼──────────────────┼──────────────────┼─────────────────┤
│ 适用规模      │ 1-50 容器        │ 3-10 节点         │ 10+ 节点         │
│ Provider     │ docker           │ swarm            │ kubernetes      │
│ 高可用        │ ❌ 单点           │ ✅ 多副本         │ ✅ 多副本        │
│ 证书共享      │ 文件             │ 文件(共享卷)    │ Secret / KV      │
│ 配置复杂度    │ ⭐⭐⭐⭐⭐ 简单  │ ⭐⭐⭐ 中等       │ ⭐ 复杂          │
│ 学习成本      │ 低               │ 中               │ 高               │
└──────────────┴──────────────────┴──────────────────┴─────────────────┘

10.2 Docker Swarm 多副本部署

YAML
# docker-compose.yml (Swarm 模式)
version: '3.8'
services:
  traefik:
    image: traefik:v3.3
    deploy:
      mode: global          # 每个节点跑一个 Traefik
      placement:
        constraints:
          - node.role == manager   # 只在 manager 节点跑(需要访问 Swarm API)
      update_config:
        parallelism: 1
        delay: 10s
        order: start-first         # 先启动新实例再停旧的(零停机)
      restart_policy:
        condition: any
        max_attempts: 3
    ports:
      - target: 80
        published: 80
        mode: host     # 🔑 host 模式让每个节点的 80 端口都能用
      - target: 443
        published: 443
        mode: host
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      # 🔑 证书存储用共享卷(NFS / GlusterFS)或 Redis
      - traefik-certs:/letsencrypt
    command:
      - --providers.swarm=true              # 🔑 Swarm 模式(不是 docker)
      - --providers.swarm.exposedbydefault=false
      - --providers.swarm.network=proxy
      # ... 其他配置同单机 ...
    networks:
      - proxy

  app:
    image: myapp:latest
    deploy:
      replicas: 3               # 3 个副本自动负载均衡
      labels:
        # 🔑 Swarm 模式 Label 必须在 deploy.labels 里
        - traefik.enable=true
        - traefik.http.routers.app.rule=Host(`app.example.com`)
        - traefik.http.routers.app.entrypoints=websecure
        - traefik.http.routers.app.tls=true
        - traefik.http.routers.app.tls.certresolver=letsencrypt
        - traefik.http.services.app.loadbalancer.server.port=3000
    networks:
      - proxy

volumes:
  traefik-certs:

networks:
  proxy:
    external: true

10.3 证书共享:Redis 方案

YAML
# 多节点 Traefik 共享 ACME 证书,避免每个节点都申请(限频)
traefik:
  command:
    - --certificatesresolvers.letsencrypt.acme.email=you@example.com
    - --certificatesresolvers.letsencrypt.acme.storage=traefik-acme   # 🔑 Key 名而非文件路径
    - --certificatesresolvers.letsencrypt.acme.tlschallenge=true
    # Redis Provider(共享配置存储)
    - --providers.redis=true
    - --providers.redis.endpoints=redis:6379

# 单独的 Redis 容器
redis:
  image: redis:7-alpine
  restart: unless-stopped
  volumes:
    - ./redis:/data
  command: redis-server --appendonly yes --requirepass YourRedisPassword
  networks:
    - proxy

🔗 第十一部分:与既有工具链联动

11.1 与 Cloudflare Tunnel 联动(第 6 篇)

PLAINTEXT
架构 A:Traefik 在 CF Tunnel 后面(内网部署,零公网 IP)

  用户 → Cloudflare CDN → CF Tunnel → Traefik → Docker 容器

  cloudflared config.yml:
    - hostname: app.example.com
      service: http://traefik:80        ← 指向 Traefik 而非单个容器
    - hostname: cloud.example.com
      service: http://traefik:80        ← 所有域名都走 Traefik
    - hostname: media.example.com
      service: http://traefik:80

  优势:CF Tunnel 只需一条 ingress 规则(全部指向 Traefik:80)
        Traefik 负责 Docker 层面的 Label 路由分发
        证书用 CF 的(Traefik 不需要 ACME)

架构 B:Traefik 做主入口,CF Tunnel 做内网穿透补充

  公网用户 → Traefik(443) → Docker 容器
  内网用户 → CF Tunnel → Traefik(8080) → 同一批容器

  两层路由不冲突,各管各的。

11.2 与 FRP 联动(第 5 篇)

PLAINTEXT
架构:FRP 暴露内网 Traefik 到公网

  公网用户 → VPS:frps(7000) → 内网:frpc → Traefik(443) → Docker 容器

  frpc.toml:
    [[proxies]]
    name = "traefik-https"
    type = "https"
    localIP = "127.0.0.1"
    localPort = 443
    customDomains = ["app.example.com", "cloud.example.com"]

  或用 TCP 模式转发 443 端口:
    [[proxies]]
    name = "traefik-tcp"
    type = "tcp"
    localIP = "127.0.0.1"
    localPort = 443
    remotePort = 8443

11.3 与 Uptime Kuma 联动(第 7 篇)

YAML
# Traefik 暴露 Uptime Kuma 面板
uptime-kuma:
  image: louislam/uptime-kuma:2
  labels:
    - traefik.enable=true
    - traefik.http.routers.kuma.rule=Host(`status.example.com`)
    - traefik.http.routers.kuma.entrypoints=websecure
    - traefik.http.routers.kuma.tls=true
    - traefik.http.routers.kuma.tls.certresolver=letsencrypt
    - traefik.http.services.kuma.loadbalancer.server.port=3001

# 同时让 Uptime Kuma 监控 Traefik 本身
# 在 Kuma 里加:
#   Type: HTTP
#   URL: https://traefik.example.com/ping    ← Traefik 的健康检查端点
#   Expected: 200
#   Interval: 30s

11.4 与 acme.sh 联动(第 4 篇)

PLAINTEXT
场景 A:Traefik 自己管证书(ACME 内置),acme.sh 退役
  → 推荐:容器环境直接用 Traefik ACME,简单

场景 B:已有 acme.sh 证书,Traefik 用已有证书
  → traefik.yml 或 dynamic.yml 里指定证书文件路径:
    tls:
      certificates:
        - certFile: /certs/example.com.crt
          keyFile: /certs/example.com.key

场景 C:acme.sh 续期后通知 Traefik 重载
  → acme.sh --install-cert 后加 hook:
    acme.sh --install-cert -d example.com \
      --reloadcmd "docker exec traefik wget -qO- http://localhost:8080/api/rawdata > /dev/null"
  → 或用 Traefik 的 file watcher(文件变更自动重载)

11.5 与 Caddy / Nginx 联动(第 3 篇)

PLAINTEXT
混合架构示例:
  Nginx (80/443) → Traefik (8080) → Docker 容器群

  Nginx 做最外层:处理静态文件 / 高并发入口 / CDN 回源
  Traefik 做内层:Docker Label 自动路由 / 中间件链

  Nginx 配置:
    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

📚 第十二部分:命令速查表 + 常见问题

12.1 命令速查

BASH
# ═══════ 部署与管理 ═══════
docker compose up -d traefik              # 启动
docker compose logs -f traefik            # 实时日志
docker compose restart traefik            # 重启(改静态配置才需要)
docker compose pull && docker compose up -d  # 升级版本

# 动态配置改了不需要重启!File Provider 自动热重载

# ═══════ 健康检查 ═══════
curl http://localhost:8080/ping           # 健康检查端点 → OK
curl http://localhost:8080/metrics        # Prometheus 指标
curl -k https://localhost:443 -H "Host: whoami.example.com"  # 测试路由

# ═══════ 调试 ═══════
# 查看 Traefik 当前所有路由(API)
curl http://localhost:8080/api/http/routers | jq .
curl http://localhost:8080/api/http/services | jq .
curl http://localhost:8080/api/http/middlewares | jq .
curl http://localhost:8080/api/tcp/routers | jq .

# 查看证书状态
curl http://localhost:8080/api/http/certificates | jq '.[] | {domain: .domain, notAfter: .NotAfter}'

# Traefik 官方 debug 命令
docker exec traefik traefik healthcheck    # 容器内健康检查
docker exec traefik traefik version        # 版本信息

# ═══════ 证书管理 ═══════
# 查看 acme.json 里的证书
cat letsencrypt/acme.json | jq '.letsencrypt.Certificates[] | .domain.main'

# 重置证书(慎用!会重新申请,可能触发 Let's Encrypt 限频)
# rm letsencrypt/acme.json && docker compose restart traefik

12.2 常见问题速查

问题原因解决方案
404 page not foundRouter 没匹配到检查 Rule 的 Host 拼写;curl -H "Host: xxx" ... 测试
502 Bad Gateway后端容器不可达检查容器和 Traefik 在同一个 Docker 网络;检查端口
503 Service UnavailableService 没注册Label 里 loadbalancer.server.port 写了吗?
证书申请失败80/443 端口不通 / DNS 没解析检查防火墙;dig app.example.com 确认 DNS
too many 502 errors后端超时调大 respondingTimeouts 或后端 readTimeout
Label 改了不生效Docker Compose 需要 recreatedocker compose up -d --force-recreate service-name
Dashboard 404--api.insecure=false 但没配 Router加 traefik.http.routers.dashboard.service=api@internal
通配符证书申请失败TLS-ALPN 不支持通配符改用 DNS-01 challenge
$ 符号在 Label 里消失Docker Compose 变量插值$ → $$(BasicAuth 哈希常见)
容器在另一个网络不可达Traefik 默认只看 proxy 网络providers.docker.network=proxy 或容器加到 proxy 网络

12.3 学习路径 + 关联阅读

PLAINTEXT
🟢 入门(1 天)
  · 2.1~2.2 理解概念 + Docker Compose 部署
  · 部署 whoami 测试 Label 路由
  · 打开 Dashboard 看路由列表

🟡 进阶(3 天)
  · 第三部分 Label 深度配置:10 个服务模板
  · 第四部分 ACME 自动 HTTPS
  · 第五部分中间件链:认证 / 限流 / 安全头

🟠 熟练(1 周)
  · 第六部分 File Provider 动态配置
  · 第七部分生产级安全加固
  · 第八部分 Prometheus + Grafana 可观测性

🔴 生产(长期)
  · 第九部分从 Caddy/Nginx 迁移
  · 第十部分 Swarm 多副本高可用
  · 第十一部分与 CF Tunnel/FRP/Kuma 联动

✅ 总结

Traefik 的核心价值就一句话:容器启动即注册,容器停止即摘除,零手写配置、零重启。当你有 10 个以上 Docker 服务时,Traefik 省下的改配置 + reload + 排查的时间是非常可观的。再加上内置 ACME、中间件链、Dashboard、Prometheus 指标,它几乎是 Docker 重度用户的唯一选择。

但也要认清它的局限:

最佳实践:

下一步建议:拿第二部分的 Docker Compose 模板跑起来,加一个 whoami 容器感受 Label 自动发现的魔力,然后逐步把现有服务从 Caddy/Nginx 迁移过来。迁移完你会感叹:「以前怎么没早点用」。

关注 易邦科学上网,及时获取最近更新:

X : https://x.com/rozmiarek760575

版权声明

作者: 易邦

链接: https://blog.e8k.net/posts/traefik-reverse-proxy-docker-guide/

许可证: 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。