Traefik как настроить
Перейти к содержимому

Traefik как настроить

  • автор:

Введение в Traefik 2.0

Traefik — это обратный прокси-сервер с открытым исходным кодом, обеспечивающий простую работу с микросервисами и/или просто контейнерами с вашими приложениями.

Обратный прокси-сервер (reverse proxy, реверс-прокси) служит для ретрансляции запросов из внешней сети к каким-либо серверам/сервисам внутренней сети (например веб-сервера, БД или файловые хранилища) и позволяет:

  • обеспечить сокрытие структуры внутренней сети и подробностей о находящейся в ней сервисах;
  • осуществлять балансировку нагрузки (load balancing) между экземплярами одного и того же сервиса или серверами с одинаковыми задачами;
  • обеспечить зашифрованное (HTTPS) соединение между клиентом и любым сервисом, в таком случае SSL сессия создается между клиентом и прокси, а между прокси и сервисом во внутренней сети устанавливается незашифрованное HTTP соединение, если сервис поддерживает HTTPS то можно организовать зашифрованное соединение и во внутренней сети;
  • организовать контроль доступа к сервисам (аутентификацию клиента), а также установить файрвол (брандмауэр).

В статье будет описываться использование Traefik в Docker в качестве реверс-прокси для других контейнеров Docker, а также не контейнеризированных сервисов.

image

Введение

Traefik позиционируется разработчиками как “Edge Router”, то есть можно направить его непосредственно в глобальную сеть одной стороной и во внутреннюю другой. Если у читателя создалось впечатление что таким образом создается единая точка отказа всей системы, то так и есть, но есть несколько моментов: во-первых, Traefik имеет развитый функционал для автоматического восстановления при сбоях; во-вторых, существует Traefik EE — платная версия, в которой помимо прочих преимуществ имеется HA (Hight Availability, Высокая доступность), что подразумевает распределение нагрузки между несколькими экземплярами сервиса (узлами), таким образом при отказе одного его задачи перераспределяются на другие узлы, а отказавший узел отключается и затем немедленно вводится обратно в эксплуатацию. В качестве примечания отметим, что в статье будет рассматриваться бесплатная версия Traefik.

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

Список основных провайдеров:

В рамках этой статьи будут рассмотрены первый и последний провайдеры из этого списка.
Вероятно, не знакомому с темой читателю будет не понятно, чем является провайдер с именем — “File”, это некоторый файл (или папка с файлами), в котором описана конфигурация какого-либо сервиса, не связанного с другими провайдерами, но который должен быть скрыт за реверс-прокси. Остальные же провайдеры являются различными системами оркестрации контейнеров.

Файл конфигурации Traefik, а также файлы для провайдера “File” могут быть написаны на TOML либо YAML, в статье будут приведены примеры на YAML так как этот синтаксис больше нравится автору, а какой-либо функциональной разницы между ними нет, а также не составляет трудности переписать файлы на другой формат конфигурации. Traefik будет развернут в Docker. Для развертывания будет использоваться docker-compose, для обеспечения простоты повторного развертывания.

*В статье будут приведены команды для ОС Linux.

Деплой Traefik

Предполагается что у читателя установлены и настроены docker и docker-compose, их установка выходит за рамки этой статьи.

Создадим в домашней папке пользователя папку traefik , в которой будем хранить всю конфигурацию, и перейдем в эту папку

mkdir ~/traefik cd ~/traefik

Для развертывания (деплоя) Traefik создадим файл docker-compose.yml и отредактируем его в любом удобном вам редакторе. Для начала этот файл будет иметь следующий вид:

version: '3' services: traefik: image: traefik:v2.2 container_name: traefik restart: unless-stopped security_opt: - no-new-privileges:true ports: - 80:80 - 443:443 volumes: - /etc/localtime:/etc/localtime:ro - /var/run/docker.sock:/var/run/docker.sock:ro - ./data/traefik.yml:/traefik.yml:ro

Во внешний мир будут смотреть порты 80 и 443 для HTTP и HTTPS соответственно. Также пробросим в контейнер сокет демона Docker для работы механизма автоматической конфигурации. Конфигурацию Traefik будем описывать в файле traefik.yml находящемся в папке data в текущей директории.

Если для разделения внешней и внутренней сетей используются networks Docker-а, то Traefik должен иметь доступ к внешней сети и ко всем внутренним в которых находятся целевые сервисы.

Создадим и будем постепенно наполнять этот файл.

Для начала опишем точки входа в наш прокси (те самые порты, которые смотрят во внешний мир):

entryPoints: http: address: ":80" https: address: ":443"

Здесь http и https это просто названия (могут быть любыми, хоть a и b ) и были выбраны так для удобства.

Теперь добавим первого провайдера — Docker, это делается следующим образом:

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

Параметром указываем что Traefik не должен брать все контейнеры подряд, далее будет объяснено каким образом мы будем сообщать какие контейнеры нас интересуют. Также здесь видно зачем мы пробрасывали сокет в контейнер — именно через него Traefik будет получать сведения о запускаемых контейнерах на этом хосте (можно подключиться и к демону на другом хосте).

Следующим шагом развернем весь HTTP трафик в HTTPS (почему это было сделано именно таким образом будет описано дальше):

http: routers: http-catchall: rule: HostRegexp(``) entrypoints: - http middlewares: - redirect-to-https middlewares: redirect-to-https: redirectScheme: scheme: https permanent: false

Traefik может проксировать не только HTTP трафик, но и просто TCP и UDP, поэтому указываем что этот блок конфигурации относится к http .

Здесь мы встречаем два из трех основных элементов роутинга в Traefik 2 routers (роутеры) и middlewares(промежуточные обработчики), рассмотрим их подробнее.

Роутеры

Рассмотрим на примере описанного выше роутера:

  • http-catchall — имя роутера, может быть любым, но обязано быть уникальным в рамках блока http всей конфигурации Traefik;
  • rule: — правило, описывает какой трафик попадает в этот роутер, в данном случае описывается HostRegexp , то есть поле Host запроса должно попадать под регулярное выражение .+ (то есть любое), здесь мы видим специфику регулярных выражений в Traefik — оно должно быть заключено в фигурные скобки и иметь наименование ( host в данном случае), то есть синтаксис имеем вид ;
  • entrypoints — массив описанных ранее точек входа, которые будут использоваться этим роутером, в нашем случае используем только http ;
  • middlewares — массив промежуточных обработчиков, куда попадает трафик перед передачей к сервису (сервисы будут рассмотрены позднее).

Подробнее о различных видах правил можно прочитать в документации.

Промежуточные Обработчики

  • redirect-to-https — имя обработчика, может быть любым, но обязано быть уникальным в рамках блока http всей конфигурации Traefik;
  • redirectScheme — тип обработчика, в данном случае изменение схемы запроса;
  • scheme: https — вынуждает клиента использовать схему HTTPS при запросе к прокси;
  • permanent: false — говорит клиенту что это не навсегда и может измениться в будущем.

Подробнее о различных обработчиках можно прочитать в документации (дальше в статье будет описан ещё один обработчик — BasicAuth).

Полностью файл traefik.yml

entryPoints: http: address: ":80" https: address: ":443" http: routers: http-catchall: rule: hostregexp(``) entrypoints: - http middlewares: - redirect-to-https middlewares: redirect-to-https: redirectScheme: scheme: https permanent: false providers: docker: endpoint: "unix:///var/run/docker.sock" exposedByDefault: false

Таким образом мы получим первую рабочую конфигурацию. Выполняем

sudo docker-compose up -d

И прокси должен подняться, можно почитать логи ( sudo docker-compose logs -f ) и убедиться, что всё работает.

Let’s Encrypt

Поскольку мы хотим использовать HTTPS нам нужно где-то взять SSL сертификаты для сервисов, есть возможность использовать свои сертификаты, но мы настроем автоматическое получение бесплатных сертификатов от Let’s Encrypt.

Добавим в конфигурацию ( traefik.yml ) новый блок:

