Когда я настраивал свой новый домашний сервер, я решил все сервисы запускать строго в Docker-контейнерах и строить всю инфраструктуру именно таким образом. Поэтому остро встал вопрос web-сервера, который будет выступать в роли обратного прокси для всех сервисов. Сначала я планировал использовать nginx, с которым уже довольно давно работаю, к которому привык и который мне понятен. Но выбрал Traefik, потому что хотел попробовать что-то новое для себя. Да, nginx в теории быстрее и вообще хорош, но именно в моём случае это не так важно.
И к тому же Traefik больше всего подходит для инфраструктуры сервисов, которые запущены в Docker или вообще в инфраструктуре k8s. Он динамически может обнаруживать сервисы и делать всё необходимое, чтобы стали доступны снаружи быстро и безопасно. Удобство ещё и в том, что не нужно заморачиваться с сертификатами. Traefik возьмёт всё это на себя, если только попросить.
Итак, начнём всё настраивать.
[ Настройка домена в Cloudflare ]
Я предпочитаю настройку Let's Encrypt через DNS-вызов (dnsChallenge). Этот метод устраняет необходимость открывать порт 80 и значительно упрощает процесс.
Traefik поддерживает множество провайдеров DNS. Я выбрал Cloudflare для своих доменов. Этот сервис бесплатен и предлагает не только улучшение производительности, но и дополнительную безопасность даже на бесплатном тарифе. К тому же, Cloudflare без проблем сейчас работает в России.
Если у вас ещё нет аккаунта в Cloudflare, рекомендую его создать. После успешной авторизации и подтверждения адреса email вам откроется возможность добавлять сайты, то есть домены (да!). Процесс добавления довольно простой.
Процесс добавления домена прост:
- Зайдите в ваш дашборд Cloudflare и выберите "Add a site".
- Введите ваш домен и продолжайте.
- Cloudflare автоматически просканирует записи текущих DNS-серверов. При необходимости обновите или настройте их. Обратите внимание, это может повлиять на работу вашего сайта.
- Cloudflare попросит указать свои NS для домена. Это можно сделать в личном кабинете вашего регистратора.
После изменения NS домена подождите от нескольких часов до суток. После того как изменения вступят в силу, Cloudflare автоматически настроит всё необходимое и отправит вам письмо о успешном подключении домена.
[ Запуск Traefik через Docker Compose ]
Прежде всего в вашей системе должны быть установлены Docker Engine и Docker Compose. Как именно это сделать именно в вашей операционной системе, лучше уточнить на официальном сайте, так как различия даже для разных дистрибутивов Linux могут быть кардинальными.
Также необхимо будет создать внешнюю сеть в режиме bridge для Docker, через которую будут взаимодействовать ваши контейнеры, включая Traefik. Я предпочитаю именно вариант работы Traefik через внешнюю сеть вместо простого мониторинга контейнеров Docker на хосте. Это позволяет контролировать, какие именно сервисы и с какими параметрами будут доступны Traefik. Я выбрал название сети web, потому что привык к таком варианту. Вы можете выбрать любое другое название.
docker network create \ --driver=bridge \ --opt com.docker.network.bridge.name=web_bridge \ web
docker network create \ --driver=bridge \ --opt com.docker.network.bridge.name=web_bridge \ web
Сам файл docker-compose.yml для нашего экземпляра Traefik очень простой по структуре.
services: traefik: image: traefik:v2.11 container_name: traefik ports: - "443:443" environment: - TZ=${TIMEZONE} - CF_API_EMAIL=${CF_API_EMAIL} - CF_API_TOKEN=${CF_API_TOKEN} volumes: # Чтобы выпущенные сертификаты не терялись при перезапуске. - ./ssl:/ssl # Путь к файлу конфигурации - ./traefik.toml:/etc/traefik/traefik.toml # Явное указание на сервис Docker с флагом read only. # Может быть специфичным для вашей системы. - /var/run/docker.sock:/var/run/docker.sock:ro # Эта секция необходима, если вы хотите использовать дашборд labels: - traefik.enable=true - traefik.http.routers.traefik.entrypoints=https - traefik.http.routers.traefik.rule=Host(`${DASHBOARD_DOMAIN}`) - traefik.http.routers.traefik.tls=true - traefik.http.routers.traefik.tls.certresolver=letsEncrypt - traefik.http.routers.traefik.service=api@internal - traefik.http.middlewares.traefik-auth.basicauth.users=${DASHBOARD_USER} - traefik.http.routers.traefik.middlewares=traefik-auth networks: - web restart: always networks: web: external: true
services: traefik: image: traefik:v2.11 container_name: traefik ports: - "443:443" environment: - TZ=${TIMEZONE} - CF_API_EMAIL=${CF_API_EMAIL} - CF_API_TOKEN=${CF_API_TOKEN} volumes: # Чтобы выпущенные сертификаты не терялись при перезапуске. - ./ssl:/ssl # Путь к файлу конфигурации - ./traefik.toml:/etc/traefik/traefik.toml # Явное указание на сервис Docker с флагом read only. # Может быть специфичным для вашей системы. - /var/run/docker.sock:/var/run/docker.sock:ro # Эта секция необходима, если вы хотите использовать дашборд labels: - traefik.enable=true - traefik.http.routers.traefik.entrypoints=https - traefik.http.routers.traefik.rule=Host(`${DASHBOARD_DOMAIN}`) - traefik.http.routers.traefik.tls=true - traefik.http.routers.traefik.tls.certresolver=letsEncrypt - traefik.http.routers.traefik.service=api@internal - traefik.http.middlewares.traefik-auth.basicauth.users=${DASHBOARD_USER} - traefik.http.routers.traefik.middlewares=traefik-auth networks: - web restart: always networks: web: external: true
Обратите внимание, что секция environment содержит переменные окружения. Они автоматичеки будут браться при запуске контейнера из файла .env, который должен быть расположен в той же директории, что и docker-compose.yml. Вы можете также указать любой другой файл с полным или относительным путём через директиву env_file. В файле обязательно нужно указать значения. Контейнер то запуститься сможет, но толку от него будет немного.
TIMEZONE=Europe/Moscow DASHBOARD_DOMAIN=traefik.example.com DASHBOARD_USER=<логин и пароль для basic auth> [email protected] CF_API_TOKEN=<ваш токен>
TIMEZONE=Europe/Moscow DASHBOARD_DOMAIN=traefik.example.com DASHBOARD_USER=<логин и пароль для basic auth> [email protected] CF_API_TOKEN=<ваш токен>
Если вы хотите пользоваться дашбордом Traefik, то выберите, по какому адресу он должен быть доступен. Чтобы он оказался скрыт от непрошенных гостей, необходимо указать пару из логина и хешированного пароля, которые можно получить через любой сервис генерации старого доброго htpwassd. Не самый безопасный способ авторизации, но простой и лёгкий.
CF_API_EMAIL – это электронная почта, которую вы указали в своём аккаунте на Cloudflare.
CF_API_TOKEN – токен для обращения к Cloudflare API. Получить его можно в разделе User API Tokens. Просто нажимаете "Create Token", выбираете там "Edit zone DNS", настраиваете все разрешения и указываете, к каким именно доменам относится получаемый токен. Также можно указать IP-адрес сервера, на котором будет работать ваш Traefik в качестве единственного разрешённого.
Все значения переменных указываются без кавычек.
И наконец, переходим к конфигурации Traefik с помощью файла traefik.toml. Он также должен быть расположен в одной директории с docker-compose.yml. Но вы можете указать свой путь к другому файлу в секции volumes.
[entryPoints] [entryPoints.https] address = ":443" # Включаем дашборд, если хотим [api] dashboard = true # Помните, я говорил про отношения Docker и Traefik через сеть? [providers.docker] exposedByDefault = false network = "web" # Настраиваем именно провайдера "cloudflare" для получения сертификатов [certificatesResolvers.letsEncrypt.acme] storage = "/ssl/acme.json" [certificatesResolvers.letsEncrypt.acme.dnsChallenge] provider = "cloudflare" delayBeforeCheck = 0
[entryPoints] [entryPoints.https] address = ":443" # Включаем дашборд, если хотим [api] dashboard = true # Помните, я говорил про отношения Docker и Traefik через сеть? [providers.docker] exposedByDefault = false network = "web" # Настраиваем именно провайдера "cloudflare" для получения сертификатов [certificatesResolvers.letsEncrypt.acme] storage = "/ssl/acme.json" [certificatesResolvers.letsEncrypt.acme.dnsChallenge] provider = "cloudflare" delayBeforeCheck = 0
Обратите внимание на то, как настроена секция [providers.docker]. Я не хочу, чтобы Traefik мониторил старты всех контейнеров на хосте. Вместого это я хочу, чтобы он следил только за теми сервисами, которые работают с ним в одной bridge-сети, и у которых установлен лейбл traefik.enable=true.
Пришло время запускать то, что у нас получилось. Для этого переходим в директорию, где сохранены все эти файлы и выполняем следующие команды. Даю сразу с комментариями, чтобы не было разрыва восприятия. Первый запуск будет сопровождаться скачиванием образа и сборкой контейнера.
# Сначала запускаем не в режиме detach, # чтобы увидеть, если что-то пойдёт не так docker-compose up # Если всё хорошо, то останавливаем контейнер через Ctrl+C # и удалить контейнер для надёжности docker-compose down # Если проблем нет, то можно уже запускать в режим detach docker-compose up -d
# Сначала запускаем не в режиме detach, # чтобы увидеть, если что-то пойдёт не так docker-compose up # Если всё хорошо, то останавливаем контейнер через Ctrl+C # и удалить контейнер для надёжности docker-compose down # Если проблем нет, то можно уже запускать в режим detach docker-compose up -d
Всё работает, можно пользоваться.
[ Вариант настройки сервиса для работы через Traefik ]
Напоследок приведу пример простого docker-compose.yml, в котором запустим один сервис в режиме взамодействия с Traefik, а другой – в режиме взаимодействия только во внутренней сети.
networks: web: external: true servicename: external: false services: # Сервис не должен общаться с Traefik, только со своими соседями service_1: build: context: ../ dockerfile: Dockerfile target: service_1 container_name: service_1 restart: always labels: - traefik.enable=false networks: - servicename ports: - "9000:9000" # Сервис общается с Traefik. # Для этого указаны домен, резолвер сертификатов и порт, который должен # слушать Traefik. service_2: build: context: ../ dockerfile: Dockerfile target: service_2 container_name: service_2 restart: always labels: - traefik.enable=true - traefik.http.routers.service_2.rule=Host(`service_2.example.com`) - traefik.http.routers.service_2.entrypoints=https - traefik.http.routers.service_2.tls=true - traefik.http.routers.service_2.tls.certresolver=letsEncrypt - traefik.http.services.service_2.loadbalancer.server.port=80 networks: - web - servicename expose: - "80"
networks: web: external: true servicename: external: false services: # Сервис не должен общаться с Traefik, только со своими соседями service_1: build: context: ../ dockerfile: Dockerfile target: service_1 container_name: service_1 restart: always labels: - traefik.enable=false networks: - servicename ports: - "9000:9000" # Сервис общается с Traefik. # Для этого указаны домен, резолвер сертификатов и порт, который должен # слушать Traefik. service_2: build: context: ../ dockerfile: Dockerfile target: service_2 container_name: service_2 restart: always labels: - traefik.enable=true - traefik.http.routers.service_2.rule=Host(`service_2.example.com`) - traefik.http.routers.service_2.entrypoints=https - traefik.http.routers.service_2.tls=true - traefik.http.routers.service_2.tls.certresolver=letsEncrypt - traefik.http.services.service_2.loadbalancer.server.port=80 networks: - web - servicename expose: - "80"
Это именно пример для иллюстрации настроек секции labels. Второй сервис после запуска заявит о себе запущенному Traefik, сообщит о своём домене, получит для него сертификат и начнёт работать через него извне.
Надеюсь, что всё описанное вам поможет в ваших начиниях и движении вперёд!