- PVSM.RU - https://www.pvsm.ru -
Мы уже рассказывали [1] на Хабре про облачную инфраструктуру Яндекса. Сегодня пришёл черёд от слов перейти к делу — мы хотим по шагам показать, как можно развернуть собственное облако на Elliptics и Cocaine.
Давайте рассмотрим установку небольшого облачка, в котором можно запустить тестовое приложение [2] использующее flask.
Это облачко состоит из следующих элементов:
На каждом этапе будем проводить проверки, тестирующие успешность выполнения этапа.
Как видно, нам понадобится 5 (можно виртуальных) машинок с ядром не ниже 3.8 для поддержки Docker. Необходимые пакеты можно найти в репозитории [3].
deb http://repo.reverbrain.com/precise/ current/amd64/
deb http://repo.reverbrain.com/precise/ current/all/
Подтянем ключ:
curl -O http://repo.reverbrain.com/REVERBRAIN.GPG
sudo apt-key add REVERBRAIN.GPG
Посмотрим, какие пакеты, относящиеся к кокаину, стали доступны:
apt-get update
apt-cache search cocaine
Мне придется в двух словах описать, как развернуть инсталляцию Elliptics из одной машины для демонстрации работы нашего кластера. Сразу замечу, что это ни в коем случае нельзя рассматривать в качестве руководства по разворачиванию этой Elliptics для боевого применения. Об этом вам обязательно расскажут ребята, которые и занимаются разработкой Elliptics. Также информацию можно найти в документации [4].
В паре команд это выглядит так:
sudo apt-get install elliptics=2.24.14.31 elliptics-client=2.24.14.31
mkdir /tmp/history/ && mkdir /tmp/root
cp /usr/share/doc/elliptics/examples/ioserv.conf ./tst_ioserv.conf
Далее в tst_ioserv.conf нам придется изменить буквально 3 строки. Итак:
group = 1
addr = localhost:1025:2-0 192.168.50.201:1025:2-1 // здесь подставьте свой IP
indexes_shard_count = 16
После этого запускаем:
dnet_ioserv -c tst_ioserv.conf
Приступим к реализации нашего мини-облака с конфигурации машин, непосредственно запускающих код нашего приложения. Ядром инсталляции будет безусловно cocaine-runtime. Также необходимо установить сам Docker и cocaine-plugin для работы с Docker.
Уточню, что хотя наша цель — запустить приложение в docker-контейнере, мы также запустим приложение как обычный процесс.
Контейнеризация — есть благо. Она решает проблемы с окружением для приложения:
Запуск без контейнера применим и удобен, когда нет зависимостей (например, приложение на go) или технической возможности использовать контейнеры (например, ядро ниже требуемой версии и обновить его нельзя).
После подключения репозитория установим необходимые пакеты:
sudo apt-get install cocaine-runtime libcocaine-core2 libcocaine-plugin-docker2 libcocaine-plugin-elliptics=2.24.14.31 elliptics-client=2.24.14.31
Для управления приложениями нам необходима утилита cocaine-tool. Проще всего ее установить из PyPI — стандартного репозитория Python пакетов.
Необходимо установить биндинг в Python для msgpack.
sudo apt-get install msgpack-python
А затем cocaine-tools.
sudo pip install cocaine-tools
Docker устанавливаем по описанию в официальной документации [5] любым удобным способом. Использован следующий путь:
curl -s https://get.docker.io/ubuntu/ | sudo sh
После успешной инсталяции пакетов cocaine-runtime стартует с дефолтной конфигурацией, которую нам необходимо изменить.
Останавливаем cocaine-runtime и переходим к модификации конфигурации.
sudo service cocaine-runtime stop
Дефолтный файл конфигурации расположен в /etc/cocaine/cocaine-default.conf и выглядит так:
{
"version": 2,
"paths": {
"plugins": "/usr/lib/cocaine",
"runtime": "/var/run/cocaine"
},
"services": {
"logging": {
"type": "logging"
},
"storage": {
"type": "storage",
"args": {
"backend": "core"
}
},
"node": {
"type": "node",
"args": {
"runlist": "default"
}
}
},
"storages": {
"core": {
"type": "files",
"args": {
"path": "/var/lib/cocaine"
}
},
"cache": {
"type": "files",
"args": {
"path": "/var/cache/cocaine"
}
}
},
"loggers": {
"core": {
"type": "syslog",
"args": {
"identity": "cocaine",
"verbosity": "info"
}
}
}
}
Сделаем конфигурацию облачной. Для этого необходимо настроить 2 сущности:
Для экспериментов удобно модифицировать копию предоставляемого конфига «по умолчанию» (например, /etc/cocaine/cocaine-cloud.conf).
Заставить cocaine-runtime сообщать агрегирующим нодам о запущенных на нем приложениях и сервисах можно, добавив секцию network в конфигурацию на одном уровне вложенности с services.
"network" : {
"group": "224.168.2.9"
},
"services": {
...
}
Единственный параметр group описывает адрес multicast-группы, в которую будет производиться рассылка оповещений.
Конфигурация storage выполняется модификацией секций services/storage и storages/core:
"storage": {
"type": "elliptics"
},
"storages" : {
"core": {
"type": "elliptics",
"args": {
"nodes" : {
"192.168.50.201" : 1025
},
"io-thread-num" : 8,
"wait-timeout" : 30,
"check-timeout" : 60,
"net-thread-num" : 8,
"groups" : [1],
"verbosity" : 2
}
}
}
Реализации storage на технологии Elliptics обеспечивается соответствующим плагином. К слову, все плагины из наших пакетов ставятся в /usr/lib/cocaine, где их по умолчанию ищет cocaine-runtime.
Теперь заставим стартовать кокаин с этим конфигом по дефолту. Создаем
/etc/default/cocaine-runtime
:
CONFIG_PATH="/etc/cocaine/cocaine-cloud.conf"
Запустим cocaine-runtime с новым конфигом, убедимся в том, что процесс работает.
После запуска на порту 10053 сервис-локатор этой ноды будет ожидать запросов о доступности приложения. Это своего рода DNS для облачных приложений и сервисов, то есть по имени сервиса или приложения вы узнаете куда надо отправлять запросы, чтобы они были обработаны.
Запросить информацию о приложениях на этой ноде можно при помощи команды cocaine-tool info
. Вывод этой команды пока не потрясает воображение, но запомните его — совсем скоро он изменится.
Напомним, что помимо cocaine-runtime и плагинов, мы установили Docker. Проверим, что он так же успешно запустился:
Предполагаем, что на второй машине будет проделаны аналогичные действия. Итого мы имеем 2 машинки с запущенным cocaine-runtime, которые смотрят в общий storage — это прекрасно!
В этой точке уже вполне можно запускать приложения. Это, конечно, пока совсем не облако, но все же. Засучив рукава, приступим.
В первую очередь нам необходимо доставить код приложения в кокаиновый storage. Эта задача решается с легкостью при помощи cocaine-tool.
Склонируем репозиторий с примером и перейдем внутрь каталога.
git clone git@github.com:cocaine/cocaine-framework-python.git -b v0.11
cd cocaine-framework-python/examples/flask/
Можно заметить, что помимо кода приложения, здесь расположен manifest.json:
{
"slave": "main.py"
}
Основная задача этого файла указать платформе, что запускать. Также можно передать environment.
Для загрузки кода приложения, которое хотим запустить как процесс, в облако необходимо выполнить следующую команду:
cocaine-tool app upload --name example
cocaine-tool запакует Ваше приложение в архив и загрузить в облачное хранилище. А потом мы посмотрим список загруженных приложений.
Вот оно, наше приложение! Главное, не забыть установить flask, который требует наше приложение. Вот этот недостаток запуска без контейнера.
sudo apt-get install python-flask
Далее необходимо настроить выделяемые для приложения ресурсы, тип изоляции, управление количеством воркеров и тд. Для этой цели служат профили. Профиль может быть связан с несколькими приложениями.
Простейший профиль — пустой JSON {}
. В этом случае используются параметры по умолчанию.
Описание всех доступных опций можно найти в описании [6]. Более интересный пример рассмотрим далее, когда будем запускать приложение в контейнере.
Назовем наш профиль default и загрузим его в облачное хранилище. Проверим, что он загрузился правильно.
Наконец-то настал момент, когда можно дернуть рубильник и приложение запустится:
cocaine-tool app start --name example --profile default
Теперь-то вывод команды cocaine-tool info
изменился. В нем показано, какие приложения запущены на данной ноде и статистика о приложениях.
Попробуем послать сообщение в наше приложение. Для этой цели достаточно вот такого небольшого скрипта на питоне:
#!/usr/bin/env python
from cocaine.services import Service
app = Service("example")
print(app.enqueue("write", "DATA").get())
print(app.enqueue("read", "DATA").get())
Приложение все еще запущено только на одной ноде, но можно сходить на вторую и запустить его и там. Теперь можно продолжить строить инфраструктуру для приложений в Docker.
Docker-registry — это ваше приватное хранилище образов (контейнеров) приложений. Более подробно о нем можно почитать в официальной документации docker [7].
Docker-registry поддерживает множество бекендов для хранения образов. Ассортимент начинается с записи образов на локальную файловую систему и простирается до хранения образов в Elliptics.
Самый простой способ запустить Docker-registry — это использовать Docker. Для этого нам нужно поставить Docker (смотри выше) и дать команду запусти нужный образ.
sudo docker run -p 5000:5000 registry
Образ registry в первый раз загрузится из репозитория, закэшируется и в следующий раз будет запускаться быстро.
После запуска Docker-registry будет слушать порт 5000. Пингуем его:
curl "http://192.168.50.4:5000/_ping"
На этом настройка завершена.
Деплой приложения в случае контейнера будет мало отличаться от «неконтейнерного». Самое главное отличие в том, что для сборки образа необходим Dockerfile. По сути он состоит из набора shell-команд, которые необходимо выполнить для формирования внутреннего состояния образа. После этого образ заливается в Docker-registry.
Далее любой Docker, который хочет запустить контейнер, может загрузить себе образ из registry. Синтаксис Dockerfile, доступные команды можно найти здесь. Вместо Dockerfile вы можете предоставить Chef-рецепт или Puppet-манифест, которые будут выполнены внутри контейнера при сборке.
В профиле для этого приложения необходимо указать тип изоляции Docker. Для этого создадим новый профиль.
{
"queue-limit": 1000,
"pool-limit": 10,
"isolate": {
"type": "docker",
"args": {
"memory_limit": 1000000000,
"endpoint": "unix:///var/run/docker.sock",
"registry": "registry.cloud.net:5000",
"cpu_shares": 0
}
},
"concurrency": 200
}
Профиль мы разместили в файле именем docker-profile.json, загрузим его с именем docker-profile
cocaine-tool profile upload --name docker-profile --profile=docker-profile.json
В этом примере мы имеем готовый Dockerfile, и загрузка приложение в облако производиться так:
sudo cocaine-tool app upload --docker=unix:///var/run/docker.sock --registry=registry.cloud.net:5000 --manifest manifest-docker.json --name example-docker --timeout 20000
Это не мгновенный процесс, все зависит от размеров вашего образа и сети. Но в результате вы должны увидеть аналогичную надпись. Далее приложение запускается.
Упущен из виду один момент — о том, какие приложения cocaine-runtime запускает при старте и как узнаёт об этом. Для этого есть runlist. По сути своей этот ассоциативный массив из приложений и их профилей, который cocane-runtime читает при старте. Он располагается, как и профили, в storage. Так как наборы запускаемых приложений на разных нодах облака могут отличаться, то и ранлисты на них могут быть разные. Имя ранлиста указывается в конфиге cocaine-runtime в параметре runlist сервиса node.
По сути своей эта часть конфигурации очень похожа на пункт «Настройка cocaine-runtime». Это закономерно, так как в роли агрегирующей ноды выступает cocaine-runtime. Всю работу по балансировке в нем выполняет gateway plugin.
У нас есть две реализации данного плагина. Первая называется adhoc. Она доступна «из коробки» и не требует установки дополнительных пакетов. Вторая реализация, используемая нами в боевых условиях, называется ipvs. Как видно из названия, она использует одноименную технологию. Adhoc разумно применять в случае, когда нет возможности использовать ipvs. И безусловно, можно написать свою реализацию данного плагина, чтобы использовать для балансировки понравившуюся вам технологию.
От слов к делу. Устанавливаем пакеты:
sudo apt-get install cocaine-runtime libcocaine-core2 libcocaine-plugin-ipvs2 libcocaine-plugin-elliptics=2.24.14.31 elliptics-client=2.24.14.31
Если нет возможности использовать ipvs, то последний из приведенных пакетов ставить нет необходимости. Различия в базовой конфигурации будут незначительны и будут указаны отдельно.
Сделать runtime агрегирующей нодой позволит добаление секции network с подсекцией gateway. Предполагаем, что как и прежде, правим копию конфига «по умолчанию». Сервис storage конфигурируем аналогично предыдущему пункту — он скоро понадобится.
"network": {
"group": "224.168.2.9",
"gateway": {
"type": "ipvs" // adhoc
}
},
"services"...
После перезапуска sudo service cocane-runtime restart
должен стартовать процесс:
cocaine /usr/bin/cocaine-runtime --daemonize --configuration /etc/cocaine/cocaine-gateway.conf --pidfile /var/run/cocaine/runtime.pid
На самом деле gateway принимает еще ряд параметров (о них можно прочитать в документации).
Давайте откроем лог (по умолчанию он пишется в syslog). Можно заметить, что было зарегистрировано подключение двух нод, а потом одна из них выключилась. Вы можете проделать аналогичные операции с вашими бекендами и убедиться, что discovery работает.
Cocaine-native-proxy позволяет ходить в облачные приложения по HTTP. Установка и настройка её предельно просты.
apt-get install cocaine-native-proxy
Затем нам необходимо указать список агрегирующих нод в секции locators. В нашем случае такая нода одна. Строго говоря, там можно указать адреса локаторов и нод не работающих в режиме агрегации. Это плохо по той причине, что такая нода ничего не знает о сервисах и приложениях на других нодах, а значит, вы не будете иметь к ним доступ. Более подробное описание параметров можно найти на github.com/cocaine/cocaine-native-proxy/blob/master/README.md [8]
{
"endpoints": [
"0.0.0.0:8080"
],
"backlog": 2048,
"threads": 2,
"application": {
"locators": ["192.168.50.103:10053"],
"service_pool": 5,
"reconnect_timeout": 180,
"request_timeout": 5
}
}
Точка входа в облако в нашей инсталяции — машина с агрегируещей нодой. Несмотря на то, что HTTP интерфейс вынесен наружу через cocaine-native-proxy, на самом деле эта прокси тоже использует агрегирующую ноду. Мы её указывали в конфиге в секции locators. Давайте же попробуем позвать наши приложения двумя способами. HTTP клиент будет использовать обработчик события http, а из клиентского кода на Python обработаем события write и read.
В клиентском коде теперь указывается адрес точки входа в облако. В моем случае — 192.168.50.103.
#!/usr/bin/env python
from cocaine.services import Service
app = Service("example", host="192.168.50.103")
print(app.enqueue("write", "DATA").get())
print(app.enqueue("read", "DATA").get())
В качестве обработчика события http вызывается приложение на flask. Сейчас в HTTP-proxy поддерживается два способа определения того, в какое приложение какое событие послать. Первый способ предполагает, что переданный URL построен по шаблону: /<appname>/<eventname>/tail?arg=1&args=2
. Соответственно URL обрезается в начале. В приложение он поступит в виде /tail?arg=1&args=2
. Это очень простой и примитивный способ.
При втором способе решение принимается на основе специальных заголовков X-Cocaine-Service, X-Cocaine-Event. Их можно проставлять при помощи nginx.
Таким образом, следующие два запроса попадают в одинаковые обработчики:
curl "http://localhost:8080/read" -H "X-Cocaine-Service: example" -H "X-Cocaine-Event: http"
curl "http://localhost:8080/example/http/read"
Наше облако позволяет запускать одновременно несколько версий одного приложения и производить балансировки нагрузки между ними. Идея заключается в том, что мы объединяем несколько приложений в группу и назначаем им веса в рамках этой группы. Таким образом доступ к приложениям будет осуществляться не по имени приложения, а по имени группы. А в рамках группы выбор будет сделан исходя из веса. Это очень полезная возможность. Располагая ей, можно вводить новые версии приложений в облако, подавая на них лишь часть нагрузки (скажем, 5%). А затем увеличивая эту долю. При этом можно не удалять предыдущую версию приложения. Ведь в случае обнаружения бага в новой версии, можно так же легко вернуть нагрузку обратно, выполнив при этом лишь небольшое число команд.
Попробуем воспроизвести этот процесс. Для этого создадим группу для приложения example, скажем exampleGroup. Добавим в неё приложение example с весом 1000. И попросим ноду обновить списки групп. В логах это видно будет по записи
service/locator: adding group 'exampleGroup'
После этого то же самое приложение будет доступно по имени exampleGroup. Для того чтобы проверить, предлагаю изменить тестовое приложение, заменив в app.py приветствие в ручке hello:
@app.route('/')
def hello(name=None):
return "HELLO! I'm version #2"
Затем зальём это приложение и запустим его и отредактируем роутинг группу, добавив приложение example2.
Если подергать приложение
curl "http://localhost:8080/exampleGroup/http/"
То можно заметить, что ответы от разных версий чередуются. Если выставить вес example2 равным 0, то нагрузка на него через HTTP-proxy перестанет идти через некоторое время. Оно характеризуется периодом, через который HTTP-proxy обновляет соединения до приложений. Используя клиентский код, изменения заметите сразу.
Вот так легко и быстро, как опытный повар готовит спагетти, мы приготовили кокаиновое облако. Теперь при наличии инфраструктуры хочется рассказать о том, как писать приложения для Cocaine на различных языках, как писать свои cервисы, как сделать фреймворк. Но об этом в другой раз.
Автор: noxiouz
Источник [9]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/open-source/56046
Ссылки в тексте:
[1] рассказывали: http://habrahabr.ru/company/yandex/blog/209324/
[2] тестовое приложение: https://github.com/cocaine/cocaine-framework-python/tree/v0.11/examples/flask
[3] репозитории: http://repo.reverbrain.com
[4] документации: http://doc.reverbrain.com/elliptics:server-tutorial
[5] документации: http://docs.docker.io/en/latest/installation/ubuntulinux/
[6] описании: https://github.com/cocaine/cocaine-core/wiki/profile
[7] документации docker: http://docs.docker.io/en/latest/reference/api/registry_index_spec/
[8] github.com/cocaine/cocaine-native-proxy/blob/master/README.md: https://github.com/cocaine/cocaine-native-proxy/blob/master/README.md
[9] Источник: http://habrahabr.ru/post/214069/
Нажмите здесь для печати.