certificatesResolvers: letsEncrypt: acme: email: postmaster@example.com storage: acme.json caServer: "https://acme-staging-v02.api.letsencrypt.org/directory" httpChallenge: entryPoint: http
  • letsEncrypt — это просто имя резолвера;
  • acme — тип резолвера (других типов в общем-то и нет);
  • storage — файл, в котором хранятся сведения о полученных сертификатах;
  • httpChallenge — тип acme-челенжа, дополнительно указываем параметр — точку входа;
  • caServer: «https://acme-staging-v02.api.letsencrypt.org/directory» — позволяет использовать не основной сервер Let’s Encrypt в тестовых целях, так как основной имеет строгие лимиты API (можно закомментировать, когда наладите получение сертификатов).

Также дополним пункт volumes в файле docker-compose.yml , чтобы сохранять сертификаты при перезапуске контейнера (предварительно создав файл data/acme.json ):

 volumes: - /etc/localtime:/etc/localtime:ro - /var/run/docker.sock:/var/run/docker.sock:ro - ./data/traefik.yml:/traefik.yml:ro - ./data/acme.json:/acme.json

Docker провайдер

HTTPS настроен, пришло время поднять первый сервис, пусть это будет дашборд самого Traefik, так как Traefik у нас в Docker, воспользуемся этим провайдером.

Для описания конфигурации в Docker Traefik использует метки (labels) контейнеров. Допишем в наш файл docker-compose.yml :

 labels: - "traefik.enable=true" - "traefik.http.routers.traefik.entrypoints=https" - "traefik.http.routers.traefik.rule=Host(`traefik.example.com`)" - "traefik.http.routers.traefik.tls=true" - "traefik.http.routers.traefik.tls.certresolver=letsEncrypt" - "traefik.http.routers.traefik.service=api@internal" - "traefik.http.services.traefik-traefik.loadbalancer.server.port=888"

Разберем построчно:
traefik.enable=true — указываем что Traefik должен обеспечить доступ к этому контейнеру, необходимо для всего остального;
traefik.http.routers.traefik.entrypoints=https — создаем новый роутер с точной входа https ;
traefik.http.routers.traefik.rule=Host( traefik.example.com ) — роутер будет жить по адресу traefik.example.com;
traefik.http.routers.traefik.tls=true — указываем что используется TLS;
traefik.http.routers.traefik.tls.certresolver=letsEncrypt — указываем через какой резолвер получать сертификат;
traefik.http.routers.traefik.service=api@internal — указываем, что сервер за этим роутером — api@internal , это специальный сервис, созданный по умолчанию, это как раз и есть дашбоард который мы хотели увидеть;
traefik.http.services.traefik-traefik.loadbalancer.server.port=888 — издержки интерфейса, без этого не заработает, но можно написать абсолютно любую цифру.

Дашбоард надо включить, для этого добавим в файл traefik.yml :

api: dashboard: true

На данном этапе можно пересоздать контейнер (нужно так как мы меняли docker-compose.yml ):

sudo docker-compose down && sudo docker-compose up -d

Когда всё поднимется можно перейти на traefik.example.com (тут на самом деле должен быть ваш домен, который направлен на хост с Traefik) и увидеть дашборд.

Дашбоард это хорошо, но мы не хотим, чтобы все пользователи интернета имели к нему доступ, закроем его от внешнего мира с помощью BasicAuth, для это в Traefik есть специальный middleware.

Для начала сгенерируем для нас строку с логином и паролем (admin/password)^

$ htpasswd -nb admin password admin:$apr1$vDSqkf.v$GTJOtsd9CBiAFFnHTI2Ds1

Теперь добавим в наш docker-compose.yml новые строчки:

 - "traefik.http.middlewares.traefik-auth.basicauth.users=admin:$$apr1$$vDSqkf.v$$GTJOtsd9CBiAFFnHTI2Ds1" - "traefik.http.routers.traefik.middlewares=traefik-auth"

Заметим, что символы $ из полученной строки мы должны заменить на $$ .
traefik.http.middlewares.traefik-auth.basicauth.users=. — создаем middleware типа basicauth с параметром users ;
traefik.http.routers.traefik.middlewares=traefik-auth — указываем что роутер traefik использует только что-то созданный middleware.

Весь docker-compose.yml

version: '3' services: traefik: image: traefik:v2.2 container_name: traefik restart: unless-stopped security_opt: - no-new-privileges:true ports: - 80:80 - 443:443 volumes: - /etc/localtime:/etc/localtime:ro - /var/run/docker.sock:/var/run/docker.sock:ro - ./data/traefik.yml:/traefik.yml:ro - ./data/acme.json:/acme.json labels: - "traefik.enable=true" - "traefik.http.routers.traefik.entrypoints=https" - "traefik.http.routers.traefik.rule=Host(`traefik.example.com`)" - "traefik.http.routers.traefik.tls=true" - "traefik.http.routers.traefik.tls.certresolver=letsEncrypt" - "traefik.http.routers.traefik.service=api@internal" - "traefik.http.services.traefik-traefik.loadbalancer.server.port=888" - "traefik.http.middlewares.traefik-auth.basicauth.users=admin:$$apr1$$vDSqkf.v$$GTJOtsd9CBiAFFnHTI2Ds1" - "traefik.http.routers.traefik.middlewares=traefik-auth"

Теперь при попытке доступа к дашборду у нас спросят логин и пароль.

Приведем также кофигурацию некого другого сервиса, развернутого через docker-compose (аналогично работает и для обычного docker):

 labels: - "traefik.enable=true" - "traefik.http.routers.test.entrypoints=https" - "traefik.http.routers.test.rule=Host(`test.example.com`)" - "traefik.http.routers.test.tls=true" - "traefik.http.routers.test.tls.certresolver=letsEncrypt" - "traefik.http.services.test-service.loadbalancer.server.port=80"

Здесь одна новая метка traefik.http.services.test-service.loadbalancer.server.port=80 — присваиваем этому контенеру имя сервиса test-service и порт 80, он автоматически присоединится к роутеру test , Traefik автоматически постороит маршрут до этого контенера, даже если он находится на другом хосте.

File провайдер

С контейнерами работает, а как быть если есть какой-то сервис работающий на выделенном хосте (пускай IP 192.168.1.222 и порт 8080) и мы его хотим пропустить через этот же прокси, заодно закрыв его с помощью HTTPS. Для этого есть решение.

Добавим в docker-compose.yml ещё один volume :

 volumes: - /etc/localtime:/etc/localtime:ro - /var/run/docker.sock:/var/run/docker.sock:ro - ./data/traefik.yml:/traefik.yml:ro - ./data/custom/:/custom/:ro - ./data/acme.json:/acme.json

Пускай описания таких хостов у нас будут лежать в data/custom/ (а что, вдруг ещё появятся).

Добавим в traefik.yml конфигурацию file провайдера для этих файлов:

providers: . file: directory: /custom watch: true

Директория следует из нашего docker-compose.yml , а watch: true значит что Traefik будет автоматически обновлять конфигурацию при обнаружении изменений в этих файлах (помните про обновление конфигурации “на лету”, вот работает даже для файлов, а не только для оркестраторов).

Перезапускаем Traefik и теперь можно создать файл с описанием нашего отдельного хоста ( data/custom/host.yml ):

http: routers: host: entryPoints: - https service: service-host rule: Host(`host.example.com`) tls: certResolver: letsEncrypt services: service-host: loadBalancer: servers: - url: http://192.168.1.222:8080/ passHostHeader: true 

Роутер описывался раньше, тут добавилось только service: service-host — связь с нашим сервисом, и конфигурация для TLS.

Описание для сервиса имеет вид:

имя_сервиса: loadBalancer: servers: - хосты для балансировки нагрузки - . 

Дополнительно мы указываем параметр passHostHeader: true чтобы тот хост думал, что он на самом деле смотрит в сеть и прокси нет.

Заключение

Приведем содержание файлов с полученными конфигурациями:

docker-compose.yml

version: '3' services: traefik: image: traefik:v2.2 container_name: traefik restart: unless-stopped security_opt: - no-new-privileges:true ports: - 80:80 - 443:443 volumes: - /etc/localtime:/etc/localtime:ro - /var/run/docker.sock:/var/run/docker.sock:ro - ./data/traefik.yml:/traefik.yml:ro - ./data/custom/:/custom/:ro - ./data/acme.json:/acme.json labels: - "traefik.enable=true" - "traefik.http.routers.traefik.entrypoints=https" - "traefik.http.routers.traefik.rule=Host(`traefik.example.com`)" - "traefik.http.routers.traefik.tls=true" - "traefik.http.routers.traefik.tls.certresolver=letsEncrypt" - "traefik.http.routers.traefik.service=api@internal" - "traefik.http.services.traefik-traefik.loadbalancer.server.port=888" - "traefik.http.middlewares.traefik-auth.basicauth.users=admin:$$apr1$$vDSqkf.v$$GTJOtsd9CBiAFFnHTI2Ds1" - "traefik.http.routers.traefik.middlewares=traefik-auth"

