• 亮色
  • 深色
  • 自动
  • RSS 订阅

    使用 Docker Compose 入门 Traefik

    2024-04-27

    官网:Traefik Getting Started Quickly - Traefik

    介绍

    将 Traefik 在 Docker Compose 中使用时,可以自动发现 network 中的其它容器的服务并自动代理,支持代理 TCP、UDP 还有 HTTP,并且能够通过其自带的页面查看容器服务的情况。

    对于向 Nginx、Caddy 之类的传统反向代理,通常都需要手动的去配置请求路由到哪些容器服务上,而 Traefik 能够自动获取到这些容器。而且还能够根据自定义的规则来分配请求应该分发给哪些容器上的服务。

    快速开始

    services:
      reverse-proxy:
        image: traefik:v3.0
        # Enables the web UI and tells Traefik to listen to docker
        command: --api.insecure=true --providers.docker
        ports:
          # The HTTP port
          - '80:80'
          # The Web UI (enabled by --api.insecure=true)
          - '8080:8080'
        volumes:
          # So that Traefik can listen to the Docker events
          - /var/run/docker.sock:/var/run/docker.sock:ro
    
      whoami:
        # A container that exposes an API to show its IP address
        image: traefik/whoami
        labels:
          - traefik.http.routers.whoami.rule=Host(`whoami.docker.localhost`)
    

    第一个 reverse-proxy 容器便是 traefik 的服务,而 whoami 是一个用于演示的 web 容器服务。这个 web 容器服务可以输出部署的计算机的信息,例如 IP 地址、主机等信息。

    可以看到在 whoamilabels 上指定了一条 traefik 的路由规则:

    - traefik.http.routers.whoami.rule=Host(`whoami.docker.localhost`)
    

    这条规则的意思是:当请求头中 Host 字段的值为 whoami.docker.localhost 时会将请求转发到该容器的服务上。

    运行这个 docker compose 配置:

    docker compose up
    

    可以使用 cURL 命令测试 whoami 服务:

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

    返回内容应该类似下面这样:

    Hostname: 032b3d60f0e7
    ...
    IP: 172.18.0.3
    ...
    Host: whoami.docker.localhost
    ...
    X-Forwarded-Host: whoami.docker.localhost
    ...
    

    此外还可以访问 8080 端口查看 Traefik Web 端界面,通过这个界面可以查看 Traefik 相关的信息。

    负载均衡

    可以使用 docker compose 的 --scale <service>=<数量> 来将一个容器运行多个服务实例,例如执行下面的命令来同时运行 2whoami 服务:

    # 无须停止 traefik
    docker compose up --scale whoami=2
    

    启动后再次执行请求命令:

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

    多请求几次就可以发现返回的内容会是刚刚运行的两个 whoami 服务。

    配置介绍

    可以通过两种方式配置 Traefik 路由方式:

    1. 启动时的静态配置
    2. 动态配置

    Tip

    快速开始中的 whoami 容器示例属于动态配置。

    静态配置

    定义静态配置又有三种不同的方式,三种方式之间同时只能使用一种。

    按优先级排列是 配置文件 > 命令行参数 > 环境变量。

    配置文件

    启动的时候,Traefik 会在下述目录读取 traefik.y[a]mltraefik.toml 文件里的静态配置:

    • /etc/traefik/
    • $XDG_CONFIG_HOME/
    • $HOME/.config/
    • .

    此外也可以用过传递 --configFile=<配置文件路径> 的方式来覆盖。

    参数

    可用的参数列表可以通过 traefik --help 命令获取,对于 Docker,可以用 docker run traefik[:version] --help 获取,或是参考 网页

    环境变量

    可以使用的环境变量参考:Traefik Environment Variables Documentation - Traefik

    动态配置

    Traefik 从 provider 上获取动态配置,目前支持的 provider 可以参考 Traefik Configuration Discovery Overview - Traefik。因为这里只学习 Docker 的用法,所以不会说明其它的 provider,也不适用于 Docker Swarm。

    首先需要确保启动时指定了 --providers.docker=true 参数,或者在 traefik 能够发现的地方放 traefik.y[a]mltraefik.toml 文件,并在其中指定好 provider 是 docker。

    示例:将访问转发到指定容器的指定端口

    例如将对 http://example.com 的访问转发到 http://<service>:<端口> 上:

    services:
      example-web-service:
        # ...
        labels:
          - traefik.http.routers.my-container.rule=Host(`example.com`)
          # Tell Traefik to use the port 12345 to connect to `my-container`
          - traefik.http.services.my-service.loadbalancer.server.port=12345
    

    这个示例里将 example.com 的访问转发到 example-web-service12345 端口上了。

    如果不指定转发端口,会使用容器的第一个公开端口。

    示例:将不同的访问转发到同一个容器的不同端口

    例如将对 example-a.comexample-b.com 的服务转发到当前容器的不同端口上:

    services:
      my-container:
        # ...
        labels:
          # 对 example-a.com 的访问转发到当前容器的 8000 端口上
          # 那个 www-router 应该是随便命名的
          - traefik.http.routers.www-router.rule=Host(`example-a.com`)
          - traefik.http.routers.www-router.service=www-service
          - traefik.http.services.www-service.loadbalancer.server.port=8000
    
          # 对 example-b.com 的访问转发到当前容器的 9000 端口上
          # 那个 admin-router 应该是随便命名的
          - traefik.http.routers.admin-router.rule=Host(`example-b.com`)
          - traefik.http.routers.admin-router.service=admin-service
          - traefik.http.services.admin-service.loadbalancer.server.port=9000
    

    Docker 的动态配置

    Tip

    label 不区分大小写

    所有支持的 label 可以参考 Traefik Docker Configuration Documentation - Traefik

    Traefik 会给每个容器创建相应的 servicerouter。service 会自动给容器的每个服务实例获取一个服务器,router 会自动获取由 defaultRule 定义的规则(如果 labels 里没有定义)。

    一般来说,在配置 Traefik 时,需要定义分配给一个或者多个 router 的 service 才能让 router 正常运行。

    但是,基于标签的配置也有例外:

    1. 如果 labels 里定义了 router 和 service,但 router 没有指定 service,那么 service 会自动分配给 router。
    2. 如果 labels 定义了 router,但是没有 service,那么会自动创建 service 并分配 router,就像最开始的示例定义了 router 但是没有 service。

    自动 service 分配

    labels:
      - traefik.http.routers.myproxy.rule=Host(`example.net`)
      # service myservice 自动分配给 router myproxy
      - traefik.http.services.myservice.loadbalancer.server.port=80
    

    自动创建 service 并分配

    labels:
      - traefik.http.routers.myproxy.rule=Host(`example.net`)
    

    router 配置

    可以用 traefik.http.routers.<你的 router 名称>. 来配置要附加到容器上的 router。

    Warning

    @ 不能作为 router 名称的一部分。

    例如,要设置一个将对 example.com 请求路由到当前容器上,而可以用:

    - traefik.http.routers.my-container.rule=Host(`example.com`)
    

    HTTPS & TLS

    这里仅介绍通过 Traefik & Cloudflare DNS 自动生成的证书。

    自动创建 Let’s Encrypt 证书

    如果证书是通过 Traefik 生成的,那么他也会自动跟踪其到期时间。默认生成的证书有效期是 2160 秒,也就是 90 天,会在到期前 30 天开始更新证书。

    如果想要指定证书有效期,可以通过指定如下参数:

    --certificatesresolvers.myresolver.acme.certificatesduration=<有效秒数>
    

    需要注意的是,Traefik 管理不了有效时间低于 1 小时的证书。

    虽然想继续写下去,但是感觉这东西应该很少用到,所以需要用到的话还是参考 官方 吧。

    下面通过一个实践例子来说明,将原本 8080 端口上的 DashBoard 放在 HTTPS 之后:

    Warning

    下面这个例子不能直接使用!

    services:
      server:
        container_name: traefik
        image: traefik:v3.0
        # Enables the web UI and tells Traefik to listen to docker
        ports:
          # The HTTP port
          - 80:80
          - 443:443
          # - 8080:8080
        volumes:
          # So that Traefik can listen to the Docker events
          - /var/run/docker.sock:/var/run/docker.sock:ro
          - ./letsencrypt:/letsencrypt
        env_file:
          - .env
        command:
          - --providers.docker
          - --entryPoints.web.address=:80
          - --entryPoints.websecure.address=:443
          # # 如果要使用 Web UI 相关的功能,记得开启此项
          # - --api.insecure=true
    
          - --certificatesresolvers.base-resolver.acme.email=<你的邮箱>
          - --certificatesresolvers.base-resolver.acme.dnschallenge=true
          - --certificatesresolvers.base-resolver.acme.dnschallenge.provider=cloudflare
          - --certificatesresolvers.base-resolver.acme.storage=/letsencrypt/acme.json
    
          # # 如果是为了调试,可以启用 DBEUG 模式
          # - --log.level=DEBUG
          # # 如果要测试申请证书,可以启用使用测试的证书申请服务器
          # - --certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory
    
        # labels:
        #   # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
        #   # >>>>>>>> Web UI 相关的 router 和 service 配置,使用前需要确保 command 中启用了 --api.insecure=true
        #   - traefik.http.routers.doamin-traefik-router.entrypoints=websecure
        #   - traefik.http.routers.doamin-traefik-router.tls.certresolver=base-resolver
        #   - traefik.http.routers.doamin-traefik-router.rule=Host(`<你的域名>`)
        #   - traefik.http.routers.doamin-traefik-router.service=traefik-service
    
        #   # 如果想通过 Host IP 来访问,那么可以再顶一个 router,像下面这样
        #   - traefik.http.routers.http-traefik-router.rule=Host(`<你的 Host IP>`)
        #   - traefik.http.routers.http-traefik-router.service=traefik-service
    
        #   - traefik.http.services.traefik-service.loadbalancer.server.port=8080
        #   # <<<<<<<< Web UI 相关的 router 和 service 配置,使用前需要确保 command 中启用了 --api.insecure=true
        #   # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    

    若要使用的话,需要进行以下步骤:

    1. 修改其中的 <你的邮箱><你的域名>
    2. Cloudflare 上配置好域名的 DNS 记录,使域名指向此服务器 IP
    3. 然后在还要创建一个 .env 文件,在其中填写 CF_DNS_API_TOKEN=<你的 Cloudflare Token>,关于 <你的 Cloudflare Token> 这个值,在后面的章节会说明怎么获得(前提是你使用 Cloudflare 来解析你的域名)。

    Tip

    如果对这个示例感兴趣的话,可以看看: https://github.com/nafnix/traefik-v3-start

    开放 HTTPS 端口

    ports 选项的 443:443 端口不多说,这是开启 HTTPS 所必要的端口。

    设置使用 DNS 和 Cloudflare 生成

    command 里使用

    --certificatesresolvers.myresolver.acme.dnschallenge=true
    --certificatesresolvers.myresolver.acme.dnschallenge.provider=cloudflare
    

    参数分别表示启用 DNS 验证域名是否有效,以及通过 Cloudflare 验证,这个不是随便哪一家都可以,需要使用可以编辑你的域名 DNS 记录的提供商,这是支持的提供商列表:https://doc.traefik.io/traefik/v3.0/https/acme/#providers

    可以看到旁边还有个参数:

    --certificatesresolvers.myresolver.acme.email=<你的邮箱>
    

    如果使用了这个参数的话,会在生成证书时将你的邮箱记录起来(但是不知道到底有什么用,可能是更新证书的时候还有 Let’s Encrypt 有什么新闻的时候会往邮箱里发东西吧。)

    保存生成的证书

    volumes 选项新增的 ./letsencrypt:/letsencrypt 对应着 command 选项的参数。

    --certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json
    

    虽然 Traefik 可以自动获取证书,但是因为 Let’s Encrypt 有请求速率限制,所以最好还是把已经取到的证书保存下来。

    如果是为了测试用途,可以把其中这个参数的注释给解除掉,相当于修改了生成证书的地址,这样子 Let’s Encrypt 速率的限制就会放宽许多,但那不意味着可以无限量生成。

    --certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory,
    

    证书生成频率限制参考:

    Tip

    测试用途的证书生成完毕后,尝试通过 HTTPS 访问网站是访问不了的,会提示类似 http: TLS handshake error from <IP:PORT> 的错误,如果没有看到错误,检查 command 里是否有使用 --log.level=DEBUG 参数。 这是 V2 的提示,V3 不知道还有没有。

    配置证书生成所需的 Cloudflare Token

    Tip

    Traefik 允许使用 secrets 替代通过环境变量的方式写 Token,可以参考 DNS Providers :: Let’s Encrypt client and ACME library written in Go.,如果使用的是 DNS provider 的话,通常是在原来的环境变量名后加个 _FILE,然后大概像下面这样:

    services:
      reverse-proxy:
        # ...
        environment:
          # 原来的方式:
          # - CF_DNS_API_TOKEN=...
          - CF_DNS_API_TOKEN_FILE=/run/secrets/cf-token
        secrets:
          - cf-token
    
    secrets:
      cf-token:
        file: ./cloudflare-token
    

    CF_DNS_API_TOKEN 环境变量的值在 Cloudflare 上获取,访问这个地址:Cloudflare | Web Performance & Security

    然后在页面上创建一个具有 DNS 编辑权限的 Token。可以参考 这段说明 或者如下步骤进行操作:

    1. 点击 Create Token
    2. 选择 Edit zone DNSUse template
    3. Zone Resources 下的最右边一项选择网站
    4. 页面最下方点击 Continue to summary
    5. 点击 Create Token
    6. 点击 Copy 复制 Token

    设置路由入口

    command 里的 --entryPoints.web.address=:80--entryPoints.websecure.address=:443 参数分别指定了两个入口:web 入口使用 80 端口,websecure 入口则使用 443 端口,意味着 router 可以使用这两个入口来分配 service。

    labels 里使用 websecure 入口作为 router 的入口,而 router 的 service 是 study-traefik-service(这个 service 也就是使用 8080 端口),并且 router 要求 Host 是 你的域名,tls 的处理则是使用 myresolver(也就是前面配置的通过 DNS 和 Cloudflare 生成的证书)。

    开启调试模式

    command 里的 --log.level=DEBUG 可以指定以调试模式运行,打印的日志比默认启动方式多很多,如果是刚开始配置项目,启用这个东西还挺不错的。