Pull to refresh

Comments 14

Вы будете смеяться, но то, что было описано в labels, можно повторить в формате TOML или YAML. Я предпочитаю YAML и ниже буду придерживаться его. Фломастеры у всех разные.
Файл traefik.yml […]

А ведь так хорошо начиналось-то...


Нет, то что было описано в labels, повторить в traefik.yml невозможно. Потому что traefik.yml — это статическая конфигурация, а labels — динамическая, и с точки зрения traefik это принципиально разные вещи.


Описаны такие статические элементы как логи, энтрипоинты, провайдеры, резолвер. Обратите внимание – статические, значит те, которые не хочется менять за всё время работы контейнера Traefik. Так-то можно и пару роутеров сюда засунуть.

Нет, нельзя туда засунуть роутеры.


Раз выяснилось, что Traefik есть дело до содержимого /etc/traefik/config, давайте положим туда конфиг, например, с информацией о сертификатах.

А это вообще работает?


Для примера объявлено несколько сетей, что иногда бывает в разных compose сборках. И указано конкретное значение метки traefik.docker.network, чтобы наш прокси ничего не перепутал и не соединил случайным образом.

А в динамической конфигурации это вообще работает? Это ж настройка провайдера...

Нельзя? Жаль. Просто видел там роутеры у других людей. Спасибо, сейчас поправлю.

Роутеры там вы могли видеть либо в traefik v1 (тут точно не знаю), либо в файловой динамической конфигурации, за которую отвечает настроенный вами file provider. Однако файловая динамическая конфигурация и статическая конфигурация — совершенно не одно и то же, и в traefik v2 они не могут заменять друг друга.

Так, с критикой автора закончил, теперь расскажу как делать правильно.


Самое первое — направление подключения. Добавлять все сети в docker-compose.yaml траефика — дело нудное и сложное в автоматизации, поступать следует строго наоборот.


Нужно создать сеть traefik и заводить в неё все контейнеры которым нужен доступ наружу:


services:
  traefik:
    image: traefik
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /etc/traefik:/etc/traefik

networks:
  default:
    name: traefik

Почему внутреннее имя сети default? Потому что от неё вы не избавитесь, она всё равно будет создана, а пространство адресов следует экономить.


Ну и в статическом конфиге неплохо бы указать эту самую сеть, чтобы traefik знал какой адрес у контейнера использовать:


[providers.docker]
watch = true
exposedbydefault = false
network = "traefik"

Кстати, рекомендую по возможности использовать конфигурацию формата toml, а не yaml. Она гораздо компактнее.


Теперь в эту настроенную сеть traefik можно добавлять контейнеры. Вот пример такого добавления:


services:
  api:
    image: ...
    restart: unless-stopped
    expose:
      - 80
    networks:
      default:
        aliases:
          - api_internal
      traefik:
    labels:
      - traefik.enable=true
      - traefik.http.routers.api-$COMPOSE_PROJECT_NAME.rule=Host(`api.example.org`)
      - traefik.http.services.api-$COMPOSE_PROJECT_NAME.loadbalancer.server.port=80

networks:
  traefik:
    name: traefik
    external: true

На что тут следует обязательно обратить внимание — это на то, что хотя динамическая конфигурация пишется индивидуально для контейнеров, пространства имён всех роутеров, сервисов и мидлварей глобальны. И если забыть использовать для них уникальный суффикс (например $COMPOSE_PROJECT_NAME) — можно "напороться" на удивительные плавающие баги из-за конфликтов настроек.


Также обратите внимание, что два сервиса находящиеся в сети traefik могут "решить" связаться по этой сети если попытаются использовать для этого имена сервисов (а эти имена могут оказаться неуникальны для разных проектов docker compose). Именно потому я назначил сервису псевдоним api_internal в сети default — чтобы другие сервисы могли ссылаться на него как api_internal. Тут требуется некоторое соглашение в команде, чтобы в соседнем проекте не оказалось сервиса api_internal. В идеале бы вообще выключить разрешение имён докера в сети traefik, но кажется докер так не умеет.

А можете поделиться рецептом, как настроить доступ к MySQL через traefik (если такое вообще возможно)? Допустим у меня есть два сайта, у них отдельные docker-compose.yml в которых поднято всё нужное, https работает через traefik, но вот к MySQL приходится обращаться напрямую, по ip и открывая порт наружу.

Делаете контейнер с ssh сервером, даёте ему порт наружу, и точно так же выделяете ему сетку. Тот, кому нужен доступ к MySQL, может установить туннель до этого контейнера, а точку назначения указать через доменное имя. Поскольку доменное имя точки назначения резолвится сервером, а сервер в докере — автоматически появляется возможность указывать имена контейнеров.


Иными словами, работает это как-то так. Служебный контейнер:


services:
  ssh-gateway:
    image: …
    restart: unless-stopped
    ports:
      - "2222:22"
    volumes:
      - ssh-gateway:/etc/ssh

networks:
  default:
    name: ssh-gateway

volumes:
  ssh-gateway:

И сервис которому нужен доступ через туннель:


services:
  db:
    image: postgres
    restart: unless-stopped
    expose:
      - 5432
    networks:
      default:
        aliases:
          - db-internal
      ssh-gateway:
        aliases:
          - db.dev.my-project.example.org

networks:
  ssh-gateway:
    name: ssh-gateway
    external: true

Теперь, когда нужен доступ к контейнеру, достаточно поднять туннель и можно подключаться к локальному концу туннеля:


ssh -N -L 5432:db.dev.my-project.example.org:5432 -p 2222 example.org

Так, вариант с ssh — универсален, однако я сейчас понял что конкретно к MySQL можно попробовать и через traefik подключиться.


План действий тут будет такой. Во-первых, для контейнера нужно настроить TCP роутер, а не HTTP, указав правило на HostSNI:


    labels:
      - traefik.enable=true
      - traefik.tcp.routers.mysql-$COMPOSE_PROJECT_NAME.rule=HostSNI(`mysql.example.org`)
      - traefik.tcp.services.mysql-$COMPOSE_PROJECT_NAME.loadbalancer.server.port=3306

Во-вторых, надо уговорить клиента использовать TLS соединение с сервером.


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

Все хорошо, но это все работает для *.example.com, а какая настройка нужна на сам example.com?

Не имеет значения. Это такой же хост с именем и адресом. Если ДНС привёл за ним к серверу с Traefik, то всё что нужно — это роутер Traefik с правилом, принимающим это имя и сертификат в файле или резолвере. Можно напрячь HostRegexp(), чтоб в него влезло и это имя. Можно вообще не использовать HostRegexp() или Host(), тогда будут приниматься любые имена. Можно, например, вот так:

.rule=Host(`example.com`) || HostRegexp(`{subdomain:[az0-9]+}.example.com`)

спасибо все заработало, не правильно были DNS указаны, для суб доменов указал, а для мейна вообще ничего))))

разница в том что с субдоменами все отлично работает, но как только делашь роутер наexample.com то на основном домене не работае, указываешь, сублрмен + example.com все снова замечательно

Чушь, все замечательно работает в любых комбинациях.


Вы случайно не забыли DNS настроить на основном домене?

В каком-то другом прокси или вебсервере работает без проблем?

Sign up to leave a comment.

Articles