首页
/ Traefik 配合 Docker 暴露服务实战:从第一个 HTTP 服务到路径路由与 TLS 加密

Traefik 配合 Docker 暴露服务实战:从第一个 HTTP 服务到路径路由与 TLS 加密

2026-09-06 09:02:23作者:宣海椒Queenly

本文以 Traefik 仓库中的官方入门指南(expose/docker/basic.md)为主体,完整讲解如何在 Docker 环境中让 Traefik 通过容器标签自动发现并暴露 HTTP 服务:从搭建第一个 whoami 路由、按 URL 路径分流到不同后端,再到为服务启用 TLS 终止。读完本文,你将掌握 traefik.http.routers.* 标签的编写方式、Docker provider 的动态配置生成机制,以及结合 File provider 挂载自签证书的完整 HTTPS 部署方案。

前置条件

开始前需要满足以下条件:

  • 已安装 Docker 与 Docker Compose;
  • 对 Docker 网络(network)与标签(labels)有基本了解;
  • Traefik 已通过 Traefik Docker 部署指南完成基础部署(本文会给出完整的自包含 Compose 文件,也可独立运行)。

暴露你的第一个 HTTP 服务

Traefik 仓库配套提供了一个 traefik/whoami 镜像,它会把收到的请求头、IP、来源地址原样回显,是验证反向代理路由结果最直观的工具。下面用它演示一次完整的基础路由。

编写 docker-compose.yml

创建 docker-compose.yml 文件,定义 Traefik 与 whoami 两个服务:

services:
  traefik:
    image: "traefik:v3.4"
    container_name: "traefik"
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    networks:
      - proxy
    command:
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--providers.docker.network=proxy"
      - "--entryPoints.web.address=:80"
    ports:
      - "80:80"
      - "8080:8080"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"

  whoami:
    image: "traefik/whoami"
    restart: unless-stopped
    networks:
      - proxy
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami.rule=Host(`whoami.docker.localhost`)"
      - "traefik.http.routers.whoami.entrypoints=web"

networks:
  proxy:
    name: proxy

各关键配置项的含义如下:

配置项 作用
--providers.docker=true 启用 Docker provider,让 Traefik 通过 Docker API 发现容器
--providers.docker.exposedbydefault=false 默认不暴露任何容器,只有带 traefik.enable=true 标签的容器才会生成路由
--providers.docker.network=proxy 指定 Traefik 使用哪个网络中的容器 IP 来访问后端(见下文源码分析)
--entryPoints.web.address=:80 定义名为 web 的 HTTP 入口,监听 80 端口
/var/run/docker.sock 只读挂载 提供 Docker API 访问通道(:ro 保证只读)
traefik.http.routers.whoami.rule 路由匹配规则:Host(\whoami.docker.localhost`)` 表示 Host 为该域名时命中
traefik.http.routers.whoami.entrypoints=web 该路由绑定到 web 入口

保存文件后启动服务:

docker compose up -d

验证路由生效

服务此时应当可以通过 http://whoami.docker.localhost/ 访问。由于该域名未必在本地解析,可以手动指定 Host 头测试:

curl -H "Host: whoami.docker.localhost" http://localhost/

预期输出类似:

Hostname: whoami
IP: 127.0.0.1
IP: ::1
IP: 172.18.0.3
IP: fe80::215:5dff:fe00:c9e
RemoteAddr: 172.18.0.2:55108
GET / HTTP/1.1
Host: whoami.docker.localhost
User-Agent: curl/7.68.0
Accept: */*
Accept-Encoding: gzip
X-Forwarded-For: 172.18.0.1
X-Forwarded-Host: whoami.docker.localhost
X-Forwarded-Port: 80
X-Forwarded-Proto: http
X-Forwarded-Server: 5789f594e7d5
X-Real-Ip: 172.18.0.1

响应中出现的 X-Forwarded-HostX-Forwarded-ProtoX-Real-Ip 等请求头说明请求确实经过了 Traefik 的反向代理层——它向 whoami 转发了请求并附加了标准的转发头。

通过 Traefik 访问 whoami 服务,页面回显了完整请求头(含 X-Forwarded-Host、X-Forwarded-Proto 等)

源码级原理:Docker provider 如何把标签变成路由

上面的效果在 Traefik 源码中由 Docker provider 完成,核心实现位于 pkg/provider/docker/pdocker.go

  1. 首次全量同步:provider 启动后调用 listContainers 列出全部容器,经 builder.build 生成动态配置,推送到 configurationChan 通道(见 pdocker.go#L82-L92)。
  2. 事件驱动增量更新:当 watch 开启(默认开启)时,Traefik 订阅容器事件流,收到 startdiehealth_status 前缀的事件时重新构建配置(见 pdocker.go#L94-L136)。这也是为什么新起的 whoami 容器无需重启 Traefik 就能被自动发现。
  3. 容器过滤pkg/provider/docker/config.gokeepContainer 实现了本文 exposedbydefault=false 的行为——若容器没有 traefik.enable=true 标签,ExtraConf.Enablefalse,容器直接被过滤(见 config.go#L197-L234)。traefik.enable 的默认值即 provider 的 ExposedByDefault 配置(默认 true,见 shared_labels.go#L42-L63)。同一函数还表明:非 running 状态或 healthcheck 不健康的容器会被过滤,其 service 保留空负载列表(可用 allowEmptyServices 调整)。
  4. 后端地址解析:Traefik 用 --providers.docker.network=proxy 指定的网络(proxy)来取容器 IP——getIPAddress 优先从该网络中查找 network.Addr,找不到会降级到第一个可用网络并打警告日志(见 config.go#L359-L425)。这就是为什么 whoami 必须和 traefik 容器接入同一个 proxy 网络。
  5. 端口探测:若没有用 traefik.http.services.<name>.loadbalancer.server.port 标签指定端口,Traefik 取容器暴露的最低端口(见 shared.go#L189-L207);服务名则默认取自 Compose 注入的 com.docker.compose.service 标签,格式为 服务名_项目名(见 shared.go#L209-L217)。

关于 Docker provider 的更多安装配置选项(如 endpointconstraintsuseBindPortIP),可查阅 Docker provider 参考文档;可使用的完整标签清单见 Docker 路由标签参考

添加路由规则:基于路径分流

接下来按 URL 路径将流量导向不同服务——这在 API 版本化、前后端分离、微服务分组中非常实用。在 docker-compose.yml 中追加一个 whoami-api 服务:

# ...

# New service
  whoami-api:
    image: "traefik/whoami"
    networks:
      - proxy
    container_name: "whoami-api"
    environment:
      - WHOAMI_NAME=API Service
    labels:
      - "traefik.enable=true"
      # Path-based routing
      - "traefik.http.routers.whoami-api.rule=Host(`whoami.docker.localhost`) && PathPrefix(`/api`)"
      - "traefik.http.routers.whoami-api.entrypoints=web"

两个路由同时命中 whoami.docker.localhost 这个 Host,为什么 /api 请求会走 whoami-api?关键在于 Traefik 的路由优先级机制:默认情况下优先级等于规则字符串的长度,更长的规则更具体、优先级更高(priority 显式设置为 0 时才回到按长度排序)。本文的 whoami-api 规则(Host(...) && PathPrefix(/api))比 whoami 规则长,因此 /api 前缀的请求优先命中 whoami-api,其余路径落到 whoami。更完整的匹配器(Path/PathPrefix/PathRegexpHost/HostRegexpQueryHeaderMethod 等)与优先级计算细节,参见 路由规则与优先级参考

应用变更:

docker compose up -d

测试路径路由

验证不同路径是否落到不同服务:

# Root path should go to the main whoami service
curl -H "Host: whoami.docker.localhost" http://localhost/

# /api path should go to the whoami-api service
curl -H "Host: whoami.docker.localhost" http://localhost/api

/api 的请求,whoami 会在环境变量部分回显 WHOAMI_NAME=API Service,证明路径分流工作正常。

启用 TLS:自签证书 + File provider 动态配置

下面为服务启用 HTTPS。本地开发环境先使用自签证书;生产环境则应换成可信 CA 签发的证书(例如 Let's Encrypt,见 进阶指南)。

生成自签证书

mkdir -p certs
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout certs/local.key -out certs/local.crt \
  -subj "/CN=*.docker.localhost"

*.docker.localhost 的通配符 CN 可以覆盖 whoami.docker.localhostdashboard.docker.localhost 等子域(注意通配只匹配一层子域)。

然后创建动态配置目录,写入 TLS 证书配置:

mkdir -p dynamic
cat > dynamic/tls.yml << EOF
tls:
  certificates:
    - certFile: /certs/local.crt
      keyFile: /certs/local.key
EOF

这里 certFile/keyFile 指向的是 容器内路径/certs/...),与下面的 volume 挂载目标一致。

更新 docker-compose.yml 启用 HTTPS

services:
  traefik:
    image: "traefik:v3.4"
    container_name: "traefik"
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    networks:
      - proxy
    command:
      - "--api.insecure=false"
      - "--api.dashboard=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--providers.docker.network=proxy"
      - "--providers.file.directory=/etc/traefik/dynamic"
      - "--entryPoints.web.address=:80"
      - "--entryPoints.websecure.address=:443"
      - "--entryPoints.websecure.http.tls=true"
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      # Add the following volumes
      - "./certs:/certs:ro"
      - "./dynamic:/etc/traefik/dynamic:ro"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.dashboard.rule=Host(`dashboard.docker.localhost`)"
      - "traefik.http.routers.dashboard.entrypoints=websecure"
      - "traefik.http.routers.dashboard.service=api@internal"
      # Add the following label
      - "traefik.http.routers.dashboard.tls=true"

  whoami:
    image: "traefik/whoami"
    restart: unless-stopped
    networks:
      - proxy
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami.rule=Host(`whoami.docker.localhost`)"
      - "traefik.http.routers.whoami.entrypoints=websecure"
      # Add the following label
      - "traefik.http.routers.whoami.tls=true"

  whoami-api:
    image: "traefik/whoami"
    container_name: "whoami-api"
    restart: unless-stopped
    networks:
      - proxy
    environment:
      - WHOAMI_NAME=API Service
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami-api.rule=Host(`whoami.docker.localhost`) && PathPrefix(`/api`)"
      - "traefik.http.routers.whoami-api.entrypoints=websecure"
      # Add the following label
      - "traefik.http.routers.whoami-api.tls=true"

networks:
  proxy:
    name: proxy

与 HTTP 版本相比,关键变化有:

  1. 新增 websecure 入口--entryPoints.websecure.address=:443,并通过 --entryPoints.websecure.http.tls=true 强制该入口所有 HTTP 流量走 TLS;
  2. File provider 加载动态 TLS 配置--providers.file.directory=/etc/traefik/dynamic 对应挂载的 ./dynamic 目录,dynamic/tls.yml 中的证书被注册进 TLS store;
  3. 各路由迁移到 websecure 入口并加 tls=truetraefik.http.routers.<name>.tls=true 是路由级开关,声明该路由需要 TLS 终止,Traefik 会按 SNI 从 TLS store 中匹配证书;
  4. Dashboard 自路由:Traefik 容器自身也打了 traefik.enable=true 标签,通过 service=api@internal 把仪表盘挂在 dashboard.docker.localhost 上;配合 --api.insecure=false,仪表盘不会裸露在 8080 管理端口,只能通过这条受 TLS 保护的 HTTPS 路由访问。

应用变更:

docker compose up -d

现在浏览器访问 https://whoami.docker.localhost/ 即可打开服务,自签证书会触发安全警告,接受后即可正常访问。

小结与进阶方向

本文完整走通了 Traefik + Docker 的三条基本能力链:标签驱动的容器发现(Docker provider 事件监听与 traefik.enable 过滤)、规则路由Host + PathPrefix 组合与基于规则长度的优先级仲裁)、TLS 终止websecure.http.tls=true 入口级强制 + 路由级 tls=true + File provider 证书配置)。三者叠加后,一个 Docker Compose 文件即可承载多域名、多路径、加密的反向代理。

掌握了这些基础之后,可以继续探索 进阶指南

  • 使用 middlewares 实现安全响应头与访问控制;
  • 使用 Let's Encrypt(ACME)自动签发与续期证书;
  • 为有状态应用配置 sticky sessions;
  • 搭建基于认证结果的多层路由。
登录后查看全文
热门项目推荐
相关项目推荐