Как все начиналось


В ходе работы с микросервисами мы неоднократно сталкивались с проблемами сервис дискавери при автоскелинге, схлопывании лишних нод.


Были перепробованы почти все решения существовавшие или существующие на данный момент, но как водится — ничего не ложилось идеально на наши динамичные окружения (десятки остановок/запусков однотипных контейнеров в час). Наиболее близкое решение было NGINX+Consul+Consul templates, но оно было некрасивым, требовало перезапуска, не давало возможности использовать внешние хелсчеки иначе как через Consul.


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


Заложенный функционал на первую итерацию


Типы балансировки:


Iphash
Leastconn
Roundrobin
Weight


Дискавери (определение пула бекендов для каждого фронтенда):


Static — просто список серверов в конфиге.
Docker — запрос в докер Docker / Swarm API фильтрованные по label и внутренним портам контейнера.
Exec — инициирование запуска внешнего скрипта, чтение из stdout и его разбор по регулярке (пока прописана жестко).
JSON — запрашивает через http-запрос URL и разбирает его по паттернам (поддерживает многоуровневый JSON).
Plaintext — запрашивает через http-запрос URL и разбирает его по заданному в конфиге регулярному выражению.
SRV — запрашивает у заданного DNS SRV записи по имени сервиса.


Хелсчеки


Так как изначально было совершенно понятно, что мы просто не потянем имплементировать хелсчеки в объеме того же haproxy, было принято решение отделаться малой кровью и сделать всего 2 типа хелсчеков.


Ping — простой TCP ping;
Exec — запуск произвольного бинарника с передачей ему параметров и чтение из stdout вывода.


Примеры использования


SRV Discovery


У нас есть произвольные сервисы, регистрирующие себя, к примеру, в Consul. Будем использовать Consul dns для определения пула серверов.


image


В этом примере мы определили тип балансировки как "srv", так же определен DNS-сервер и его порт, на который будут идти запросы на сервис дискавери. Определена частота обновления списка серверов, а так же немаловажная переменная — политика для случая, когда DNS-сервер не ответил. Для максимальной консистентности окружения нужно ставить failpolicy="setempty". В таком случае при отсутствии ответа от DNS весь пул бекенд серверов будет обнулен, а входящие соединения будут сбрасываться. В противном случае нужно использовать failpolicy = "keeplast", тогда балансировщик будет использовать последние данные, пришедшие до сбоя соединения с DNS.


toml
[servers.sample2]
bind = "localhost:3001"
protocol = "tcp"
balance = "weight"

  [servers.sample2.discovery]
  failpolicy = "keeplast"
  kind = "srv"
  srv_lookup_server = "66.66.66.66:8600" # dns server and port
  srv_lookup_pattern = "api.service.ireland.consul." # SRV service pattern 

  [servers.sample2.healthcheck]
  fails = 1                      
  passes = 1
  interval = "2s"                
  kind = "ping"
  timeout = "500ms" 

Docker / Swarm Балансировка.


По сути различий в API и способе настройки для Docker/Docker Swarm нет. Мы можем одинаково работать и с Docker хостом, и с Docker Swarm кластером. Рассмотрим работу с ними в одном примере.


image


Сейчас мы будем балансировать определенными сервисами, используя Docker Swarm, как более общий пример. Все нижеописанное работает и для отдельного Docker хоста. В данном примере мы определяем тип дискавери как "docker", определяем базовый docker url, лейблы и внутренний порт докер контейнера (со стороны сети самого контейнера) по которым балансировщик будет делать выборку, из которых формируется пул бекенд серверов.


Для данного примера мы будем использовать более "продвинутый" тип хелсчеков, а именно exec-хелсчеки. Помимо параметров частоты запуска чеков есть еще время отработки скрипта. Время между запусками должно быть больше времени отработки скрипта, чтобы не было "набегов". Команда запуска данного хелсчека формируется как /path/to/script [ip] [port]. После отработки скрипта, он должен выводить в stdout строку, которая сравнивается с положительным и отрицательным предполагаемым результатом.


[servers.sample3]
bind = "localhost:3002"
protocol = "tcp"
balance = "weight"

  [servers.sample3.discovery]
    interval = "10s"
    timeout = "2s"
    kind = "docker"
    docker_endpoint = "http://localhost:2377"  # Docker / Swarm API    
    docker_container_label = "api=true"  # label to filter containers
    docker_container_private_port = 80   # gobetween will take public container port for this private port

[servers.sample3.healthcheck] 
kind = "exec"
interval = "2s"  
exec_command = "/etc/gobetween/checks/exec_healthcheck.sh"
exec_expected_positive_output = "1"           
exec_expected_negative_output = "0"           
exec_timeout_duration = "1s"             

В последующих статьях я планирую привести несколько примеров более сложного использования других видов дискавери. Также будет описана специфика конфигурирования и установки под Windows.


Поделиться с друзьями
-->

