traefik

https://doc.traefik.io/traefik

Welcome

Screenshot_2021_02_02-12_49_03-2023-05-16welcome.png

Traefik это опенсорсный Edge Router который делает публикацию твоих сервисов простой и веселой. Он принимает запросы от имени твой системы и ищет какой компонент отвечает за его обработку

Что отличает Traefik от других, помимо множества функций, так это то что он автоматически определяет правильную конфигурацию для твоих сервисов. Магия происходит когда Traefik инспектирует твою инфраструктуру в которой он находит релевантную информацию о том какой сервис обрабатывает какие запросы

Traefik нативно совместим со всеми крупными технологиями кластеризации, такими как: Kubernetes, Docker, Docker Swarm, AWS, Mesos, Marathon, и список продолжает расти. А еще он может работать одновременно с несколькими (обычно это нужно для старого софта на bare metal)

С Traefik'ом нет необходимости поддерживать и синхронизировать кучу раздельных конфигов, все происходит автоматически в реальном времени (без рестартов и прерываний подключений). С Traefik'ом ты оплачиваешь время на разработку и деплой новых фич, а не на конфигурирование и поддержку этого в рабочем состоянии

Разрабатывая Traefik, наша основная цель сделать его простым в использовании и мы уверены это тебе понравится

-- The Traefik Maintainer Team

Getting Started

Concepts

Introduction

Traefik основан на концептах EntryPoints, Routers, Middlewares и Services

Основные функции включают: динамическое конфигурирование, автоматический service discovery и поддержку множества бекендов и протоколов

  1. EntryPoints - это сетевые входные точки внутрь Traefik. Они определяют порт который будет принимать пакеты и протокол TCP/UDP
  2. Routers - Роутер отвечает за подключение входящих запросов к сервисам которые их обслужат
  3. Middlewares - прикрепляются к роутерам и могут модифицировать запросы или ответы перед тем как те будут отправлены твоему сервису
  4. Services - Сервисы отвечают за конфигурирование того как достигать актуальные сервисы которые должны обрабатывать входящие запросы

Edge Router

Traefik это Edge Router, это значит что он дверь в вашу платформу и он слышит и роутит каждый входящий запрос: он знает всю логику и каждое правило которые определяют какие сервисы обрабатывают какие запросы (базируясь на path, host, headers, etc)

Auto Service Discovery

Тогда как традиционные edge router'ы (или reverse proxies) нуждаются в конфиге который должен содержать все возможные маршруты до твоих сервисов, Traefik получает эту информацию из самих же сервисов

Деплоя свои сервисы ты прикрепляешь к ним информацию которая говорит Traefik'у характеристики запросов которые может обработать сервис

Screenshot_2021_02_02-12_49_03-2023-05-16autosd.png

Это значит что когда сервис задеплоен, Traefik замечает его немедленно и обновляет свои правила роутинга в реальном времени. Аналогично, когда сервис удален из инфраструктуры, соответствующее правило также удаляется

Тебе больше не требуется создавать и синхронизировать конфиги загроможденные айпишниками и другими правилами

Quick Start

Docker

Launch Traefik With the Docker Provider

Создай файл docker-compose.yml в котором опиши сервис reverse-proxy который использует официальный образ Traefik

version: '3'

services:
  reverse-proxy:
    # The official v2 Traefik docker image
    image: traefik:v2.10
    # 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

Запусти следующей командой:

docker-compose up -d reverse-proxy

Дальше ты можешь открыть в браузере http://localhost:8080/api/rawdata и увидеть Traefik'овый API

(7tech.local:default) vandud@192-168-0-106: ~ 🚀 curl -s http://172.28.103.198:8080/api/rawdata | jq
{
  "routers": {
    "api@internal": {
      "entryPoints": [
        "traefik"
      ],
      "service": "api@internal",
      "rule": "PathPrefix(`/api`)",
      "priority": 2147483646,
      "status": "enabled",
      "using": [
        "traefik"
      ]
    },
...
    "wordpress@docker": {
      "entryPoints": [
        "http"
      ],
      "service": "wordpress-wordpress-1-75b7desrqbkk8g5mmncpzeati",
      "rule": "Host(`wordpress.docker`)",
      "status": "enabled",
      "using": [
        "http"
      ]
    }
  },
  "middlewares": {
    "dashboard_redirect@internal": {...},
    "dashboard_stripprefix@internal": {...},
  },
  "services": {
    "api@internal": {
      "status": "enabled",
      "usedBy": [
        "api@internal"
      ]
    },
...
    "wordpress-wordpress-1-75b7desrqbkk8g5mmncpzeati@docker": {
      "loadBalancer": {
        "servers": [
          {
            "url": "http://10.0.11.6:80"
          }
        ],
        "passHostHeader": true
      },
      "status": "enabled",
      "usedBy": [
        "wordpress@docker"
      ],
      "serverStatus": {
        "http://10.0.11.6:80": "UP"
      }
    }
  }
}
Traefik Detects New Services and Creates the Route for You

Теперь когда у нас поднят Traefik мы будем деплоить новые сервисы
Добавь в docker-compose:

  whoami:
    image: traefik/whoami
    labels:
      - "traefik.http.routers.whoami.rule=Host(`wordpress.docker`) && PathPrefix(`/whoami`)"

