Это небольшой пересказ с примерами конфигураций моего небольшого и спонтанного “воркшопа” про то, как я организовал своё локальное окружение для одновременной разработки n-ного количества web-сервисов.

Предисловие

Пару лет назад, когда я только освоился с технологией docker, у меня был такой подход, что каждый сервис, с которым мне приходится работать через браузер или http client, занимает определённый порт на localhost. Это было больно по нескольким причинам:

  • Необходимо точно помнить какие порты уже заняты, да ещё и помнить какой сервис занимает какой порт.
  • Для сайтов, работающих по доменным именам, в основном приходилось делать дополнительные записи в файл hosts.

Всё это выливалось в постоянные телодвижения по конфигурации не только когда приходится настраивать окружение на свежеустановленной системе, но и даже при работе на системе, где уже всё настроено.

Да, это не так критично, но всё-равно тратится время. Поэтому в конечном итоге я начал искать пути решения этой проблемы. Поиск по сайтам в google и в ответах на stackoverflow приносил мало результатов. Решения в основном заключались в настройке локального dns-сервера и статической или динамической его конфигурации. Решения эти были довольно сложными в реализации, да и ещё не было понимания, как это повлияет на ОС.

Так я и сидел, прокидывая порты на хост, решая конфликты и вспоминая четырёхзначные числа. Пока я не нашёл traefik, который позиционирует себя как “продвинутый HTTP reverse proxy и load balancer”. Первая часть меня зацепила, и, почитав документацию, я пришёл к выводу, что это идеальное решение проблемы, описанной выше.

Использование traefik

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

Первым шагом будет запуск traefik. Для простоты (а именно это приоритет в локальной разработке) всё буду делать через docker-compose и docker-compose.yml файлы. Таким образом, выделим директорию где-то на диске и начнём. У меня это директория ~/code/libs. Создаём внутри неё новую директорию traefik.

1
2
3
4
$ cd ~/code/libs
$ mkdir traefik
$ cd traefik
$ touch docker-compose.yml

Перейдя туда, начинаем описывать конфигурацию стека из одного сервиса:

traefik/docker-compose.yml
1
2
3
4
5
6
7
8
9
10
11
version: '3.7'

services:
traefik:
image: traefik:2.2.8
command: --api.insecure=true --providers.docker --providers.docker.network=traefik_default --providers.docker.exposedByDefault=false
ports:
- 80:80
- 8080:8080
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro

Немного о конфигурации. Порт 80 используется для непосредственных запросов в сервисы через traefik. На порту 8080 располагается небольшой dashboard с небольшим количеством информации для мониторинга. Том с сокетом докера необходимо добавить в контейнер, иначе traefik не сможет использовать провайдер докера. И немного по ключу command:

  • --api.insecure=true включаем доступ до dashboard
  • --providers.docker включаем провайдер docker
  • --providers.docker.network=traefik_default говорим traefik использовать только его дефолтную сеть для поиска сервисов, готовых к взаимодействию
  • --providers.docker.exposedByDefault=false данный флаг отключает автоматическое подключение всех контейнеров к traefik. В противном случае, traefik будет пытаться создать прокси до всех сервисов, у которых открыт хотя бы один порт.

Запускаем стек:

1
$ docker-compose up --build -d

И проверим, что всё запустилось, перейдя в браузере по http://localhost:8080. Отобразится такая страница с dashboard.

Следующим шагом будет запуск какого-нибудь сервиса с web-интерфейсом. Для примера я взял AWS-S3 совместимое хранилище minio. Рядом с директорией traefik создадим директорию minioс файлом docker-compose.yml внутри:

1
2
3
4
$ cd ..
$ mkdir minio
$ cd minio
$ touch docker-compose.yml

Базовая конфигурация этого сервиса через docker-compose будет выглядеть следующим образом:

minio/docker-compose.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
version: '3.7'

services:
minio:
image: minio/minio:RELEASE.2020-08-07T01-23-07Z
expose:
- 9000
environment:
MINIO_ACCESS_KEY: "foobarbaz1234"
MINIO_SECRET_KEY: "secret1234"
command: minio --compat server /data
volumes:
- ./data:/data

Теперь необходимо сделать так, чтобы traefik и minio смогли общаться между собой. Для этого в конец minio/docker-compose.yml добавляем сеть traefik_default как внешнюю. И сразу же скажем сервису minio использовать новую сеть. Конфигурация теперь будет иметь вид:

minio/docker-compose.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
version: '3.7'

services:
minio:
image: minio/minio:RELEASE.2020-08-07T01-23-07Z
expose:
- 9000
environment:
MINIO_ACCESS_KEY: "foobarbaz1234"
MINIO_SECRET_KEY: "secret1234"
command: minio --compat server /data
volumes:
- ./data:/data
networks:
- proxy # <- добавили внешнюю сеть в сервис

networks:
proxy:
external:
name: traefik_default # <- объявили внешнюю сеть

Ну и наконец, собственно магия traefik. В сервис необходимо добавить три лейбла:
- traefik.enable=true, чтобы сервис был включён в список конечных точек проксирования.

1
traefik.http.routers.<serviceName>.rule=Host(`<hostName>.docker.localhost`)
где `<serviceName>` нужно заменить на название сервиса, оно не обязательно должно совпадать с названием в `docker-compose.yml`, `<hostName>` нужно заменить на валидный поддомен. Валидность заключается в соблюдении правил построении доменных имён.
  • traefik.http.services.<serviceName>.loadbalancer.server.port=<port> обязательно необходимо добавлять, если сервис слушает порт отличный от 80.

Заключительный вид файла minio/docker-compose.yml:

minio/docker-compose.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
version: '3.7'

services:
minio:
image: minio/minio:RELEASE.2020-08-07T01-23-07Z
expose:
- 9000
environment:
MINIO_ACCESS_KEY: "foobarbaz1234"
MINIO_SECRET_KEY: "secret1234"
command: minio --compat server /data
volumes:
- ./data:/data
networks:
- proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.minio.rule=Host(`minio.docker.localhost`)"
- "traefik.http.services.minio.loadbalancer.server.port=9000"

networks:
proxy:
external:
name: traefik_default

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

1
$ docker-compose up --build -d

Теперь достаточно перейти по url http://minio.docker.localhost и убедиться, что всё работает.

Вот таким образом можно без каких-либо проблем задавать доступность сервисов по доменному имени в домене *.docker.localhost на локальной машине. Дополнительно traefik предоставляет гибкую настройку множества параметров, в том числе и https, а также поддерживает работу не только с обычными контейнерами docker, запускаемыми через docker run или docker-compose, но и docker swarm, и kubernetes.