Что такое Traefik и как с ним работать?

Traefik — реверсивный прокси, предназначенный для работы с Docker. Он позволяет легко и декларативно проксировать сервисы в контейнерах. Сначала лейблы могут показаться сложными, но к ним быстро привыкаешь :)
В нескольких примерах постараюсь показать, как просто запрашивать сайты и API, автоматизировать получение сертификатов и даже подключить несколько middleware (например, для добавления заголовков).
Для начала стоит создать конфиг трафика:
# traefik.yml
# ...
providers:
docker:
network: traefik
exposedbydefault: false
# добавляем file провайдер, который будет подтягивать данные из
# директории external
file:
directory: ./external
# ...
Для проксирования сервисов в локальной сети требуется добавить docker-host сервис, так как localhost внутри контейнера укажет на сеть самого контейнера, а не на локальную сеть машины.
# docker-compose.yml
version: "3.8"
services:
# ...
traefik:
# ...
networks:
- traefik
# добавляем общую для докерхоста и траефика сеть
- local
docker-host:
image: qoomon/docker-host
cap_add: [ "NET_ADMIN", "NET_RAW" ]
restart: always
networks:
- local
# ...
networks:
traefik:
external:
name: traefik
local:
# Dockerfile
FROM traefik:v2.5.2
WORKDIR /traefik
COPY ./traefik.yml ./traefik.yml
CMD ["traefik"]
# docker-compose.yml
version: "3.8"
services:
traefik:
build: .
container_name: traefik
restart: always
ports:
# открываем порты для http, https, и дашборда траефика,
# последним не стоит светить за пределы локальной сети
# к нему мы будем стучаться через ssh (об этом ниже)
- 80:80
- 443:443
- 127.0.0.1:8080:8080
volumes:
# траефику нужен доступ к docker.sock, чтобы мониторить контейнеры
- /var/run/docker.sock:/var/run/docker.sock:ro
# а вот и вольюм для сертификатов
- /data/letsencrypt:/letsencrypt
networks:
- traefik
# для примера подключим whoami - простенький сервис, который показывает
# инфу о запросе в текстовом виде
whoami:
image: "traefik/whoami"
restart: always
labels:
# включаем траефик для этого контейнера
- traefik.enable=true
# задаем сеть траефика
- traefik.docker.network=traefik
# самое интересное: добавляем роутер и правило для него
# в нашем случае роутер будет назваться whoami
# и будет доступен по домену example.com
# обязательно добавьте название роутера, оно должно
# быть уникальным, в нашем случаем это whoami (идет после
# traefik.http.routers.)
- traefik.http.routers.whoami.rule=Host(`example.com`)
# Задаем через какой энтрипоинт будет доступен роутер
- traefik.http.routers.whoami.entrypoints=https
# задаем certresolver
- traefik.http.routers.whoami.tls.certresolver=simple-resolver
# на самом деле порт не обязательно указывать явно
# traefik в состоянии сам определить, какой порт слушает сервис,
# но может так случиться, что один контейнер слушает сразу несколько
# портов (к примеру так делает rabbitMq), тогда надо будет создавать
# несколько роутеров и явно прописывать несколько портов
- traefik.http.services.whoami.loadbalancer.server.port=80
networks:
- traefik
# ну и собственно сети
networks:
traefik:
external:
name: traefik
Всё, теперь можно запускать и радоваться
Если хотите посмотреть на dashboard, можно зафорварить порты через SSH…
ssh -L 8080:localhost:8080 root@example.com
…и открыть в браузере localhost:8080