whoami это простой сервис который выдает информации о машине на которую он задеплоен
Деплоим и видим что traefik его обнаружил
Screenshot_2021_02_02-12_49_03-2023-05-17-at-15whoami.png

🚀 curl -s http://wordpress.docker/whoami
Hostname: 102031f395a3
IP: 127.0.0.1
IP: 10.0.11.16
IP: 172.18.0.5
RemoteAddr: 10.0.11.9:60988
GET /whoami HTTP/1.1
Host: wordpress.docker
User-Agent: curl/7.87.0
Accept: */*
Accept-Encoding: gzip
X-Forwarded-For: 10.0.0.2
X-Forwarded-Host: wordpress.docker
X-Forwarded-Port: 80
X-Forwarded-Proto: http
X-Forwarded-Server: 72df34ecb3e3
X-Real-Ip: 10.0.0.2
More Instances? Traefik Load Balances Them

https://doc.traefik.io/traefik/getting-started/quick-start/#more-instances-traefik-load-balances-them

Kubernetes

https://doc.traefik.io/traefik/getting-started/quick-start-with-kubernetes/

Configuration Introduction

Как происходит магия
Screenshot_2021_02_02-12_49_03-2023-05-18-at-1traefikmagik.png

Конфигурация в Traefik может быть заслана двумя путями:

Элементы статической конфигурации задают подключение к провайдерам и определяют входные точки в Traefik (entrypoints) (эти элементы обычно не меняются)

Динамическая конфигурация содержит все что определяет как будут обрабатываться запросы твоей системой
Эта конфигурация может меняться и бесшовно подгружаться на горячую (без прерывания подключений)

The Dynamic Configuration

Traefik берет динамическую конфигурацию от провайдеров: будь это оркестратор, сервис регистри или простой конфиг файл

The Static Configuration

Здесь есть три взаимоисключающих пути (можно использовать только один в один момент времени) задания статической конфигурации:

  1. Конфиг файл
  2. CLI аргументы
  3. Переменные окружения

Эти варианты применяются в порядке описаном выше

Если значение опции не указано - применяется дефолтное значение
Более того, если опция имеет под-опции, и какая-либо из этих подопций не указано - будет использовано значение по умолчанию (насколько я понял для всей опции, а не только для неуказанной под-опции)

Например, опции --providers.docker достаточно для того чтобы включить docker провайдер, несмотря на то что существует другие под-опции такие как --providers.docker.endpoint. После установки этот параметр устанавливает (и сбрасывает) все значения по умолчанию для вложенных параметров --providers.docker

Configuration File

При старте Traefik ищет статическую конфигурацию в файле traefik.yml (или traefik.yaml или traefik.toml) который ожидает найти в:

Положение конфигурационного файла можно переопределить через опцию configFile

traefik --configFile=foo/bar/myconfigfile.yml
Arguments

Список всех доступных аргументов можно посмотреть в выводе опции --help

traefik --help
# or

docker run traefik[:version] --help
# ex: docker run traefik:v2.10 --help

Либо в https://doc.traefik.io/traefik/reference/static-configuration/cli/

Environment Variables

Все доступные переменные окружения можно посмотреть в https://doc.traefik.io/traefik/reference/static-configuration/env/

Available Configuration Options

Все конфигурационные опции задокументированы в связанных с ними секциях
Ты можешь просмотреть доступные функции в меню, providers, или routing section

Install Traefik

Ты можешь установить Traefik следующими способами:

Use the Official Docker Image

https://doc.traefik.io/traefik/getting-started/install-traefik/#use-the-official-docker-image

Use the Helm Chart

https://doc.traefik.io/traefik/getting-started/install-traefik/#use-the-helm-chart

Exposing the Traefik dashboard

https://doc.traefik.io/traefik/getting-started/install-traefik/#exposing-the-traefik-dashboard

Use the Binary Distribution

https://doc.traefik.io/traefik/getting-started/install-traefik/#use-the-binary-distribution

Compile your Binary from the Sources

https://doc.traefik.io/traefik/getting-started/install-traefik/#compile-your-binary-from-the-sources

FAQ

https://doc.traefik.io/traefik/getting-started/faq/#faq

Configuration Discovery

Overview

У Traefik'а много друзей
Screenshot_2021_02_02-12_49_03-2023-05-18-at-16manyfriends.png

Обнаружение конфигурации в Traefik'e достигается через провайдеры

Провайдеры это компоненты инфраструктуры, такие как оркестраторы, контейнерные движки, облачные провайдеры или key-value базы данных. Идея в том что Traefik делает запросы в API провайдера чтобы найти релевантную информацию о маршрутизации, и когда Traefik обнаруживает изменения он динамически обновляет маршруты

Orchestrators

Все провайдеры отличаются, но их можно поделить на 4 категории:

Provider Namespace

Когда ты описываешь некоторые объекты в динамической конфигурации Traefik'a, такие как middleware, сервисы, TLS опции или транспорты, они селятся в неймспейс своего провайдера. Например, если ты опишешь middleware используя docker лейблы, он будет обитать в неймспейсе docker провайдера

Если ты используешь множество провайдеров и хочешь сослаться на объект описанный в другом неймспейсе (у другого провайдера), тогда имя объекта должно иметь суффикс из @<provider-name>

resource-name>@<provider-name>

As Kubernetes also has its own notion of namespace, one should not confuse the provider namespace with the Kubernetes Namespace of a resource when in the context of cross-provider usage

In this case, since the definition of a Traefik dynamic configuration object is not in Kubernetes, specifying a Kubernetes Namespace when referring to the resource does not make any sense

On the other hand, if you were to declare a middleware as a Custom Resource in Kubernetes and use the non-CRD Ingress objects, you would have to add the Kubernetes Namespace of the middleware to the annotation like this <middleware-namespace>-<middleware-name>@kubernetescrd

Referencing a Traefik Dynamic Configuration Object from Another Provider

Описание ресурса через файл-провайдер

http:
  middlewares:
    add-foo-prefix:
      addPrefix:
        prefix: "/foo"

Использование его в другом провайдере

your-container: #
  image: your-docker-image

  labels:
    # Attach add-foo-prefix@file middleware (declared in file)
    - "traefik.http.routers.my-container.middlewares=add-foo-prefix@file"

Supported Providers

Таблица поддерживаемых провайдеров
https://doc.traefik.io/traefik/providers/overview/#supported-providers

Provider Type Configuration Type Provider Name
Docker Orchestrator Label docker
Kubernetes IngressRoute Orchestrator Custom Resource kubernetescrd
Kubernetes Ingress Orchestrator Ingress kubernetes
Kubernetes Gateway API Orchestrator Gateway API Resource kubernetesgateway
Consul Catalog Orchestrator Label consulcatalog
Nomad Orchestrator Label nomad
ECS Orchestrator Label ecs
Marathon Orchestrator Label marathon
Rancher Orchestrator Label rancher
File Manual YAML/TOML format file
Consul KV KV consul
Etcd KV KV etcd
ZooKeeper KV KV zookeeper
Redis KV KV redis
HTTP Manual JSON format http
Configuration Reload Frequency
providers.providersThrottleDuration

Опционально, по умолчанию 2 секунды

Иногда некоторые провайдеры могут испытывать внезапные всплески изменений которые генерируют множество событий изменения конфигурации. Если Traefik возьмет их всех в расчет то будет вызвано релоадов конфигурации больше чем необходимо

Чтобы обойти это можно задать опцию providers.providersThrottleDuration. Она задает длительность ожидания между релоадами. Таким образом если пройзойдет множество изменений то применена будет последняя версия конфигурации на момент релоада

Эта опция не может быть задана на каждый провайдер по отдельности, но throttling algorithm применяется к каждому из них независимо

Значение опции должно быть указано в секундах или как валидный формат продолжительности (см. time.ParseDuration)

providers:
  providersThrottleDuration: 10s
--providers.providersThrottleDuration=10s

Restrict the Scope of Service Discovery

По умолчанию Traefik создает роуты для всех найденных контейнеров
Если ты хочешь ограничить область Traefik SD, иными словами запретить создание роутов для некоторых контейнеров, ты можешь сделать это двумя различными способами:

exposedByDefault and traefik.enable

Список провайдеров поддерживающих эти опции

Constraints

Список провайдеров поддерживающих ограничения

Docker

История лейблов и контейнеров
Screenshot_2021_02_02-12_49_03-2023-05-18-at-20storylc.png

Прикрепи лейблы к своим контейнерам и позволь Traefik'у сделать все остальное

Traefik работает и с Docker Engine (standalone) и с Docker Swarm

Configuration Examples

Routing Configuration

Когад в качестве провайдера используется docker, Traefik использует лейблы контейнера для получения конфигурации
Подробнее лейблы можно посмотреть тут https://doc.traefik.io/traefik/routing/providers/docker/

Routing Configuration with Labels

По умолчанию Traefik смотрит на лейблы уровня контейнера при standalone docker engine

Когда используется docker-compose лейблы указываются в директиве labels у сервиса

Можно отметить что важно понимать что не только сам докер может "работать через docker провайдер", также такие инструменты как terraform, ansible и прочие могут создавать контейнеры и вешать на них лейблы (таким образом они могут работать с Traefik'ом)

Port Detection

Traefik получает private IP и порт контейнера из docker api
Обнаружение порта работает следующим образом:

Host networking

https://doc.traefik.io/traefik/providers/docker/#host-networking

IPv4 && IPv6

https://doc.traefik.io/traefik/providers/docker/#ipv4-ipv6

Docker API Access

Traefik'у требуется доступ к docker socket'у для получения оттуда динамической конфигурации
Ты можешь указать какой docker api endpoint использовать с помощью директивы endpoint

Небезопасно просто так выдавать доступ к сокету для Traefik (да и вообще кому угодно). Если произойдет компрометация веб сервера то злоумышленник может получить полный доступ к кластеру. Можно найти некоторые варианты решения этой проблемы по ссылке https://doc.traefik.io/traefik/providers/docker/#docker-api-access

Docker Swarm Mode

Чтобы включить режим Docker Swarm (вместо standalone docker engine) как провайдер конфигураций, нужно установить опцию swarmMode в true

Routing Configuration with Labels

В режиме Swarm, Traefik использует лейблы найденные на сервисах а не на отдельных контейнерах

Поэтому, если ты используешь compose файл, лейблы надо определять в секции deploy

Port Detection

Docker Swarm не предоставляет никакой информации о портах Traefik'у
Поэтому ты должен вручную указывать порт, который будет использоваться для коммуникации с контейнером. Задается он через лейбл traefik.http.services.<service_name>.loadbalancer.server.port

Docker API Access

Режим Docker Swarm следует тем же правилам что и не-swarm режим

Однако Swarm API доступен только на менеджер ноде, поэтому надо деплоить Traefik на менеджер ноду

version: '3'

services:
  traefik:
    # ...
    deploy:
      placement:
        constraints:
          - node.role == manager

Следуя гайдлайнам из предыдущей секции, если ты заэкспозишь Docker API по TCP, то Traefik может быть зашедулен на любую другую ноду (воркер), лишь бы API оттуда был доступен

Provider Configuration

endpoint

Required, Default="unix:///var/run/docker.sock"

Смотри секции Docker API Access and Docker Swarm API Access для дополнительной информации

Можно предоставить доступ к API через сокет, тогда нужно прокинуть этот сокет в контейнер с Traefik через volume

version: '3'

services:
  traefik:
     image: traefik:v2.10 # The official v2 Traefik docker image
     ports:
       - "80:80"
     volumes:
       - /var/run/docker.sock:/var/run/docker.sock

И указать путь до него --providers.docker.endpoint=unix:///var/run/docker.sock

Можно и по ssh --providers.docker.endpoint=ssh://traefik@192.168.2.5:2022 (надо чтобы пользователь из под которого запущен Traefik мог сделать этот ssh)

useBindPortIP

Optional, Default=false

Traefik роутит запросы на IP:port контейнера. Когда указана опция useBindPortIP=true, ты говоришь ему использовать IP:port приаттаченный к контейнеру биндингом вместо того чтобы ходить по внутренней сети

Когда используется в связке с лейблом traefik.http.services.<name>.loadbalancer.server.port, Traefik попытается найти биндинг на порт из лейбла. Если не выйдет то он откатится на использование внутренней сети, но порт продолжит использовать тот что указан в лейбле

Screenshot_2021_02_02-12_49_03-2023-05-19-at-00.png

--providers.docker.useBindPortIP=true
exposedByDefault

Optional, Default=true

Экспозить контейнеры по умолчанию сквозь Traefik. Если проставить в false, то контейнеры не имеющие лейбла traefik.enable=true будут игнорироваться в итогой конфигурации роутинга

--providers.docker.exposedByDefault=false
network

Optional, Default=""

Определяет дефолтную докер-сеть для связи со всеми контейнерами

Эта опция может быть переопределена по-контейнерно с помощью лейбла traefik.docker.network

--providers.docker.network=test
defaultRule

Optional, Default=Host({{ normalize .Name }})

Эта опция определяет какое правило роутинга применять к контейнеру если для него не указано правила через лейбл

Оно должно быть валидным go-template выражением и может использовать sprig функции
Имя контейнера доступно через ContainerName, имя сервиса через Name, лейблы через Labels

providers:
  docker:
    defaultRule: "Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)"
    # ...
swarmMode

Optional, Default=false

Включает swarm режим вместо одиночного docker демона

--providers.docker.swarmMode=true
swarmModeRefreshSeconds

Optional, Default=15

Определяет интервал поллинга в swarm режиме

--providers.docker.swarmModeRefreshSeconds=30
httpClientTimeout

Optional, Default=0

Определяет клиентский таймаут для HTTP подключений. Если стоит ноль то таймаута нет

--providers.docker.httpClientTimeout=300
watch

Optional, Default=true

Отслеживать ли эвенты докера

--providers.docker.watch=false
constraints

Optional, Default=""

В опция constraints может быть проставлено выражение которое Traefik будет матчить с лейблами контейнеров чтобы определить нужно ли создавать роут для этого контейнера. Если ни один из лейблов контейнера не подходит под выражение, то роута для этого контейнера создано не будет. Если выражение пустое то под него подходит любой контейнер

Синтаксис выражения базируется на функциях Label("key", "value") и LabelRegex("key", "value") с простой булевой логикой
Примеры

# Includes only containers having a label with key `a.label.name` and value `foo`
constraints = "Label(`a.label.name`, `foo`)"

# Excludes containers having any label with key `a.label.name` and value `foo`
constraints = "!Label(`a.label.name`, `value`)"

# With logical AND.
constraints = "Label(`a.label.name`, `valueA`) && Label(`another.label.name`, `valueB`)"

# With logical OR.
constraints = "Label(`a.label.name`, `valueA`) || Label(`another.label.name`, `valueB`)"

# With logical AND and OR, with precedence set by parentheses.
constraints = "Label(`a.label.name`, `valueA`) && (Label(`another.label.name`, `valueB`) || Label(`yet.another.label.name`, `valueC`))"

# Includes only containers having a label with key `a.label.name` and a value matching the `a.+` regular expression.
constraints = "LabelRegex(`a.label.name`, `a.+`)"

Задается так

providers:
  docker:
    constraints: "Label(`a.label.name`,`foo`)"

Или так

--providers.docker.constraints=Label(`a.label.name`,`foo`)
tls

Optional

Описывает TLS конфигурацию для защищенного подключения к докеру

ca

Optional

ca это путь до ca используемого для защищенного подключения к докеру

providers:
  docker:
    tls:
      ca: path/to/ca.crt
cert

cert это путь до публичного сертификата используемого для безопасного подключения к докеру. При использовании этой опции становится обязательным использование опции key

providers:
  docker:
    tls:
      cert: path/to/foo.cert
      key: path/to/foo.key
key

Optional

key это путь до приватного ключа используемого для безопасного подключения к докеру. При использовании этой опции становится обязательным использование опции cert

providers:
  docker:
    tls:
      cert: path/to/foo.cert
      key: path/to/foo.key
insecureSkipVerify

Optional, Default=false

Если insecureSkipVerify стоит в true, то TLS подключение до докера будет разрешено с любым сертификатом предоставленым сервером вне зависимости от того какие хостнеймы он покрывает

--providers.docker.tls.insecureSkipVerify=true
allowEmptyServices

Optional, Default=false

Если параметру присвоено значение true, создается любой балансировщик нагрузки серверов, определенный для контейнеров Docker, независимо от работоспособности соответствующих контейнеров. Затем он также остается живым и отзывчивым даже в те моменты, когда он становится пустым, то есть когда все его дочерние контейнеры становятся неработоспособными. Это приводит к получению 503 HTTP-ответов вместо 404 в приведенных выше случаях.

--providers.docker.allowEmptyServices=true

Kubernetes IngressRoute

https://doc.traefik.io/traefik/providers/kubernetes-crd/

Kubernetes Ingress

https://doc.traefik.io/traefik/providers/kubernetes-ingress/

Kubernetes Gateway API

https://doc.traefik.io/traefik/providers/kubernetes-gateway/

Consul Catalog

https://doc.traefik.io/traefik/providers/consul-catalog/

Nomad

https://doc.traefik.io/traefik/providers/nomad/

ECS

https://doc.traefik.io/traefik/providers/ecs/

Marathon

https://doc.traefik.io/traefik/providers/marathon/

Rancher

https://doc.traefik.io/traefik/providers/rancher/

File

Старый добрый конфигурационный файл

File провайдер позволяет тебе описывать динамическую конфигурацию через yaml или toml файл

Есть поддержка как одного конфига так и множества раздельных файлов

Этот провайдер может быть хорошим решением для переиспользования общих элементов из других провайдеров, например описание разрешенных middleware, basic auth, etc)

Configuration Examples

Сперва включаем file провайдер

providers:
  file:
    directory: "/path/to/dynamic/conf"
--providers.file.directory=/path/to/dynamic/conf

Далее описываем в файле роутеры, middleware и сервисы

http:
  # Add the router
  routers:
    router0:
      entryPoints:
      - web
      middlewares:
      - my-basic-auth
      service: service-foo
      rule: Path(`/foo`)

  # Add the middleware
  middlewares:
    my-basic-auth:
      basicAuth:
        users:
        - test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/
        - test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0
        usersFile: etc/traefik/.htpasswd

  # Add the service
  services:
    service-foo:
      loadBalancer:
        servers:
        - url: http://foo/
        - url: http://bar/
        passHostHeader: false

Provider Configuration

Чтобы посмотреть все опции которые можно описать через file провайдер см. dynamic configuration и static configuration

Есть ограничения. См. "Limitations" https://doc.traefik.io/traefik/providers/file/#provider-configuration

filename

Описывает путь до конфига

filename и directory взаимноисключащие, рекомендуется использовать directory

--providers.file.filename=/path/to/config/dynamic_conf.yml
directory

Описывает путь до директории которая содержит конфиги

filename и directory взаимноисключащие, рекомендуется использовать directory

--providers.file.directory=/path/to/config
watch

Установка опции watch в true позволит Traefik'у автоматически отслеживать изменения в файлах. Это работает и с директорией и с файлом

providers:
  file:
    directory: /path/to/dynamic/conf
    watch: true
Go Templating

Go Templating only works with dedicated dynamic configuration files. Templating does not work in the Traefik main static configuration file

Traefik поддерживает использование go-template для автомтической генерации повторяющихся секций в конфигах. Эти секции должны быть валидными go-template и могут содержать sprig функции

Проиллюстрируем это на возможности простого описания множества роутеров, сервисов и TLS сертификатов

http:
  routers:
    {{range $i, $e := until 100 }}
    router{{ $e }}-{{ env "MY_ENV_VAR" }}:
      # ...
    {{end}}

  services:
    {{range $i, $e := until 100 }}
    application{{ $e }}:
      # ...
    {{end}}

tcp:
  routers:
    {{range $i, $e := until 100 }}
    router{{ $e }}:
      # ...
    {{end}}

  services:
    {{range $i, $e := until 100 }}
    service{{ $e }}:
      # ...
    {{end}}

tls:
  certificates:
  {{ range $i, $e := until 10 }}
  - certFile: "/etc/traefik/cert-{{ $e }}.pem"
    keyFile: "/etc/traefik/cert-{{ $e }}.key"
    store:
    - "my-store-foo-{{ $e }}"
    - "my-store-bar-{{ $e }}"
  {{end}}

Consul

https://doc.traefik.io/traefik/providers/consul/

Etcd

https://doc.traefik.io/traefik/providers/etcd/

ZooKeeper

https://doc.traefik.io/traefik/providers/zookeeper/

Redis

https://doc.traefik.io/traefik/providers/redis/

HTTP

https://doc.traefik.io/traefik/providers/http/

Routing & Load Balancing

Overview

Что происходит с запросами?

Давайте приблизимся к архитектуре Traefik'a и поговорим о том какие компоненты позволяют создавать роуты

Сперва, когда ты запускаешь Traefik, ты определяешь entrypoint'ы (в их наипростейшей форме - номер порта). Далее, подключенный к этим entrypoint'ас, роутер анализирует входящие запросы чтобы увидеть как они матчатся на набор рулов. Если матчатся, то роутер может трансформировать запрос используя фрагменты middleware перед тем как перенаправить его к твоему сервису

Screenshot_2021_02_02-12_49_03-2023-05-19-at-1archataglance.png

Clear Responsibilities

Example with a File Provider

Ниже пример полного конфига для file-провайдера который будет форвардить запросы к http://example.com/whoami/ на сервис доступный на ttp://private/whoami-service/. В процессе Traefik будет производить проверку что пользователь аутентифицирован (используется BasicAuth middleware)

Статическая конфигурация:

entryPoints:
  web:
    # Listen on port 8081 for incoming requests
    address: :8081

providers:
  # Enable the file provider to define routers / middlewares / services in file
  file:
    directory: /path/to/dynamic/conf

Динамическая конфигурация:

# http routing section
http:
  routers:
    # Define a connection between requests and services
    to-whoami:
      rule: "Host(`example.com`) && PathPrefix(`/whoami/`)"
       # If the rule matches, applies the middleware
      middlewares:
      - test-user
      # If the rule matches, forward to the whoami service (declared below)
      service: whoami

  middlewares:
    # Define an authentication mechanism
    test-user:
      basicAuth:
        users:
        - test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/

  services:
    # Define how to reach an existing service on our infrastructure
    whoami:
      loadBalancer:
        servers:
        - url: http://private/whoami-service

Transport configuration

Много чего происходит в подключении между клиентом и Traefik'ом, а также между Traefik'ом и бэкенд серверами
В дополнение несколько параметров позволяют глобально конфигурировать поведение подключений между Traefik'ом и бэкендами. Это достигается за счет секции serversTransport

insecureSkipVerify

Optional, Default=false

insecureSkipVerify выключает проверку сертификата

--serversTransport.insecureSkipVerify=true
rootCAs

Лист CA сертификатов для использования с самосертами

serversTransport:
  rootCAs:
    - foo.crt
    - bar.crt
maxIdleConnsPerHost

Optional, Default=2

Если стоит не ноль, то maxIdleConnsPerHost контролирует максимальное кол-во idle (keep-alive) подключений которые будут поддерживаться per-host

--serversTransport.maxIdleConnsPerHost=7
forwardingTimeouts

Секция forwardingTimeouts о количестве тайм-аутов, относящихся к переадресации запросов на внутренние серверы

forwardingTimeouts.dialTimeout

Optional, Default=30s

dialTimeout - как долго может устанавливаться подключение. Ноль означает отсутствие таймаута

forwardingTimeouts.responseHeaderTimeout

Optional, Default=0s

responseHeaderTimeout если не ноль то задает длительность ожидания ответных заголовков после полной отправки запроса (включая тело запроса). Это время не включает время чтения тела ответа. Ноль означает отсутствие таймаута

forwardingTimeouts.idleConnTimeout

Optional, Default=90s

idleConnTimeout максимальное время жизни idle (keep-alive) подключения пока оно будет оставаться в idle состоянии перед закрытием. Ноль означает отсутствие таймаута

serversTransport:
  forwardingTimeouts:
    dialTimeout: 1s
    responseHeaderTimeout: 1s
    idleConnTimeout: 1s

EntryPoints

Открытие подключений для входящих запросов

Screenshot_2021_02_02-12_49_03-2023-05-19-at-15entrypoints.png

Entrypoints это сетевые входные точки в Traefik. Они определяют порт через который будут приниматься пакеты и то будет это TCP или UDP

Configuration Examples

Port 80 only:

entryPoints:
  web:
   address: ":80"

Мы описали entrypoint который слушает 80 порт и назвали его web


Port 80 & 443:

entryPoints:
  web:
    address: ":80"

  websecure:
    address: ":443"

Описаны два entrypoint'a один называется web, другой websecure
web слушает 80 порт, а websecure 443


UDP на порту 1704:

entryPoints:
  streaming:
    address: ":1704/udp"

Configuration

General

Entrypoint'ы это часть статической конфигурации. Они могут быть заданы через файл или через cli

entryPoints:                               |
  name:                                    |
    address: ":8888" # same as ":8888/tcp" | --entryPoints.name.address=:8888 # same as :8888/tcp
    http2:                                 |
      maxConcurrentStreams: 42             | --entryPoints.name.http2.maxConcurrentStreams=42
    http3:                                 |
      advertisedPort: 8888                 | --entryPoints.name.http3.advertisedport=8888
    transport:                             |
      lifeCycle:                           |
        requestAcceptGraceTimeout: 42      | --entryPoints.name.transport.lifeCycle.requestAcceptGraceTimeout=42
        graceTimeOut: 42                   | --entryPoints.name.transport.lifeCycle.graceTimeOut=42
      respondingTimeouts:                  |
        readTimeout: 42                    | --entryPoints.name.transport.respondingTimeouts.readTimeout=42
        writeTimeout: 42                   | --entryPoints.name.transport.respondingTimeouts.writeTimeout=42
        idleTimeout: 42                    | --entryPoints.name.transport.respondingTimeouts.idleTimeout=42
    proxyProtocol:                         |
      insecure: true                       | --entryPoints.name.proxyProtocol.insecure=true
      trustedIPs:                          |
        - "127.0.0.1"                      | --entryPoints.name.proxyProtocol.trustedIPs=127.0.0.1,192.168.0.1
        - "192.168.0.1"                    |
    forwardedHeaders:                      |
      insecure: true                       | --entryPoints.name.forwardedHeaders.insecure=true
      trustedIPs:                          |
        - "127.0.0.1"                      | --entryPoints.name.forwardedHeaders.trustedIPs=127.0.0.1,192.168.0.1
        - "192.168.0.1"                    |
Address

Адрес определяет порт и опционально хостнейм на котором нужно слушать входящие запросы и пакеты. Он также определяет протокол который нужно использовать (TCP/UDP). Если протокол не указан то по умолчанию используется TCP. Формат следующий:

[host]:port[/tcp|/udp]

Если нужно использовать и TCP и UDP на одном и том же порту, то нужно описать два entrypoint'a

entryPoints:
  tcpep:
   address: ":3179"
  udpep:
   address: ":3179/udp"

Пример указания конкретного айпишника

entryPoints:
  specificIPv4:
    address: "192.168.2.7:8888"
  specificIPv6:
    address: "[2001:db8::1]:8888"
HTTP/2
maxConcurrentStreams

Optional, Default=250

maxConcurrentStreams определяет кол-во одновременных потоков на подключение которые может инициировать клиент. Значение должно быть больше нуля

--entryPoints.name.http2.maxConcurrentStreams=250
HTTP/3
http3

Опция http3 включает протокол HTTP/3 для entrypoint'a. HTTP/3 требует TCP entrypoint, так как HTTP/3 всегда стартует как TCP подключение которое впоследствии будет апгрейднуто до UDP. В основном это тот же entrypoint что и для TLS

Так как HTTP/3 использует UDP, то ты не можешь иметь TCP entrypoint с HTTP/3 на том же порту что и UDP entrypoint. HTTP/3 требует использования TLS, поэтому только роутеры с TLS могут использовать HTTP/3

Так как спецификация HTTP/3 все еще находится в состоянии черновика, то поддержка протокола в Traefik является экспериментальной и должна быть активирована в секции experimental статической конфигурации

experimental:
  http3: true

entryPoints:
  name:
    http3: {}
advertisedPort

http3.advertisedPort описывает какой UDP порт предлагать как HTTP/3 authority. По умолчанию это entypoint порт. Эта опция может быть использована для оверайдинга outhority в заголовке alt-svc, например если публично торчащий порт отличается от того который слушает Traefik

experimental:
  http3: true

entryPoints:
  name:
    http3:
      advertisedPort: 443
Forwarded Headers

Можно сказать Traefik'у доверять информации из X-Forwarded-* заголовков

entryPoints:
  web:
    address: ":80"
    forwardedHeaders:
      trustedIPs:
        - "127.0.0.1/32"
        - "192.168.1.7"
entryPoints:
  web:
    address: ":80"
    forwardedHeaders:
      insecure: true
Transport
respondingTimeouts

respondingTimeouts таймаут для входящих в Traefik запросов. Это опция не имеет эффекта для UDP entrypoint'ов
https://doc.traefik.io/traefik/routing/entrypoints/#respondingtimeouts

transport.respondingTimeouts.readTimeout
Optional, Default=0s

readTimeout задает максимальную продоложительность чтения всего запроса включая тело
Если стоит 0 то лимита нет
Может быть указано в форматах поддерживаемых типом time.ParseDuration. Если юнит не указан то воспринимается как секунды

transport.respondingTimeouts.writeTimeout
Optional, Default=0s

writeTimeout задает таймаут записи ответа
Этот таймаут покрывает время от начала чтения заголовков запроса и до конца записи ответа
Если 0 то лимита нет

transport.respondingTimeouts.idleTimeout
Optional, Default=180s

idleTimeout как долго keep-alive подключение может находиться в состоянии idle Если 0 то лимита нет

entryPoints:
  name:
    address: ":8888"
    transport:
      respondingTimeouts:
        readTimeout: 42
        writeTimeout: 42
        idleTimeout: 42
lifeCycle

Контролирует поведение Traefik'a во время фазы выключения

lifeCycle.requestAcceptGraceTimeout
Optional, Default=0s

Как долго продолжать принимать запросы перед инициацией graceful termination периода (как гласит опция graceTimeOut). Эта опция дает нижележащим балансерам возможность выкинуть Traefik из ротации

Может быть указано в форматах поддерживаемых типом time.ParseDuration. Если юнит не прописан то воспринимается как секунды

Ноль отключает прием запросов в grace period, то есть он будет переходить в grace period сразу

lifeCycle.graceTimeOut
Optional, Default=10s

Задает как долго ждать завершения уже начатых (активных) запросов перед тем как окончательно выключиться

Может быть указано в форматах поддерживаемых типом time.ParseDuration. Если юнит не прописан то воспринимается как секунды

В этот период уже не будут приниматься новые запросы

entryPoints:
  name:
    address: ":8888"
    transport:
      lifeCycle:
        requestAcceptGraceTimeout: 42
        graceTimeOut: 42
ProxyProtocol

https://doc.traefik.io/traefik/routing/entrypoints/#proxyprotocol

HTTP Options

Вся эта секция посвящена только опциям связанных с entrypoint'ами и применимым только к http роутингу

Redirection
entryPoints:
  web:
    address: :80
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https

  websecure:
    address: :443
entryPoint

Эта секция удобна для включения постоянного редиректа всех входящих запросов на другой entrypoint

entryPoint.to
Required

Может содержать имя другого entrypoint (websecure) или порт :443

entryPoint.scheme
Optional, Default="https"

Схема цели редиректа (http/https/etc)

entryPoint.permanent
Optional, Default=true

Перманентен ли редирект

entryPoint.priority
Optional, Default=MaxInt32-1 (2147483646)

Приоритет сгенеренного роутера

entryPoints:
  foo:
    http:
      redirections:
        entryPoint:
          priority: 10
          permanent: true
          scheme: https
          to: websecure
Middlewares

Список middlewares которые будут добавляться по умолчанию в список middlewares каждого роутера ассоциированного с этим entrypoint'ом

entryPoints:
  websecure:
    address: ':443'
    http:
      middlewares:
        - auth@file
        - strip@file
TLS

Эта секция о дефолтных настройках TLS которые будут применяться ко всем роутерам ассоциированным с этим entrypoint'ом

Если TLS секция (любое из ее полей) прописано пользователем, то дефолтная конфигурация не применяется вообще

Эта секция конфигурируется точно так же как у HTTP роутеров

entryPoints:
  websecure:
    address: ':443'
    http:
      tls:
        options: foobar
        certResolver: leresolver
        domains:
          - main: example.com
            sans:
              - foo.example.com
              - bar.example.com
          - main: test.com
            sans:
              - foo.test.com
              - bar.test.com

UDP Options

Эта секция посвящена опциям от entrypoint для UDP роутинга

Timeout

Optional, Default=3s

Как долго сессии ждать в idle состоянии перед тем как высвободить связанные с ней ресурсы. Значение должно быть больше нуля

--entrypoints.foo.address=:8000/udp
--entrypoints.foo.udp.timeout=10s

Routers

Соединение запросов с сервисами

Screenshot_2021_02_02-12_49_03-2023-05-19-at-19routers.png

Роутер занят направлением входящих запросов в сервисы которые их обработают. В процессе этого роутеры могут использовать middleware для модифицирования запросов перед отправкой их в сервис

Configuration Example

Requests /foo are Handled by service-foo -- Using the File Provider

http:
  routers:
    my-router:
      rule: "Path(`/foo`)"
      service: service-foo

Forwarding all (non-tls) requests on port 3306 to a database service

tcp:
  routers:
    to-database:
      entryPoints:
        - "mysql"
      # Catch every request (only available rule for non-tls routers. See below.)
      rule: "HostSNI(`*`)"
      service: database
## Static configuration
entryPoints:
  web:
    address: ":80"
  mysql:
    address: ":3306"

Configuring HTTP Routers

Символ @ запрещено использовать в именах роутеров

EntryPoints

Если не прописано то роутер будет принимать запросы из всех описанных entrypoint'ов. Если надо ограничить охват то можно прописать опцию entryPoints

http:
  routers:
    Router-1:
      # won't listen to entry point web
      entryPoints:
        - "websecure"
        - "other"
      rule: "Host(`example.com`)"
      service: "service-1"
Rule

Рулы это набор матчеров сконфигурированных значениями которые определяют подпадает ли конкретный запрос под их критерии. Если рулы пройдены, то роутер становится активным, вызывает middleware и потом перенаправляет запрос в сервис

Чтобы указать значение рула используй обратные тики ` или экранированные двойные кавычки \"
Одиночные кавычки не разрешено использовать в качестве значения golang string literals

Примеры:

rule = "Host(`example.com`)"
rule = "Host(`example.com`) || (Host(`example.org`) && Path(`/traefik`))"

Далее таблица с доступными матчерами

Rule Description
Headers(`key`, `value`) Check if there is a key keydefined in the headers, with the value value
HeadersRegexp(`key`, `regexp`) Check if there is a key keydefined in the headers, with a value that matches the regular expression regexp
Host(`example.com`, ...) Check if the request domain (host header value) targets one of the given domains.
HostHeader(`example.com`, ...) Same as Host, only exists for historical reasons.
HostRegexp(`example.com`, `{subdomain:[a-z]+}.example.com`, ...) Match the request domain. See "Regexp Syntax" below.
Method(`GET`, ...) Check if the request method is one of the given methods (GET, POST, PUT, DELETE, PATCH, HEAD)
Path(`/path`, `/articles/{cat:[a-z]+}/{id:[0-9]+}`, ...) Match exact request path. See "Regexp Syntax" below.
PathPrefix(`/products/`, `/articles/{cat:[a-z]+}/{id:[0-9]+}`) Match request prefix path. See "Regexp Syntax" below.
Query(`foo=bar`, `bar=baz`) Match Query String parameters. It accepts a sequence of key=value pairs.
ClientIP(`10.0.0.0/16`, `::1`) Match if the request client IP is one of the given IP/CIDR. It accepts IPv4, IPv6 and CIDR formats.
Priority
Middlewares
Service
TLS
General
options
certResolver
domains

Configuring TCP Routers

General
EntryPoints
Rule
Priority
Middlewares
Services
TLS
General
passthrough
options
certResolver
domains

Configuring UDP Routers

General
EntryPoints
Services

Services

Providers