data/traefik.yml

api: dashboard: true entryPoints: http: address: ":80" https: address: ":443" http: routers: http-catchall: rule: hostregexp(``) entrypoints: - http middlewares: - redirect-to-https middlewares: redirect-to-https: redirectScheme: scheme: https permanent: false providers: docker: endpoint: "unix:///var/run/docker.sock" exposedByDefault: false file: directory: /custom watch: true certificatesResolvers: letsEncrypt: acme: email: postmaster@example.com storage: acme.json #caServer: "https://acme-staging-v02.api.letsencrypt.org/directory" httpChallenge: entryPoint: http

data/custom/host.yml

http: routers: host: entryPoints: - https service: service-host rule: Host(`host.example.com`) tls: certResolver: letsEncrypt services: service-host: loadBalancer: servers: - url: http://192.168.1.222:8080/ passHostHeader: true 

В статье было описано как настроить Traefik в качестве обратного HTTP прокси при использовании провайдеров Docker и File. Было настроено использование бесплатных SSL сертификатов от Let’s Encrypt, настроено принудительное перенаправление клиентов на протокол HTTPS, а также приведен пример настройки аутентификации клиентов прокси перед доступом к сервисам.

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

Бонус. Мониторинг

Traefik позволяет собирать сведения о своей работе в различных форматах, рассмотрим, как это делается при использовании Prometheus.

Добавим новую точку входа:
data/traefik.yml :

entryPoints: . metrics: address: ":8082"
 ports: - 80:80 - 443:443 - 8082:8082

И добавим возможность собирать с этого порта метрики для Prometheus, data/traefik.yml :

metrics: prometheus: entryPoint: metrics

Осталось только настроить Prometheus на сбор метрик с traefik_ip:8082 .

Приведем содержание файлов с полученными конфигурациями:

docker-compose.yml

version: '3' services: traefik: image: traefik:v2.2 container_name: traefik restart: unless-stopped security_opt: - no-new-privileges:true ports: - 80:80 - 443:443 - 8082:8082 volumes: - /etc/localtime:/etc/localtime:ro - /var/run/docker.sock:/var/run/docker.sock:ro - ./data/traefik.yml:/traefik.yml:ro - ./data/custom/:/custom/:ro - ./data/acme.json:/acme.json labels: - "traefik.enable=true" - "traefik.http.routers.traefik.entrypoints=https" - "traefik.http.routers.traefik.rule=Host(`traefik.example.com`)" - "traefik.http.routers.traefik.tls=true" - "traefik.http.routers.traefik.tls.certresolver=letsEncrypt" - "traefik.http.routers.traefik.service=api@internal" - "traefik.http.services.traefik-traefik.loadbalancer.server.port=888" - "traefik.http.middlewares.traefik-auth.basicauth.users=admin:$$apr1$$vDSqkf.v$$GTJOtsd9CBiAFFnHTI2Ds1" - "traefik.http.routers.traefik.middlewares=traefik-auth"

data/traefik.yml

api: dashboard: true entryPoints: http: address: ":80" https: address: ":443" metrics: address: ":8082" metrics: prometheus: entryPoint: metrics http: routers: http-catchall: rule: hostregexp(``) entrypoints: - http middlewares: - redirect-to-https middlewares: redirect-to-https: redirectScheme: scheme: https permanent: false providers: docker: endpoint: "unix:///var/run/docker.sock" exposedByDefault: false file: directory: /custom watch: true certificatesResolvers: letsEncrypt: acme: email: postmaster@example.com storage: acme.json #caServer: "https://acme-staging-v02.api.letsencrypt.org/directory" httpChallenge: entryPoint: http

Настройка на локальном компьютере

По очереди запускаем каждый из проектов yarn start и вносим маленькие изменения, чтобы видеть отличия проектов.

Например в src -> App.js каждого проекта вместо Edit src/App.js and save to reload. напишем proj1 и proj2 соответственно.

Этап 2. Упаковываем каждый проект в контейнер

В результате каждый проект будет в отдельном контейнере. Все действия далее описываю только для одного проекта. Их нужно сделать для двух проектов.

Пишем инструкции для запуска контейнера

В корне проекта создаем .env

PROJ_NAME=proj1 DOMAIN=proj1.com 

.env — файл в котором задекларированы глобальные переменные рабочей среды (environment). В данном случае эти переменные важны для запуска контейнера. PROJ_NAME — уникальное название проекта, которое будет использоваться в нескольких местах в docker-compose . Название переменной может быть любым. DOMAIN — доменное имя по которому будет доступен проект

Далее в корне проекта создаем dockerfile

dockerfile — это инструкция по созданию контейнера

FROM mhart/alpine-node:latest # Качаем node контейнер с docker hub RUN yarn global add serve # Устанавливаем serve для сервирования нашего проекта WORKDIR /app # Указываем главную папку в контейнере, в которой будет наш проект COPY package.json yarn.lock ./ # Копируем файлы package.json и yarn.lock в папку, указанную выше (не забываем ./) RUN yarn # Устанавливаем зависимости COPY . . # Копируем остальные файлы проекта в главную папку RUN yarn build # Компилируем проект CMD serve -s /app/build # Серверуем проект из новой папки build (по умолчанию порт 5000)

Далее в корне проекта создаем docker-compose.yml

docker-compose — это инструкция по запуску контейнер(а/ов) Для знающих структуру docker-compose , я не указываю version: ‘3.x’ , так как это не является обязательным с версии 1.27.0

services: # Объявляем список сервисов proj1: # Указываем название сервиса. Так как это ключ, взять его из .env не получится. container_name: $ # Указываем название контейнера (не обязательно). PROJ_NAME берется из .env build: . # Запускаем dockerfile из корня проекта restart: always # Автоматически перезагружать контейнер, если он случайно выключился environment: NODE_ENV: production # ENV переменная production сообщит yarn, что не нужно качать зависимости для разработки, если такие имеются. ports: # Слева порт, который будет доступен на локальной машине. Справа - тот, к которому нужен доступ в контейнере. - 3000:5000 # 5000 - порт который по умолчанию открывает serve в dockerfile

В корне проекта создаем .dockerignore

.dockerignore — это список исключаемых из копирования COPY . . файлов и папок в dockerfile

.git node_modules build 

Запускаем и проверяем контейнер

Из корня проекта выполняем docker-compose up

При первом запуске будут выполняться все инструкции dockerfile , что займет какое-то время. Дальнейшие запуски, при отсутствии изменений в проекте, будут проходить быстрее.

Когда видим строку вида proj1 | INFO: Accepting connections at http://localhost:5000 , можем проверить в браузере localhost:3000 . Результатом должен быть react проект с надписью proj1 .

В командной строке нажимаем ctrl+c , чтобы выйти из контейнера и проверяем проект proj2

Этап 3. Создаем 2 фейковых url для теста

В результате будет 2 адреса, каждый из которых будет вести на localhost . Нужно для дальнейшей настройки traefik .

В hosts файле на компьютере добавляем запись

127.0.0.1 proj1.com 127.0.0.1 proj2.com 

Этап 4. Создаем traefik контейнер

В результате будет traefik контейнер, который будет работать на 80 порту

Создаем docker-compose для traefik

В удобном месте создаем папку traefik .

Затем в ней создаем файл docker-compose.yml

services: traefik: # Указываем название сервиса container_name: traefik # Указываем название контейнера (не обязательно) image: traefik:latest # Вместо dockerfile берем готовый traefik (:latest - последняя версия) с docker hub restart: always ports: - "80:80" # Порт 80 будет доступен как в контейнере, так и снаружи. volumes: - /var/run/docker.sock:/var/run/docker.sock # Позволяет следить traefik'у отслеживать все изменения в docker. # Нужно для отслеживания подключения/отключения других контейнеров. - ./:/etc/traefik # Переносит файл traefik.yml с основными настройками в контейнер.

