Traefik как обратный прокси для Docker контейнеров

Использование Traefik в качестве обратного прокси для Docker контейнеров

 5 мин. на чтение
traefik

Traefik - это обратный прокси сервер. Его основными преимуществами является наличие собственной панели мониторинга, а так же поддержка Docker “из коробки”. В этой статье я попытаюсь осветить аспект использования Traefik в качестве реверс-прокси.


Иногда нужно, чтобы Ваше приложение было доступно из мира. Это может быть какой-то концепт на начальной стадии, который нужно показать заказчику. Оригинальная идея, чтобы ‘прощупать’ потенциального потребителя или даже внутренний корпоративный сервис с небольшой нагрузкой. В такой ситуации настраивать полноценное продуктивное окружения может быть излишним. Идеальным же решением, как мне видится, будет запуск группы сервисов в Docker контейнерах, перед которыми предварительно будет развернут Traefik. Он же и будет осуществлять ретрансляцию запросов из внешней сети, что так же позволит скрыть структуру внутренней.

Конфигурирование Traefik

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

Пусть вся информация о конфигурации нашего реверс-прокси будет хранится в директории traefik/config. Там же создадим наш файл конфигурации traefik.yml:

# mkdir traefik && mkdir traefik/config
# cd traefik/config
# mcedit traefik.yml

Первым делом добавим две точки входа в наш прокси. Таким образом из вне будут доступны порты 80 и 443 для протоколов HTTP и HTTPS соответственно. Причем traefik автоматически будет производи редирект на защищенное соединение.

entryPoints:
  http:
    address: :80
    http:
      redirections:
        entryPoint:
          to: https
          scheme: https

  https:
    address: :443

Чтобы использовать шифрованный протокол HTTPS, необходимо где-то взять SSL сертификаты. Traefik позволяет автоматически получать бесплатные сертификаты Let’s Encrypt. Для этого добавим в файл конфигурации следующий блок:

certificatesResolvers:
  letsEncrypt:
    acme:
      email: your_email@your_domain
      storage: acme.json
      caServer: "https://acme-staging-v02.api.letsencrypt.org/directory"	  
      httpChallenge:
        entryPoint: http

Здесь cтоит обратить внимание на два момента:

  • storage - acme.json файл, куда будут сохранятся данные о полученных сертификатах;
  • caServer - https://acme-staging-v02.api.letsencrypt.org/directory ACME URL тестового сервера Let’s Encrypt. После того как получение сертификатов будет отлажено, данную строку можно закомментировать или удалить;

Ключевым достоинством Traefik является возможность изменения его конфигурации без приостановки работы. Это достигается путем использования так называемых провайдеров. Следующий блок позволит отслеживать метки (labels) в запускаемых контейнерах и конфигурировать наш прокси “на лету”:

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

В заключение разрешим доступ к интерфейсу информационной панели:

api:
  dashboard: true

Запуск Traefik

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

# docker network create traefik_net

Далее создадим файл acme.json и дадим права записи и чтения. В этом файле будет хранится информация о сертификатах, выданных Let’s Encrypt.

# touch acme.json && chmod 600 acme.json

Теперь сгенерируем пароль для панели мониторинга с помощью утилиты htpasswd. Если она отсутствует, то необходимо ее установить:

# dnf install httpd-tools

Создаем шифрованный пароль для пользователя admin:

# htpasswd -nb admin my_password
admin:$apr1$Z6j6uukd$0BZKFwmZV0bhEgiHA7uP/0

Теперь можно приступать к запуску самого Traefik. Для этого создадим файл docker-compose.yml следующего содержания:

version: '3'
services:
  traefik:
    image: traefik:v2.3.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
      - ./config/traefik.yml:/traefik.yml:ro
	  - ./config/acme.json:/acme.json
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.traefik.entrypoints=https"      
      - "traefik.http.routers.traefik.rule=Host(`your_subdomain.your_domain`)"
      - "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=your_secret_password"	  
      - "traefik.http.routers.traefik.middlewares=traefik-auth"
networks:
  default:
    external:
      name: traefik_net	  

Имейте ввиду, что символы $ из полученной строки шифрованного пароля нужно будет продублировать (заменить символ $ на $$).

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

# docker-compose up -d

И проверим поднялся ли контейнер:

# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             
	STATUS              PORTS                                      NAMES
431a99940fc0        traefik:v2.3.2      "/entrypoint.sh trae…"   36 seconds ago      
	Up 34 seconds       0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp   traefik

После запуска контейнера будет доступна информационная панель, попасть в нее, можно будет, введя в браузер адрес https://your_subdomain.your_domain. Этот хост, который был указан в docker-compose.yml:

- "traefik.http.routers.traefik.rule=Host(`your_subdomain.your_domain`)"

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

version: '3'
services:
  test:
    image: nginx:latest    
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    volumes:
      - nginx_data:/etc/nginx
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.test.entrypoints=https, http"
      - "traefik.http.routers.test.rule=Host(`test.your_domain`)"
      - "traefik.http.routers.test.tls=true"
      - "traefik.http.routers.test.tls.certresolver=letsEncrypt"
networks:
  default:
    external:
      name: traefik_net
volumes:
  nginx_data:

Аналогичным образом, так же как мы запускали сам traifk запустим наш контейнер nginx:

# docker-compose up -d

И проверим все ли хорошо:

# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             
	STATUS              PORTS                                      NAMES
b69590bfe9b2        nginx:latest        "/docker-entrypoint.…"   4 minutes ago       
	Up About a minute   80/tcp                                     test
431a99940fc0        traefik:v2.3.2      "/entrypoint.sh trae…"   9 minutes ago       
	Up 9 minutes        0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp   traefik

С контейнером все хорошо, и пройдя в браузере по ссылке https://test.your_domain, мы должны увидеть приветственную информацию nginx.

Traefik в качестве Load Balancer

Одной из замечательных возможностей traefik, является осуществление балансировки нагрузки между экземплярами одного и того же сервиса. Чтобы продемонстрировать данный функционала, запустим два экземпляра нашего тестового nginx-контейнера, выполнив следующую команду:

# docker-compose up --scale test=2 -d

После зайдем в раздел HTTP Services панели мониторинга traefik и убедимся, что балансировщик видит оба экземпляра тестового сервиса. Действительно в разделе Servers их два:

Службы traefik

Если же посмотреть логи docker-compose, то можно увидеть, что traefik балансирует входящие запросы на оба экземпляра:

test_2  | 192.168.144.2 - - [23/Oct/2020:20:43:02 +0000] "GET / HTTP/1.1" 200 612 "-" 
	"Mozilla/5.0 ..." "xxx.xxx.xxx.xxx"
test_1  | 192.168.144.2 - - [23/Oct/2020:20:43:36 +0000] "GET / HTTP/1.1" 200 612 "-" 
	"Mozilla/5.0 ..." "xxx.xxx.xxx.xxx"
test_2  | 192.168.144.2 - - [23/Oct/2020:20:44:31 +0000] "GET / HTTP/1.1" 200 612 "-" 
	"Mozilla/5.0 ..." "xxx.xxx.xxx.xxx"	
test_1  | 192.168.144.2 - - [23/Oct/2020:20:44:35 +0000] "GET / HTTP/1.1" 200 612 "-" 
	"Mozilla/5.0 ..." "xxx.xxx.xxx.xxx"