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

前面几篇文章里,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)
┌────────────────────────────────────────────────────────────────────────────┐
│ 三大反向代理全景对比(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 核心架构
┌──────────────────────────────────────────────────────────────────────────┐
│ 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 能力矩阵
┌──────────────────────────────────────────────────────────────────┐
│ 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 核心概念速览(必须先理解)
┌──────────────────────────────────────────────────────────────────┐
│ 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 + 示例服务
# ~/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# 初始化
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,命名遵循层级结构:
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)大全
# 单个 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 模板
# ════ 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 方式对比与选择
┌──────────────────────────────────────────────────────────────────────┐
│ 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)
# 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 证书存储与多副本共享
单实例(默认):
证书存在 /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 中间件核心概念
┌──────────────────────────────────────────────────────────────────┐
│ 中间件执行顺序(链式处理,按顺序执行) │
│ │
│ 请求 → 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 常用中间件完整配置
# ════ 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-chain5.3 全局中间件:所有路由共享
# 在 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 里:# ~/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?
Docker Provider 的局限:
· 只能配 Docker 容器的路由
· 复杂的中间件链在 Label 里写很长很难维护
· TCP/UDP 路由在 Label 里不够灵活
File Provider 的优势:
· 用 YAML/TOML 写配置,结构清晰
· 支持热重载(文件改了自动生效,零重启)
· 适合配:非 Docker 服务 / 复杂中间件链 / TLS 选项 / 全局默认值
· 和 Docker Provider 同时使用,互补
典型用法:
· Docker 容器 → Label 配 Router + Service
· File 配 → 全局 TLS 选项 / 复杂中间件 / 非 Docker 服务路由6.2 动态配置文件示例
# ~/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 安全加固(不要裸奔!)
# 生产环境绝不能 --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-ipfilter7.2 生产级 Docker Compose 完整模板
# ~/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# ~/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 使用指南
访问 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 监控
# 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# 关键 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_connectionsGrafana 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 格式 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"
}# 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 配置对照
┌──────────────────────────────────────┬──────────────────────────────────────┐
│ 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 配置对照
┌──────────────────────────────────────┬──────────────────────────────────────┐
│ 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 渐进式迁移策略
阶段 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
┌──────────────────────────────────────────────────────────────────────┐
│ Traefik 部署模式选择 │
├──────────────┬──────────────────┬──────────────────┬─────────────────┤
│ 维度 │ 单机 Docker │ Docker Swarm │ Kubernetes │
├──────────────┼──────────────────┼──────────────────┼─────────────────┤
│ 适用规模 │ 1-50 容器 │ 3-10 节点 │ 10+ 节点 │
│ Provider │ docker │ swarm │ kubernetes │
│ 高可用 │ ❌ 单点 │ ✅ 多副本 │ ✅ 多副本 │
│ 证书共享 │ 文件 │ 文件(共享卷) │ Secret / KV │
│ 配置复杂度 │ ⭐⭐⭐⭐⭐ 简单 │ ⭐⭐⭐ 中等 │ ⭐ 复杂 │
│ 学习成本 │ 低 │ 中 │ 高 │
└──────────────┴──────────────────┴──────────────────┴─────────────────┘10.2 Docker Swarm 多副本部署
# 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: true10.3 证书共享:Redis 方案
# 多节点 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 篇)
架构 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 篇)
架构: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 = 844311.3 与 Uptime Kuma 联动(第 7 篇)
# 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: 30s11.4 与 acme.sh 联动(第 4 篇)
场景 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 篇)
混合架构示例:
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 命令速查
# ═══════ 部署与管理 ═══════
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 traefik12.2 常见问题速查
| 问题 | 原因 | 解决方案 |
|---|---|---|
404 page not found | Router 没匹配到 | 检查 Rule 的 Host 拼写;curl -H "Host: xxx" ... 测试 |
502 Bad Gateway | 后端容器不可达 | 检查容器和 Traefik 在同一个 Docker 网络;检查端口 |
503 Service Unavailable | Service 没注册 | Label 里 loadbalancer.server.port 写了吗? |
| 证书申请失败 | 80/443 端口不通 / DNS 没解析 | 检查防火墙;dig app.example.com 确认 DNS |
too many 502 errors | 后端超时 | 调大 respondingTimeouts 或后端 readTimeout |
| Label 改了不生效 | Docker Compose 需要 recreate | docker 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 学习路径 + 关联阅读
🟢 入门(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 联动- 📖 官方文档:doc.traefik.io/traefik
- 📦 Release:github.com/traefik/traefik/releases
- 🧩 中间件大全:doc.traefik.io/traefik/v3.3/middlewares/overview/
- 🔗 关联阅读:
- Caddy 反向代理 + HTTPS 指南 — 极简反代方案
- Nginx 反向代理与负载均衡指南 — 高性能反代方案
- acme.sh 证书自动化完全指南 — Traefik ACME 的替代方案
- Cloudflare Tunnel 完全指南 — 零公网 IP 暴露 Traefik
- Uptime Kuma 监控告警完全指南 — 监控 Traefik 和后端服务
- FRP 内网穿透完全指南 — 通过 FRP 暴露内网 Traefik
✅ 总结
Traefik 的核心价值就一句话:容器启动即注册,容器停止即摘除,零手写配置、零重启。当你有 10 个以上 Docker 服务时,Traefik 省下的改配置 + reload + 排查的时间是非常可观的。再加上内置 ACME、中间件链、Dashboard、Prometheus 指标,它几乎是 Docker 重度用户的唯一选择。
但也要认清它的局限:
- 极致性能不如 Nginx(Go vs C,QPS 差 2-3 倍,但 99% 场景够用)
- 配置语法比 Caddy 复杂(Label 写起来有点长,但比 Nginx conf 简单)
- 非 Docker 环境优势不明显(File Provider 和 Nginx 体验差不多)
最佳实践:
- Docker 容器多(10+) → Traefik 首选 ✅
- 就 3-5 个服务 → Caddy 更简单 ✅
- 超高并发 CDN 入口 → Nginx 更稳 ✅
- 三者可以共存,各管一层
下一步建议:拿第二部分的 Docker Compose 模板跑起来,加一个 whoami 容器感受 Label 自动发现的魔力,然后逐步把现有服务从 Caddy/Nginx 迁移过来。迁移完你会感叹:「以前怎么没早点用」。