volumes — позволяет использовать файлы из локального компьютера не копируя их в контейнер. В данном случае мы берем файл traefik.yml , который мы создадим дальше, в этой-же папке и «говорим» контейнеру, что это файл, который в контейнере будет находится здесь: /etc/traefik

Создаем traefik.yml

traefik.yml — это статическая конфигурация traefik. Позже мы добавим и динамическую конфигурацию.

В папке traefik создаем файл traefik.yml

# log: # level: DEBUG # Раскомментировать только в случае необходимости выведения в консоль всей информации о работе traefik providers: docker: true # Говорит traefik'у, что мы работаем с docker entrypoints: web: # Название точки входа, к которой будут подключатся другие контейнеры. # может быть любым. Главное, чтобы совпадал с названием в других местах. address: :80 # traefik будет доступен на 80 порту.

Запускаем traefik контейнер

В папке traefik выполняем docker-compose up . В консоле должно появится сообщение Configuration loaded from file: /etc/traefik/traefik.yml , что означает, что файл traefik.yml успешно импортирован в контейнер, и настройки traefik считываются с него. В браузере при переходе на localhost , мы должны видеть 404 page not found , что означает, что traefik работает, но страницы с таким адресом не найдено.

Останавливаем контейнер и переходим далее

Этап 5. Связываем контейнеры между собой

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

Создаем общую сеть, к которой будут подключатся все контейнеры

В терминале пишем: docker network create traefik

Где traefik , название сети (может быть любым. Главное, чтобы совпадало с настройками далее)

Меняем docker-compose файл в traefik проекте

В папке traefik в файле docker-compose под блоком services пишем новый блок networks

networks: # Блок для объявления внутренних docker сетей, к которым нужно будет подключить контейнер. default: external: name: traefik # Название созданной выше сети
services: traefik: container_name: traefik image: traefik:latest restart: always ports: - "80:80" volumes: - /var/run/docker.sock:/var/run/docker.sock - ./:/etc/traefik networks: default: external: name: traefik

Меняем docker-compose файл в react проекте

Действия описанные далее для proj1 нужно выполнять для каждого проекта.

В папке proj1 в файле docker-compose меняем

ports: - 3000:5000
ports: [5000]

Контейнер по прежнему слушает на порту 5000 , но уже не открывает его для локального компьютера. Этот порт теперь доступен только контейнерам в той-же сети.

Под блоком services пишем

networks: default: external: name: traefik

В блоке services в подблок proj1 добавляем

labels: - "traefik.http.routers.$.rule=Host(`$`)" - "traefik.http.routers.$.entrypoints=web"

PROJ_NAME и DOMAIN берутся из .env

Краткое объяснение:

Мы хотим передать некоторые настройки из этого контейнера в traefik . Так как traefik умеет читать yaml формат, то в идеале было бы хорошо передать их именно в этом формате. Но у нас нет такой возможности. Обходным путем является записи в labels . Важно помнить, что мы оставляем структуру yaml . Вот пример того, как это бы выглядело в yaml формате:

traefik: http: routers: proj1: # Это наше уникальное название, но оно должно быть одинаковым для для настроек этого контейнера. # Именно по этому в "labels", proj1 используется в нескольких местах. Берем из ".env". # Для proj2 это название может быть proj2 rule: Host(`proj1.com`) # proj1.com это наш домен. Берем из ".env". entrypoints: web # это то название, которое записано в traefik.yml

Так как ранее, в настройках traefik мы указали

volumes: - /var/run/docker.sock:/var/run/docker.sock

то при подключении нового контейнера, traefik сможет считывать его labels , и сможет интерпретировать их как yaml с настройками.

services: proj1: container_name: $ build: . restart: always environment: NODE_ENV: production ports: [5000] labels: - "traefik.http.routers.$.rule=Host(`$`)" - "traefik.http.routers.$.entrypoints=web" networks: default: external: name: traefik

В Host() нужно записывать домен именно в косых кавычках «

Запускаем все контейнеры traefik , proj1 и proj2 . В результате, вводя в браузере proj1.com и proj2.com мы попадаем на наши 2 проекта.

Этап 6. Добавляем поддержку ssl

В результате проекты будут доступны по ссылкам https://proj.

Если сейчас мы попытаемся посетить https:// proj1.com , то проект будет не доступен. Для того, чтобы это изменить, необходимо произвести некоторые изменения в настройках как traefik так и проектах.

В папке traefik в файле docker-compose в блок ports добавляем

- "443:443"

тем самым открывая доступ по https снаружи.

В файле traefik.yml в блок entrypoints добавляем

websecure: # Также как и название "web" может быть любым. Главное, чтобы именно оно использовалось далее. address: :443

Под блоком entrypoints добавляем

certificatesresolvers: myresolver: # Название может быть любым. К этому блоку будут обращатся контейнеры для получения сертификатов acme: # Здесь будут находится настройки для получения сертификатов. storage: /etc/traefik/acme.json # Включаем сохранение информации о полученых сертификатах в папку "traefik", # чтобы не генерировать их каждый раз при запуске контейнера. # Если сертификат ранее был создан и является актуальным, # то traefik возьмет его из файла "acme.json"
# log: # level: DEBUG providers: docker: true entrypoints: web: address: :80 websecure: address: :443 certificatesresolvers: myresolver: acme: storage: /etc/traefik/acme.json

В папке proj1 в файле docker-compose в блоке labels заменяем entrypoints

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

на выше созданный websecure

- "traefik.http.routers.$.entrypoints=websecure"
- "traefik.http.routers.$.tls.certresolver=myresolver" # Где "myresolver", это название резолвера созданного выше

Запускаем все контейнеры traefik , proj1 и proj2 . В результате, вводя в браузере https:// proj1.com и https:// proj2.com мы попадаем на наши 2 проекта.

Так как сертификаты не прошли необходимых проверок let’s encrypt , необходимо в браузере дать разрешение на использование сайта с этими сертификатами. Можно также в терминале ввести curl -k https://proj1.com . -k поможет вывести html без проверки сертификата.

Этап 7. Переносим контейнеры на сервер, если нужно

Возможно вы хотите работать не только с доменами ведущими на сервер, но и поддоменами, которые ведут на 127.0.0.1 (localhost). Например, набирая proj1.com вы попадаете на сервер, а набирая dev.proj1.com , попадаете на localhost для локальной разработки. В таком случае, необходимо, чтобы:

  • Запись A для dev.proj1.com и dev.proj2.com , указывала на 127.0.0.1 .
  • Запись A для www.dev.proj1.com и www.dev.proj2.com , указывала на 127.0.0.1 .

Соответственно, на локальном компьютере в проектах в .env указываются домены ведущие на localhost, а на сервере указываются домены ведущие на сервер.

Если есть домены ведущие на localhost , можно продолжать работать на локальном компьютере. Если нет, переносим проекты на сервер.
  1. Переносим наши проекты на сервер ( traefik proj1 и proj2 )
  2. Заменяем названия проектов
    • Название подблока services в docker-compose.yml
    • PROJ_NAME в .env
  3. Заменяем домены proj1.com в проектах на необходимые
    • DOMAIN в .env

Для удобства я продолжу использовать домены proj1 и proj2 .

Этап 8. Настройка резолвера (ACME-клиента)

В данный момент мы у нас еще нет настоящих сертификатов. Для их получения необходимо настроить acme клиент.

На странице https://doc.traefik.io/traefik/https/acme/ ищем конфигурацию для своего DNS провайдера. Далее я привожу пример того, как это сделать с cloudflare.

На сайте указано, что для подключения к cloudflare понадобится CF_DNS_API_TOKEN . Получив токен нужно сделать его доступным в environment при запуске docker.

В папке traefik создаем файл .env и пишем туда токен

CF_DNS_API_TOKEN=токен 

В файле docker-compose.yml в подблоке traefik добавляем

environment: CF_DNS_API_TOKEN: $

Токен передастся из .env в traefik

В файле traefik.yml меняем блок acme

certificatesresolvers: myresolver: acme: # caserver: "https://acme-staging-v02.api.letsencrypt.org/directory" dnsChallenge: provider: cloudflare email: ваш_email storage: /etc/traefik/acme.json

caserver можно раскомментировать для того, чтобы проверить правильность настройки, но не получать реальный сертификат, так как кол-во сертификатов ограничено Чтобы увидеть результат работы acme клиента нужно также раскомментировать log: level: DEBUG перед запуском контейнера.

Если вы, при запуске контейнера, где-то в консоли видите эти строки, значит все в порядке. Можно закомментировать caserver и повторить процедуру для получения сертификатов.

[INFO] [доменные_имена] acme: Validations succeeded; requesting certificates" [INFO] [доменные_имена] Server responded with a certificate." 

Этап 9. Убираем www и заменяем http:// на https://

В папке proj1 в файле docker-compose добавляем labels

- "traefik.http.routers.$.tls.domains[0].main=$" # Указывает на то, что основной домен это $ - "traefik.http.routers.$.tls.domains[0].sans=www.$" # Указывает на то, что дополнительный домен это www.$

Теперь проект будет доступен по ссылкам как с «www» так и без.

В папке traefik в файле traefik.yml меняем entrypoints

entrypoints: web: address: :80 http: redirections: entrypoint: to: websecure scheme: https websecure: address: :443

Теперь все http:// запросы будут превращаться в https://

В папке traefik создаем файл dynamic_conf.yml

http: middlewares: www-remover: redirectregex: regex: ^https://www\.(.*) replacement: https://$1 routers: www-router: rule: HostRegexp(``) tls: true service: noop@internal middlewares: www-remover tls: options: default: sniStrict: true

В middlewares описано то, что делать с ссылками, которые поступают из роутеров. Если у ссылки есть «www.», вызывается www-remover middleware.

sniStrict: true использовать, если необходимо заблокировать раздачу страниц с несуществующих поддоменов. Например correct_page.proj1.com будет доступен, а wrong_page.proj1.com — нет.

В traefik.yml добавляем дополнительный провайдер, для применения динамической конфигурации из файла

providers: docker: true file: filename: /etc/traefik/dynamic_conf.yml # указываем на выше созданный файл

Теперь traefik обрабатывает не только labels из docker, но и подгруженный файл.

Готово

Дополнительно

Отдельный docker контейнер доступный по url /example_container

Если proj1.com уже получил сертификат и необходимо добавить отдельный сервис на proj1.com/example_container, то структура docker-compose будет следующая

название_сервиса: container_name: название_контейнера прочие_настройки: . labels: - "traefik.http.routers.$.rule=Host(`$`) && PathPrefix(`/example_container`)" - "traefik.http.routers.$.tls.certresolver=myresolver"

Ссылки

Пример настройки react контейнера

dedops commented Jul 26, 2022

Добрый день! У меня аналогичная схема, но есть нюанс. Если идти напрямую на порт 5000(react), он корректно работает по http, общается с бэком также по http.
Мне нужно сделать,чтобы пользователь в браузере работал по https. Это примерно то, что описывается в статье. Ставим перед реактом Traefik, он принимает запрос по https, а дальше обращается к react. Но судя по всему, он не идет по http, а продолжает идти по https, после чего не отвечает бэк, т.к. он не умеет в https.

Хотелось бы следующую схему Client(https) — [Traefik (https) Traefik(http)] — React(http) ——-
Возможно ли это? И как реализовать, если да?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Footer

© 2023 GitHub, Inc.

You can’t perform that action at this time.

Настройка обратного прокси Traefik v2 для контейнеров Docker в Ubuntu

Docker – довольно эффективный метод запуска приложений в среде производства. Но в некоторых ситуациях нужно запустить несколько приложений на одном хосте Docker. Возможно ли это и как это сделать? Да, в такой ситуации можно настроить обратный прокси-сервер, который будет поддерживать открытыми только порты 80 и 443.

Traefik – это обратный прокси с поддержкой Docker и встроенным дашбордом для мониторинга. Уже довольно давно широко используется Traefik v1. Но в этом мануале мы установим и настроим Traefik v2, который довольно сильно отличается от v1.

Одно из основных отличий Traefik v2 от v1 в том, что в нем нет фронтендов и бэкендов – их функциональность распределена между маршрутизаторами, промежуточном программным обеспечением и сервисами. Ранее бэкенд выполнял работу по внесению изменений в запросы и передаче этого запроса той стороне, что должна была его обрабатывать. За счет внедрения промежуточного программного обеспечения, которое может изменять запросы перед их отправкой сервису, Traefik v2 более подробно распределяет задачи. Промежуточное ПО позволяет записывать внесение изменений пошагово, благодаря чему их могут использовать разные маршрутизаторы; также их можно использовать повторно. Маршрутизаторы также могут использовать множество различных промежуточных программ.

Данный мануал поможет настроить Traefik для маршрутизации запросов к двум разным контейнерам приложений: WordPress и Adminer. Каждый контейнер подключен к БД MySQL. Сервер Traefik будет обслуживать весь трафик по HTTPS с помощью сертификата Let’s Encrypt.

Требования

  • Сервер Ubuntu 20.04, настроенный с помощью этого мануала.
  • Запущенный хост Docker. В этом вам поможет мануал Установка и использование Docker в Ubuntu 20.04.
  • Docker Compose (установить приложение можно с помощью этого мануала).
  • Домен и три записи А: db-admin.your_domain, blog.your_domain and monitor.your_domain. Каждая запись должна указывать на IP-адрес хоста Docker. В мануале используется условное обозначение your_domain, которое вы должны заменить вашим доменом.

1: Настройка и запуск Traefik

Проект Traefik предоставляет официальный образ Docker, который поможет быстро запустить Traefik в контейнере Docker.

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

Используйте утилиту htpasswd, чтобы создать зашифрованный пароль. Установите утилиту. Она входит в пакет apache2-utils.

sudo apt-get install apache2-utils

Затем с помощью htpasswd создайте пароль. Замените secure_password в команде паролем, который вы хотите использовать для администратора Traefik:

htpasswd -nb admin secure_password

Вывод утилиты будет иметь такой вид:

Он используется в конфигурационном файле Traefic для настройки базовой аутентификации HTTP в панели мониторинга Traefik. Скопируйте всю строку, чтобы затем вставить ее в файл.

Чтобы настроить сервер Traefik, создайте файлы traefik.toml и traefik_dynamic.toml в формате TOML. TOML – это стандартизированный язык конфигурации (аналогичный INI-файлам). Эти конфигурационные файлы позволяют настроить сервер Traefik и разные дополнительные интеграции, или провайдеры. В данном мануале мы используем три доступных провайдера Traefik: api, docker и acme, который нужен для поддержки TLS сертификатов Let’s Encrypt.

Откройте файл traefik.toml:

В раздел entryPoints добавьте две именованные точки входа, чтобы открыть доступ к портам http 80 и https 443, доступ к которым по умолчанию будет у всех бэкендов. Назовем эти точки web и websecure соответственно.

[entryPoints] [entryPoints.web] address = «:80»
[entryPoints.web.http.redirections.entryPoint] to = «websecure»
scheme = «https»
[entryPoints.websecure] address = «:443»

Обратите внимание: вы также автоматически перенаправляете трафик на TLS.

Затем настройте провайдер api, который дает доступ к интерфейсу дашборда мониторинга. Вам нужно только добавить заголовок [api], после этого дашборд включится по умолчанию.

.
[api] dashboard = true

Для полной защиты веб-запросов мы будем использовать Let’s Encrypt, чтобы создать валидные сертификаты TLS. Traefik v2 поддерживает Let’s Encrypt «из коробки», и вы можете настроить его, создав резольвер сертификатов типа acme.

Давайте настроим его сейчас:

.
[certificatesResolvers.lets-encrypt.acme] email = «your_email@your_domain»
storage = «acme.json»
[certificatesResolvers.lets-encrypt.acme.tlsChallenge]

Этот раздел называется acme. ACME – это протокол, с помощью которого сервер взаимодействует с Let’s Encrypt для управления сертификатами. Чтобы Traefik создавал сертификаты для хостов, укажите в строке email свой адрес электронной почты. Затем нужно указать, что хранить информацию Let’s Encrypt нужно в файле JSON, acme.json.

Раздел acme.tlsChallenge позволяет определить, как Let’s Encrypt может подтверждать сертификаты. Мы настроим его для обслуживания фала как части задачи для порта 443.

Теперь нужно настроить Traefik на взаимодействие с Docker.

