Русский
Русский
English
Статистика
Реклама

Docker swarm

Конференция DEVOXX UK. Выбираем фреймворк Docker Swarm, Kubernetes или Mesos. Часть 2

14.06.2020 10:15:40 | Автор: admin
Docker Swarm, Kubernetes и Mesos являются наиболее популярными фреймворками для оркестровки контейнеров. В своем выступлении Арун Гупта сравнивает следующие аспекты работы Docker, Swarm, и Kubernetes:

  • Локальный девелопмент.
  • Функции развертывания.
  • Мультиконтейнерные приложения.
  • Обнаружение служб service discovery.
  • Масштабирование сервиса.
  • Run-once задания.
  • Интеграция с Maven.
  • Скользящее обновление.
  • Создание кластера БД Couchbase.

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

Арун Гупта главный технолог open-source продуктов Amazon Web Services, который уже более 10 лет развивает сообщества разработчиков Sun, Oracle, Red Hat и Couchbase. Имеет большой опыт работы в ведущих кросс-функциональных командах, занимающихся разработкой и реализацией стратегии маркетинговых кампаний и программ. Руководил группами инженеров Sun, является одним из основателей команды Java EE и создателем американского отделения Devoxx4Kids. Арун Гупта является автором более 2 тысяч постов в IT-блогах и выступил с докладами более чем в 40 странах.

Конференция DEVOXX UK. Выбираем фреймворк: Docker Swarm, Kubernetes или Mesos. Часть 1

Концепт масштабирования Scale означает возможность управлять количеством реплик, увеличивая или уменьшая число экземпляров приложения.



Для примера: если хотите масштабировать систему до 6 реплик, используйте команду docker service scale web=6.

Наряду с концептом Replicated Service в Docker существует концепт общих сервисов Global Service. Скажем, я хочу запустить экземпляр одного и того же контейнера на каждом узле кластера, в данном случае это контейнер приложения веб-мониторинга Prometheus. Это приложение используется, когда требуется собрать метрики о работе хостов. В этом случае вы используете подкоманду - mode=global - name=prom prom/Prometheus.



В результате приложение Prometheus будет запущено на всех узлах кластера без исключения, и если в кластер будут добавлены новые ноды, то оно автоматически запустится в контейнере и в этих нодах. Надеюсь, вы поняли разницу между Replicated Service и Global Service. Обычно Replicated Service это то, с чего вы начинаете.



Итак, мы рассмотрели основные понятия, или основные сущности Docker, а теперь рассмотрим сущности Kubernetes. Kubernetes это тоже своего рода планировщик, платформа для оркестровки контейнеров. Нужно помнить, что основной концепт планировщика это знание того, как запланировать работу контейнеров на разных хостах. Если же перейти на более высокий уровень, можно сказать, что оркестровка означает расширение ваших возможностей до управления кластерами, получения сертификатов и т.д. В этом смысле и Docker, и Kubernetes являются платформами оркестровки, причем оба имеют встроенный планировщик.

Оркестровка представляет собой автоматизированное управление связанными сущностями кластерами виртуальных машин или контейнеров.Kubernetes это совокупность сервисов, реализующих контейнерный кластер и его оркестровку. Он не заменяет Docker, но серьёзно расширяет его возможности, упрощая управление развертыванием, сетевой маршрутизацией, расходом ресурсов, балансировкой нагрузки и отказоустойчивостью запускаемых приложений.
По сравнению с Kubernetes, Docker ориентирован именно на работу с контейнерами, создавая их образы с помощью docker-файла. Если сравнить объекты Docker и Kubernetes, можно сказать, что Docker управляет контейнерами, в то время как Kubernetes управляет самим Docker.
Кто из вас имел дело с контейнерами Rocket? А кто-нибудь использует Rocket в продакшене? В зале поднял руку всего один человек, это типичная картина. Это альтернатива Docker, которая до сих пор не прижилась в сообществе разработчиков.

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



Таким образом, вы можете разместить в одном поде, например, WildFly контейнер, логин-контейнер, прокси-контейнер, или кэш-контейнер, причем вы должны ответственно подходить к составу компонентов контейнера, который собираетесь масштабировать.

Обычно вы обертываете свой контейнер в набор реплик Replica Set, поскольку хотите запускать в поде определенное количество экземпляров. Replica Set приказывает запустить столько реплик, сколько требует сервис масштабирования Docker, и указывает, когда и каким образом это проделать.

Поды похожи на контейнеры в том смысле, что если происходит сбой пода на одном хосте, он перезапускается на другом поде с другим IP-адресом. Как Java-разработчик, вы знаете, что когда создаете java-приложение и оно связывается с базой данных, вы не можете полагаться на динамический IP-адрес. В этом случае Kubernetes использует Service этот компонент публикует приложение как сетевой сервис, создавая статичное постоянное сетевое имя для набора подов, одновременно реализуя балансировку нагрузки между подами. Можно сказать, что это служебное имя базы данных, и java-приложение не полагается на IP-адрес, а взаимодействует только с постоянным именем базы данных.

Это достигается тем, что каждый Pod снабжается определенной меткой Label, которые хранятся в распределенном хранилище etcd, и Service следит за этими метками, обеспечивая связь компонентов. То есть поды и сервисы устойчиво взаимодействуют друг с другом с помощью этих меток.

Теперь давайте рассмотрим, как создать кластер Kubernetes. Для этого, как и в Docker, нам нужен мастер-нод и рабочий нод. Нод в кластере обычно представлен физической или виртуальной машиной. Здесь, как и в Docker, мастер представляет собой центральную управляющую структуру, которая позволяет контролировать весь кластер через планировщик и менеджер контроллеров. По умолчанию мастер-нод существует в единственном числе, но есть множество новых инструментов, позволяющих создавать несколько мастер-нодов.



Master-node обеспечивает взаимодействие с пользователем при помощи API-сервера и содержит распределенное хранилище etcd, в котором находится конфигурация кластера, статусы его объектов и метаданные.

Рабочие узлы Worker-node предназначены исключительно для запуска контейнеров, для этого в них установлены два сервиса Kubernetes сетевой маршрутизатор proxy service и агент планировщика kubelet. Во время работы этих узлов Docker выполняет их мониторинг с помощью systemd (CentOS) или monit (Debian) в зависимости от того, какой операционной системой вы пользуетесь.

Рассмотрим архитектуру Kubernetes более широко. У нас имеется Master, в составе которого присутствуют API сервер (поды, сервисы и т.д.), управляемый с помощью CLI kubectl. Kubectl позволяет создавать ресурсы Kubernetes. Он передает API-серверу команды типа создать под, создать сервис, создать набор реплик.

Далее здесь имеется планировщик Scheduler, менеджер контроллеров Controller Manager и хранилище etcd. Менеджер контроллеров, получив указания API-сервера, сопоставляет метки реплик с метками подов, обеспечивая устойчивое взаимодействие компонентов. Планировщик, получив задание создать под, просматривает рабочие ноды и создает его там, где это предусмотрено. Естественно, что он получает эту информацию из etcd.

Далее у нас имеется несколько рабочих нодов, и API-сервер общается с содержащимися в них Kubelet-агентами, сообщая им, как должны создаваться поды. Здесь расположен прокси, который предоставляет вам доступ к приложению, использующему эти поды. Справа на слайде показан мой клиент это запрос интернета, который поступает в балансировщик нагрузки, тот обращается к прокси, который распределяет запрос по подам и направляет ответ обратно.



Вы видите итоговый слайд, который изображает кластер Kubernetes и то, как работают все его компоненты.

Давайте подробнее поговорим о Service Discovery и балансировщике нагрузки Docker. Когда вы запускаете свое Java-приложение, обычно это происходит в нескольких контейнерах на нескольких хостах. Существует компонент Docker Compose, который позволяет с легкостью запускать мультиконтейнерные приложения. Он описывает мультиконтейнерные приложения и запускает их при помощи одного или нескольких yaml-файлов конфигурации.



По умолчанию это файлы docker-compose.yaml и docker-compose.override.yaml, при этом множества файлов указываются с помощью f. В первой файле вы прописываете сервис, образы, реплики, метки и т.д. Второй файл используется для перезаписи конфигурации. После создания docker-compose.yaml он развертывается в мультихостовом кластере, который ранее создал Docker Swarm. Вы можете создать один базовый файл конфигурации docker-compose.yaml, в который будете добавлять файлы специфических конфигураций для разных задач с указанием определенных портов, образов и т.д., позже мы поговорим об этом.

На этом слайде вы видите простой пример файла Service Discovery. В первой строке указывается версия, а строка 2 показывает, что он касается сервисов db и web.



Я добиваюсь того, чтобы после поднятия мой web-сервис мог общаться с db-сервисом. Это простые java-приложения, развертываемые в контейнерах WildFly. В 11 строке я прописываю среду couchbase_URI=db. Это означает, что мой db-сервис использует эту базу данных. В строке 4 указан образ couchbase, а в строках 5-9 и 15-16 соответственно порты, необходимые для обеспечения работы моих сервисов.

Ключом к пониманию процесса обнаружения сервисов служит то, что вы создаете некоторого рода зависимости. Вы указываете, что web-контейнер должен запуститься раньше db-контейнера, но это только на уровне контейнеров. Как реагирует ваше приложение, как оно стартует это совершенно другие вещи. Например, обычно контейнер поднимается за 3-4 секунды, однако запуск контейнера базы данных занимает гораздо больше времени. Так что логика запуска вашего приложения должна быть запечена в вашем java-приложении. То есть приложение должно пинговать базу данных, чтобы убедиться в ее готовности. Поскольку база данных couchbase это REST API, вы должны вызвать этот API и спросить: Эй, ты готов? Если да, то я готов присылать тебе запросы!

Таким образом, зависимости на уровне контейнеров определяются с помощью сервиса docker-compose, но на уровне приложений зависимости и жизнеспособность определяются на основе опросов responsibility. Затем вы берете файл docker-compose.yaml и развертываете в мультихостовом Docker с помощью команды docker stack deploy и подкоманды - compose-file= docker-compose.yaml webapp. Итак, у вас есть большой стек, в котором расположено несколько сервисов, решающих несколько задач. В основном это задачи запуска контейнеров.



Рассмотрим, как работает балансировщик нагрузки Load balancer. В приведенном примере я с помощью команды docker service create создал сервис WildFly контейнер, указав номер порта в виде 8080:8080. Это означает, что порт 8080 на хосте локальной машине будет увязан с портом 8080 внутри контейнера, так что вы сможете получить доступ к приложению через localhost:8080. Это будет порт доступа ко всем рабочим узлам.



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

Итак, запросы клиента поступают балансировщику нагрузки, он перенаправляет их на любой из хостов, и если, используя таблицу IP-адресов, попадает на хост с незапущенным контейнером, то автоматически перенаправляет запрос хосту, на котором запущен контейнер.

Одиночный хоп обходится не дорого, но он полностью бесшовный в отношении масштабирования ваших сервисов в сторону увеличения или уменьшения. Благодаря этому вы можете быть уверены, что ваш запрос попадет именно тому хосту, где запущен контейнер.
Теперь давайте рассмотрим, как работает Service Discovery в Kubernetes. Как я говорил, сервис это абстракция в виде набора подов с одинаковым IP-адресом и номером порта и простым TCP/UDP балансировщиком нагрузки. На следующем слайде показан файл конфигурации Service Discovery.



Создание таких ресурсов, как поды, сервисы, реплики и т.д. происходит на основе файла конфигурации. Вы видите, что он разбит на 3 части с помощью строк 17 и 37, которые состоят только из - -.

Посмотрим сначала на строку 39 в ней значится kind: ReplicaSet, то есть то, что мы создаем. В строках 40-43 расположены метаданные, с 44 строки указывается спецификация для нашего набора реплик. В строке 45 указано, что у меня имеется 1 реплика, ниже указаны ее метки Labels, в данном случае это имя wildfly. Еще ниже, начиная с 50 строки, указывается, в каких контейнерах должна запускаться данная реплика это wildfly-rs-pod, а строки 53-58 содержат спецификацию этого контейнера.

23:05 мин

Продолжение будет совсем скоро


Немного рекламы :)


Спасибо, что остаётесь с нами. Вам нравятся наши статьи? Хотите видеть больше интересных материалов? Поддержите нас, оформив заказ или порекомендовав знакомым, облачные VPS для разработчиков от $4.99, уникальный аналог entry-level серверов, который был придуман нами для Вас: Вся правда о VPS (KVM) E5-2697 v3 (6 Cores) 10GB DDR4 480GB SSD 1Gbps от $19 или как правильно делить сервер? (доступны варианты с RAID1 и RAID10, до 24 ядер и до 40GB DDR4).

Dell R730xd в 2 раза дешевле в дата-центре Equinix Tier IV в Амстердаме? Только у нас 2 х Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6GHz 14C 64GB DDR4 4x960GB SSD 1Gbps 100 ТВ от $199 в Нидерландах! Dell R420 2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB от $99! Читайте о том Как построить инфраструктуру корп. класса c применением серверов Dell R730xd Е5-2650 v4 стоимостью 9000 евро за копейки?
Подробнее..

Конференция DEVOXX UK. Выбираем фреймворк Docker Swarm, Kubernetes или Mesos. Часть 3

15.06.2020 16:10:07 | Автор: admin
Docker Swarm, Kubernetes и Mesos являются наиболее популярными фреймворками для оркестровки контейнеров. В своем выступлении Арун Гупта сравнивает следующие аспекты работы Docker, Swarm, и Kubernetes:

  • Локальный девелопмент.
  • Функции развертывания.
  • Мультиконтейнерные приложения.
  • Обнаружение служб service discovery.
  • Масштабирование сервиса.
  • Run-once задания.
  • Интеграция с Maven.
  • Скользящее обновление.
  • Создание кластера БД Couchbase.

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

Арун Гупта главный технолог open-source продуктов Amazon Web Services, который уже более 10 лет развивает сообщества разработчиков Sun, Oracle, Red Hat и Couchbase. Имеет большой опыт работы в ведущих кросс-функциональных командах, занимающихся разработкой и реализацией стратегии маркетинговых кампаний и программ. Руководил группами инженеров Sun, является одним из основателей команды Java EE и создателем американского отделения Devoxx4Kids. Арун Гупта является автором более 2 тысяч постов в IT-блогах и выступил с докладами более чем в 40 странах.

Конференция DEVOXX UK. Выбираем фреймворк: Docker Swarm, Kubernetes или Mesos. Часть 1
Конференция DEVOXX UK. Выбираем фреймворк: Docker Swarm, Kubernetes или Mesos. Часть 2

В строке 55 содержится COUCHBASE_URI, указывающий к на сервис этой базы данных, который тоже создан с помощью файла конфигурации Kubernetes. Если посмотреть на строку 2, можно увидеть kind: Service это сервис, который я создаю под именем couchbase-service, и это же имя указанно в строке 4. Ниже приводятся несколько портов.



Ключевыми строками являются 6 и 7. В service я говорю: Эй, вот эти метки, которые я ищу!, и эти labels не что иное, как имена пар переменных, а строка 7 указывает на мое приложение couchbase-rs-pod. Далее перечислены порты, дающие доступ к этим самым labels.

В строке 19 я создаю новый тип ReplicaSet, в строке 31 содержится имя образа, а строки 24-27 указывают на метаданные, ассоциированные с моим подом. Это именно то, что ищет service и с чем должно быть установлено соединение. В конце файла расположен некий вид связи строк 55-56 и 4, говорящий: используй этот service!.

Итак, я запускаю свой service при наличии набора реплик, и поскольку каждый набор реплик имеет свой порт с соответствующей меткой, он включается в состав service. С точки зрения разработчика, вы просто обращаетесь к сервису, который затем задействует нужный вам набор реплик.

В итоге у меня имеется под WildFly, который общается с бэкендом базы данных через Couchbase Service. Я могу использовать фронтенд с несколькими подами WildFly, который также через couchbase service связывается с бэкендом couchbase.



Позже мы рассмотрим, как service, расположенный вне кластера, через свой IP-адрес общается с элементами, которые расположены внутри кластера и имеют внутренний IP-адрес.

Итак, stateless-контейнеры это хорошо, но насколько хороша идея использовать stateful-контейнеры? Давайте рассмотрим настройки системы для stateful, или постоянных контейнеров. В Docker существует 4 различных подхода к расположению хранилища данных, на которые следует обратить внимание. Первый это Implicit Per-Container, означающий, что при использовании satateful-контейнеров couchbase, MySQL или MyDB, все они запускаются с дефолтным Sandbox. То есть все, что хранится в базе данных, хранится в самом контейнере. Если контейнер пропадает, данные пропадают вместе с ним.

Второй это Explicit Per-Container, когда вы создаете конкретное хранилище командой docker volume create и сохраняете в нем данные. Третий подход Per-Host связан с маппингом хранилищ, когда все, что хранится в контейнере, одновременно дублируется на хосте. Если контейнер даст сбой, данные останутся на хосте. Последнее это использование нескольких хостов Multi-Host, что целесообразно на стадии продакшена различных решений. Предположим, ваши контейнеры с вашими приложениями запущены на хосте, но при этом вы хотите хранить свои данные где-то в интернете, причем для этого используется автоматический маппинг для распределенных систем.



Каждый из этих методов использует конкретное местоположение хранилища. Implicit и Explicit Per-Container хранят данные на хосте по адресу /var/lib/docker/volumes. При использовании метода Per-Host хранилище монтируется внутри контейнера, а сам контейнер монтируется на хосте. Для мультихостов могут использоваться решения типа Ceph, ClusterFS, NFS и т.д.

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

При сбое хоста директория хранилища недоступна в трех первых случаях, в последнем случае связь с хранилищем не прерывается. Наконец, функция shared полностью исключена для хранилища в первом случае и возможна в остальных. Во втором случае можно поделиться хранилищем в зависимости от того, поддерживает ваша база данных распределенные хранилища или нет. В случае Per-Host распределение данных возможно только на данном хосте, а для мультихоста оно обеспечивается расширением кластера.

Это следует учитывать при создании stateful-контейнеров. Еще один полезный инструмент Docker это плагин Volume, работающий по принципу батареи присутствуют, но подлежат замене. При запуске контейнера Docker он говорит: Эй, запустив контейнер с базой данных, ты можешь хранить свои данные в этом контейнере! Эта функция по умолчанию, но вы можете ее изменить. Этот плагин позволяет использовать вместо контейнерной БД сетевой диск или что-то подобное. Он включает в себя драйвер по умолчанию для host-based хранилищ и позволяет интеграцию контейнеров с внешними системами хранения данных, такими как Amazon EBS, Azure Storage и постоянными дисками GCE Persistent.

На следующем слайде показана архитектура плагина Docker Volume.



Синим цветом обозначен клиент Docker, связанный с синим Docker-хостом, на котором имеется Local storage engine, предоставляющий вам контейнеры для хранения данных. Зеленым цветом обозначены клиент Plugin Client и Plugin Daemon, которые также подсоединены к хосту. Они предоставляют возможность хранить данные в сетевых хранилищах нужного вам вида Storage Backend.

Плагин Docker Volume может использоваться с хранилищем Portworx. Модуль PX-Dev собственно является запускаемым вами контейнером, который подключается к Docker-хосту и позволяет легко сохранять данные на Amazon EBS.



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

Концепция хранилищ в Kubernetes похожа на Docker и представлена директориями, которые доступны вашему контейнеру в поде. Они не зависимы от времени жизни любого контейнера. Наиболее распространенными доступными типами хранилищ являются hostPath, nfs, awsElasticBlockStore и gsePersistentDisk. Рассмотрим, как работают эти хранилища в Kubernetes. Обычно процесс их подключения состоит из 3 шагов.

Первый заключается в том, что кто-то на стороне сети, обычно это администратор, предоставляет вам постоянное хранилище. Для этого имеется соответствующий файл конфигурации PersistentVolume. Далее разработчик приложения пишет файл конфигурации под названием PersistentVolumeClaim, или запрос на хранилище PVC, в котором говорится: у меня подготовлено распределенное хранилище объемом 50Гб, но чтобы другие люди тоже могли использовать его емкость, я сообщаю в этом PVC, что сейчас мне нужно всего 10 Гб. Наконец, третий шаг состоит в том, что ваш запрос монтируется как хранилище, и приложение, в котором имеется под, или набор реплик, или что-то подобное, начинает им пользоваться. Важно помнить, что этот процесс состоит из 3-х упомянутых этапов и позволяет масштабирование.



На следующем слайде показан постоянный контейнер Kubernetes Persistence Container архитектуры AWS.



Внутри коричневого прямоугольника, который изображает кластер Kubernetes, расположен один мастер-нод и два рабочих нода, обозначенные желтым цветом. В одном из worker node находится оранжевый под, хранилище, контроллер реплик и зеленый контейнер Docker Couchbase. Внутри кластера над нодами прямоугольником лилового цвета обозначен доступный извне Service. Эта архитектура рекомендуется для сохранения данных на самом устройстве. При необходимости я могу хранить свои данные в EBS за пределами кластера, как это показано на следующем слайде. Это типичная модель для масштабирования, однако при ее применении нужно учитывать финансовый аспект хранить данные где-то в сети может быть дороже, чем на хосте. При выборе решений контейнеризации это один из весомых аргументов.



Так же, как в случае с Docker, вы можете использовать постоянные контейнеры Kubernetes вместе с Portworx.



Это то, что в нынешней терминологии Kubernetes 1.6 называется StatefulSet способ работы со Stateful-приложениями, которым обрабатываются события об остановке работы Pod и осуществления Graceful Shutdown. В нашем случае такими приложениями являются базы данных. В моем блоге вы можете прочесть, как создавать StatefulSet в Kubernetes при помощи Portworx.
Давайте поговорим про аспект разработки. Как я сказал, Docker имеет 2 версии СЕ и ЕЕ, в первом случае речь идет о стабильной версии Community Edition, которая обновляется раз в 3 месяца в отличие от ежемесячно обновляемой версии ЕЕ. Вы можете скачать Docker для Mac, Linux или Windows. После установки Docker будет автоматически обновляться, и начать работать с ним очень легко.



В Kubernetes я предпочитаю версию Minikube это хороший способ начать работу с этой платформой путем создания кластера на одиночном узле. Для создания кластеров из нескольких нодов выбор версий шире: это kops, kube-aws (CoreOS+AWS), kube-up (устарела). Если вы собираетесь использовать Kubernetes на основе AWS, рекомендую присоединится к группе AWS SIG, которая встречается в сети каждую пятницу и публикует различные интересные материалы по работе с Kubernetes AWS.

Рассмотрим, как на этих платформах выполняется скользящее обновление Rolling Update. Если имеется кластер из нескольких нодов, то в нем используется конкретная версия образа, например, WildFly:1. Скользящее обновление означает, что версия образа последовательно заменяется новой на каждом ноде, один за другим.



Для этого используется команда docker service update (имя сервиса), в которой я указываю новую версию образа WildFly:2 и метод обновления update-parallelism 2. Цифра 2 означает, что система будет обновлять по 2 образа приложения одновременно, затем последует 10-ти секундная задержка update delay 10s, после чего будут обновлены 2 следующих образа еще на 2-х нодах, и т.д. Этот простой механизм скользящего обновления предоставляется вам как составная часть Docker.

В Kubernetes скользящее обновление происходит таким образом. Контроллер репликации rc создает набор реплик одной версии, и каждый под в этом webapp-rc снабжен меткой label, находящейся в etcd. Когда мне нужен какой-то под, я через Application Service обращаюсь к хранилищу etcd, которое по указанной метке предоставляет мне этот под.



В данном случае у нас в Replication controller имеется 3 пода, в которых запущено приложение WildFly версии 1. При обновлении в фоновом режиме создается еще один контроллер репликации с тем же именем и индексом в конце - xxxxx, где х случайные числа, и с теми же метками labels. Теперь Application Service располагает тремя подами с приложением старой версии и тремя подами с новой версией в новом Replication controller. После этого старые поды удаляются, контроллер репликации с новыми подами переименовывается и включается в работу.



Перейдем к рассмотрению мониторинга. В Docker имеется множество встроенных команд для мониторинга. Например, интерфейс командной строки docker container stats позволяет каждую секунду выводить в консоль данные о состоянии контейнеров использование процессора, диска, загрузку сети. Инструмент Docker Remote API предоставляет данные о том, как клиент общается с сервером. Он использует простые команды, но в его основе лежит Docker REST API. В данном случае слова REST, Flash, Remote обозначают одно и то же. Когда вы общаетесь с хостом, это REST API. Docker Remote API позволяет получить больше сведений о запущенных контейнерах. В моем блоге изложены детали использования этого мониторинга с Windows Server.

Мониторинг системных событий docker system events при запуске мультихостового кластера дает возможность получить данные о падении хоста или падении контейнера на конкретном хосте, масштабировании сервисов и тому подобного. Начиная с версии Docker 1.20, в него включен Prometheus, который осуществляет встраивание endpoints в существующие приложения. Это позволяет получать метрики через HTTP и отображать их на панелях мониторинга.

Еще одна функция мониторинга это cAdvisor (сокращение от container Advisor). Он анализирует и предоставляет данные об использовании ресурсов и производительности из запущенных контейнеров, предоставляя метрики Prometheus прямо из коробки. Особенность этого инструмента в том, что он предоставляет данные только за последние 60 секунд. Поэтому вам нужно предусмотреть возможность собирать эти данные и помещать в базу данных, чтобы иметь возможность мониторинга длительного по времени процесса. Его также можно использовать для отображения метрик на панели мониторинга в графическом виде с помощью Grafana или Kibana. В моем блоге есть подробное описание того, как использовать cAdvisor для мониторинга контейнеров с помощью панели Kibana.

На следующем слайде показано, как выглядит результат работы Prometheus endpoint и доступные для отображения метрики.



Слева внизу вы видите метрики HTTP-запросов, ответов и т.д., справа их графическое отображение.

Kubernetes также содержит встроенные инструменты мониторинга. На этом слайде показан типовой кластер, содержащий один мастер и три рабочих узла.



В каждом из рабочих нодов расположен автоматически запускаемый cAdvisor. Кроме этого, здесь имеется Heapster система мониторинга производительности и сбора метрик, совместимая с Kubernetes версии 1.0.6 и выше. Heapster позволяет собирать не только показатели производительности рабочих нагрузок, модулей и контейнеров, но и события и другие сигналы, генерируемые целым кластером. Для сбора данных он общается с Kubelet каждого пода, автоматически сохраняет информацию в базе данных InfluxDB и выводит в виде метрик на панель мониторинга Grafana. Однако учтите если вы используете miniKube, эта функция по умолчанию недоступна, поэтому для мониторинга придется использовать аддоны. Так что все зависит от того, где вы запускаете контейнеры и какими инструментами мониторинга можете воспользоваться по умолчанию, а какие требуется установить в виде отдельных дополнений.

На следующем слайде изображены панели мониторинга Grafana, которые показывают рабочее состояние моих контейнеров. Здесь достаточно много интересных данных. Конечно, существует множество коммерческих инструментов мониторинга процессов Docker и Kubernetes, например SysDig, DataDog, NewRelic. Некоторые из них имеют 30-ти бесплатный пробный период, так что можно попробовать и подобрать себе наиболее подходящий. Лично я предпочитаю использовать SysDig и NewRelic, которые отлично интегрируются в Kubernetes. Существуют инструменты, которые одинаково хорошо интегрируются в обе платформы и Docker, и Kubernetes.


Немного рекламы :)


Спасибо, что остаётесь с нами. Вам нравятся наши статьи? Хотите видеть больше интересных материалов? Поддержите нас, оформив заказ или порекомендовав знакомым, облачные VPS для разработчиков от $4.99, уникальный аналог entry-level серверов, который был придуман нами для Вас: Вся правда о VPS (KVM) E5-2697 v3 (6 Cores) 10GB DDR4 480GB SSD 1Gbps от $19 или как правильно делить сервер? (доступны варианты с RAID1 и RAID10, до 24 ядер и до 40GB DDR4).

Dell R730xd в 2 раза дешевле в дата-центре Equinix Tier IV в Амстердаме? Только у нас 2 х Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6GHz 14C 64GB DDR4 4x960GB SSD 1Gbps 100 ТВ от $199 в Нидерландах! Dell R420 2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB от $99! Читайте о том Как построить инфраструктуру корп. класса c применением серверов Dell R730xd Е5-2650 v4 стоимостью 9000 евро за копейки?
Подробнее..

Docker swarm и балансировка нагрузки по нодам

03.11.2020 18:06:31 | Автор: admin

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

1) Описание проблемы

Чтобы понять проблему, рассмотрим ее на примере проекта нашей компании. Исторически сложилось, что мы использовали монолитную архитектуру с оркестрацией на docker swarm. Помимо монолита у нас имеется ряд вспомогательных сервисов и консьюмеров. Источником основной нагрузки на сервера выступает php-fpm, который выполняет код монолита. В продакшене мы имели следующую схему.

На схеме показаны два сервера. Первый сервер DB1 это MySQL база данных, которая не управляется Docker Swarm, поскольку установлена непосредственно на основную систему для большей производительности при работе с диском. Второй Web 1 сервер, это непосредственно наш монолит с его консьюмерами и сервисами, которые запущены внутри. По данной схеме видно, что не все возможности оркестрации используются , поскольку у нас единственный сервер. Отказоустойчивость также очень мала в случае падения сервера весь наш продукт становится не работоспособным.

На начальном этапе это решение закрывало задачи которые стояли пред нами. Swarm снял с нас надобность следить и обновлять вручную контейнеры меньше ручных операций и больше автоматизации.

Данная схема достаточно хорошо работала, но с ростом количества пользователей нагрузка на сервер Web 1 значительно росла и становилось понятно, что его мощностей уже не достаточно. Мы понимали, что покупать более мощный сервер менее перспективно в плане отказоустойчивости и дороже по цене, чем масштабироваться горизонтально, увеличивая количество серверов. К тому же у нас в продакшене уже был готовый инструмент на сервере Web1, который успешно выполнял свою задачу. Поэтому мы добавили под управление Docker Swarm еще один сервер. Получилась следующая схема.

Мы получили кластер из двух серверов, в котором Web 1 является master нодой, а web2 обычный worker. В этой схеме мы были уверены в master ноде, поскольку это все тот же сервер, который у нас был . Мы понимали, что он надежный и имеет высокую доступность. А вот сервер Web 2 был темной лошадкой, поскольку его выбрали cloud сервером, исходя из ценовой политики, который ранее не испытывали в продакшене. При этом сервера не находятся в одном помещении, поэтому могут быть проблемы с сетевым взаимодействием.

Отсюда мы получили следующие важные для нас критерии: кластер должен автоматически перестраиваться в случае отказа воркера (Web 2) и забирать всю нагрузку и сервисы на себя, но после появления воркера (Web 2) автоматически раскидывать всю нагрузку обратно равномерно по серверам. По сути, это стандартная задача, которую должен решать Docker Swarm.

Мы провели эксперимент, отключили сервер Web 2 сами и посмотрели, что будет делать Swarm. Он сделал, что и ожидалось поднял все сервисы на master ноде (Web 1). Проверив то, что наш кластер верно себя ведет при отказе второго сервера, мы обратно включили Web 2.

На этом этапе мы обнаружили первую проблему нагрузка осталась по прежнему на сервере Web 1 и Docker Swarm лишь запустил сервисы, которые запускались глобально для всего кластера. Столкнувшись с первым ограничением, мы поняли, что сервера не так часто становятся недоступными. Поэтому в случае отказа Web 2 сервера, мы сами проведем балансировку, воспользовавшись командой:

docker service update  --force

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

Спустя некоторое время, выполняя deploy кода на боевой кластер, мы начали замечать, что иногда после обновления контейнеров нагрузка снова делилась неравномерно по серверам. Причиной этого факта было то, что основной сервис в нашем кластере php-fpm, который является источником нагрузки, запускал больше php-fpm реплик (контейнеров) на одном из серверов, чем на другом. Эта проблема была достаточно критичной, поскольку мы хотели равномерной утилизации серверов и не перегружать один из них, а также проводить deploy без вхождений на сервер и ручной балансировки этих реплик.

Первое очевидное решение, которое пришло на ум выставить deploy сервиса php-fpm глобально, чтобы Swarm сам их запускал на каждой доступной ноде. Но данное решение было не очень подходящим в перспективе, поскольку не факт, что кластер будет содержать ноды только для обработки запросов пользователей хотелось оставить гибкость в настройке кластера и иметь возможность не запускать php-fpm реплику на какой-то группе серверов.

Обратившись к документации Docker, мы нашли следующий вариант: для разрешения проблемы распределения контейнеров по серверам, Docker Swarm имеет механизм placement, который позволяет указать конкретному сервису на каких серверах с каким label запускать контейнеры. Он дает возможность запустить контейнеры на ряде серверов в кластере, но все так же остается проблема с балансировкой. Для ее решения в Docker документации предлагается установить лимиты на ресурсы и зарезервировать в Docker Swarm необходимые нам мощности. Такой подход в связке с placement казался самым подходящим, чтобы закрыть нашу задачу.

Мы выполнили настройку кластера, выставили резервацию ресурсов под основной сервис php-fpm и выполнили проверку как поведет себя Docker Swarm при отключении ноды Web 2. Оказалось, что решив проблему с распределением сервиса php-fpm по серверам, мы указали резервацию ресурсов, которая не позволяла запускать php-fpm контейнеров больше, чем сейчас есть на данном сервере. Соответственно с отключением сервера Web 2 все остальные контейнеры запускались на сервере Web1, но сервис php-fpm оставался в подвешенном состоянии, поскольку из-за ограничения резервации ресурсов процессора он не имел подходящих нод для запуска всех реплик. С включением сервера Web 2 происходил запуск всех реплик php-fpm, которые не могли найти подходящий сервер, все остальные сервисы продолжали работу на сервере Web 1. В разрезе того, что основную нагрузку дает php-fpm, мы получили равномерное распределение загрузки серверов, при этом решили проблему с балансировкой нагрузки после отказа одной ноды и возвращения ее в строй. Но спустя некоторое время обнаружилась новая проблема.

Однажды нам понадобилось отключить Web 2 сервер для технических работ. В этот момент разработчики заливали код через ci на наш кластер и обнаружилось, что пока сервер Web 2 выключен, обновление кода не происходит. Это было очень плохо, поскольку сами разработчики не должны заботиться о состоянии кластера и иметь возможность в любой момент залить код на продакшен окружение. Источником проблемы как раз была резервация ресурсов под контейнер в Docker Swarm. Из-за недостатка свободных ресурсов, Swarm выдавал информацию об отсутствии подходящих нод для запуска и наше обновление кода благополучно зависало до появления второй ноды (Web 2) в кластере.

2) Наше решение проблемы

Выполнив поиск возможных решений этой проблемы, мы поняли что уперлись в тупик. Мы хотели, чтобы во всех случаях, пока работает хотя бы один сервер, наш продукт продолжал свою работу, а по возвращению сервера в кластер нагрузка делилась по ним равномерно. При этом, в любом состоянии кластера, будь то один сервер или десять, мы могли обновлять код. На этом этапе мы решили попробовать автоматизировать наши действия, которые мы выполняли руками для распределения нагрузки, когда еще не было резервации ресурсов, а именно запускать команду docker service update --force в нужный момент, чтобы все происходило автоматически.

Именно эта идея и стала основой для нашего мини-проекта Swarm Manager. Swarm Manager это обычный bash-скрипт, который основывается на докер команды и ssh, осуществляет ту самую балансировку в нужный момент. Для его работы как демона мы запускаем его в cron контейнере. Визуально это выглядит следующим образом.

В целом видно, что в контейнер мы передаем cron конфиг с вызовом нашего скрипта swarm_provisioner.sh, который уже выполняет действия по балансировке. Чтобы swarm_provisioner.sh смог корректно работать на любой из нод кластера, необходимо разрешить ssh подключение к root пользователю с любого сервера кластера к любому серверу в кластере. Это даст возможность скрипту зайти на удаленный сервер и проверить запущенные на нем контейнеры. Для тех, кому не подходит пользователь root, можно поменять пользователя в swarm_provisioner.sh, заменив root в переменной SSH_COMMAND на подходящего пользователя с доступом к команде docker ps. Рассмотрим пример cron file:

SHELL=/bin/bash*/1 * * * * /swarm_provisioner.sh "web-group" "edphp-fpm" "-p 22"

Как видим, это обычный cron файл с вызовом каждую минуту скрипта swarm_provisioner.sh с заданными параметрами.

Рассмотрим параметры, которые передаются в скрипт.

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

Второй параметр имя сервиса, балансируемого по нодам, с приставкой названия кластера. В примере кластер называется ed, а сервис - php-fpm.

Третий параметр это порт ssh, по которому скрипт будет стучаться на сервера в кластере с указанным label и проверять количество запущенных контейнеров сервиса. Если скрипт увидит перекос по запущенным контейнерам на серверах, он выполнит команду docker service update --force.

В итоге данный сервис запускается на любой мастер ноде, как показано ниже, и выполняет распределение нужного нам docker swarm сервиса равномерно по серверам. В случае, если контейнеры распределены равномерно, он просто выполняет проверку без запуска каких-либо других действий.

swarm-manager:image: swarm-manager:latestvolumes:- /var/run/docker.sock:/var/run/docker.sock:ro- /swarm-keys:/root/.sshdeploy:replicas: 1update_config:parallelism: 1delay: 1sorder: start-firstrestart_policy:condition: on-failureplacement:constraints:- node.role==manager

3) Выводы

Мы получили инструмент, который решил наши проблемы. На данном этапе это только первая версия. Скорее всего, в будущем мы выполним замену ssh на docker api, которое позволит более просто запускать этот сервис из коробки, и поработаем над ограничениями, которые сейчас существуют.

Ссылка на проект.

Подробнее..

Из песочницы Общий обзор архитектуры сервиса для оценки внешности на основе нейронных сетей

16.07.2020 20:13:21 | Автор: admin

Вступление


Привет!


В данной статье я поделюсь опытом построения микросервисной архитектуры для проекта, использующего нейронные сети.


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


Приятного чтения!


Пару слов о задаче и ее решении


Основная идея на основе фото дать оценку привлекательности человека по десятибалльной шкале.


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


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


При работе над пайплайном оценки привлекательности, задача была декомпозирована на следующие составляющие:


  1. Выделение лиц на фото
  2. Оценка каждого из лиц
  3. Рендер результата

Первое решается силами предобученной MTCNN. Для второго была обучена сверточная нейросеть на PyTorch, в качестве backbone был использован ResNet34 из баланса качество / скорость инференса на CPU



Функциональная диаграмма пайплайна оценки


Анализ требований к архитектуре проекта


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



Жизненный цикл ML проекта


Данный проект не исключение было принято решение обернуть пайплайн оценки в онлайн-сервис, для этого требовалось погрузиться в архитектуру. Были обозначены следующие базовые требования:


  1. Единое хранилище логов все сервисы должны писать логи в одно место, их должно быть удобно анализировать
  2. Возможность горизонтального масштабирования сервиса оценки как наиболее вероятного Bottleneck
  3. На оценку каждого изображения должно быть выделено одинаковое кол-во ресурсов процессора во избежание выбросов в распределении времени на инференс
  4. Быстрое (пере)развертывание как конкретных сервисов, так и стэка в целом
  5. Возможность, при необходимости, использовать в разных сервисах общие объекты

Архитектура


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


Для того, чтобы избавиться от лишней головной боли, в качестве фронтенда был выбран Telegram API.


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



Структурная диаграмма готовой архитектуры


Поговорим подробнее о каждом из компонентов диаграммы, обозначим их Single Responsibility в процессе оценки изображения.


Микросервис attrai-telegram-bot


Данный микросервис инкапсулирует все взаимодействия с Telegram API. Можно выделить 2 основных сценария работа с пользовательским изображением и работа с результатом пайплайна оценки. Разберем оба сценария в общем виде.


При получении пользовательского сообщения с изображением:


  1. Производится фильтрация, состоящая из следующих проверок:
    • Наличия оптимального размера изображения
    • Количества изображений пользователя, уже находящихся в очереди
  2. При прохождении первичной фильтрации изображение сохраняется в docker volume
  3. В очередь to_estimate продьюсится таска, в которой, в том числе, фигурирует путь до изображения, лежащего в нашем volume
  4. Если вышеперечисленные этапы пройдены успешно пользователь получит сообщение с примерным временем обработки изображения, которое рассчитывается на основе количества тасков в очереди. В случае ошибки пользователь будет явным образом об этом оповещен путем отправки сообщения с информацией о том, что могло пойти не так.

Также, данный микросервис, как celery worker, слушает очередь after_estimate, которая предназначается для тасков, прошедших через пайплайн оценки.


При получении новой таски из after_estimate:


  1. Если изображение обработано успешно отправляем результат пользователю, если нет оповещаем об ошибке
  2. Удаляем изображение, являющееся результатом пайплайна оценки

Микросервис оценки attrai-estimator


Данный микросервис является celery worker и инкапсулирует в себе всё, что связано с пайплайном оценки изображения. Алгоритм работы тут один разберем его.


При получении новой таски из to_estimate:


  1. Прогоняем изображение через пайплайн оценки:
    1. Загружаем изображение в память
    2. Приводим изображение к нужному размеру
    3. Находим все лица (MTCNN)
    4. Оцениваем все лица (оборачиваем найденные в прошлом пункте лица в батч и инференсим ResNet34)
    5. Рендерим итоговое изображением
      1. Отрисоваем bounding boxes
      2. Отрисовываем оценки
  2. Удаляем пользовательское (исходное) изображение
  3. Сохраняем выход с пайплайна оценки
  4. Кладем таску в очередь after_estimate, которую слушает разобранный выше микросервис attrai-telegram-bot

Graylog (+ mongoDB + Elasticsearch)


Graylog это решение для централизованного управления логами. В данном проекте, он использовался по своему прямому назначению.


Выбор пал именно на него, а не на привычный всем ELK стэк, по причине удобства работы с ним из под Python. Все, что необходимо сделать для логирования в Graylog, это добавить GELFTCPHandler из пакета graypy к остальным root logger handlers нашего python-микросервиса.


Я, как человек, который до этого работал только с ELK стэком, в целом, получил позитивный опыт во время работы с Graylog. Единственное, что удручает превосходство по фичам Kibana над веб-интерфейсом Graylog.


RabbitMQ


RabbitMQ это брокер сообщений на основе протокола AMQP.


В данном проекте он использовался как наиболее стабильный и проверенный временем брокер для Celery и работал в durable режиме.


Redis


Redis это NoSQL СУБД, работающая со структурами данных типа ключ значение


Иногда возникает необходимость использовать в разных python-микросервисах общие объекты, реализующие какие-либо структуры данных.


Например, в Redis хранится hashmap вида telegram_user_id => количество активных тасок в очереди, что позволяет ограничить количество запросов от одного пользователя определенным значением и, тем самым, предотвратить DoS-атаки.


Формализуем процесс успешной обработки изображения


  1. Пользователь отправляет изображение в Telegram бота
  2. attrai-telegram-bot получает сообщение от Telegram API и разбирает его
  3. Таск с изображением добавляется в асинхронную очередь to_estimate
  4. Пользователь получает сообщение с планируемым временем оценки
  5. attrai-estimator берет таск из очереди to_estimate, прогоняет через пайплайн оценки и продьюсит таск в очередь after_estimate
  6. attrai-telegram-bot, слушающий очередь after_estimate, отправляет результат пользователю

DevOps


Наконец, после обзора архитектуры, можно перейти к не менее интересной части DevOps


Docker Swarm




Docker Swarm - система кластеризации, функционал которой реализован внутри Docker Engine и доступен из коробки.


При помощи роя, все ноды нашего кластера можно разделить на 2 типа worker и manager. На машинах первого типа разворачиваются группы контейнеров (стэки), машины второго типа отвечают за скалирование, балансировку и другие классные фичи. Менеджеры по умолчанию являются и воркерами.



Кластер с одним leader manager и тремя worker


Минимально возможный размер кластера 1 нода, единственная машина будет одновременно выступать как leader manager и worker. Исходя из размера проекта и минимальных требований к отказоустойчивости, было принято решение использовать именно этот подход.


Забегая вперед, скажу, что с момента первой production-поставки, которая была в середине июня, проблем, связанных с данной организацией кластера, не было (но это не значит, что подобная организация хоть сколько-нибудь допустима в любых средне-крупных проектах, на которые накладываются требования по отказоустойчивости).


Docker Stack


В режиме роя за развертывание стэков (наборов docker services) отвечает docker stack


Он поддерживает docker-compose конфиги, позволяя дополнительно использовать deploy параметры.


