Как все начиналось
В ходе работы с микросервисами мы неоднократно сталкивались с проблемами сервис дискавери при автоскелинге, схлопывании лишних нод.
Были перепробованы почти все решения существовавшие или существующие на данный момент, но как водится — ничего не ложилось идеально на наши динамичные окружения (десятки остановок/запусков однотипных контейнеров в час). Наиболее близкое решение было 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 для определения пула серверов.
В этом примере мы определили тип балансировки как "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 кластером. Рассмотрим работу с ними в одном примере.
Сейчас мы будем балансировать определенными сервисами, используя 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)
o_serega
16.06.2016 12:44+1Понравилось докер дискавери, ждем мультипоинт. Очень не хватало лод балансера заточенного на работу с докер окружением
nickdoikov
16.06.2016 16:11мы не сможем гарантировать консистентность списка. в единицу времени при нескольких докер эндпоинтах.
если небольшой рассинхрон это ок то можно об этом подумать.
Хотя как по-мне проще и лучше использовать Dokcer Swarm. есть вариант когда на менеджмент ноде сворма прописывается статик лист докер нод.
Тогда на сами докер ноды устанавливать ничего не нужно. А уж менеджмент ноду использовать как эндпоинт для ЛБ.
ShapovalovTS
16.06.2016 15:03В ходе работы с микросервисами мы неоднократно сталкивались с проблемами сервис дискавери при автоскелинге, схлопывании лишних нод.
А вы пробовали Kubernetes? Там round-robin load balancing + service discovery из коробки.nickdoikov
16.06.2016 16:01На кубернейтсе не заканчиваются контейнеры, а уж тем более микросервисы. Тут более общий подход. Посмотрите вики проекта. Там намного больше способов дискавери чем описано в данной статъе.
o_serega
16.06.2016 16:13>>>Там round-robin load balancing
У гобитвина, на данный момент, уже 4 способа есть, плюс за счет exec хелчеков, можно запилить более хитрую проверку (возможно это можно и в кубернетес, не проверял). Плюс не все хотят завязываться на кубернетес и допиивать его под свои нужды. Да и многим докер сворм + гобитвин + консул + какой-то аркестратор — более чам достаточно.
Возможно, авторы проекта, рассмотрят вариант интеграции гобитвина с кубернетс. Во всяком случаи kube-proxy заменяемая часть.nickdoikov
16.06.2016 16:21для того чтоб заменить cube proxy нужно:
1) Поддержка UDP которой пока нет
2) Api которое только начинает имплеменрироваться.
Всему свое время.
voidnugget
16.06.2016 15:28Если есть consul и нужен service discovery с планировщиком и гарантией состояний — надо было внедрять nomad.
Нужно понимать что есть CRDT, а есть консенсус на Raft/Paxos…
Вот в случае с Consul'ом для Health Check используется CRDT c Serf'a.
В статье шла речь о том что вам нужен консенсус и планировщик для гарантий синхронизации — надо было использовать Nomad для этого.
В общем эт уже разработано, надо было чуть глубже вникать в стэк HashiCorp.
Kubernetes тоже прокатит — там Raft вроде как.
Советую ознакомится со способами решения задач синхронизации в распределённых средах и как они ложатся на САР теорему.nickdoikov
16.06.2016 16:06Было желание сделать простое и гибкое решение с кастомизацией. Оттуда и возник exec, json и plaintext discovery.
Смотрите на это проект как на балансировщик с плюшками, позволяющими решить множество задач быстро и просто.voidnugget
16.06.2016 16:53Там просто когда таких балансировщиков с плюшками десяток запущено и нужно что бы у них одни и те же состояния были — начинаются проблемы, эти проблемы решаются с помощью решения задачи принятия консенсуса, в случае с тем же Nomad/Kubernetes.
rustler2000
18.06.2016 09:18Интерестно бы было увидить в тестах traefik.
Пробовали с аффинити/so_reuseport запускать?nickdoikov
18.06.2016 10:47а какой смысл сравнивать HTTP балансер с l4 балансером?
Если у меня TCP (в будущем и UDP) сервис базирующийся на протоколе, который к HTTP и HTTPS не имеет никакого отношения, чем мне поможет traefik?
Тесты на одинаковые кейсы сделаем позже, но это разные инструменты с частично пересекающимеся сферами использования.rustler2000
18.06.2016 19:17L4 там вопрос времени исключительно — (https://github.com/containous/traefik/issues/10). Но перф ядра былобы интерестно сравнить.
А что про аффинити/so_reuseport?nickdoikov
19.06.2016 23:22Вот когда будет- сравним.
А пока что провел тест на простейшем варианте настройки traefik. Как и следовало ожидать чистый L4 быстрее
traefik Gobetween
Req/Sec 10591 14540
mb/s 8.35 11.9
Хотя неправильно проводить тесты на ЛБ работающих на разных леерах.
ihormanchik
шикарно! спасибо за интересный проект!
если есть результаты перформанс тестов / сравнение по перформансу с другими балансировщиками — не стесняйтесь публиковать ;)
nickdoikov
Спасибо за отзыв, но если внимательно глянуть в ссылки после статьи есть те самые тесты производительности:
Сравнивали с Haproxy и Nginx
https://github.com/yyyar/gobetween/wiki/Performance-tests-results
ihormanchik
простите, был невнимателен :) спасибо