Добавьте следующие конфигурации в файл:

.
[providers.docker] watch = true
network = «web»

Провайдер docker включает Traefik в качестве прокси-сервера для контейнеров Docker. Он будет следить (на что указывает строка watch) за новыми контейнерами в сети web, которую мы скоро создадим.

В окончательной конфигурации мы также используем провайдер file. В Traefik v2 нельзя смешивать и связывать статические и динамические конфигурации. Потому в файле traefik.toml мы определяем только статические конфигурации, а динамические конфигурации будут храниться в другом файле, traefik_dynamic.toml. Здесь мы используем провайдер file, чтобы сообщить Traefik, что он должен читать динамические конфигурации из другого файла.

Добавьте следующий раздел:

[providers.file] filename = «traefik_dynamic.toml»

На этом этапе файл traefik.toml должен выглядеть так:

[entryPoints] [entryPoints.web] address = «:80»
[entryPoints.web.http.redirections.entryPoint] to = «websecure»
scheme = «https»
[entryPoints.websecure] address = «:443»
[api] dashboard = true
[certificatesResolvers.lets-encrypt.acme] email = «your_email@your_domain»
storage = «acme.json»
[certificatesResolvers.lets-encrypt.acme.tlsChallenge] [providers.docker] watch = true
network = «web»
[providers.file] filename = «traefik_dynamic.toml»

Сохраните и закройте файл.

Теперь создайте traefik_dynamic.toml.

Значения динамической конфигурации, которые нужно сохранить в отдельном файле, будут определять поведение промежуточного ПО и маршрутизаторов. Чтобы защитить свой дашборд паролем, необходимо настроить маршрутизатор API и промежуточное ПО для обработки базовой аутентификации HTTP. Начнем с установки промежуточного программного обеспечения.

Промежуточное ПО настраивается отдельно для каждого протокола: если, например, вы работаете с HTTP, вы указываете его как раздел, привязанный к http.middlewares. Затем идет имя промежуточного программного обеспечения (чтобы позже на него было удобно ссылаться), а затем – его тип (в данном случае это будет basicAuth). Назовем это промежуточное ПО simpleAuth.

Откройте новый файл traefik_dynamic.toml:

Поместите в него такой код:

[http.middlewares.simpleAuth.basicAuth] users = [
«admin:$apr1$ruca84Hq$mbjdMZBAG.KWn7vfN/SNK/»
]

Чтобы настроить маршрутизатор для api, вам снова нужно привязать имя протокола, но вместо http.middlewares вы будете использовать http.routers, за которым следует имя маршрутизатора. В этом случае api предоставляет собственный именованный маршрутизатор, который можно настроить с помощью раздела [http.routers.api]. мы также настроим домен, который будем использовать с дашбордом. Для этого нужно установить ключ rule и задать хост, точку входа для использования сети websecure и промежуточное ПО для simpleAuth.

Добавьте следующие конфигурации в файл:

.
[http.routers.api] rule = «Host(`monitor.your_domain`)»
entrypoints = [«websecure»] middlewares = [«simpleAuth»] service = «api@internal»
[http.routers.api.tls] certResolver = «lets-encrypt»

Точка входа сети web обрабатывает порт 80, а точка входа в сеть websecure использует порт 443 для поддержки TLS/SSL. Весь трафик, поступающий на порт 80, автоматически перенаправляется на точку входа в websecure, чтобы обеспечить безопасные соединения для всех запросов.

Обратите внимание, последние три строки здесь настраивают сервис, включают tls и устанавливают в certResolver to “lets-encrypt”. Сервисы позволяют определить, где запрос будет полностью обработан. Сервис api@internal – это встроенный сервис, который работает за настроенным вами API. Как и маршрутизаторы и промежуточное программное обеспечение, сервисы можно настраивать в текущем файле, но для достижения желаемого результата в нашем случае этого делать не нужно.

Готовый файл traefik_dynamic.toml будет выглядеть так:

[http.middlewares.simpleAuth.basicAuth] users = [
«admin:$apr1$ruca84Hq$mbjdMZBAG.KWn7vfN/SNK/»
] [http.routers.api] rule = «Host(`monitor.your_domain`)»
entrypoints = [«websecure»] middlewares = [«simpleAuth»] service = «api@internal»
[http.routers.api.tls] certResolver = «lets-encrypt»

Сохраните и закройте файл.

Теперь можно запустить Traefik.

2: Запуск контейнера Traefik

Сейчас мы создадим сеть Docker для поддержки взаимодействия прокси-сервера и контейнеров. Такая сеть необходима, потому что ее можно использовать для работы с приложениями, запущенными в Docker Compose. Мы назовем такую сеть web.

docker network create web

Когда контейнер Traefik будет запущен, нужно будет добавить его в эту сеть. Позже можно добавить в нее дополнительные контейнеры, чтобы использовать Traefik в качестве прокси-сервера для этих контейнеров.

Затем создайте пустой файл, в котором будут храниться данные о шифровании Let’s Encrypt. Поместите его в контейнер, чтобы Traefik мог его использовать.

Затем заблокируйте доступ к файлу acme.json, чтобы только пользователь root мог читать и изменять его. Если вы этого не сделаете, Traefik не сможет использовать этот файл.

chmod 600 acme.json

Когда файл будет передан в Docker, внутри контейнера ему автоматически присвоят владельца root.

Теперь создайте контейнер Traefik с помощью команды:

docker run -d \
-v /var/run/docker.sock:/var/run/docker.sock \
-v $PWD/traefik.toml:/traefik.toml \
-v $PWD/traefik_dynamic.toml:/traefik_dynamic.toml \
-v $PWD/acme.json:/acme.json \
-p 80:80 \
-p 443:443 \
—network web \
—name traefik \
traefik:v2.2

Давайте разделим команду на части и рассмотрим ее подробно:

  • Флаг –d запускает контейнер как демон, в фоновом режиме. Затем команда добавляет в контейнер файл docker.sock, благодаря чему процесс Traefik может прослушивать изменения в контейнерах. Также в контейнер можно поместить файлы traefik.toml, traefik_dynamic.toml и acme.json.
  • После этого порты 80 и 443 хоста Docker подключаются к таким же портам в контейнере Traefik. Теперь Traefik получает все запросы HTTP и HTTPS.
  • В качестве сети контейнера нужно указать web. Контейнер называется traefik.
  • Для создания контейнера используется образ traefik:v2.2.

ENTRYPOINT образа Docker – это команда, которая запускается всегда, когда контейнер создается из образа. В этом случае она представляет собой бинарный файл traefik внутри контейнера. Дополнительные аргументы этой команде можно передать при запуске контейнера, но все необходимые данные мы указали ранее в файле traefik.toml.

После запуска контейнера у вас будет дашборд, с помощью которого можно просмотреть состояние контейнеров. Вы также можете использовать эту панель для визуализации интерфейсов и бэкэндов, которые зарегистрировал Traefik. Откройте панель мониторинга в браузере:

Вам будет предложено ввести имя и пароль администратора, и пароль (имя – admin, пароль – строка, которую вы создали в разделе 1).

После аутентификации на экране появится дашборд.

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

Теперь у вас есть прокси-сервер Traefik, настроенный для взаимодействия с Docker и готовый управлять другими контейнерами Docker.

3: Добавление контейнеров для Traefik

Контейнер Traefik запущен и готов обслуживать другие контейнеры приложений. Добавьте следующие контейнеры:

  1. Блог, запущенный с помощью официального образа WordPress.
  2. Сервер управления базами данных на основе официального образа Adminer.

Управлять этими приложениями можно с помощью Docker Compose, используя файл docker-compose.yml:

Укажите в файле версии и сеть:

version: «3»
networks:
web:
external: true
internal:
external: false

Версия 3 Docker Compose – это новейшая версия формата файла Compose.

Чтобы сервер Traefik мог опознать приложения, они должны входить в одну сеть. Поскольку используемая сеть создана вручную, нужно указать ее имя (web) и присвоить строке external значение true. Затем нужно определить другую сеть, чтобы связать эти контейнеры с контейнером базы данных, который не будет обслуживаться через контейнер Traefik. Эта внутренняя сеть будет называться internal.

Затем нужно определить все сервисы по очереди. Давайте начнем с контейнера blog, который основан на официальном образе WordPress. Добавьте в файл такую конфигурацию:

.
services:
blog:
image: wordpress:4.9.8-apache
environment:
WORDPRESS_DB_PASSWORD:
labels:
— traefik.http.routers.blog.rule=Host(`blog.your_domain`)
— traefik.http.routers.blog.tls=true
— traefik.http.routers.blog.tls.certresolver=lets-encrypt
— traefik.port=80
networks:
— internal
— web
depends_on:
— mysql

Параметр environment позволяет указать переменные среды, которые будут установлены внутри контейнера. Если значение WORDPRESS_DB_PASSWORD не установлено, Docker Compose будет извлекать его из оболочки и передавать при создании контейнера. Определить эту переменную среды можно в оболочке перед запуском контейнеров – при этом пароли не попадут в конфигурационный файл.

В разделе labels указываются значения для Traefik. Метки Docker сами по себе ничего не делают, но Traefik читает их и понимает, как обращаться с контейнерами. Каждая метка выполняет такие действия:

  • traefik.http.routers.adminer.rule=Host(`blog.your_domain`) создает новый маршрутизатор для контейнера и затем определяет правило маршрутизации, которое проверяет, адресован ли запрос данному контейнеру.
  • traefik.routers.custom_name.tls=true указывает, что данный маршрутизатор должен использовать TLS.
  • traefik.routers.custom_name.tls.certResolver=lets-encrypt указывает, что для этого маршрутизатора следует использовать созданный вами ранее резольвер сертификатов, let’s-encrypt.
  • traefik.port задает открытый порт, который Traefik должен использовать для маршрутизации трафика в этот контейнер.

При такой конфигурации весь трафик, отправленный на порт 80 или 443 хоста Docker, будет перенаправлен в контейнер blog.

Этот контейнер принадлежит двум различным сетям, чтобы Traefik мог найти его через сеть web и связываться с контейнером базы данных через сеть internal.

Параметр depends_on помогает Docker Compose понять, что этот контейнер нужно запускать после запуска его зависимостей. Поскольку для работы WordPress необходима рабочая БД, контейнер mysql запускается раньше, чем контейнер blog.

Теперь настройте сервис MySQL:

services:
.
mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD:
networks:
— internal
labels:
— traefik.enable=false

Для создания этого контейнера используется официальный образ MySQL 5.7. Обратите внимание: параметру environment снова не присвоено никакого значения. Для переменных MYSQL_ROOT_PASSWORD и WORDPRESS_DB_PASSWORD необходимо установить одинаковое значение, чтобы контейнер WordPress мог связываться с MySQL. Контейнер mysql не должен взаимодействовать с Traefik или с внешним миром, потому он принадлежит только сети internal. Поскольку Traefik имеет доступ к сокету Docker, процесс по-прежнему будет открывать маршрутизатор контейнера mysql по умолчанию. Поэтому нужно добавить метку traefik.enable=false, чтобы Traefik не смог сделать этот контейнер доступным.

Теперь нужно добавить параметры контейнера Adminer:

services:
.
adminer:
image: adminer:4.6.3-standalone
labels:
— traefik.http.routers.adminer.rule=Host(`db-admin.your_domain`)
— traefik.http.routers.adminer.tls=true
— traefik.http.routers.adminer.tls.certresolver=lets-encrypt
— traefik.port=8080
networks:
— internal
— web
depends_on:
— mysql

Этот контейнер основан на официальном образе Adminer. Параметры network и depends_on совпадают с этими же параметрами контейнера blog.

Строка traefik.http.routers.adminer.rule=Host(`db-admin.your_domain`) позволяет Traefik проверить запрошенный хост. Если он соответствует шаблону db-admin.your_domain, Traefik направит трафик в контейнер adminer через порт 8080.

В результате файл docker-compose.yml будет выглядеть так:

version: «3»
networks:
web:
external: true
internal:
external: false
services:
blog:
image: wordpress:4.9.8-apache
environment:
WORDPRESS_DB_PASSWORD:
labels:
— traefik.http.routers.blog.rule=Host(`blog.your_domain`)
— traefik.http.routers.blog.tls=true
— traefik.http.routers.blog.tls.certresolver=lets-encrypt
— traefik.port=80
networks:
— internal
— web
depends_on:
— mysql
mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD:
networks:
— internal
labels:
— traefik.enable=false
adminer:
image: adminer:4.6.3-standalone
labels:
labels:
— traefik.http.routers.adminer.rule=Host(`db-admin.your_domain`)
— traefik.http.routers.adminer.tls=true
— traefik.http.routers.adminer.tls.certresolver=lets-encrypt
— traefik.port=8080
networks:
— internal
— web
depends_on:
— mysql

Сохраните и закройте файл.

Прежде чем запустить контейнеры, нужно задать значения переменных WORDPRESS_DB_PASSWORD и MYSQL_ROOT_PASSWORD.

export WORDPRESS_DB_PASSWORD=secure_database_password
export MYSQL_ROOT_PASSWORD=secure_database_password

Вместо secure_database_password укажите надежный пароль БД. В WORDPRESS_DB_PASSWORD и MYSQL_ROOT_PASSWORD нужно использовать один и тот же пароль.

Теперь запустите контейнеры:

docker-compose up -d

Снова откройте дашборд Traefik. Вы увидите, что в нем появились разделы backend и frontend, а в них – новые серверы.

Если вы изучите раздел Routers, вы найдете маршрутизаторы adminer и blog, настроенные с поддержкой TLS.

Откройте в браузере blog.your_domain (где your_domain – ваш домен). Соединение будет перенаправлено на зашифрованный порт 443. Теперь можно закончить установку WordPress.

После этого откройте в браузере db-admin.your_domain. Контейнер mysql не доступен в интернете, но контейнер adminer имеет доступ к нему через внутреннюю сеть (internal).

На экране появится форма входа Adminer. Укажите mysql в поле Server, используйте имя пользователя root, в поле пароля введите значение переменной MYSQL_ROOT_PASSWORD. Поле Database оставьте пустым. Нажмите Login.

После этого вы получите доступ к интерфейсу Adminer.

Оба сайта теперь работают. Чтобы получить доступ к дашборду, введите monitor.your_domain.

Заключение

Теперь сервер Traefik v2 проксирует запросы в контейнеры приложений Docker.

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

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

Docker Compose + Traefik: L7-маршрутизация для множества проектов

Цель данной статьи: демонстрация запуска нескольких инстансов одного проекта в docker compose, доступ к которым будет осуществляться через Traefik (прокси). Приведу пример, зачем всё это нужно и почему тут Traefik, а не Nginx.

Есть задача запускать множество окружений-песочниц, на которых QA-инженеры будут тестировать код. Песочницы должны динамически создаваться и удаляться при отрабатывании пайплайна в CI-системе (например Gitlab). Стэк – Nginx + httpd. Как правильно организовать данную схему?

Разумеется, приложение распилено на микросервисы, и можно написать плейбук или скрипты для запуска приложения в docker-compose и с нужным количеством реплик. Но как проксировать трафик извне до поднятых приложений?

Основная проблема заключается в следующем:

  • допустим, первый инстанс запущен, Nginx в контейнере слушает на порту 80 и данный порт проброшен на хост. Чтобы запустить второй инстанс, нужно мапить новый порт, например, 81. И так далее. Это очень неудобно при динамическом запуске и удалении окружений, т.к. нужно как-то вести учёт портов, что вообще звучит безумно;
  • можно не мапить порты на хост, каждый проект будет иметь запущенный Nginx на 80 порту, но в рамках своей сети, но тогда к приложению не будет доступа извне;

Как будет реализовано: каждый проект будет запускаться в своём отдельном сетевом namespace, т.е. через docker compose up -d (будет создана своя отдельная сеть под проект). Nginx в каждом проекте будет запущен на 80 порту, никаких конфликтов по портам на хосте не возникает. Остается открытым вопрос доступа к приложению. Здесь-то в игру и вступает Traefik, который решит описанную выше проблему.

Traefik – относительно новый продукт, который является L7-балансировщиком. Он написан для применения как раз в схемах с использованием микросервисов. А потому может использоваться как Ingress Controller в Kubernetes или же как прокси для standalone докер-контейнеров, что как раз и необходимо. Traefik запускается также в контейнере, а взаимодействие с другими контейнерами осуществляется через API-докера (с указанием пути до unix-сокета) и labels.

Как будет работать вся схема:

  • HTTP-запрос извне приходит на Traefik, где настроено прослушивание нужного домена и TLS при необходимости;
  • Traefik подключается к Docker через API и позволяет увидеть все labels у сервисов, на основе чего и принимает решение об отправке запроса в тот или иной контейнер;
  • Запущенное приложение в докер имеет определенные метки (домен, порт);

Схематично вышеописанное представлено на изображении ниже:

Структура тестового “проекта” следующая:

. ├── app │ └── index.html ├── default.conf └── docker-compose.yaml
  • каталоге app содержится код, в данном случае просто статический файл, внутри которого строка “It works! PS. dev2 proj” или “It works! PS. dev1 proj“, чтобы была разницу между инстансами
  • в default.conf содержится конфиг для Nginx
  • и непосредственно docker-compose.yaml

Запуск и настройка Traefik

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

Терминация TLS в данном случае должна выполняться на стороне Traefik, но в рамках статьи этот момент сознательно опущен, чтобы не усложнять материал. Например, для реального рабочего проекта на основе Traefik, настройки TLS были выполнены на вышестоящем прокси-сервере с Nginx, т.к. именно он являлся точкой входа для других несвязанных сервисов, а потому все TLS-сертификаты выполнялись на ином сервере. Но это частный случай, и всё зависит от задачи. В любом случае, Traefik позволяет терминировать TLS при необходимости.

  • Конфигурационный файл traefik.yml, который монтируется в контейнер:
mkdir /opt/traefik && cd /opt/traefik
api: dashboard: true insecure: true accessLog: <> log: level: INFO entryPoints: http: address: ":80" https: address: ":443" http: routers: host: entryPoints: - http rule: Host(`domain.ru`) providers: docker: endpoint: "unix:///var/run/docker.sock" exposedByDefault: false

В конфиге выше указывается минимальный набор:

  • настраивается прослушивание на 80 и 443 портах
  • активируется логирование и дашборд (http доступ в веб-интерфейс Traefik)
  • указывается провайдер – API docker

Данный конфиг монтируется внутрь контейнера. Для примера ниже представлен docker-compose.yaml:

version: '3.8' services: traefik: image: traefik:v2.2 volumes: - ./traefik.yml:/traefik.yml:ro - /var/run/docker.sock:/var/run/docker.sock:ro ports: - 80:80 - 8080:8080 restart: always networks: - default networks: default: external: name: gateway

docker-compose.yaml – типовой файл, при запуске которого будет запущен Traefik в отдельной внутренней сети с именем default, которая создается автоматически. Но есть один примечательный и очень важный момент, связанный с этой сетью. Помимо сети по умолчанию (default), вручную создается отдельная сеть, в которую приходят запросы извне. В рамках данной статьи сеть названа gateway. И внутренняя сеть default линкуется с внешней сетью gateway.

  • Важно указать, что сеть gateway является внешней, т.е. при создании будет указано internal=false :
docker network create \ --driver=bridge \ --attachable \ --internal=false \ gateway
  • После того, как подготовлен конфиг Traefik, создана внешняя сеть gateway и подготовлен docker-compose.yaml, можно выполнять запуск Traefik:
docker-compose up -d

После запуска нужно убедиться, что ошибок в логах нет, и проверить дашборад (http://127.0.0.1:8080/dashboard/). Он не обязателен в рамках данной статьи, но просто наглядно демонстрирует, что Traefik запущен. Также в дашборде будут видны правила маршрутизации. По умолчанию доступ к дашборду по http.

Запуск проекта dev1

После того, как Traefik запущен, подготавливается первый инстанс проекта. Напомню, что для примера в качестве демонстрации используется максимально простой вариант: Nginx с проксированием до apache.

Для первого инстанса используется следующий docker-compose.yaml:

version: "3" services: nginx: image: nginx:latest volumes: - ./app/index.html:/app/index.html - ./default.conf:/etc/nginx/conf.d/default.conf labels: - "traefik.enable=true" - "traefik.http.routers.nginx-dev1.rule=Host(`dev1.domain.ru`)" - "traefik.http.services.nginx-dev1.loadbalancer.server.port=8080" - "traefik.docker.network=gateway" networks: - default - dev1 httpd: image: httpd:latest volumes: - ./app/index.html:/usr/local/apache2/htdocs/index.html networks: - dev1 networks: default: external: true name: gateway dev1: internal: true

Пояснения по конфигу:

  • для сервиса Nginx, который является точкой входа трафика, назначены соответствующие метки с именем домена, порта – по ним Traefik будет понимать, что при запросах на dev1.domain.ru направлять трафик надо именно в этот контейнер
  • по умолчанию для всех сервисов используется внутренняя сеть dev1, с помощью которой сервисы могут взаимодействовать между собой, данная сеть описана в самом низу
  • помимо дефолтной внутренней сети, для сервиса Nginx также прописана дополнительная сеть с именем default – в конфиге указано, что она является внешней и линкуется с ранее созданной вручную сетью gateway, к которой также подключен Traefik

Таким образом, все внешние запросы при обращении на dev1.domain.ru из Traefik будут приходить в контейнер с Nginx, где указаны метки для dev1 соответственно – сеть gateway будет обеспечивать эту связанность. И при этом не рушится межсервисное взаимодействие в рамках одного проекта – это обеспечивает внутренняя сеть dev1.

Конфигурационный файл default.conf:

server < listen 8080; server_name _; root /app; index index.php index.html; location / < proxy_pass http://httpd:80; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; >>

Пояснения по конфигу:

  • конфиг-файл универсальный, т.е. между разными инстансами не нужно вносить в него никаких правок
  • сервис Nginx может обращаться к сервису httpd в рамках своей внутренней сети dev1, т.к. в docker-compose.yaml у сервиса апача задано имя “httpd”.

В файле app/index.html просто содержится статическая строчка для наглядного примера, в какой контейнер какого проекта пришёл запрос.

  • Все вышеописанные файлы можно скопировать в /opt/dev1 и выполнить запуск:
docker-compose up -d

Для проверки, что проект успешно запустился, можно зайти внутрь контейнера с Nginx и курлом проверить ответ:

docker exec -it project1_nginx_1 bash root@6139a191e911:/# curl localhost:8080 It works! PS. dev1 proj

Запуск проекта dev2

Запуск второго инстанса выполняется аналогично, например, в /opt/dev2. Меняются лишь метки и наименование внутренней сети. Ниже представлен пример docker-compose.yaml для наглядной демонстрации разницы между первыми и вторым инстансом:

version: "3" services: nginx: image: nginx:latest volumes: - ./app/index.html:/app/index.html - ./default.conf:/etc/nginx/conf.d/default.conf labels: - "traefik.enable=true" - "traefik.http.routers.nginx-dev2.rule=Host(`dev2.domain.ru`)" - "traefik.http.services.nginx-dev2.loadbalancer.server.port=8080" - "traefik.docker.network=gateway" networks: - default - dev2 httpd: image: httpd:latest volumes: - ./app/index.html:/usr/local/apache2/htdocs/index.html networks: - dev2 networks: default: external: true name: gateway dev2: internal: true

После запуска второго проекта, если используется тестовый домен для проверки, можно вписать в /etc/hosts своего рабочего компьютера 127.0.0.1, на котором слушает Traefik, и выполнять проверку:

127.0.0.1 dev1.domain.ru 127.0.0.1 dev2.domain.ru

При обращении на первый или второй инстанс запросы будут соотвественно распределяться в нужный контейнер.

Заключение

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

Из явных плюсов данного подхода можно отметить следующее:

  • вся L7-маршрутизация может быть выполнена на стороне Traefik, т.е. в одном месте
  • при необходимости добавить новую копию проекта, можно просто выполнить его запуск, указав в метках новый домен и новую внутренюю сеть

Минусов, как таковых, в данном случае нет, разве что дополнительная сложность, но это плата за удобство.

Следующим шагом уже будет использование системы оркестрации контейнеров, т.к. там куда больше возможностей по управлению разными копиями одно или нескольких проектов. А текущая же реализация подойдет для проведения каких-либо QA-тестов на небольшом проекте, если на нём ещё не используется kubernetes.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *