使用 Traefik 进行 HTTP 路由

引言

在本地开发期间,通常需要运行多个 HTTP 服务。您可能同时拥有 API 和前端应用程序、用于模拟数据端点的 WireMock 服务,或者数据库可视化工具(例如 phpMyAdmin 或 pgAdmin)。在许多开发环境中,这些服务暴露在不同的端口上,这需要您记住每个端口上是什么,但也会引入其他问题(例如 CORS)。

通过充当唯一的暴露服务,然后根据请求 URL(通过路径或主机名)将请求路由到适当的服务,反向代理可以极大地简化此设置。Traefik 是一个现代的云原生反向代理和负载均衡器,它可以使开发和部署多服务应用程序变得更加容易。本指南将向您展示如何将 Traefik 与 Docker 配合使用来增强您的开发环境。

在本指南中,您将学习如何

  1. 使用 Docker 启动 Traefik
  2. 配置路由规则以在两个容器之间分流流量
  3. 在容器化开发环境中使用 Traefik
  4. 使用 Traefik 将请求发送到非容器化工作负载

先决条件

按照本操作指南进行操作需要满足以下先决条件

将 Traefik 与 Docker 配合使用

Traefik 的独特功能之一是它可以通过多种方式进行配置。当使用 Docker 提供程序时,Traefik 使用 labels... 从其他正在运行的容器获取其配置。Traefik 会监听引擎事件(用于容器启动和停止),提取 labels,并更新其配置。

虽然有 许多 Traefik 监控的 labels...,但最常用的两个是

我们来快速演示如何启动 Traefik,然后配置另外两个容器,使其可以使用不同的主机名进行访问。

  1. 为了使两个容器能够相互通信,它们需要位于同一个网络上。使用 docker network create 命令创建一个名为 traefik-demo 的网络

    $ docker network create traefik-demo
    
  2. 使用以下命令启动一个 Traefik 容器。该命令将 Traefik 暴露在端口 80,挂载 Docker socket(用于监控容器以更新配置),并传递 --providers.docker 参数来配置 Traefik 使用 Docker 提供程序。

    $ docker run -d --network=traefik-demo -p 80:80 -v /var/run/docker.sock:/var/run/docker.sock traefik:v3.1.2 --providers.docker
    
  3. 现在,启动一个简单的 Nginx 容器,并定义 Traefik 用于配置 HTTP 路由的 labels。请注意,此 Nginx 容器未暴露任何端口。

    $ docker run -d --network=traefik-demo --label 'traefik.http.routers.nginx.rule=Host(`nginx.localhost`)' nginx
    

    容器启动后,打开浏览器访问 http://nginx.localhost... 查看应用程序(所有基于 Chromium 的浏览器都会将 *.localhost 请求本地路由,无需额外设置)。

  4. 启动第二个将使用不同主机名的应用程序。

    $ docker run -d --network=traefik-demo --label 'traefik.http.routers.welcome.rule=Host(`welcome.localhost`)' docker/welcome-to-docker
    

    容器启动后,打开浏览器访问 http://welcome.localhost...。您应该会看到一个“欢迎使用 Docker”的网站。

在开发中使用 Traefik

既然您已经体验了 Traefik,是时候尝试在开发环境中使用它了。在本例中,您将使用一个具有分离前端和后端的示例应用程序。该应用程序堆栈具有以下配置:

  1. 所有发往 /api 的请求都转到 API 服务
  2. 所有发往 localhost 的其他请求都转到前端客户端
  3. 由于应用程序使用 MySQL,db.localhost 应该提供 phpMyAdmin,以便在开发期间轻松访问数据库
Architecture diagram showing Traefik routing requests to other containers based on the path of the request

该应用程序可以在 GitHub 上访问:dockersamples/easy-http-routing-with-traefik...

  1. compose.yaml 文件中,Traefik 使用以下配置:

    services:
      proxy:
        image: traefik:v3.1.2
        command: --providers.docker
        ports:
          - 80:80
        volumes:
          - /var/run/docker.sock:/var/run/docker.sock

    请注意,这本质上与之前使用的配置相同,但现在是 Compose 语法。

  2. 客户端服务具有以下配置,它将启动容器并为其提供 labels,以便在 localhost 接收请求。

    services:
      # …
      client:
        image: nginx:alpine
        volumes:
          - "./client:/usr/share/nginx/html"
        labels:
          traefik.http.routers.client.rule: "Host(`localhost`)"
  3. api 服务具有类似的配置,但您会注意到路由规则有两个条件:主机必须是“localhost”,并且 URL 路径必须带有“/api”前缀。由于此规则更具体,Traefik 将优先于客户端规则对其进行评估。

    services:
      # …
      api:
        build: ./dev/api
        volumes:
          - "./api:/var/www/html/api"
        labels:
          traefik.http.routers.api.rule: "Host(`localhost`) && PathPrefix(`/api`)"
  4. 最后,phpmyadmin 服务配置为接收主机名“db.localhost”的请求。该服务还定义了环境变量以自动登录,从而使进入应用程序变得更容易一些。

    services:
      # …
      phpmyadmin:
        image: phpmyadmin:5.2.1
        labels:
          traefik.http.routers.db.rule: "Host(`db.localhost`)"
        environment:
          PMA_USER: root
          PMA_PASSWORD: password
  5. 在启动堆栈之前,如果 Nginx 容器仍在运行,请将其停止。

就是这样了。现在,您只需要使用 docker compose up 命令启动 Compose 堆栈,所有服务和应用程序即可进行开发。

将流量发送到非容器化工作负载

在某些情况下,您可能希望将请求转发到未在容器中运行的应用程序。在下面的架构图中,使用了与之前相同的应用程序,但 API 和 React 应用程序现在在本机主机上运行。

An architecture diagram showing several components and the routing between them. Traefik is able to send requests to both non-containerized and containerized workloads

为此,Traefik 需要使用另一种方法来配置自己。File provider... 允许您在 YAML 文档中定义路由规则。这是一个示例文件:

http:
  routers:
    native-api:
      rule: "Host(`localhost`) && PathPrefix(`/api`)"
      service: native-api
    native-client:
      rule: "Host(`localhost`)"
      service: native-client

  services:
    native-api:
      loadBalancer:
        servers:
          - url: "http://host.docker.internal:3000/"
    native-client:
      loadBalancer:
        servers:
          - url: "http://host.docker.internal:5173/"

此配置表明,针对 localhost/api 的请求将被转发到名为 native-api 的服务,该服务然后将请求转发到 http://host.docker.internal:3000...。主机名 host.docker.internal 是 Docker Desktop 提供的一个名称,用于将请求发送到主机。

使用此文件,唯一的更改是 Traefik 的 Compose 配置。具体有两处更改:

  1. 配置文件被挂载到 Traefik 容器中(具体的目的地路径由您决定)
  2. command 命令更新为添加 file provider 并指向配置文件的位置
services:
  proxy:
    image: traefik:v3.1.2
    command: --providers.docker --providers.file.filename=/config/traefik-config.yaml --api.insecure
    ports:
      - 80:80
      - 8080:8080
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./dev/traefik-config.yaml:/config/traefik-config.yaml

启动示例应用程序

要运行将 Traefik 的请求转发到本机运行应用程序的示例应用程序,请使用以下步骤:

  1. 如果您的 Compose 堆栈仍在运行,请使用以下命令将其停止:

    $ docker compose down
    
  2. 使用提供的 compose-native.yaml 文件启动应用程序

    $ docker compose -f compose-native.yaml up
    

    打开 http://localhost... 将返回 502 Bad Gateway 错误,因为其他应用程序尚未运行。

  3. 通过运行以下步骤启动 API:

    cd api
    yarn install
    yarn dev
    
  4. 在新的终端窗口中运行以下步骤启动前端(从项目根目录开始)

    cd client
    yarn install
    yarn dev
    
  5. http://localhost... 打开应用程序。您应该会看到一个从 http://localhost/api/messages... 获取消息的应用程序。您也可以打开 http://db.localhost... 直接从 Mongo 数据库查看或调整可用消息。Traefik 将确保请求被正确路由到相应的容器或应用程序。

  6. 完成后,运行 docker compose down 停止容器,并通过按 ctrl+c 停止 Yarn 应用程序。

总结

运行多个服务不再需要复杂的端口配置和良好的记忆力。借助 Traefik 等工具,您可以轻松启动所需的服务并轻松访问它们——无论是应用程序本身(如前端和后端)还是额外的开发工具(如 phpMyAdmin)。

页面选项