2024年4月20日

HYEOS

随事而为

快速使用 Docker 玩转 Traefik v2

8 min read

前提条件

  • VPS 环境
  • Docker
  • 有一个域名 example.com
  • 使用 cloudflare 管理域名的 DNS
  • 80/443 已打开

章节

  1. Traefik 路由到 Docker 容器
  2. Traefik 路由到本地 IP 地址
  3. Traefik 中间件
  4. Let's Encrypt 证书 HTTP challenge
  5. Let's Encrypt 证书 DNS challenge
  6. 转发 HTTP 流量到 HTTPS

Traefik 路由到 Docker 容器

创建一个新的 docker 网络

docker network create traefik_net

Traefik 和 containers(容器)需要在同一网络上。Compose 会自动创建一个,但事实是隐藏的,以后可能会发生混乱。最好仅创建自己的网络并将其设置为每个 compose 文件中的默认网络。

使用 docker network inspect traefik_net 查看连接到该网络的容器

创建 traefik.yml

该文件包含所谓的静态 traefik 配置。

在此基本示例中,只有很少的关于 settings(设置)的解释。

因为 exposedbydefault 被设置为 false,标签 "traefik.enable=true" 将需要用于应该由 traefik 路由的容器。

这个文件将通过 bind mount 传递给 docker 容器,这将在我们为 traefik 使用 docker-compose.yml 时完成。

traefik.yml

## STATIC CONFIGURATION
log:
  level: INFO

api:
  insecure: true
  dashboard: true

entryPoints:
  web:
    address: ":80"

providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false

稍后,当 traefik 容器运行时,使用命令 docker logs traefik, 检查是否有提示:"Configuration loaded from file: /traefik.yml"。您不想成为对 traefik.yml 进行更改的 moron,并且它什么也不做,因为实际上并未使用该文件。

创建 .env

它包含一些环境变量。

域名,API密钥,IP地址,密码…… 无论是针对一种情况的特定情况,还是针对另一种情况的不同情况,所有这些都理想地放在了这里。运行 docker-compose up 命令时,这些变量可用于 docker-compose。

这使得编写文件可以更自由地从一个系统移动到另一个系统,并且可以对 .env 文件进行更改, 因此,在大型编写文件或类似文件中忘记更改某些主机规则中的域名的错误可能性更小。

.env

MY_DOMAIN=example.com
DEFAULT_NETWORK=traefik_net

命令 docker-compose config 显示了填充变量后的 compose 状态。

这些变量仅在容器的初始构建期间填写。如果一个 env 变量也应该在运行的容器中可用,则需要在 compose 文件的 environment 部分声明它。

创建 traefik-docker-compose.yml

这是一个简单的典型 compose 文件。

端口 80 已映射,因为我们希望 traefik 负责端口上的内容-将其用作 entrypoint(入口点)。端口 8080 用于 traefik 显示信息的仪表板。需要挂载 docker.sock,因此它实际上可以完成与 docker 交互的工作。 traefik.yml 的挂载是给出静态 traefik 配置的原因。默认网络被设置为第一步中创建的网络,因为它将在所有其他 compose 文件中设置。

traefik-docker-compose.yml

version: "3.7"

services:
  traefik:
    image: "traefik:v2.1"
    container_name: "traefik"
    hostname: "traefik"
    ports:
      - "80:80"
      - "8080:8080"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      - "./traefik.yml:/traefik.yml:ro"

networks:
  default:
    external:
      name: $DEFAULT_NETWORK

运行 traefik-docker-compose.yml

docker-compose -f traefik-docker-compose.yml up -d 将会启动 traefik 容器。

traefik 正在运行,您可以在 ip:8080 上检查它,在那里您可以得到仪表板。

也可以用 docker logs traefik 检查日志。

 

通常你会看到指南只有一个名为 docker-compose.yml 文件。其中包含多个服务/容器。然后只需 docker-compose up -d 就可以开始了。当所有都是一个 compose 时,你甚至不需要费心去定义网络。但这一次,我更喜欢在学习新东西时采取小而独立的步骤。这就是为什么使用自定义命名的 docker-compose 文件,因为它允许更容易的分离。

您在教程中还可以看到没有提及 traefik.yml,而东西只是使用 traefik 的命令或标签从 docker-compose 传递的。

如: command: --api.insecure=true --providers.docker

但是这样一来,组合文件看起来会更加混乱,您仍然无法从那里进行任何操作,有时您仍然需要 traefik.yml。

所以...现在,使用结构良好的可读 traefik.yml

在 traefik 应该路由的容器上添加标签

以下是 whoami,nginx,apache,portainer 的示例。

- "traefik.enable=true"

启用 traefik

- "traefik.http.routers.whoami.entrypoints=web"

定义名为 whoami 的路由,该路由在入口点 Web(端口80)上侦听

- "traefik.http.routers.whoami.rule=Host(whoami.$MY_DOMAIN)"

定义此 whoami 路由规则,特别是当 url 等于 whoami.example.com(域名来自 .env 文件)时, 这意味着路由可以完成其工作并将其路由到服务。

不需要其他任何东西,traefik 可以从这些标签来自 docker 容器的上下文中了解其余信息。

whoami-docker-compose.yml

version: "3.7"

services:
  whoami:
    image: "containous/whoami"
    container_name: "whoami"
    hostname: "whoami"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami.entrypoints=web"
      - "traefik.http.routers.whoami.rule=Host(`whoami.$MY_DOMAIN`)"

networks:
  default:
    external:
      name: $DEFAULT_NETWORK

nginx-docker-compose.yml

version: "3.7"

services:
  nginx:
    image: nginx:latest
    container_name: nginx
    hostname: nginx
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.nginx.entrypoints=web"
      - "traefik.http.routers.nginx.rule=Host(`nginx.$MY_DOMAIN`)"

networks:
  default:
    external:
      name: $DEFAULT_NETWORK

apache-docker-compose.yml

version: "3.7"

services:
  apache:
    image: httpd:latest
    container_name: apache
    hostname: apache
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.apache.entrypoints=web"
      - "traefik.http.routers.apache.rule=Host(`apache.$MY_DOMAIN`)"

networks:
  default:
    external:
      name: $DEFAULT_NETWORK

portainer-docker-compose.yml

version: "3.7"

services:
  portainer:
    image: portainer/portainer
    container_name: portainer
    hostname: portainer
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - portainer_data:/data
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.portainer.entrypoints=web"
      - "traefik.http.routers.portainer.rule=Host(`portainer.$MY_DOMAIN`)"

networks:
  default:
    external:
      name: $DEFAULT_NETWORK

volumes:
  portainer_data:

运行容器

docker-compose -f whoami-docker-compose.yml up -d
docker-compose -f nginx-docker-compose.yml up -d
docker-compose -f apache-docker-compose.yml up -d
docker-compose -f portainer-docker-compose.yml up -d

停止所有容器运行:docker stop $(docker ps -q)

Traefik 路由到本地 IP 地址

当 url 应该瞄准其他东西而不是 docker 容器。

定义 file provider,添加所需的路由和服务

所需要的是一个能够捕获某些 url 并将其路由到某些 IP 的 router。前面的示例显示了如何在端口 80 上捕获任何 url,但是没有人告诉它在符合规则的情况下该怎么做。Traefik 只是知道,因为它是在容器的上下文中使用标签完成的, 而且由于 docker 在 traefik.yml 中被设置为提供程序。

对于这种 “在某些 IP 上发送流量”,需要 traefik 服务, 并且要定义 traefik 服务,需要一个新的提供程序, 即 file provider - 只是一个(fucking stupid)文件,告诉 traefik 该怎么做。

某种常见的做法是将 traefik.yml 本身设置为 file provider,以便进行处理。

在提供者下有一个新的 file 部分,并设置了 traefik.yml 本身。

然后添加动态配置的东西。

一个名为 route-to-local-ip 的 router,有一个简单的子域主机名规则。符合该规则(在本例中为确切的网址 test.example.com)的内容将发送到 loadbalancer 服务, 该服务会将其路由到特定的 IP 和特定的端口。

traefik.yml

## STATIC CONFIGURATION
log:
  level: INFO

api:
  insecure: true
  dashboard: true

entryPoints:
  web:
    address: ":80"

providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false
  file:
    filename: "traefik.yml"

## DYNAMIC CONFIGURATION
http:
  routers:
    route-to-local-ip:
      rule: "Host(`test.example.com`)"
      service: route-to-local-ip-service
      priority: 1000
      entryPoints:
        - web

  services:
    route-to-local-ip-service:
      loadBalancer:
        servers:
          - url: "http://10.0.19.5:80"

路由的优先级设置为 1000,这是一个非常高的值,超过了任何其他可能的路由。

不幸的是 .env 变量在这里不起作用,否则主机规则中的域名和 IP 将来自变量。因此,抬起头来,您肯定会忘记更改这些。

运行 traefik-docker-compose

测试是否有效

docker-compose -f traefik-docker-compose.yml up -d

Traefik 中间件

任何容器的身份验证中间件的示例。

创建一个新文件 users_credentials

包含 username:passwords 对,htpasswd 格式

在下面的示例中,密码 krakatoa 设置为下面所有3个帐户

users_credentials

me:$apr1$L0RIz/oA$Fr7c.2.6R1JXIhCiUI1JF0
admin:$apr1$ELgBQZx3$BFx7a9RIxh1Z0kiJG0juE/
bastard:$apr1$gvhkVK.x$5rxoW.wkw1inm9ZIfB0zs1

挂载 users_credentials

traefik-docker-compose.yml

version: "3.7"

services:
  traefik:
    image: "traefik:v2.1"
    container_name: "traefik"
    hostname: "traefik"
    ports:
      - "80:80"
      - "8080:8080"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      - "./traefik.yml:/traefik.yml:ro"
      - "./users_credentials:/users_credentials:ro"

networks:
  default:
    external:
      name: $DEFAULT_NETWORK

在应该进行身份验证的任何容器中添加两个标签

  • 第一个标签将名为 auth-middleware 的新中间件附加到已存在的 whoami 路由器上。
  • 第二个标签为该中间件提供了 basicauth 类型,并告诉它用于认证用户的文件在哪里。

无需在此处安装 users_credentials,它是 traefik 所需的文件, 这些标签是一种将信息传递给 traefik 的方式,它应该在容器的上下文中进行操作。

whoami-docker-compose.yml

version: "3.7"

services:
  whoami:
    image: "containous/whoami"
    container_name: "whoami"
    hostname: "whoami"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami.entrypoints=web"
      - "traefik.http.routers.whoami.rule=Host(`whoami.$MY_DOMAIN`)"
      - "traefik.http.routers.whoami.middlewares=auth-middleware"
      - "traefik.http.middlewares.auth-middleware.basicauth.usersfile=/users_credentials"

networks:
  default:
    external:
      name: $DEFAULT_NETWORK

nginx-docker-compose.yml

version: "3.7"

services:
  nginx:
    image: nginx:latest
    container_name: nginx
    hostname: nginx
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.nginx.entrypoints=web"
      - "traefik.http.routers.nginx.rule=Host(`nginx.$MY_DOMAIN`)"
      - "traefik.http.routers.nginx.middlewares=auth-middleware"
      - "traefik.http.middlewares.auth-middleware.basicauth.usersfile=/users_credentials"

networks:
  default:
    external:
      name: $DEFAULT_NETWORK

运行容器,现在需要登录名和密码

docker-compose -f traefik-docker-compose.yml up -d
docker-compose -f whoami-docker-compose.yml up -d
docker-compose -f nginx-docker-compose.yml up -d

Let's Encrypt 证书,HTTP challenge

  • LE - Let's Encrypt. 提供免费证书的服务
  • Certificate - 存储在服务器文件中的加密密钥,允许进行加密通信并确认身份
  • ACME - 一种协议(精确商定的通信方式),以协商来自 LE 的证书。它是 traefik 的一部分。
  • DNS - 互联网上的服务器,将域名转换为 IP 地址

Traefik 使用 ACME 向 LE 请求特定域的证书,如 example.com。LE 用一些随机生成的文本来回答,然后 traefik 把这些文本放在服务器的特定位置。然后,LE 向 DNS 互联网服务器询问 example.com,结果指向了某个 IP 地址。LE 通过端口 80/443 查找该 IP 地址,查找包含该随机文本的文件。

如果存在,那么这证明了要求证书的人都控制了服务器和域,因为它显示了对 DNS 记录的控制权。证书已颁发,有效期为 3 个月,traefik 将在少于 30 天的时间内自动尝试续订。

现在我们来看看该怎么做。

创建一个具有 600 权限的空 acme.json 文件

该文件将存储证书以及有关证书的所有信息。

touch acme.json && chmod 600 acme.json

在 traefik.yml 中添加 443 入口点和证书解析器

在 entrypoint 部分中,新的 entrypoint 被添加为 websecure,端口 443

certificatesResolvers 是一个配置部分,它告诉 traefik 如何使用 acme resolver 获取证书。

certificatesResolvers:
  lets-encr:
    acme:
      #caServer: https://acme-staging-v02.api.letsencrypt.org/directory
      storage: acme.json
      email: whatever@gmail.com
      httpChallenge:
        entryPoint: web
  • 解析器的名称为 lets-encr,并使用 acme
  • 注释掉了 staging caServer 使 LE 颁发了一个 staging 证书,这是一个无效的证书,不会给绿锁,但没有限制,所以很适合测试。如果它在工作,它会说,我们加密。
  • Storage 告诉在哪里存储给定的证书 - acme.json
  • 邮件是 LE 发送证书过期通知的地方
  • httpChallenge 有一个入口点,因此 acme 在端口 80 上执行 http challenge

这就是 acme 所需要的一切

traefik.yml

## STATIC CONFIGURATION
log:
  level: INFO

api:
  insecure: true
  dashboard: true

entryPoints:
  web:
    address: ":80"
  websecure:
    address: ":443"

providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false

certificatesResolvers:
  lets-encr:
    acme:
      #caServer: https://acme-staging-v02.api.letsencrypt.org/directory
      storage: acme.json
      email: whatever@gmail.com
      httpChallenge:
        entryPoint: web

暴露/映射端口 443 并将 acme.json 挂载在 traefik-docker-compose.yml 中

注意:acme.json 不是 :ro - 只读

traefik-docker-compose.yml

version: "3.7"

services:
  traefik:
    image: "traefik:v2.1"
    container_name: "traefik"
    hostname: "traefik"
    env_file:
      - .env
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      - "./traefik.yml:/traefik.yml:ro"
      - "./acme.json:/acme.json"

networks:
  default:
    external:
      name: $DEFAULT_NETWORK

向容器添加所需的标签

与第一章中的纯 HTTP 相比,它只是将路由器的入口点从 web 更改为 websecure, 并将名为 lets-encr 的证书解析器分配给现有路由器

whoami-docker-compose.yml

version: "3.7"

services:
  whoami:
    image: "containous/whoami"
    container_name: "whoami"
    hostname: "whoami"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami.entrypoints=websecure"
      - "traefik.http.routers.whoami.rule=Host(`whoami.$MY_DOMAIN`)"
      - "traefik.http.routers.whoami.tls.certresolver=lets-encr"

networks:
  default:
    external:
      name: $DEFAULT_NETWORK

nginx-docker-compose.yml

version: "3.7"

services:
  nginx:
    image: nginx:latest
    container_name: nginx
    hostname: nginx
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.nginx.entrypoints=websecure"
      - "traefik.http.routers.nginx.rule=Host(`nginx.$MY_DOMAIN`)"
      - "traefik.http.routers.nginx.tls.certresolver=lets-encr"

networks:
  default:
    external:
      name: $DEFAULT_NETWORK

运行容器

稍等一分钟

容器现在只能在 https 上工作并且具有 greenlock

 

检查 acme.json 的内容

如果要重新开始,请删除acme.json

Let's Encrypt 证书在 cloudflare 上 DNS challenge

  • LE - Let's Encrypt. 提供免费证书的服务
  • Certificate - 存储在服务器文件中的加密密钥,允许进行加密通信并确认身份
  • ACME - 一种协议(精确商定的通信方式),以协商来自 LE 的证书。它是 traefik 的一部分。
  • DNS - 互联网上的服务器,将域名转换为 IP 地址

Traefik 使用 ACME 向 LE 请求特定域的证书,如 example.com。LE 用一些随机生成的文本来回答,然后 traefik 把这些文本放在服务器的特定位置。然后,LE 向 DNS 互联网服务器询问 example.com,结果指向了某个 IP 地址。LE 通过端口 80/443 查找该 IP 地址,查找包含该随机文本的文件。

如果存在,那么这证明了要求证书的人都控制了服务器和域,因为它显示了对 DNS 记录的控制权。证书已颁发,有效期为 3 个月,traefik 将在少于 30 天的时间内自动尝试续订。

与 httpChallenge 相比的好处是能够使用通配符证书。这些是验证所有子域 *.example.com 的证书 另外,无需打开任何端口。

但 traefik 需要能够对 DNS 记录进行自动更改,因此需要管理网站 DNS 的人对此提供支持。这就是为什么选择 cloudflare。

现在我们来看看该怎么做。为所有规划的子域添加类型 A DNS 记录

[whoami, nginx, *] 是示例子域,每个子域都应有一个指向 traefik IP 的 A 记录。

创建一个具有 600 权限的空 acme.json 文件

touch acme.json && chmod 600 acme.json

将 443 入口点和证书解析器添加到 traefik.yml

在 entrypoint 部分中,新的 entrypoint 被添加为 websecure,端口 443

certificatesResolvers 是一个配置部分,它告诉 traefik 如何使用 acme resolver 获取证书。

certificatesResolvers:
lets-encr:
  acme:
    #caServer: https://acme-staging-v02.api.letsencrypt.org/directory
    email: whatever@gmail.com
    storage: acme.json
    dnsChallenge:
      provider: cloudflare
      resolvers:
        - "1.1.1.1:53"
        - "8.8.8.8:53"
  • 解析器的名称为 lets-encr,并使用 acme
  • 注释掉了 staging caServer 使 LE 颁发了一个 staging 证书,这是一个无效的证书,不会给绿锁,但没有限制,所以很适合测试。如果它在工作,它会说,我们加密。
  • Storage 告诉在哪里存储给定的证书 - acme.json
  • 邮件是 LE 发送证书过期通知的地方
  • dnsChallenge 是由一个 provider 指定的,

在这个例子中是 cloudflare。每个提供程序在 .env 文件中需要不同名称的环境变量, 但这是稍后的内容,这里只需要提供程序的名称

  • 解析器是在挑战期间使用的知名 DNS 服务器的 IP

traefik.yml

## STATIC CONFIGURATION
log:
  level: INFO

api:
  insecure: true
  dashboard: true

entryPoints:
  web:
    address: ":80"
  websecure:
    address: ":443"

providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false

certificatesResolvers:
  lets-encr:
    acme:
      #caServer: https://acme-staging-v02.api.letsencrypt.org/directory
      email: whatever@gmail.com
      storage: acme.json
      dnsChallenge:
        provider: cloudflare
        resolvers:
          - "1.1.1.1:53"
          - "8.8.8.8:53"

在 .env 文件中添加所需的变量

我们知道根据支持的提供商列表添加哪些变量

对于 cloudflare 变量是

  • CF_API_EMAIL - cloudflare login
  • CF_API_KEY - global api key

.env

MY_DOMAIN=example.com
DEFAULT_NETWORK=traefik_net
CF_API_EMAIL=whateverbastard@gmail.com
CF_API_KEY=8d08c87dadb0f8f0e63efe84fb115b62e1abc

暴露/映射端口 443 并将 acme.json 挂载在 traefik-docker-compose.yml 中

注意:acme.json 不是 :ro - 只读

traefik-docker-compose.yml

version: "3.7"

services:
  traefik:
    image: "traefik:v2.1"
    container_name: "traefik"
    hostname: "traefik"
    env_file:
      - .env
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      - "./traefik.yml:/traefik.yml:ro"
      - "./acme.json:/acme.json"

  networks:
    default:
      external:
        name: $DEFAULT_NETWORK

在容器上添加所需的标签

与第一章中简单的 http 相比

  • 路由器的入口点从 web 切换到 websecure
  • 分配给路由器的名为 lets-encr 的证书解析器
  • 定义将要获得证书的主域的标签,在这里是 whoami.example.com,域名是从 .env 文件中提取的

whoami-docker-compose.yml

version: "3.7"

services:
  whoami:
    image: "containous/whoami"
    container_name: "whoami"
    hostname: "whoami"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami.entrypoints=websecure"
      - "traefik.http.routers.whoami.rule=Host(`whoami.$MY_DOMAIN`)"
      - "traefik.http.routers.whoami.tls.certresolver=lets-encr"
      - "traefik.http.routers.whoami.tls.domains[0].main=whoami.$MY_DOMAIN"


networks:
  default:
    external:
      name: $DEFAULT_NETWORK

nginx-docker-compose.yml

version: "3.7"

services:
  nginx:
    image: nginx:latest
    container_name: nginx
    hostname: nginx
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.nginx.entrypoints=websecure"
      - "traefik.http.routers.nginx.rule=Host(`nginx.$MY_DOMAIN`)"
      - "traefik.http.routers.nginx.tls.certresolver=lets-encr"
      - "traefik.http.routers.nginx.tls.domains[0].main=nginx.$MY_DOMAIN"

networks:
  default:
    external:
      name: $DEFAULT_NETWORK

运行容器

docker-compose -f traefik-docker-compose.yml up -d
docker-compose -f whoami-docker-compose.yml up -d
docker-compose -f nginx-docker-compose.yml up -d

DNS 挑战的全部重点就是获取通配符!

很公平

因此,对于通配符,这些标签将加入 traefik compose。

  • 与以前一样使用相同的 lets-encr 证书解析器,它在 traefik.yml 中定义
  • 子域(*.example.com)的通配符被设置为要获取证书的主域
  • 裸域(只是简单的example.com)设置为sans(主题备用名称)

同样,您确实需要 * .example.com 和 example.com

在 DNS 控制面板中设置为 A 记录,指向 traefik 的 IP

traefik-docker-compose.yml

version: "3.7"

services:
  traefik:
    image: "traefik:v2.1"
    container_name: "traefik"
    hostname: "traefik"
    env_file:
      - .env
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      - "./traefik.yml:/traefik.yml:ro"
      - "./acme.json:/acme.json"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.traefik.tls.certresolver=lets-encr"
      - "traefik.http.routers.traefik.tls.domains[0].main=*.$MY_DOMAIN"
      - "traefik.http.routers.traefik.tls.domains[0].sans=$MY_DOMAIN"

networks:
  default:
    external:
      name: $DEFAULT_NETWORK

现在,如果容器想作为子域进行访问,则只需要一个具有 url 规则的常规路由器,位于 443 端口入口点,并使用相同的 lets-encr 证书解析器

whoami-docker-compose.yml

version: "3.7"

services:
  whoami:
    image: "containous/whoami"
    container_name: "whoami"
    hostname: "whoami"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami.entrypoints=websecure"
      - "traefik.http.routers.whoami.rule=Host(`whoami.$MY_DOMAIN`)"
      - "traefik.http.routers.whoami.tls.certresolver=lets-encr"

networks:
  default:
    external:
      name: $DEFAULT_NETWORK

nginx-docker-compose.yml

version: "3.7"

services:
  nginx:
    image: nginx:latest
    container_name: nginx
    hostname: nginx
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.nginx.entrypoints=websecure"
      - "traefik.http.routers.nginx.rule=Host(`nginx.$MY_DOMAIN`)"
      - "traefik.http.routers.nginx.tls.certresolver=lets-encr"

networks:
  default:
    external:
      name: $DEFAULT_NETWORK

这是apache,但这次运行在裸域 example.com 上

apache-docker-compose.yml

version: "3.7"

services:
  apache:
    image: httpd:latest
    container_name: apache
    hostname: apache
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.apache.entrypoints=websecure"
      - "traefik.http.routers.apache.rule=Host(`$MY_DOMAIN`)"
      - "traefik.http.routers.apache.tls.certresolver=lets-encr"

networks:
  default:
    external:
      name: $DEFAULT_NETWORK

转发 HTTP 流量到 HTTPS

http 停止使用 https 设置,最好将 http(80) 重定向到 https(443)。

Traefik 有专门的中间件 — redirectscheme。

当 traefik.yml 本身设置为文件提供程序时,可以在动态部分的 traefik.yml 中的多个位置声明此重定向。

或在任何正在运行的容器中使用标签,此示例在 traefik compose 中进行操作。

使用 traefik 中的标签添加新路由和重定向方案

- "traefik.enable=true"

在这个 traefik 容器上启用 traefik,不是说这里需要到服务的典型路由,而是说没有它其他标签就不能工作

- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"

创建一个名为 redirect-to-https 的新中间件,输入 “redirectscheme” 并为其分配方案 https

- "traefik.http.routers.redirect-https.rule=hostregexp(`{host:.+}`)"

创建一个名为 redirect-https 的新路由,并使用一个正则表达式规则来捕获所有传入请求

- "traefik.http.routers.redirect-https.entrypoints=web"

声明此路由器在哪个入口点上侦听 - Web(端口80)

- "traefik.http.routers.redirect-https.middlewares=redirect-to-https"

将新创建的 redirectscheme 中间件分配给此新创建的路由。

traefik-docker-compose.yml

version: "3.7"

services:
  traefik:
    image: "traefik:v2.1"
    container_name: "traefik"
    hostname: "traefik"
    env_file:
      - .env
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      - "./traefik.yml:/traefik.yml:ro"
      - "./acme.json:/acme.json"
    labels:
      - "traefik.enable=true"
      ## DNS CHALLENGE
      - "traefik.http.routers.traefik.tls.certresolver=lets-encr"
      - "traefik.http.routers.traefik.tls.domains[0].main=*.$MY_DOMAIN"
      - "traefik.http.routers.traefik.tls.domains[0].sans=$MY_DOMAIN"
      ## HTTP REDIRECT
      - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
      - "traefik.http.routers.redirect-https.rule=hostregexp(`{host:.+}`)"
      - "traefik.http.routers.redirect-https.entrypoints=web"
      - "traefik.http.routers.redirect-https.middlewares=redirect-to-https"

networks:
  default:
    external:
      name: $DEFAULT_NETWORK