Traefik 配合 Docker 暴露服务实战:从第一个 HTTP 服务到路径路由与 TLS 加密
【免费下载链接】traefikThe Cloud Native Application Proxy项目地址: https://gitcode.com/GitHub_Trending/tr/traefik
本文以 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-Host、X-Forwarded-Proto、X-Real-Ip等请求头说明请求确实经过了 Traefik 的反向代理层——它向 whoami 转发了请求并附加了标准的转发头。
源码级原理:Docker provider 如何把标签变成路由
上面的效果在 Traefik 源码中由 Docker provider 完成,核心实现位于 pkg/provider/docker/pdocker.go:
- 首次全量同步:provider 启动后调用
listContainers列出全部容器,经builder.build生成动态配置,推送到configurationChan通道(见 pdocker.go#L82-L92)。 - 事件驱动增量更新:当
watch开启(默认开启)时,Traefik 订阅容器事件流,收到start、die或health_status前缀的事件时重新构建配置(见 pdocker.go#L94-L136)。这也是为什么新起的 whoami 容器无需重启 Traefik 就能被自动发现。 - 容器过滤:pkg/provider/docker/config.go 的
keepContainer实现了本文exposedbydefault=false的行为——若容器没有traefik.enable=true标签,ExtraConf.Enable为false,容器直接被过滤(见 config.go#L197-L234)。traefik.enable的默认值即 provider 的ExposedByDefault配置(默认true,见 shared_labels.go#L42-L63)。同一函数还表明:非 running 状态或 healthcheck 不健康的容器会被过滤,其 service 保留空负载列表(可用allowEmptyServices调整)。 - 后端地址解析:Traefik 用
--providers.docker.network=proxy指定的网络(proxy)来取容器 IP——getIPAddress优先从该网络中查找network.Addr,找不到会降级到第一个可用网络并打警告日志(见 config.go#L359-L425)。这就是为什么 whoami 必须和 traefik 容器接入同一个proxy网络。 - 端口探测:若没有用
traefik.http.services.<name>.loadbalancer.server.port标签指定端口,Traefik 取容器暴露的最低端口(见 shared.go#L189-L207);服务名则默认取自 Compose 注入的com.docker.compose.service标签,格式为服务名_项目名(见 shared.go#L209-L217)。
关于 Docker provider 的更多安装配置选项(如endpoint、constraints、useBindPortIP),可查阅 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/PathRegexp、Host/HostRegexp、Query、Header、Method等)与优先级计算细节,参见 路由规则与优先级参考。
应用变更:
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.localhost、dashboard.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 版本相比,关键变化有:
- 新增
websecure入口:--entryPoints.websecure.address=:443,并通过--entryPoints.websecure.http.tls=true强制该入口所有 HTTP 流量走 TLS; - File provider 加载动态 TLS 配置:
--providers.file.directory=/etc/traefik/dynamic对应挂载的./dynamic目录,dynamic/tls.yml中的证书被注册进 TLS store; - 各路由迁移到
websecure入口并加tls=true:traefik.http.routers.<name>.tls=true是路由级开关,声明该路由需要 TLS 终止,Traefik 会按 SNI 从 TLS store 中匹配证书; - 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;
- 搭建基于认证结果的多层路由。
【免费下载链接】traefikThe Cloud Native Application Proxy项目地址: https://gitcode.com/GitHub_Trending/tr/traefik
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考