Проксирование внешних сервисов
Traefik можно использовать не только для сервисов в Docker, но и для внешних. Из коробки поддерживает балансировку нагрузки — если у вас реплицирован сервис, достаточно указать все хосты, и Traefik сам займётся остальной работой.
Для проксирования внешних сервисов (вне докер сети) надо добавить провайдер в traefik.yml
# traefik.yml
# ...
providers:
docker:
network: traefik
exposedbydefault: false
# добавляем file провайдер, который будет подтягивать данные из
# директории external
file:
directory: ./external
# ...
Для проксирования сервисов в локальной сети нужно добавить сервис docker-хоста, потому что localhost внутри контейнера ссылается на сеть самого контейнера, а не на локальную сеть хост-машины.
# docker-compose.yml
version: "3.8"
services:
# ...
traefik:
# ...
networks:
- traefik
# добавляем общую для докерхоста и траефика сеть
- local
docker-host:
image: qoomon/docker-host
cap_add: [ "NET_ADMIN", "NET_RAW" ]
restart: always
networks:
- local
# ...
networks:
traefik:
external:
name: traefik
local:
# Dockerfile
FROM traefik:v2.5.2
WORKDIR /traefik
COPY ./traefik.yml ./traefik.yml
# копируем папку с конфигами внешних сервисов
COPY ./external ./external
CMD ["traefik"]
И сам конфиг внешнего сервиса. Все конфиги кидаем в директорию external.
# external/example.yml
http:
services:
example-api:
loadBalancer:
servers:
# если сервис расположен на внешнем хосте,
# то просто пишем ip или домен
- url: "http://123.456.789.123:4716"
example-web-client:
loadBalancer:
servers:
# если на localhost, то docker-host
- url: "http://docker-host:8132"
routers:
example-web-client:
entryPoints:
- https
# вебклиент будет доступен по любым путям на домене # web.example.com
rule: "Host(`site.example.com`)"
service: example-web-client
tls:
certResolver: simple-resolver
example-api:
entryPoints:
- https
# апи будет доступно только на site.example.com/api(.*)
# при этом для вебклиента не надо прописывать дополнительные
# правила, траефик сам будет роутить запросы на /api,
# это работает примерно как специфичность в css
rule: "Host(`site.example.com`) && PathPrefix(`/api`)"
service: example-api
tls:
certResolver: simple-resolver
Wildcard сертификаты
Traefik умеет работать с Wildcard-сертификатами! Перепишем docker-compose.yml так, чтобы whoami был доступен по *.example.com
Для начала нужно добавить wildcard-резолвер в конфигурацию Traefik.
# traefik.yml
certificatesResolvers:
# ...
wildcard-resolver:
acme:
dnschallenge:
# указываем dns провайдера, в этом примере будет godaddy,
# но траефик умеет работать и с другими:
# https://doc.traefik.io/traefik/https/acme/#dnschallenge
provider: godaddy
email: me@example.com
storage: /letsencrypt/acme.jso
# docker-compose.yml
version: "3.8"
services:
traefik:
build: ./proxy
container_name: traefik
restart: always
environment:
# указываем api ключи нашего провайдера из переменных окружения
- GODADDY_API_KEY=${GODADDY_API_KEY}
- GODADDY_API_SECRET=${GODADDY_API_SECRET}
- GODADDY_POLLING_INTERVAL=10
- GODADDY_PROPAGATION_TIMEOUT=300
ports:
- 80:80
- 443:443
- 127.0.0.1:8080:8080
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- /data/letsencrypt:/letsencrypt
labels:
- traefik.enable=true
- traefik.http.routers.api.entrypoints=http
networks:
- local
- traefik
whoami:
image: "traefik/whoami"
restart: always
labels:
- traefik.enable=true
- traefik.docker.network=traefik
# меняем правила для роутера
- traefik.http.routers.whoami.rule="Host(`example.com`) || HostRegexp(`{subdomain:.+}.example.com`)"
- traefik.http.routers.whoami.entrypoints=https
# задаём wildcard-resolver
- traefik.http.routers.whoami.tls.certresolver=wildcard-resolver
# домены, на которые ресолвер будет получать серты
- traefik.http.routers.whoami.tls.domains[1].main=example.com
- traefik.http.routers.whoami.tls.domains[1].sans=*.example.com
- traefik.http.services.whoami.loadbalancer.server.port=80
networks:
- traefik
# ...
Middlewares
Traefik позволяет создавать Middleware и применять их к роутерам и даже энтрипоинтам!
К примеру, если нужно убрать какой-нибудь сервис из поисковой выдачи, можно просто добавить хедер X-Robots-Tag: noindex, nofollow
# docker-compose.yml
# ...
whoami:
image: "traefik/whoami"
restart: always
labels:
- traefik.enable=true
- traefik.docker.network=traefik
- traefik.http.routers.whoami.rule="Host(`example.com`) || HostRegexp(`{subdomain:.+}.example.com`)"
- traefik.http.routers.whoami.entrypoints=https
- traefik.http.routers.whoami.tls.certresolver=wildcard-resolver
- traefik.http.routers.whoami.tls.domains[1].main=example.com
- traefik.http.routers.whoami.tls.domains[1].sans=*.example.com
- traefik.http.services.whoami.loadbalancer.server.port=80
# Создаем middleware, где:
# noindex - название
# headers - тип мидлвера
- "traefik.http.middlewares.noindex.headers.customresponseheaders.X-Robots-Tag=noindex, nofollow"
# Добавляем наш мидлвер роутеру
- traefik.http.routers.whoami.middlewares=noindex@docker
На роутер можно повесить сразу несколько мидлверов — в этом случае их нужно указывать через запятую.
- “traefik.http.routers.whoami.middlewares=noindex@docker, something@docker, example@file”
Middleware можно применять не только к роутерам, но и ко всем энтрипоинтам. В этом случае middleware всё равно создаём в лейблах — можно сделать это прямо в конфигурации Traefik.
# docker-compose.yml
# ...
traefik:
# ...
labels:
- "traefik.enable=true"
- "traefik.docker.network=traefik"
- "traefik.http.routers.api.entrypoints=http"
- "traefik.http.middlewares.noindex.headers.customresponseheaders.X-Robots-Tag=noindex, nofollow"
# ...
И добавить в traefik.yml мидлвер в энтрипоинт
# traefik.yml
# ...
entrypoints:
http:
address: :80
http:
redirections:
entryPoint:
to: https
scheme: https
permanent: true
https:
address: :443
# добавляем http middleware
http:
middlewares:
- "noindex@docker"
# ...
Подробнее читайте в документации https://doc.traefik.io/traefik/middlewares/overview/