Например, при помощи данных параметров были ограничены ресурсы на каждый из инстансов микросервиса оценки (выделяем на N инстансов N ядер, в самом микросервисе ограничиваем кол-во ядер, используемое PyTorch`ем, одним)


attrai_estimator:image: 'erqups/attrai_estimator:1.2'deploy:replicas: 4resources:limits:cpus: '4'restart_policy:condition: on-failure

Важно отметить, что Redis, RabbitMQ и Graylog stateful сервисы и масштабировать их так же просто, как attrai-estimator, не получится


Предвещая вопрос почему не Kubernetes?


Кажется, что использование Kubernetes в проектах маленького и среднего размера оверхед, весь необходимый функционал можно получить от Docker Swarm, который довольно user friendly для оркестратора контейнеров, а также имеет низкий порог вхождения.


Инфраструктура


Развертывалось это все на VDS со следующими характеристиками:


  • CPU: 4 ядра Intel Xeon Gold 5120 CPU @ 2.20GHz
  • RAM: 8 GB
  • SSD: 160 GB

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


Но, сразу после деплоя, я запостил ссылку на одну из самых популярных в СНГ имиджборд (ага, ту самую), после чего люди заинтересовались и за несколько часов сервис успешно обработал десятки тысяч изображений. При этом в пиковые моменты ресурсы CPU и RAM не были использованы даже наполовину.




Еще немного графики


Количество уникальных пользователей и запросов на оценку, с момента деплоя, в зависимости от дня



Распределение времени инференса пайплайна оценки



Выводы


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


Думаю, проекты маленького и среднего размеров, использующие в своем процессе реалтайм инференс нейронных сетей на CPU, успешно могут перенять практики, описанные в данной статье.


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


Потыкать бота можно в Telegram @AttraiBot, работать будет, как минимум, до конца осени 2020 года. Напомню никакие пользовательские данные не хранятся ни исходные изображения, ни результаты пайплайна оценки все сносится после обработки.

Подробнее..

Мёртвые оркестраторы оказывается не такие уж мёртвые

08.10.2020 08:19:04 | Автор: admin


На прошлой неделе Слёрм провел опрос об оркестраторах среди тех, кто прошел курсы или вечернюю школу по Kubernetes.
Результат, мягко говоря, удивил: 49 человек из 316 ответивших используют Docker Swarm, на котором мы поставили крест ещё два года назад! И это среди тех, кто как минимум знаком с Kubernetes.
Возможно, это аномалия. Возможно, Docker Swarm не так мертв, как нам казалось. Расскажите, пожалуйста, пользуетесь ли вы Свормом и почему?


В мобильной версии хабра не видно опросов. На этот случай гуглоформа с опросами: https://forms.gle/veaYBFirHVJoPtvm8

Подробнее..

Перевод Docker Compose от разработки до продакшена

24.07.2020 20:08:25 | Автор: admin

Перевод транскрипции подкаста подготовлен в преддверии старта курса Администратор Linux





Docker Compose это удивительный инструмент для создания рабочего
окружения для стека, используемого в вашем приложении. Он позволяет вам определять
каждый компонент вашего приложения, следуя четкому и простому синтаксису в YAML-
файлах
.


С появлением docker compose v3 эти YAML-файлы могут использоваться непосредственно в рабочей среде, при работе с
кластером Docker Swarm.


Но значит ли это, что вы можете использовать один и тот же docker-compose файл в
процессе разработки и в продакшен среде? Или использовать этот же файл для
стейджинга? Ну, в целом да, но для такого функционала нам необходимо следующее:


  • Интерполяция переменных: использование переменных среды для некоторых
    значений, которые изменяются в каждой среде.
  • Переопределение конфигурации: возможность определить второй (или любой
    другой последующий) docker-compose файл, который что-то изменит относительно
    первого, и docker compose позаботится о слиянии обоих файлов.

Различия между файлами для разработки и продакшена


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


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


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


Переопределение конфигурации


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


Docker compose поддерживает объединение различных compose-файлов для
получения окончательной конфигурации. Как это работает можно увидеть на примере:


$ cat docker-compose.ymlversion: "3.2"services:  whale:    image: docker/whalesay    command: ["cowsay", "hello!"]$ docker-compose upCreating network "composeconfigs_default" with the default driverStarting composeconfigs_whale_1Attaching to composeconfigs_whale_1whale_1  |  ________whale_1  | < hello! >whale_1  |  --------whale_1  |     \whale_1  |      \whale_1  |       \whale_1  |                     ##        .whale_1  |               ## ## ##       ==whale_1  |            ## ## ## ##      ===whale_1  |        /""""""""""""""""___/ ===whale_1  |   ~~~ {~~ ~~~~ ~~~ ~~~~ ~~ ~ /  ===- ~~~whale_1  |        \______ o          __/whale_1  |         \    \        __/whale_1  |           \____\______/composeconfigs_whale_1 exited with code 0

Как было сказано, docker compose поддерживает объединение нескольких compose-
файлов, это позволяет переопределять различные параметры во втором файле. Например:


$ cat docker-compose.second.ymlversion: "3.2"services:  whale:    command: ["cowsay", "bye!"]$ docker-compose -f docker-compose.yml -f docker-compose.second.yml upCreating composeconfigs_whale_1Attaching to composeconfigs_whale_1whale_1  |  ______whale_1  | < bye! >whale_1  |  ------whale_1  |     \whale_1  |      \whale_1  |       \whale_1  |                     ##        .whale_1  |               ## ## ##       ==whale_1  |            ## ## ## ##      ===whale_1  |        /""""""""""""""""___/ ===whale_1  |   ~~~ {~~ ~~~~ ~~~ ~~~~ ~~ ~ /  ===- ~~~whale_1  |        \______ o          __/whale_1  |         \    \        __/whale_1  |           \____\______/composeconfigs_whale_1 exited with code 0

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


К счастью, docker compose автоматически ищет специальный файл с именем
docker-compose.override.yml для переопределения значений docker-compose.yml. Если
переименовать второй файл, то получится тот же результат, только с помощью изначальной команды:


$ mv docker-compose.second.yml docker-compose.override.yml$ docker-compose upStarting composeconfigs_whale_1Attaching to composeconfigs_whale_1whale_1  |  ______whale_1  | < bye! >whale_1  |  ------whale_1  |     \whale_1  |      \whale_1  |       \whale_1  |                     ##        .whale_1  |               ## ## ##       ==whale_1  |            ## ## ## ##      ===whale_1  |        /""""""""""""""""___/ ===whale_1  |   ~~~ {~~ ~~~~ ~~~ ~~~~ ~~ ~ /  ===- ~~~whale_1  |        \______ o          __/whale_1  |         \    \        __/whale_1  |           \____\______/composeconfigs_whale_1 exited with code 0

Хорошо, так запомнить проще.


Интерполяция переменных


Файлы конфигурации поддерживают интерполяцию
переменных
и значения по умолчанию. То есть вы можете сделать следующее:


services:  my-service:    build:      context: .    image: private.registry.mine/my-stack/my-service:${MY_SERVICE_VERSION:-latest}...

И если вы выполняете docker-compose build (или push) без переменной окружения
$MY_SERVICE_VERSION, будет использовано значение latest, но если вы установите
значение переменной окружения до сборки, оно будет использовано при сборке или пуше
в регистр private.registry.mine.


Мои принципы


Подходы, которые удобны для меня, могут пригодиться и вам. Я следую этим
простым правилам:


  • Все мои стеки для продакшена, разработки (или других сред) определяются через
    файлы docker-compose.
  • Файлы конфигурации, необходимые для охвата всех моих сред, максимально
    избегают дублирования.
  • Мне нужна одна простая команда для работы в каждой среде.
  • Основная конфигурация определяется в файле docker-compose.yml.
  • Переменные среды используются для определения тегов образов или других
    переменных, которые могут меняться от среды к среде (стейджинг, интеграция,
    продакшен).
  • Значения переменных для продакшена используются в качестве значений по
    умолчанию, это минимизирует риски в случае запуска стека в продакшене без
    установленной переменной окружения.
  • Для запуска сервиса в продакшен-среде используется команда docker stack deploy compose-file docker-compose.yml --with-registry-auth my-stack-name.
  • Рабочее окружение запускается с помощью команды docker-compose up -d.

Давайте посмотрим на простой пример.


# docker-compose.yml...services:  my-service:    build:      context: .    image: private.registry.mine/my-stack/my-service:${MY_SERVICE_VERSION:-latest}    environment:      API_ENDPOINT: ${API_ENDPOINT:-https://production.my-api.com}...

И


# docker-compose.override.yml...services:  my-service:    ports: # This is needed for development!      - 80:80    environment:      API_ENDPOINT: https://devel.my-api.com    volumes:      - ./:/project/src...

Я могу использовать docker-compose (docker-compose up), чтобы запустить стек в
режиме разработки с исходным кодом, смонтированным в /project/src.


Я могу использовать эти же файлы на продакшене! И я мог бы использовать точно
такой же файл docker-compose.yml для стейджинга. Чтобы развернуть это на
продакшен, мне просто нужно собрать и отправить образ с предопределенным тегом
на этапе CI:


export MY_SERVICE_VERSION=1.2.3docker-compose -f docker-compose.yml builddocker-compose -f docker-compose.yml push

На продакшене это можно запустить с помощью следующих команд:


export MY_SERVICE_VERSION=1.2.3docker stack deploy my-stack --compose-file docker-compose.yml --with-registry-auth

И если вы хотите сделать то же самое на стейдже, необходимо просто определить
необходимые переменные окружения для работы в среде стейджинга:


export MY_SERVICE_VERSION=1.2.3export API_ENDPOINT=http://staging.my-api.comdocker stack deploy my-stack --compose-file docker-compose.yml --with-registry-auth

В итоге мы использовали два разных docker-compose файла, которые без
дублирования конфигураций могут использоваться для любой вашей среды!




Узнать подробнее о курсе Администратор Linux



Подробнее..

Разработка механизма распараллеливания кода на языке python с использованием docker-контейнеров

23.11.2020 18:14:08 | Автор: admin
Современный этап развития технологий, в том числе и вычислительной техники, показывает нам рост объёмов данных и потребностей во все более мощных вычислителях. В основе развития центральных процессоров всегда лежала технология увеличения числа транзисторов на кристалле микропроцессора. Известный закон Мура гласит: при сохранении этой тенденции мощность вычислительных устройств за относительно короткий промежуток времени (24 месяца) может вырасти экспоненциально

Однако, тот же Мур в 2003 году опубликовал работу No Exponential is Forever: But Forever Can Be Delayed!, в которой признал, что экспоненциальный рост физических величин в течение длительного времени невозможен. Лишь эволюция транзисторов и технологий их изготовления позволяла продлить действие закона ещё на несколько поколений.

В 2007 году Мур заявил, что закон, очевидно, скоро перестанет действовать из-за атомарной природы вещества и ограничения скорости света. На текущий момент предельный размер транзистора в процессоре составляет 5 нанометров. Имеются также пробные образцы трёхнанометрового процессора, но его выпуск начнётся не раньше 2021 года. Это говорит о том, что в скором времени дальнейшее увеличение количества транзисторов на кристалле прекратится (пока не будет открыт новый материал или кардинально обновлен технологический процесс).

Одним из решений данной проблемы являются параллельные вычисления. Под этим термином понимается такой способ организации компьютерных вычислений, при котором программы разрабатываются как набор взаимодействующих вычислительных процессов, работающих параллельно (одновременно).

Параллельные вычисления по способу синхронизации делятся на два вида.

В первом варианте взаимодействие процессов происходит через разделяемую память: на каждом процессоре мультипроцессорной системы запускается отдельный поток исполнения. Все потоки принадлежат одному процессу. Потоки обмениваются данными через общий для данного процесса участок памяти. Количество потоков соответствует количеству процессоров. Потоки создаются либо средствами языка программирования (например, Java, C#, C++ начиная с C++11, C начиная с C11), либо с помощью библиотек. При этом возможно создавать потоки явно (например, в С/C++ с помощью PThreads), декларативно (например, с помощью библиотеки OpenMP), либо автоматически встроенными средствами компилятора (например, High Performance Fortran). Описанный вариант параллельного программирования обычно требует какой-то формы захвата управления (мьютексы, семафоры, мониторы) для координации потоков между собой.

Во втором варианте взаимодействие осуществляется при помощи передачи сообщений. На каждом процессоре многопроцессорной системы запускается однопоточный процесс, который обменивается данными с другими процессами, работающими на других процессорах, с помощью сообщений. Процессы создаются явно, путём вызова соответствующей функции операционной системы, а обмен сообщениями производится с помощью специальной библиотеки (например, реализация протокола MPI), или с помощью средств языка (например, High Performance Fortran, Erlang или occam).

Кроме двух вышеописанных, применяется также и гибридный вариант: на многопроцессорных системах с распределённой памятью (DM-MIMD), где каждый узел системы представляет собой мультипроцессор с общей памятью (SM-MIMD), можно использовать следующий подход. На каждом узле системы запускается многопоточный процесс, который распределяет потоки между процессорами данного узла. Обмен данными между потоками на узле осуществляется через общую память, а обмен данными между узлами через передачу сообщений. В этом случае количество процессов определяется количеством узлов, а количество потоков количеством процессоров на каждом узле. Гибридный способ параллельного программирования более сложен (требуется особым образом переписывать параллельную программу), но наиболее эффективен в использовании аппаратных ресурсов каждого узла многопроцессорной системы.

В данной статье я предлагаю адаптировать такой гибридный подход для распараллеливания вычислений на языке Python. Ключевой особенностью работы является использование технологии docker-контейнеров. Разрабатываемый фреймворк будет иметь клиент-серверную архитектуру, включающую следующие элементы.

На стороне клиента:

  1. Сериализатор: в соответствии с названием, сериализует функции и их переменные (то есть позволяет сохранять их на внешнее устройство или сеть с последующей загрузкой в память на этом же или другом узле). Также стоит выделить декоратор parallel, который представляет собой функцию-обертку, позволяющую применять сериализатор для функций различного вида.
  2. Классы для конфигурации подключения к серверу/кластеру
  3. Дополнительные языковые средства, позволяющие отмечать функции, подлежащие распараллеливанию.

На стороне сервера:

  1. Десериализатор соответственно, десериализует полученные данные (см. выше).
  2. Executor класс, обрабатывающий десериализованные данные (функции и их аргументы), а также устанавливающий необходимые библиотеки в виртуальное окружение интерпретатора Python.

Общая архитектура разрабатываемой системы представлена на рисунке.

image

Для связи между клиентом и сервером могут использоваться сокеты либо фреймворк twisted, взаимодействие с которыми будет выполняться посредством разработанного API.

Реализация данной системы предполагает использование технологии docker. Это позволяет обеспечить удобство и высокую скорость настройки ПО для начала работы: достаточно запустить кластер docker-swarm, развернуть docker-образ на выбранном сервере и выставить количество репликаций.

Другими важными плюсами применения технологии docker являются создание однородной вычислительной среды путем виртуализации UNIX-подобной системы(Ubuntu облегченная Alpine Linux), а также наличие swarm-режима, который позволяет запускать множества контейнеров на разных серверах и оперативно балансировать нагрузку, перебрасывая задания на свободные контейнеры.

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

Заряжай Patroni. Тестируем Patroni Zookeeper кластер (Часть первая)

24.12.2020 20:04:36 | Автор: admin
Кадр из фильма Рембо IVКадр из фильма Рембо IV

Вступление

Если выработаете сcrucial data, торано или поздно задумаетесь отом, что неплохобы поднять кластер отказоустойчивости. Даже если основной сервер сбазой улетит вглухой нокаут, show must goon, нетакли? При этом мыподразумеваем две вещи:

  • база данных совсей ееструктурой нам по-прежнему доступна;

  • все данные находятся всостоянии максимально близком ктому, которое было нагероически павшем лидере.

Patroni лишь одно изрешений проблемы. Донего япопробовал несколько других сервисов инаGithub одного изних (небуду показывать курсором) досих пор висит без ответа открытый мной баг репорт, хотя прошло уже несколько месяцев. Удругого была несамая полная документация. Какие-то мне показались недостаточно надежными.

ПоPatroniже действительно много инфы, ионкачественно поддерживается разработчиками. Также ониспользует DCS для синхронизации нод, что должно предотвращать ситуации соsplit brain.

Изминусов совершенно точно нужно назватьто, что это неout ofthe box solution. Как сказано вдоке:

Patroni is a template for you to create your own customized, high-availability solution using Python...

Иключевое здесь слово template. Тоесть все придется собирать самому. Новкаком-то смысле это плюс покрайней мере, мыдетально будем знать, что именно идёт впрод.

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

Если выустали уже отвступлений ирветесь вбой, смело переходите кследующей главе.

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

Что, ещё один туториал о Patroni?

Зачем читать именно этот туториал?

Есть уже немало туториалов, которые рассказывают, как поднять кластер Patroni. Этот затрагивает вопросы деплоя в среде docker swarm и использования Zookeeper в качестве DCS.

Почему Zookeeper?

Ясчитаю, это один измоментов, которые стоит рассмотреть всерьез, прежде чем выбрать конечный сетап для продакшена. Дело втом, что Patroni использует сторонние сервисы чтобы установить иобслуживать коммуникацию между своими нодами. Ихобщее название DCS (Dynamic Configuration Storage).

Если выуже смотрели какие-то изтуториалов оPatroni, то, должно быть, заметили, что самый частый кейс это когда вкачестве DCS используют Etcd кластер.

Интересный момент вработе Etcd кластера заключается втом, что:

Since etcd writes data to disk, its performance strongly depends on disk performance. For this reason, SSD is highly recommended.

(из документации Etcd)

Словом, если увас нет поSSD диску накаждой машине, где будут работать ноды Etcd, товывзоне опасности. Конечно, пока нагрузка небольшая, тоничего критичного происходить небудет, ноесли это рабочий, нагруженный прод, тоочень возможно (идаже вероятно), что выпросто перегрузите Etcd кластер. Аэто приведет кIO ошибкам при доступе кбазе. Звучит скверно? Насамом деле так иесть. Ловить такие ошибки напроде очень неприятно.

Здесь нам напомощь иможет прийти Zookeeper, который ничего непишет надиск ихранит все данные впамяти. Такой вариант оптимален вситуации, когда ненавсех серверах есть SSD, зато RAM хватает.

Почему Docker Swarm?

Уменя небыло выбора, так как одним изключевых требований было, чтобы кластер был развернут вSwarmе. Так что, если это иваш кейс тоже, товывправильном месте!

Для техже, кто открыл пост сжеланием потестировать технологию, выбор Docker Swarmа тоже может быть вполне органичным. Хотябы потой причине, что вам непридется устанавливать инастраивать никаких сторонних сервисов (нузаисключением самого Dockerа, разумеется) или тянуть налокальную машину ворох непонятных зависимостей. Полагаю, недалеко отистины утверждение, что Docker унас увсех итак уже настроен везде где только можно, мывсе знаем, как онработает, так что почемубы неиспользовать его.

Потребуется лишь одна команда, чтобы сделать тюнинг Dockerа, который позволит развернуть налокальной машине кластер Patroni на3ноды без виртуальных машин, Kubernetes или подобных вещей.

Если вынехотите копать всторону каких-то еще инструментов запределами Dockerа ихотите сделать все чисто иаккуратно, тоданный туториал вам более чем подойдет.

А в конце будет небольшой бонус

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

Окей, достаточно разговоров. Давайте перейдем кпрактике.

Docker Swarm

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

Яисхожу изпредположения, что увас уже установлен инастроен Docker Engine. Втаком случае, нужно только выполнить следующую команду:

docker swarm init//now check your single-node clusterdocker node lsID                            HOSTNAME       STATUS       AVAILABILITY          a9ej2flnv11ka1hencoc1mer2 *   floitet        Ready          Active       

Одна изважных фич Swarmа заключается втом, что теперь мыможем использовать нетолько обычные Docker контейнеры, ноитак называемые сервисы. Сервисы это посути дела абстракция над контейнерами. Если отталкиваться отаналогии сООП, тосервис это класс, аконтейнер конкретный объект класса. Параметры иправила сервиса задаются при деплое изyml-файла.

Рекомендую запомнить hostname ноды потом мыиспользуем его для указания constraint вконфигурационном файле.

Вцелом, это все приготовления вчасти Docker Swarmа, которые нужно сделать. Здесь никаких проблем быть недолжно. Так что двинемся дальше.

Zookeeper

Прежде чем мыначнем деплой самого Patroni, нам нужно сначала развернуть кластер сDCS (внашем случае, как мыпомним, это Zookeeper). Явзял версию 3.4, иона работает вполне стабильно. Далее идет docker-compose конфиг инекоторые комментарии помоментам, которые, как мне кажется, имеет смысл отдельно упомянуть.

  • docker-compose-zookeeper.yml

docker-compose-zookeeper.yml
version: '3.7'services:  zoo1:    image: zookeeper:3.4    hostname: zoo1    ports:      - 2191:2181    networks:      - patroni    environment:      ZOO_MY_ID: 1      ZOO_SERVERS: server.1=0.0.0.0:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888    deploy:      replicas: 1      placement:        constraints:          - node.hostname == floitet      restart_policy:        condition: any  zoo2:    image: zookeeper:3.4    hostname: zoo2    networks:      - patroni    ports:      - 2192:2181    environment:      ZOO_MY_ID: 2      ZOO_SERVERS: server.1=zoo1:2888:3888 server.2=0.0.0.0:2888:3888 server.3=zoo3:2888:3888    deploy:      replicas: 1      placement:        constraints:          - node.hostname == floitet      restart_policy:        condition: any  zoo3:    image: zookeeper:3.4    hostname: zoo3    networks:      - patroni    ports:      - 2193:2181    environment:      ZOO_MY_ID: 3      ZOO_SERVERS: server.1=zoo1:2888:3888 server.2=zoo2:2888:3888 server.3=0.0.0.0:2888:3888    deploy:      replicas: 1      placement:        constraints:          - node.hostname == floitet      restart_policy:        condition: anynetworks:  patroni:    driver: overlay    attachable: true
Details

Конечноже, важно дать каждой ноде уникальное имя ивнешний порт. Hostname лучше ставить одинаковое сименем сервиса.

zoo1:    image: zookeeper:3.4    hostname: zoo1    ports:      - 2191:2181

Стоит отметить ито, как мыперечисляем hostы встроке ниже: для первого сервиса server.1 будет привязан к0.0.0.0, а, например, для zoo2 это уже будет server.2 соответственно ит.д.

ZOO_SERVERS: server.1=0.0.0.0:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888

Атаким образом мыконтролируем распределение сервисов понодам. Поскольку нода унас сейчас только одна, мыспокойно моглибы убрать эти строчки изконфига, нокогда серверов будет несколько, можно будет менять node.hostname итем самым определять накакую ноду пойдет сервис.

     placement:        constraints:          - node.hostname == floitet

Ипоследний момент, который мыздесь обсудим, это network. Янамерен деплоить все сервисы Zookeeperа ивсе сервисы Patroni водну сеть сдрайвером overlay, чтобы они были изолированы отдругих сервисов имогли общаться между собой поименам, анепоIP (как это выглядит, будет видно дальше).

networks:  patroni:    driver: overlay// мы должны отметить сеть как attachable  // чтобы потом можно было присоединять к ней остальные сервисы    attachable: true

Итак, можно задеплоить Zookeeper:

sudo docker stack deploy --compose-file docker-compose-zookeeper.yml patroni

Теперь нужно проверить, что все работает. Первое что можно сделать это просто посмотреть список сервисов:

sudo docker service lsgxfj9rs3po7z        patroni_zoo1        replicated          1/1                 zookeeper:3.4         *:2191->2181/tcpibp0mevmiflw        patroni_zoo2        replicated          1/1                 zookeeper:3.4         *:2192->2181/tcpsrucfm8jrt57        patroni_zoo3        replicated          1/1                 zookeeper:3.4         *:2193->2181/tcp

Иследующим шагом можно сделать пинг сервисов спомощью специальной команды mntr:

echo mntr | nc localhost 2191// with the output being smth like thiszk_version3.4.14-4c25d480e66aadd371de8bd2fd8da255ac140bcf, built on 03/06/2019 16:18 GMTzk_avg_latency6zk_max_latency205zk_min_latency0zk_packets_received1745zk_packets_sent1755zk_num_alive_connections3zk_outstanding_requests0zk_server_statefollowerzk_znode_count16zk_watch_count9zk_ephemerals_count4zk_approximate_data_size1370zk_open_file_descriptor_count34zk_max_file_descriptor_count1048576zk_fsync_threshold_exceed_count0

Также можно проверить логи сервиса, если есть желание:

docker service logs $zookeeper-service-id // service-id comes from 'docker service ls' command. // in my case it could be docker service logs gxfj9rs3po7z

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

Patroni

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

Сначала создадим отдельную директорию patroni-test иперейдем внеё. Для того чтобы успешно сбилдить рабочий имейдж нам понадобится пара дополнительных файлов, сних иначнем.

  • patroni.yml

Это основной конфигурационный файл. Одна изособенностей Patroni, что мыможем задавать параметры для кластера изразных мест иpatroni.yml одно изних. Этот файл мыбудем копировать вкастомный имейдж, так что любые изменения, внесенные внего, требуют ребилда образа.

Явитоге пришел кидее, что буду хранить здесь только тепараметры, которые наверняка почти никогда небуду трогать, поэтому складываю здесь только самые необходимые настройки. Ниже япривожу базовый конфиг. Если захочется, томожно добавить внего каких-то параметров, допустим, для движка Posgtresа (например, max_connections ит.п.). Нодля тестового стенда этого вполне достаточно.

patroni.yml
scope: patroninamespace: /service/bootstrap:    dcs:        ttl: 30        loop_wait: 10        retry_timeout: 10        maximum_lag_on_failover: 1048576        postgresql:            use_pg_rewind: true    postgresql:      use_pg_rewind: true    initdb:    - encoding: UTF8    - data-checksums    pg_hba:    - host replication all all md5    - host all all all md5zookeeper:  hosts:       - zoo1:2181      - zoo2:2181      - zoo3:2181postgresql:    data_dir: /data/patroni    bin_dir: /usr/lib/postgresql/11/bin    pgpass: /tmp/pgpass    parameters:        unix_socket_directories: '.'tags:    nofailover: false    noloadbalance: false    clonefrom: false    nosync: false
Details

Важно указать Patroni путь кбинарным файлам Postgresа. Вмоем случае, так как яиспользую Postgres11, директория выглядит так: /usr/lib/postgresql/11/bin.

Вдиректории, внутри уже созданного контейнера, Patroni будет искать файлы Postgresа. Без этой настройки скорее всего ничего невзлетит (покрайней мере уменя невзлетело). Итакже еще есть data_dir это место вконтейнере, где будут храниться данные. Позже мысделаем mount этой директории кместу налокальном жестком диске, чтобы непотерять все полимеры, если кластер всеже упадет безнадежно. Это добавит нам работы посозданию этих папок локально, но, по-моему, оно того стоит.

postgresql:    data_dir: /data/patroni    bin_dir: /usr/lib/postgresql/11/bin

Также яперечисляю все сервера Zookeeperа вэтом конфиг файле, чтобы потом передать информацию оних утилите patronictl. Стоит отметить, что если неуказать ихвpatroni.yml, томыостанемся витоге снерабочим patronictl. Как вывидите, перечисляя сервера, янепишу никакиеIP, аиспользую ихимена. Это тасамая фича Docker Swarmа окоторой ярассказывал выше.

zookeeper:  hosts:       - zoo1:2181      - zoo2:2181      - zoo3:2181
  • patroni-entrypoint.sh

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

patroni-entrypoint.sh
#!/bin/shreadonly CONTAINER_IP=$(hostname --ip-address)readonly CONTAINER_API_ADDR="${CONTAINER_IP}:${PATRONI_API_CONNECT_PORT}"readonly CONTAINER_POSTGRE_ADDR="${CONTAINER_IP}:5432"export PATRONI_NAME="${PATRONI_NAME:-$(hostname)}"export PATRONI_RESTAPI_CONNECT_ADDRESS="$CONTAINER_API_ADDR"export PATRONI_RESTAPI_LISTEN="$CONTAINER_API_ADDR"export PATRONI_POSTGRESQL_CONNECT_ADDRESS="$CONTAINER_POSTGRE_ADDR"export PATRONI_POSTGRESQL_LISTEN="$CONTAINER_POSTGRE_ADDR"export PATRONI_REPLICATION_USERNAME="$REPLICATION_NAME"export PATRONI_REPLICATION_PASSWORD="$REPLICATION_PASS"export PATRONI_SUPERUSER_USERNAME="$SU_NAME"export PATRONI_SUPERUSER_PASSWORD="$SU_PASS"export PATRONI_approle_PASSWORD="$POSTGRES_APP_ROLE_PASS"export PATRONI_approle_OPTIONS="${PATRONI_admin_OPTIONS:-createdb, createrole}"exec /usr/local/bin/patroni /etc/patroni.yml
Details. Важно!

Насамом деле, основной смысл вообще делать такой скрипт заключается втом, что мыпросто несможем стартануть сервис сPatroni, незнаяIP адрес hostа. Ивтом случае, когда hostом оказывается Docker-контейнер, нам как-то нужно сначала узнать какойже IPэтот контейнер получил, итолько потом мыможем запустить Patroni. Эта потребность закрывается вот здесь:

readonly CONTAINER_IP=$(hostname --ip-address)readonly CONTAINER_API_ADDR="${CONTAINER_IP}:${PATRONI_API_CONNECT_PORT}"readonly CONTAINER_POSTGRE_ADDR="${CONTAINER_IP}:5432"...export PATRONI_RESTAPI_CONNECT_ADDRESS="$CONTAINER_API_ADDR"export PATRONI_RESTAPI_LISTEN="$CONTAINER_API_ADDR"export PATRONI_POSTGRESQL_CONNECT_ADDRESS="$CONTAINER_POSTGRE_ADDR"

Как яуже говорил раньше, параметры конфига Patroni можно передавать разными способами. Вэтом скрипте мыпользуемся тем, что один изтаких способов это Environment configuration. PATRONIRESTAPICONNECTADDRESS, PATRONIRESTAPILISTEN, PATRONIPOSTGRESQLCONNECTADDRESS специальные переменные среды, окоторых Patroni знает заранее икоторые будут считаны. Икстати, они переписывают локальные настройки изpatroni.yml, так что beaware!

Иеще момент. Документация Patroni нерекомендует использовать superuserа для подключения кбазе приложений. Т.е. нужно создать отдельного юзера, который мыбудем использовать непосредственно для коннекта, аsuperuserа иreplicatorа трогать небудем совсем. Создать такого юзера можно также через переменную среды. Если хотите, чтобы юзер назывался как-то иначе чем approle, просто замените вэтой строке approle начто-то другое.

export PATRONI_approle_PASSWORD="$POSTGRES_APP_ROLE_PASS"export PATRONI_approle_OPTIONS="${PATRONI_admin_OPTIONS:-createdb, createrole}"

Ивпоследней строчке, когда всё уже готово кстарту, мыделаем запуск Patroni сервиса суказанием откуда брать основной конфиг файл:

exec /usr/local/bin/patroni /etc/patroni.yml
  • Dockerfile

Dockerfile ярешил сделать настолько простым, насколько это только возможно. Ноэтого вполне достаточно, чтобы сделать билд рабочего Docker-образа. Давайте глянем, что внем все-таки происходит.

Dockerfile
FROM postgres:11 RUN apt-get update -y\     && apt-get install python3 python3-pip -y\    && pip3 install --upgrade setuptools\    && pip3 install psycopg2-binary \    && pip3 install patroni[zookeeper] \    && mkdir /data/patroni -p \    && chown postgres:postgres /data/patroni \    && chmod 700 /data/patroni COPY patroni.yml /etc/patroni.ymlCOPY patroni-entrypoint.sh ./entrypoint.shUSER postgresENTRYPOINT ["bin/sh", "/entrypoint.sh"]
Details

Одна изглавных деталей здесь это директория, которую мыуказываем создать внутри контейнера, атакже еевладелец иего права. Позже, когда будем деплоить Patroni, нужно будет подобнымже образом создать папки налокальной машине, куда мысможем сделать mount этой директории изконтейнера.

// владелец должен быть 'postgres', а mode 700    mkdir /data/patroni -p \    chown postgres:postgres /data/patroni \    chmod 700 /data/patroni     ...// устанавливаем в кач-ве активного юзера внутри контейнера // юзера postgres        USER postgres

Файлы, которые мысоздали, ранее копируются вимейдж вэтих строчках:

COPY patroni.yml /etc/patroni.ymlCOPY patroni-entrypoint.sh ./entrypoint.sh

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

ENTRYPOINT ["bin/sh", "/entrypoint.sh"]

Вот, пожалуй, ився основная подготовка. Теперь мыготовы создать наш кастомный Patroni имейдж.

docker build -t patroni-test .

Самое время обсудить последний посписку, нонеповажности файл для Patroni compose yml.

  • docker-compose-patroni.yml

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

docker-compose-patroni.yml
version: "3.4"networks:    patroni_patroni:         external: trueservices:    patroni1:        image: patroni-test        networks: [ patroni_patroni ]        ports:            - 5441:5432            - 8091:8091        hostname: patroni1        volumes:          - /patroni1:/data/patroni        environment:            PATRONI_API_CONNECT_PORT: 8091            REPLICATION_NAME: replicator             REPLICATION_PASS: replpass            SU_NAME: postgres            SU_PASS: supass            POSTGRES_APP_ROLE_PASS: appass        deploy:          replicas: 1          placement:            constraints: [node.hostname == floitet]    patroni2:        image: patroni-test        networks: [ patroni_patroni ]        ports:            - 5442:5432            - 8092:8091        hostname: patroni2        volumes:          - /patroni2:/data/patroni        environment:            PATRONI_API_CONNECT_PORT: 8091            REPLICATION_NAME: replicator             REPLICATION_PASS: replpass            SU_NAME: postgres            SU_PASS: supass            POSTGRES_APP_ROLE_PASS: appass        deploy:          replicas: 1          placement:            constraints: [node.hostname == floitet]    patroni3:        image: patroni-test        networks: [ patroni_patroni ]        ports:            - 5443:5432            - 8093:8091        hostname: patroni3        volumes:          - /patroni3:/data/patroni        environment:            PATRONI_API_CONNECT_PORT: 8091            REPLICATION_NAME: replicator             REPLICATION_PASS: replpass            SU_NAME: postgres            SU_PASS: supass            POSTGRES_APP_ROLE_PASS: appass        deploy:          replicas: 1          placement:            constraints: [node.hostname == floitet]
Details

Первое, очем хочется сказать, это момент сexternal network, окотором говорилось ранее. Мыхотим разместить Patroni сервисы тамже, где мыдержим исервисы Zookeeper. Таким образом мысможем обращаться ксервисам поименам, ивсе имена: zoo1, zoo2, zoo3, которые мыперечислили вpatroni.yml, задавая сервера Zookeeperа, будут работать, как надо.

networks:    patroni_patroni:         external: true

Нужно отметить, что унас будут два end pointа: сама база данных иAPI. Идля того идля другого требуется открыть порты:

ports:    - 5441:5432    - 8091:8091...environment:    PATRONI_API_CONNECT_PORT: 8091// также нужно убедиться, что в PATRONI_API_CONNECT_PORT мы передаем// тот же самый, который мы открываем для сервиса   

Также, нам, конечно, нужно передать все переменные среды, которые мызаявили вentrypoint скрипте. Ноиэто еще невсё. Есть вопрос сдиректорией для mountа, который мытоже здесь решаем:

volumes:   - /patroni3:/data/patroni

Как видно изэтой строки, тудиректорию /data/patroni, которая была создана вDockerfile, мымонтируем клокальной директории. Так вот эту локальную директорию нам нужно создать. Инетолько создать, ноивыставить правильного юзера ирежим доступа, например так:

sudo mkdir /patroni3sudo chown 999:999 /patroni3sudo chmod 700 /patroni3// 999 это дефолтный uid для юзера postgres  // эти шаги нужно повторить для каждой ноды Patroni

Мы наконец готовы деплоить Patroni кластер:

sudo docker stack deploy --compose-file docker-compose-patroni.yml patroni

После деплоя влогах сервиса мыдолжны увидеть что-то втаком ключе:

INFO: Lock owner: patroni3; I am patroni1INFO: does not have lockINFO: no action.  i am a secondary and i am following a leader

Былобы печально, еслибы мымогли проверить статус кластера иноды только читая логи. Так что предлагаю коснуться немного способов проверки состояния кластера иначать ссамого простого patronictl. Для этого нужно сначала получить idлюбого контейнера Patroni:

sudo docker psCONTAINER ID        IMAGE                 COMMAND                  CREATED             STATUS              PORTS                          NAMESa0090ce33a05        patroni-test:latest   "bin/sh /entrypoint."   3 hours ago         Up 3 hours          5432/tcp                       patroni_patroni1.1.tgjzpjyuip6ge8szz5lsf8kcq...

Ипотом зайти вконтейнер спомощью exec команды:

sudo docker exec -ti a0090ce33a05 /bin/bash// при вызове команды мы должны передать имя кластера// это параметр 'scope' в patroni.yml ('patroni' в нашем случае)patronictl list patroni// и тут ошибка...Error: 'Can not find suitable configuration of distributed configuration store\nAvailable implementations: exhibitor, kubernetes, zookeeper'

Команда patronictl полагается наpatroni.yml, чтобы получить информацию осерверах Zookeeperа. Оннезнает, где мыэтот файл положили. Так что явно укажем ему путь:

patronictl -c /etc/patroni.yml list patroni// and here is the nice output with the current states+ Cluster: patroni (6893104757524385823) --+----+-----------+| Member   | Host      | Role    | State   | TL | Lag in MB |+----------+-----------+---------+---------+----+-----------+| patroni1 | 10.0.1.93 | Replica | running |  8 |         0 || patroni2 | 10.0.1.91 | Replica | running |  8 |         0 || patroni3 | 10.0.1.92 | Leader  | running |  8 |           |+----------+-----------+---------+---------+----+-----------+

PostgreSQL Connection

Готово! Теперь мыможем подключаться кPostgres ичто-то туда писать. Ноделать это мысможем только изнашей сети patroni_patroni. Так что сначала создаем контейнер сподходящим образом, ипотом изнего уже выполняем команды:

docker run --rm -ti --network=patroni_patroni postgres:11 /bin/bash// доступ к конкретной нодеpsql --host patroni3 --port 5432 -U approle -d postgres// доступ к лидеру через haproxy// так что нужно указать какую-либо через флаг '-d' 

Вот мыинастроили сам кластер Patroni. Нонаш сетап былбы неполным, еслибы мынаэтом иостановились. Есть ещё несколько важных моментов, которые мыдолжны закрыть, нообэтом вовторой части.

Все config файлы для первой части можно забрать отсюда.

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru