Pull to refresh

Comments 20

Интересна конкретная реалзиация «диспечера».
Диспетчер построен на базе RabbitMQ плюс некоторые свои обвязки позволяющие либо объединять некоторые потоки событий, либо наоборот дробить в зависимости от нагружености и потребностей. События диспетчер рассылает используя push подход, то есть сервису не нужно держать коннект с диспетчером.
А можно поподробней про docker-контейнеры на нескольких серверах? Запускаете ли несколько одинаковых микросервисов на одном хосте? Как мониторите? Как логируете?
Ну на самом деле почти каждый из этих вопросов тянет на отдельную статью. Но если коротко, то:

1. docker-контейнеры на нескольких серверах — это вообще отдельная большая история в том числе и для нас, есть куда развиваться и что еще попробовать, но в общих чертах все запросы проходят через reverse proxy то есть поднимая еще одну копию сервиса в докер контейнере мы просто дописываем нужную запись в upstream у nginx, при этом физически два контейнера могут быть запущены на разных машинах

2. запускаем ли одинаковые микросервисы на одном хосте? Да, как минимум при деплое, я упоминал, что мы используем BGD технику, которая подразумевает в какой то момент работу параллельно двух контейнеров одного микросервиса.

3. Для мониторинга у нас используется DataDog, influxdb + grafana, но мы продолжаем экспериментировать в этом направлении, потому как мониторинг при нашем подходе очень важная составляющая, собираемся попробовать prometheus

4. Логируем logstash + Elastic но тоже продолжаем экспериментировать в этом направлении, эта связка не очень нравится своей громоздкостью, но пока не нашли достаточно надежной, обкатанной альтернативы
1. Т.е. на каждом хосте есть nginx, который проксирует запросы контейнерам, а в апстрим добавляются по DNS-имени. Как вы всё это админите? Руками?
2. Опять-же — как это всё оркестрируется — именно процесс деплоя по BGD
3. Но для мониторинга подразумевается наличие отдельного процесса (преимущественно в том же контейнере), что противоречит идеалогии docker
4. По логированию я так понял используете встроенный механизм логирования в докере?
1. нет nginx не на каждом хосте, то есть возможны например варианты поднимаем инстанс на амазоне, запускаем в нем один контейнер, добавляем запись в апстрим, то есть по сути nginx может быть вообще один, который проксирует запросы контейнерам в том числе и на разных машинах, но на деле получается nginx не один. Админим не руками, ansible'ом, пока мы небольшие справляемся.
2. И тут ansible и опять же пока мы небольшие этого достаточно, но в целом экспериментируем и ищем пути большей автоматизации с целью большего исключения человеческого фактора. Переключаем тоже используя ansible, в идеале конечно настроить автоматическое переключение и откат в случае неудачи. То есть например при запуске нового контейнера (green) автоматом часть трафика заруливается на него, в течении какого то времени собираются метрики и если заданные пороговые значения не превышены, то старый контейнер (blue) выключается, а весь трафик заруливается на green, если же пороговые значения превышены, то весь трафик возвращается на blue. Но пока делаем это руками используя ansible
3. Мониторинг, тут есть какбы две стороны медали, первая сервис сам о себе шлет некоторый набор метрик — это первая сторона и вторая — это действительно нужен отдельный процесс, но не нужен в том же контейнере, у нас запущен в отдельном контейнере c -v /var/run/docker.sock:/var/run/docker.sock --pid=host чего в большинстве случаев достаточно для мониторинга, по крайней мере нам пока хватает.
А вот на счет идеологии докера, к сожалению мы иногда ее нарушаем, хотя стараемся этого не делать. Приведу пример плохой практики случавшейся у нас: — микросервис на php, который также требует запуска некоторой команды по расписанию, выполнено в виде контейнера с php-fpm и кроном. Сейчас наученные опытом мы избегаем таких решений, но тем не менее это случалось в нашей практике и до сих пор остались микросервисы работающие в таком исполнении.
4. и да и нет, сервисы свои логи пишут в логстеш, есть еще datadog, который имеет готовые решения для логирования некоторых событий докера
1. Если Nginx может быть один, значит Вы используете проброс портов от docker-а и они все слушают на внешнем интерфейсе железки, что в некоторых случаях неудобно.

Не смотрели в сторону Kubernetes или им подобных?
Да, в некоторых случаях контейнеры слушают внешние интерфейсы железки и да это неудобно, согласен.
Когда начинали небыло столь широкого выбора инструментов готовых для запуска в проде, поэтому сделали так. Сейчас собираемся менять схему, пока не решили, что имено будем использовать, смотрим в сторону docker swarm и kubernets, так что этот выбор нам еще предстоит сделать. Ну а когда решим готовы поделиться опытом, ну и чужой опыт всегда ценим, лучше учиться на чужих ошибках, чем на своих
Если вы будете использовать встроенные возможности Docker swarm для multihost network, то до выхода новой версии 1.12 (обещают 14.07 выпустить), вы столкнетесь с ограничениями системы внутри docker контейнера на одновременные соединения /proc/net/core/somaxconn (по умолчанию 128).
«значит Вы используете проброс портов от docker-а и они все слушают на внешнем интерфейсе железки» — не обязательно так. Можно использовать внутреннею сеть докера и dns записи привязанные к контейнеру и его внутреннему IP. Docker поддерживает multihost networking, но в таком варианте каждый микросервис состоит не из 1, а из 2х и более контейнеров: 1 — nginx к которому идет обращение по внутреннему dns имени и внутри которого upstream (внутрениие IP адреса) на контейнеры с phpfpm\go\python, таким образом достигается гибкое горизонтальное маштабирование.
А чем реализована внутренняя сеть докера и dns? Если на всех хостах сеть докера будет 172.17.0.0/16, то он сам разрулит какой IP какому контейнеру и они не будут пересекаться?
Если в кратце, то создается сеть внутри докера с признаком overlay, создается 172.18.0.0/16 и все контейнеры которые запускатся в этой сети используют этот диапозон, и да, докер сам разрулит.
Есть есть время прочитайте https://docs.docker.com/engine/userguide/networking/dockernetworks/ и https://docs.docker.com/engine/userguide/networking/get-started-overlay/

Ещё варианты решения проблемы с пробросом портов:


  • использовать -p server_int_ip:server_port:container_port;
  • запускать docker -d/docker daemon с --iptables=false и управлять пробрасыванием портов самостоятельно.

Второй вариант мы используем на системах с centos 7 и firewalld, соответственно для моста докера создаётся отдельная зона, на ней открываются нужные порты, используется docker-proxy, который слушает 0.0.0.0. По умолчанию, доступа снаружи к этим портам нет, т. к. оно закрыто firewall'ом. ICC работает из коробки при использовании опции --link, а при использовании внешних/vpn-адресов для зоны docker разрешается доступ к нужным портам (или ко всем сразу, как вариант).


firewall-cmd --zone=docker --list-all
docker (active)
  interfaces: docker0
  sources: 
  services: 
  ports: 1024-65535/tcp
  masquerade: no
  forward-ports: 
  icmp-blocks: 
  rich rules:

Добавлю, что при этом у нас используется tinc (p2p vpn), что даёт прозрачную сеть всему кластеру.

scriptuoz, не опишете поподробней устройство именно микросервисов? Хотелось бы копнуть поглубже, увидеть их изнутри. Т.е. они у вас синхронные или асинхронные, с внутренней очередью задач и т.д. Насколько для асинхронных задач хорошо подошёл REST, не применяли ли вебсокеты?
claygod извиняюсь за задержку.
в одном комментарии пожалуй все сразу не расскажешь. У нас есть некоторые требовавния к микросервисам, они должны предоставлять HTTP API, они должны слать метрики и должны слать логи. Эти требования обязательные. Все остальное в общем достаточно гибко, каждый микросервис может быть реализован на каком-то своем технологическом стеке, в том числе и каждый сервис сам решает нужна ли ему внутри очередь или нет. В какой то мере диспетчер выполняет роль очереди на уровне приложения, тем не менее каждый микросервис может использовать свои очереди, есть доступные инструменты, которые уже настроены и работают и которыми может воспользоваться любой сервис, например RabbitMQ.
Микросервисы у нас по своей природе асинхронные, более того все общение происходит через диспетчер, куда каждый микросервис посылает событие при изменившемся состоянии, на это событие могут реагировать другие сервисы в результате чего также изменят свое состояние и кинут свое событие в диспетчер.
на счет REST не могу сказать, что мы во всех микросервисах остались в рамках идеально чистого REST, но в целом это работает, что касается асинхронных задач, то в целом наш подход похож на описываемый здесь http://restcookbook.com/Resources/asynchroneous-operations/
вебсокеты не применяли, пока. В ближайшее время выкатим решение на SSE
Спасибо, очень интересный ответ!

Как я понял, микросервис получив запрос отдаёт 202 (принял), и потом в диспетчер кидает подготовленный ответ. Если микросервис быстрый, вы оставляете диспетчера ждать ответа, или он в любом случае неблокирующийся? А есть блокирующиеся варианты, т.е. где вы в угоду скорости отказались от асинхронности?
Диспетчер от сервиса должен дождаться только подтверждения, что сообщение получено, задерживать диспетчер не хорошо, у него работы много =)
Да, есть блокирующиеся вызовы, это обращения напрямую от сервиса к сервису, такие у нас тоже есть, но их очень мало, как правило это получение каких-то данных, которые есть в другом сервисе. Мы иногда думаем над тем, чтобы заменить и такие вызовы, чем то вроде:
сервис А кидает сообщение в диспетчер что ему нужны данные Y (событие: «нужны данные Y»)
диспетчер посылает уведомление в сервисы/сервис которые обладают этими данными, на самом деле диспетчер не знает, кто обладает этими данными, диспетчер просто знает, что сервис B попросил уведомлять его о событиях «нужны данные Y»
сервис или сервисы B получив такое уведомление, формируют данные и в событии отправляют их в диспетчер, кто быстрее тот и молодец, диспетчер перенаправляет событие с данными в запросивший сервис А. Это позволит избежать ожиданий, когда один сервис висит ожидая данных от другого, к тому же есть возможность получить данные от того сервиса кто сделает это быстрее. Но пока мы в случае когда необходимо просто получить данные другого сервиса у нас выполняется прямой запрос из одного сервиса в другой, таких вызовов очень немного и м не хотим чтобы их кол-во росло, потому что сложнее за всем этим потом следить.
Да, есть блокирующиеся вызовы, это обращения напрямую от сервиса к сервису, такие у нас тоже есть, но их очень мало, как правило это получение каких-то данных, которые есть в другом сервисе.

Я понял, что это небольшое отступление от правил во имя производительности ))

По поводу блокировок я вот почему спросил:
Есть комплекс юнитов, брокеров и т.д. который можно назвать приложением. Снаружи к приложению не приходят запросы, на которые надо дать мгновенный ответ? Как решается такого рода проблема?

Ещё интересно, нет ли у вас какой-то «песочницы» для микросервисов, в которой они обкатываются? Какое-то тренировочное место… И есть ли дефолтное ТЗ для разработки микросервиса? Любопытно было бы посмотреть.
На счет запросов снаружи не совсем понял суть проблемы. Если имеется в виду как организовано общение с браузером клиента, то это примерно так: есть приложение SPA на Reactjs, которое общается с нашим API, которое доступно снаружи, API в свою очередь посылает события в диспетчер и точно также получает события из диспетчера по результатам, которых меняет свое состояние. Сейчас идем к тому, что API будет получая определенные события, отправлять сообщения на клиент посредством SSE.

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

Дефолтного ТЗ нет, мы стараемся не пользоваться шаблонами в таких вопросах как постановка задач, каждый раз это написание с нуля и продумывание деталей, зато с каждым разом мы открываем все новые и новые детали =)
Sign up to leave a comment.

Articles