Комментарии (19)


  1. ihormanchik
    16.06.2016 12:39

    шикарно! спасибо за интересный проект!
    если есть результаты перформанс тестов / сравнение по перформансу с другими балансировщиками — не стесняйтесь публиковать ;)


    1. nickdoikov
      16.06.2016 12:43
      +2

      Спасибо за отзыв, но если внимательно глянуть в ссылки после статьи есть те самые тесты производительности:
      Сравнивали с Haproxy и Nginx

      https://github.com/yyyar/gobetween/wiki/Performance-tests-results


      1. ihormanchik
        16.06.2016 12:45

        простите, был невнимателен :) спасибо


  1. o_serega
    16.06.2016 12:44
    +1

    Понравилось докер дискавери, ждем мультипоинт. Очень не хватало лод балансера заточенного на работу с докер окружением


    1. nickdoikov
      16.06.2016 16:11

      мы не сможем гарантировать консистентность списка. в единицу времени при нескольких докер эндпоинтах.
      если небольшой рассинхрон это ок то можно об этом подумать.
      Хотя как по-мне проще и лучше использовать Dokcer Swarm. есть вариант когда на менеджмент ноде сворма прописывается статик лист докер нод.
      Тогда на сами докер ноды устанавливать ничего не нужно. А уж менеджмент ноду использовать как эндпоинт для ЛБ.


      1. voidnugget
        16.06.2016 16:54

        Прикрутите Raft и будет вам консистентность…


  1. ShapovalovTS
    16.06.2016 15:03

    В ходе работы с микросервисами мы неоднократно сталкивались с проблемами сервис дискавери при автоскелинге, схлопывании лишних нод.

    А вы пробовали Kubernetes? Там round-robin load balancing + service discovery из коробки.


    1. nickdoikov
      16.06.2016 16:01

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


    1. o_serega
      16.06.2016 16:13

      >>>Там round-robin load balancing
      У гобитвина, на данный момент, уже 4 способа есть, плюс за счет exec хелчеков, можно запилить более хитрую проверку (возможно это можно и в кубернетес, не проверял). Плюс не все хотят завязываться на кубернетес и допиивать его под свои нужды. Да и многим докер сворм + гобитвин + консул + какой-то аркестратор — более чам достаточно.

      Возможно, авторы проекта, рассмотрят вариант интеграции гобитвина с кубернетс. Во всяком случаи kube-proxy заменяемая часть.


      1. nickdoikov
        16.06.2016 16:21

        для того чтоб заменить cube proxy нужно:
        1) Поддержка UDP которой пока нет
        2) Api которое только начинает имплеменрироваться.

        Всему свое время.


        1. o_serega
          16.06.2016 16:22

          Надеюсь, Вы все же рассмотрите такую возможность в будущем


  1. voidnugget
    16.06.2016 15:28

    Если есть consul и нужен service discovery с планировщиком и гарантией состояний — надо было внедрять nomad.

    Нужно понимать что есть CRDT, а есть консенсус на Raft/Paxos…
    Вот в случае с Consul'ом для Health Check используется CRDT c Serf'a.
    В статье шла речь о том что вам нужен консенсус и планировщик для гарантий синхронизации — надо было использовать Nomad для этого.

    В общем эт уже разработано, надо было чуть глубже вникать в стэк HashiCorp.
    Kubernetes тоже прокатит — там Raft вроде как.

    Советую ознакомится со способами решения задач синхронизации в распределённых средах и как они ложатся на САР теорему.


    1. nickdoikov
      16.06.2016 16:06

      Было желание сделать простое и гибкое решение с кастомизацией. Оттуда и возник exec, json и plaintext discovery.
      Смотрите на это проект как на балансировщик с плюшками, позволяющими решить множество задач быстро и просто.


      1. voidnugget
        16.06.2016 16:53

        Там просто когда таких балансировщиков с плюшками десяток запущено и нужно что бы у них одни и те же состояния были — начинаются проблемы, эти проблемы решаются с помощью решения задачи принятия консенсуса, в случае с тем же Nomad/Kubernetes.


  1. rustler2000
    18.06.2016 09:18

    Интерестно бы было увидить в тестах traefik.

    Пробовали с аффинити/so_reuseport запускать?


    1. nickdoikov
      18.06.2016 10:47

      а какой смысл сравнивать HTTP балансер с l4 балансером?
      Если у меня TCP (в будущем и UDP) сервис базирующийся на протоколе, который к HTTP и HTTPS не имеет никакого отношения, чем мне поможет traefik?
      Тесты на одинаковые кейсы сделаем позже, но это разные инструменты с частично пересекающимеся сферами использования.


      1. rustler2000
        18.06.2016 19:17

        L4 там вопрос времени исключительно — (https://github.com/containous/traefik/issues/10). Но перф ядра былобы интерестно сравнить.

        А что про аффинити/so_reuseport?


        1. nickdoikov
          19.06.2016 23:22

          Вот когда будет- сравним.
          А пока что провел тест на простейшем варианте настройки traefik. Как и следовало ожидать чистый L4 быстрее

          traefik Gobetween
          Req/Sec 10591 14540
          mb/s 8.35 11.9

          Хотя неправильно проводить тесты на ЛБ работающих на разных леерах.


  1. nickdoikov
    26.06.2016 08:01

    https://habrahabr.ru/post/304096/
    продолжение