У певному сенсі суть його роботи протилежна вже традиційній віртуалізації - замість ділення фізичної машини на купу віртуальних, Mesos пропонує їх об’єднувати в одне ціле, в єдиний віртуальний ресурс.
Mesos розподіляє ресурси CPU та пам’яті в кластері для задач в схожій манері як ядро Linux виділяє ресурси заліза між локальними процесами. По-суті, у випадку Mesos, це і є мікросервісами.
Уявімо собі, що є необхідність виконати різні типи задач. Для цього можна виділити окремі віртуальні машини (окремий кластер) для кожного типу. Ці віртуальні машини ймовірно не будуть повністю завантаженими і певний час будуть простоювати, тобто не працюватимуть з максимальною ефективністю. Якщо ж всі віртуальні машини для всіх задач об’єднати в єдиний кластер, ми можемо підвищити ефективність використання ресурсів і паралельно з тим підвищити швидкість їх виконання (у разі якщо задачі короткострокові чи віртуальні машини не завантажені повністю увесь час). Наступний малюнок, надіюсь, прояснить сказане:
Але це далеко не все. Кластер Mesos (із фреймворком до нього) здатен перестворювати окремі ресурси, у разі їх падіння, масштабувати ресурси вручну чи автоматично за певних умов і т.п.
Пройдемось по компонентам Mesos кластеру.
Mesos Masters
Головні контролюючі сервери кластеру. Власне вони і відповідають за надання ресурсів, розподілу задач поміж діючими Mesos слейвами. Для забезпечення високого рівня доступності їх має бути декілька і бажано непарна кількість, але звісно більша за 1. Це обумовлено рівнем кворуму. Активним майстром (лідером) в певний момент часу може бути лише один сервер.
Mesos Slaves
Сервіси (вузли), що надають потужності для виконання задач. Задачі можуть виконуватись як у власних Mesos-контейнерах, так і в Docker.
Frameworks
Сам Mesos - це лише "серце" кластеру, він надає лише середовище роботи задач. Всю логіку запуску задач, моніторинг їх роботи, масштабування і т.п. виконують фреймворки. По аналогії з Linux, це така собі init/upstart-система для запуску процесів. Ми будемо розглядати роботу фреймворку Marathon, що призначений більше для запуску постійних задач (довгострокова робота серверів і т.п.) чи короткотермінових. Для запуску задач по графіку варто скористатись іншим фреймворком - Chronos (по аналогії з cron).
Загалом, фреймворків є достатньо велика кількість, серед яких:
- Aurora (вміє як запускати задачі по графіку, так і запускати довготермінові задачі). Розробка компанії Twitter.
- Hadoop
- Jenkins
- Spark
- Torque
ZooKeeper
Демон, що відповідає за координацію Mesos Masters вузлів. Він проводить обирання/переобирання майстра за наявності кворуму. Інші вузли кластеру отримують адресу про поточного майстра запитом на групу zookeeper нод типу zk://master-node1:2138,master-node2:2138,master-node3:2138/mesos. Mesos Slaves, в свою чергу, також підключаються лише до поточного майстра, використовуючи аналогічний запит. Ми будемо встановлючати їх на ноди із Mesos майстрами, проте звісно їх можна і винести окремо. У такому разі кількість нод Mesos-майстрів може бути парною, наприклад два, адже за уникнення Split-brain відповідатимуть саме Zookeeper-ноди.
У цій статті піде мова про Mesos, його установку та особливості використання.
Для майбутніх вузлів я обрав наступні адреси:
mesos-master1 10.0.3.11
mesos-master2 10.0.3.12
mesos-master3 10.0.3.13
---
mesos-slave1 10.0.3.51
mesos-slave2 10.0.3.52
mesos-slave3 10.0.3.53
Тобто 3 майстра і 3 слейва. На майстрах також буде знаходитись фреймворк Marathon, котрий, за бажанням, можна розмістити на окремому вузлі.
На етапі тестування краще обирати віртуальні машини, що працюють на платформах апаратної віртуалізації (VirtualBox, XEN, KVM), адже, скажімо, встановити Docker в LXC-контейнер наразі не дуже можливо. Docker ми будемо використовувати для ізоляції задач, запущених на Mesos слейвах.
Тож, маючи 6 готових серверів (віртуальних машин) з Ubuntu 14.04, ми готові до бою.
MESOS MASTERS/MARATHON INSTALLATION
Виконуємо повністю аналогічні дії на всіх 3-ох Mesos майстер-серверах.
# apt-get install software-properties-common
Додаємо Mesos/Marathon-репозиторії:
# apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv E56151BF
# DISTRO=$(lsb_release -is | tr '[:upper:]' '[:lower:]')
# CODENAME=$(lsb_release -cs)
# echo "deb http://repos.mesosphere.com/${DISTRO} ${CODENAME} main" | \
sudo tee /etc/apt/sources.list.d/mesosphere.list
Mesos та Marathon потребують Java-машину для роботи. Тому установимо останню від Oracle:
# add-apt-repository ppa:webupd8team/java
# apt-get update
# apt-get install oracle-java8-installer
Перевіримо чи Java працює:
$ java -version
java version "1.8.0_91"
Java(TM) SE Runtime Environment (build 1.8.0_91-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.91-b14, mixed mode)
Хоча, здається, OpenJDK також підійде.
На кожен із майбутніх майстрів проінсталюємо власне сам Mesos та Marathon:
# apt-get -y install mesos marathon
Між іншим, фреймворк Marathon написаний на Scala.
По залежностях також буде встановлено Zookeeper. Укажемо йому адреси наших майстер нод:
# vim /etc/mesos/zk
zk://10.0.3.11:2181,10.0.3.12:2181,10.0.3.13:2181/mesos
2181 - порт на якому працює Zookeeper.
Для кожної ноди майстра оберемо унікальний ID:
# vim /etc/zookeeper/conf/myid
1
Для другого і третього сервера я обрав 2 та 3 відповідно. Номера можна обирати від 1 до 255.
Редагуємо /etc/zookeeper/conf/zoo.cfg:
# vim /etc/zookeeper/conf/zoo.cfg
server.1=10.0.3.11:2888:3888
server.2=10.0.3.12:2888:3888
server.3=10.0.3.13:2888:3888
1,2,3 - ID, що ми вказали в /etc/zookeeper/conf/myid кожного сервера.
2888 - порт, що використовує Zookeeper для комунікацій з обраним майстром, а 3888 - для проведення нових виборів, якщо з діючим майстром щось сталось.
Переходимо до налаштування кворуму. Кворум - це мінімальна кількість робочих вузлів, що необхідна для обирання нового лідера. Ця опція необхідна для запобігання Split-brain кластеру. Уявімо собі, що кластер складається лише з 2-х серверів з кворумом 1. У такому разі лише наявність 1 робочого серверу достатня для обирання нового лідера: власне кожен сервер може сам обрати себе лідером. У разі падіння одного із серверів така поведінка більш ніж логічна. Проте, що буде, коли лише мережевий зв'язок між ними порушиться? Правильно: ймовірний варіант, коли кожен із серверів по-черзі перетягуватимуть на себе основний трафік. У разі якщо ж такий кластер складається з баз - то можлива взагалі втрата еталонних даних і буде навіть не зрозуміло з чого відновлюватись.
Отже, наявність 3 серверів - це мінімум для забезпечення високої доступності. Значення кворуму у такому випадку - 2. Якщо лише один сервер перестане бути доступним - інші 2 зможуть когось обрати між собою. Якщо ж два сервера зазнають поломки чи вузли не бачитимуть один одного в мережі - група, задля збереження даних, взагалі не буде проводити обирання нового майстра до досягнення необхідного кворуму (появи одного з серверів в мережі).
Чому не має особливого сенсу обирати 4 (парну кількість) серверів? Тому, що в такому випадку, як і у випадку з 3-ома серверами, лише відсутність одного сервера некритична для роботи кластеру: падіння другого сервера буде фатальною для кластера через причину можливого Split-brain. Але у випадку 5 серверів (та рівні кворуму 3) вже падіння 2 серверів не зламають кластер. Ось так. Отже, краще за все обирати кластери з 5 і більше вузлів, а то хтозна, що може статись під час технічного обслуговування на одному із хостів.
Вказуємо однаковий рівень кворуму для майстрів:
# echo "2" > /etc/mesos-master/quorum
Указуємо IP відповідно для кожного вузлу:
# echo 10.0.3.11 | tee /etc/mesos-master/ip
Якщо в серверів відсутній доменнейм - копіюємо IP-адресу для використання її у якості імені хосту:
# cp /etc/mesos-master/ip /etc/mesos-master/hostname
Аналогічно для 10.0.3.12 та 10.0.3.13.
Налаштовуємо фреймворк Marathon. Створимо директорію для конфігураційних файлів та скопіюємо в неї хостнейм:
# mkdir -p /etc/marathon/conf
# cp /etc/mesos-master/hostname /etc/marathon/conf
Скопіюємо для Marathon налаштування Zookeeper:
# cp /etc/mesos/zk /etc/marathon/conf/master
# cp /etc/marathon/conf/master /etc/marathon/conf/zk
Останній дещо відредагуємо:
# vim /etc/marathon/conf/zk
zk://10.0.3.11:2181,10.0.3.12:2181,10.0.3.13:2181/marathon
І нарешті забороняємо завантаження демону mesos-slave на майстрах:
# echo manual | sudo tee /etc/init/mesos-slave.override
# stop mesos-master
Перезапускаємо сервіси на всіх вузлах:
# restart mesos-master
# restart marathon
Відкриємо веб-панель Mesos на порту 5050:
У разі, якщо лідером був обраний інший сервер, відбудеться переадресація на інший сервер:
Marathon має приємний темний інтерфейс:
MESOS SLAVES INSTALLATION
Як і для установки Mesos майстрів, додамо репозиторії та встановимо необхідні пакети:
# apt-get install software-properties-common
# apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv E56151BF
# DISTRO=$(lsb_release -is | tr '[:upper:]' '[:lower:]')
# CODENAME=$(lsb_release -cs)
# echo "deb http://repos.mesosphere.com/${DISTRO} ${CODENAME} main" | \
tee /etc/apt/sources.list.d/mesosphere.list
# add-apt-repository ppa:webupd8team/java
# apt-get update
# apt-get install oracle-java8-installer
$ java -version
java version "1.8.0_91"
Java(TM) SE Runtime Environment (build 1.8.0_91-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.91-b14, mixed mode)
Необхідно також заборонити запуск процесів Zookeeper та Mesos-master:
# echo manual | sudo tee /etc/init/zookeeper.override
# echo manual | sudo tee /etc/init/mesos-master.override
# stop zookeeper
# stop mesos-master
Вкажемо домени та IP-адреси для слейву:
# echo 10.0.3.51 | tee /etc/mesos-slave/ip
# cp /etc/mesos-slave/ip /etc/mesos-slave/hostname
Аналогічну дію також необхідно виконати для 10.0.3.52 та 10.0.3.53 (звісно з окремою адресою для кожного сервера).
Та описати всі майстри в /etc/mesos/zk:
# vim /etc/mesos/zk
zk://10.0.3.11:2181,10.0.3.12:2181,10.0.3.13:2181/mesos
Слейви будуть часом опитувати Zookeeper на предмет поточного лідера і підключатись до нього, надаючи свої ресурси.
У процесі запуску слейвів, може виникнути наступна помилка:
Failed to create a containerizer: Could not create MesosContainerizer:
Failed to create launcher: Failed to create Linux launcher: Failed to mount
cgroups hierarchy at '/sys/fs/cgroup/freezer': 'freezer' is already
attached to another hierarchy
У такому разі потрібно внести зміни до /etc/default/mesos-slave:
# vim /etc/default/mesos-slave
...
MESOS_LAUNCHER=posix
...
Та запустити mesos-slave знову:
# start mesos-slave
Якщо все буде виконано вірно, слейви підключаться у якості ресурсів до поточного лідера:
Кластер готовий! Запустимо якусь задачу на Marathon. Для цього відкриємо Marathon на будь-якому майстер-вузлі, натиснемо синю кнопку Create Application та введемо все як на малюнку:
Задача почала виконуватись:
У веб-панелі Mesos одразу з'явиться активна задача, котру поставив фреймворк, та історія завершених задач. Річ у тім, що ця задача буде завершуватись та починатись знову (завдяки перестворенню контейнера додатку Marathon-ом), адже вона короткострокова. Саме тому в Completed Tasks буде приведений повний лістинг всіх старих завдань:
Цю ж задачу можна виконати використовуючи API Marathon, описавши її в форматі JSON:
# cd /tmp
# vim hello2.json
{
"id": "hello2",
"cmd": "echo hello; sleep 10",
"mem": 16,
"cpus": 0.1,
"instances": 1,
"disk": 0.0,
"ports": [0]
}
# curl -i -H 'Content-Type: application/json' -d@hello2.json 10.0.3.11:8080/v2/apps
HTTP/1.1 201 Created
Date: Tue, 21 Jun 2016 14:21:31 GMT
X-Marathon-Leader: http://10.0.3.11:8080
Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Expires: 0
Location: http://10.0.3.11:8080/v2/apps/hello2
Content-Type: application/json; qs=2
Transfer-Encoding: chunked
Server: Jetty(9.3.z-SNAPSHOT)
{"id":"/hello2","cmd":"echo hello; sleep 10","args":null,"user":null,"env":{},"instances":1,"cpus":0.1,"mem":16,"disk":0,"executor":"","constraints":[],"uris":[],"fetch":[],"storeUrls":[],"ports":[0],"portDefinitions":[{"port":0,"protocol":"tcp","labels":{}}],"requirePorts":false,"backoffSeconds":1,"backoffFactor":1.15,"maxLaunchDelaySeconds":3600,"container":null,"healthChecks":[],"readinessChecks":[],"dependencies":[],"upgradeStrategy":{"minimumHealthCapacity":1,"maximumOverCapacity":1},"labels":{},"acceptedResourceRoles":null,"ipAddress":null,"version":"2016-06-21T14:21:31.665Z","residency":null,"tasksStaged":0,"tasksRunning":0,"tasksHealthy":0,"tasksUnhealthy":0,"deployments":[{"id":"13bea032-9120-45e7-b082-c7d3b7d0ad01"}],"tasks":[]}%
У цьому (і попередньому) прикладі для виконання задачі, що буде виводити hello та робити затримку в 10 секунд, буде віддано лише 16МБ пам'яті та 0.1 CPU.
Вивід запущених задач можна спостерігати, натиснувши посилання Sandbox в останній колонці основної панелі діючого Mesos-майстра:
DOCKER
Для кращого рівня ізоляції та додаткових можливостей в Mesos була інтегрована підтримка Docker. Про Docker я писав трохи більше ніж рік тому, і стаття лишилась досить таки актуальною. Раджу ознайомитись за необхідністю.
Із активацією Docker в Mesos також особливо немає нічого складного. Спочатку необхідно проінсталювати сам Docker на всі слейви кластеру:
# apt-get install apt-transport-https ca-certificates
# apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
# echo "deb https://apt.dockerproject.org/repo ubuntu-precise main" | tee /etc/apt/sources.list.d/docker.list
# apt-get update
# apt-get install docker-engine
Для перевірки коректності установки запустимо тестовий контейнер hello-world:
# docker run hello-world
Вказуємо новий тип контейнеризації для Mesos Slaves:
# echo "docker,mesos" | sudo tee /etc/mesos-slave/containerizers
Створення нового контейнеру, обазу якого ще немає в локальному кеші, може зайняти більше часу. Тож піднімемо значення таймауту реєстрації нового контейнера в фреймворку:
# echo "5mins" | sudo tee /etc/mesos-slave/executor_registration_timeout
Ну і, як зазвичай, перевантажимо сервіс після змін:
# service mesos-slave restart
Створимо нову задачу в Marathon і запустимо її в Docker-контейнері. JSON виглядатиме наступним чином:
# vim /tmp/Docker.json
{
"container": {
"type": "DOCKER",
"docker": {
"image": "libmesos/ubuntu"
}
},
"id": "ubuntu",
"cpus": 0.5,
"mem": 128,
"uris": [],
"cmd": "while sleep 10; do date -u +%T; done"
}
Тобто в контейнері буде запущено вічний цикл while з виведенням поточної дати. Нескладно, правда?
Docker.json можна також прямо влити через веб-панель Marathon, активувавши перемикач JSON під час створення нової задачі:
Чи за бажанням ввести дані в окремі поля:
Через певний час, в залежності від швидкості інтернет-з’єднання, контейнер буде запущено. Існування його можна спостерігати на Mesos-слейві, котрий отримав задачу на виконання:
root@mesos-slave1:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
81f39fc7474a libmesos/ubuntu "/bin/sh -c 'while sl" 2 minutes ago Up 2 minutes mesos-4e1e9267-ecf7-4b98-848b-f9a7a30ad209-S2.5c1831a6-f856-48f9-aea2-74e5cb5f067f
root@mesos-slave1:~#
Створимо трішки складнішу задачу Marathon, вже з healthcheck-ами. У разі незадовільної перевірки роботи контейнера, останній буде перестворено фреймворком.
{
"cmd": "env && python3 -m http.server $PORT0",
"container": {
"docker": {
"image": "python:3"
},
"type": "DOCKER"
},
"cpus": 0.25,
"healthChecks": [
{
"gracePeriodSeconds": 3,
"intervalSeconds": 10,
"maxConsecutiveFailures": 3,
"path": "/",
"portIndex": 0,
"protocol": "HTTP",
"timeoutSeconds": 5
}
],
"id": "python-app",
"instances": 2,
"mem": 50,
"ports": [
0
],
"upgradeStrategy": {
"minimumHealthCapacity": 0.5
}
}
Наразі буде запущено цілих два інстанси python-app, тобто 2 веб-сервери Python з перебросом порту.
Додамо його також через веб-панель Marathon:
Тепер у панелі Marathon можемо спостерігати, що з’явились перевірки роботи інстансу:
На Mesos майстрі з’явились нові довгострокові задачі:
Можна також побачити який фреймворк їх поставив на виконання:
Проте як дізнатись порт, котрий призначено для нових веб-серверів Python? Є декілька способів і один із них - запит до API Marathon:
$ curl -X GET -H "Content-Type: application/json" 10.0.3.12:8080/v2/tasks | python -m json.tool
...
{
...
{
"appId": "/python-app",
"healthCheckResults": [
{
"alive": true,
"consecutiveFailures": 0,
"firstSuccess": "2016-06-24T10:35:20.785Z",
"lastFailure": null,
"lastFailureCause": null,
"lastSuccess": "2016-06-24T12:53:31.372Z",
"taskId": "python-app.53d6ccef-39f7-11e6-a2b6-0800272ca725"
}
],
"host": "10.0.3.51",
"id": "python-app.53d6ccef-39f7-11e6-a2b6-0800272ca725",
"ipAddresses": [
{
"ipAddress": "10.0.3.51",
"protocol": "IPv4"
}
],
"ports": [
31319
],
"servicePorts": [
10001
],
"slaveId": "4e1e9267-ecf7-4b98-848b-f9a7a30ad209-S2",
"stagedAt": "2016-06-24T10:35:10.767Z",
"startedAt": "2016-06-24T10:35:11.788Z",
"version": "2016-06-24T10:35:10.702Z"
},
{
"appId": "/python-app",
"healthCheckResults": [
{
"alive": true,
"consecutiveFailures": 0,
"firstSuccess": "2016-06-24T10:35:20.789Z",
"lastFailure": null,
"lastFailureCause": null,
"lastSuccess": "2016-06-24T12:53:31.371Z",
"taskId": "python-app.53d6a5de-39f7-11e6-a2b6-0800272ca725"
}
],
"host": "10.0.3.52",
"id": "python-app.53d6a5de-39f7-11e6-a2b6-0800272ca725",
"ipAddresses": [
{
"ipAddress": "10.0.3.52",
"protocol": "IPv4"
}
],
"ports": [
31307
],
"servicePorts": [
10001
],
"slaveId": "4e1e9267-ecf7-4b98-848b-f9a7a30ad209-S2",
"stagedAt": "2016-06-24T10:35:10.766Z",
"startedAt": "2016-06-24T10:35:11.784Z",
"version": "2016-06-24T10:35:10.702Z"
}
]
}
Тож за адресами http://10.0.3.52:31307 та http://10.0.3.51:31319 сервери і будуть чекати підключень:
Аналогічно номери портів можна дізнатись з панелі Marathon.
Нові контейнери на кінцевих хостах:
root@mesos-slave1:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d5e439d61456 python:3 "/bin/sh -c 'env && p" 2 hours ago Up 2 hours mesos-4e1e9267-ecf7-4b98-848b-f9a7a30ad209-S2.150ac995-bf3c-4ecc-a79c-afc1c617afe2
...
root@mesos-slave2:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1fa55f8cb759 python:3 "/bin/sh -c 'env && p" 2 hours ago Up 2 hours mesos-4e1e9267-ecf7-4b98-848b-f9a7a30ad209-S2.9392fec2-23c1-4c05-a576-60e9350b9b20
...
Є також можливість указувати статичні порти для кожного нового завдання Marathon. Це реалізується за рахунок режиму Bridged Network Mode. Коректний JSON для створення задачі матиме наступний вигляд:
{
"id": "bridged-webapp",
"cmd": "python3 -m http.server 8080",
"cpus": 0.5,
"mem": 64,
"disk": 0,
"instances": 1,
"container": {
"type": "DOCKER",
"volumes": [],
"docker": {
"image": "python:3",
"network": "BRIDGE",
"portMappings": [
{
"containerPort": 8080,
"hostPort": 31240,
"servicePort": 9000,
"protocol": "tcp",
"labels": {}
},
{
"containerPort": 161,
"hostPort": 31241,
"servicePort": 10000,
"protocol": "udp",
"labels": {}
}
],
"privileged": false,
"parameters": [],
"forcePullImage": false
}
},
"healthChecks": [
{
"path": "/",
"protocol": "HTTP",
"portIndex": 0,
"gracePeriodSeconds": 5,
"intervalSeconds": 20,
"timeoutSeconds": 20,
"maxConsecutiveFailures": 3,
"ignoreHttp1xx": false
}
],
"portDefinitions": [
{
"port": 9000,
"protocol": "tcp",
"labels": {}
},
{
"port": 10000,
"protocol": "tcp",
"labels": {}
}
]
}
Тож tcp-порт контейнеру 8080 (containerPort) буде переадресований в порт 31240 (hostPort) на слейв-машині. Аналогічно з udp - 161-й в 31241. Звісно, що конкретно в цьому випадку, причин робити переадресацію udp зовсім немає і ця можливість приведена лише для прикладу.
MESOS-DNS
Очевидно, що доступ по IP-адресам не зовсім зручний. Більш того, слейв, на якому буде запущено кожен наступний контейнер з задачею, буде обиратись випадковим чином. Тому було б зовсім не зайвим мати можливість автоматично прив’язувати до контейнерів DNS-імена.
З цим може допомогти Mesos-DNS. Це DNS-сервер для Mesos кластеру, котрий використовує API Mesos майстра для отримання імен запущених задач та IP-адрес слейвів, на котрих запущені задачі.
Ім'я домена по-замовчуванню буде формуватись таким чином: ім'я задачі в Mesos + .marathon.mesos. MESOS-DNS буде обслуговувати лише цю зону - всі інші будуть перенаправлятись на стандартний DNS-сервер.
Mesos-DNS написаний на мові Go і поширюється у вигляді готового cкомпільваного бінарного файлу. Для якого в ідеалі необхідно написати init чи systemd скрипт (в залежності від версії дистрибутива), проте готові рекомендації є в мережі https://github.com/mesosphere/mesos-dns-pkg/tree/master/common.
Для тествування Mesos-DNS я створив окремий сервер з адресою 10.0.3.60, хоча з таким же успіхом його можна створити в контейнері з Marathon.
Завантажуємо на новий сервер останній реліз Mesos-DNS в директорію /usr/sbin та перейменовуємо бінарник:
# cd /usr/sbin
# wget https://github.com/mesosphere/mesos-dns/releases/download/v0.5.2/mesos-dns-v0.5.2-linux-amd64
# mv mesos-dns-v0.5.2-linux-amd64 mesos-dns
# chmod +x mesos-dns
Створюємо конфігураційний файл:
# vim /etc/mesos-dns/config.json
{
"zk": "zk://10.0.3.11:2181,10.0.3.12:2181,10.0.3.13:2181/mesos",
"masters": ["10.0.3.11:5050","10.0.3.12:5050","10.0.3.13:5050"],
"refreshSeconds": 60,
"ttl": 60,
"domain": "mesos",
"port": 53,
"resolvers": ["8.8.8.8","8.8.4.4"],
"timeout": 5,
"email": "root.mesos-dns.mesos"
}
Тобто Mesos-DNS за допомогою запита на Zookeeper (zk) буде дізнаватись інформацію про діючий мастер і опитуватиме його з частотою раз у хвилину (refreshSeconds). У випадку запиту всіх інших доменів окрім зони mesos - запити будуть переадресовані на DNS-сервери Google (пареметр resolvers). Працюватиме сервіс на стандартному 53 порті, як і будь-який інший DNS-сервер.
Параметр masters не обов’язковий. Спочатку Mesos-DNS буде шукати лідера, використовуючи запит на Zookeeper сервера і, якщо вони не доступні, буде проходитись по списку серверів, що вказані в masters.
Ось гарна стаття, котра описує всі можливі варіанти опцій http://mesosphere.github.io/mesos-dns/docs/configuration-parameters.html
Цього достатньо, тож запускаємо Mesos-DNS:
# /usr/sbin/mesos-dns -config=/etc/mesos-dns/config.json
2016/07/01 11:58:23 Connected to 10.0.3.11:2181
2016/07/01 11:58:23 Authenticated: id=96155239082295306, timeout=40000
Звісно, що також до всіх нод кластеру Mesos варто додати адресу сервера Mesos-DNS у якості основної до /etc/resolv.conf, на першу позицію:
# vim /etc/resolv.conf
nameserver 10.0.3.60
nameserver 8.8.8.8
nameserver 8.8.4.4
Після зміни resolv.conf, варто пересвідчитись, що всі імена резовляться саме через 10.0.3.60:
# dig i.ua
; <<>> DiG 9.9.5-3ubuntu0.8-Ubuntu <<>> i.ua
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 24579
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;i.ua. IN A
;; ANSWER SECTION:
i.ua. 2403 IN A 91.198.36.14
;; Query time: 58 msec
;; SERVER: 10.0.3.60#53(10.0.3.60)
;; WHEN: Mon Jun 27 16:20:12 CEST 2016
;; MSG SIZE rcvd: 49
Для демонстрації роботи Mesos-DNS запустимо через Marathon наступну задачу:
# cat nginx.json
{
"id": "nginx",
"container": {
"type": "DOCKER",
"docker": {
"image": "nginx:1.7.7",
"network": "HOST"
}
},
"instances": 1,
"cpus": 0.1,
"mem": 60,
"constraints": [
[
"hostname",
"UNIQUE"
]
]
}
# curl -X POST -H "Content-Type: application/json" http://10.0.3.11:8080/v2/apps -d@nginx.json
Ця задача встановить контейнер з Nginx, а Mesos-DNS зареєструє для нього ім’я nginx.marathon.mesos:
# dig +short nginx.marathon.mesos
10.0.3.53
При масштабуванні задачі nginx (тобто при створенні додаткових інстансів), Mesos-DNS розпізнає це і створить додаткові A-записи для того ж домену:
# dig +short nginx.marathon.mesos
10.0.3.53
10.0.3.51
Таким чином буде працювати балансування між двома нодами на рівні DNS.
Варто зауважити, що Mesos-DNS, окрім А записів, також створює SRV-запис в DNS для кожної задачі (контейнера), що працює у якості сервера. SRV-запис пов’язує назву сервіса та хостнейм-IP-порт на якому він доступний. Перевіримо SRV-запис для задачі nginx, що ми запустили раніше (не масштабовану до двох інстансів):
# dig _nginx._tcp.marathon.mesos SRV
; <<>> DiG 9.9.5-3ubuntu0.8-Ubuntu <<>> _nginx._tcp.marathon.mesos SRV
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 11956
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; QUESTION SECTION:
;_nginx._tcp.marathon.mesos. IN SRV
;; ANSWER SECTION:
_nginx._tcp.marathon.mesos. 60 IN SRV 0 0 31514 nginx-9g7b9-s0.marathon.mesos.
;; ADDITIONAL SECTION:
nginx-9g7b9-s0.marathon.mesos. 60 IN A 10.0.3.51
;; Query time: 2 msec
;; SERVER: 10.0.3.60#53(10.0.3.60)
;; WHEN: Fri Jul 01 12:03:37 CEST 2016
;; MSG SIZE rcvd: 124
Задля того, щоб не вносити зміни до налаштувань resolv.conf кожного сервера - можна внести зміни до внутрішнього DNS інфраструктури. У випадку Bind9 ці зміни будуть виглядати так:
# vim /etc/bind/named.conf.local
zone "mesos" {
type forward;
forward only;
forwarders { 192.168.0.100 port 8053; };
};
А до конфігу Mesos-DNS (уявімо, що він наразі за адресою 192.168.0.100) слід внести наступні зміни:
# vim /etc/mesos-dns/config.json
...
"externalon": false,
"port": 8053,
...
"externalon": false вказує на те, що Mesos-DNS має відмовляти в обслуговуванню запитам, що прийшли не з домену mesos.
Після внесених змін необхідно перевантажити Bind та Mesos-DNS.
Mesos-DNS також має API https://docs.mesosphere.com/1.7/usage/service-discovery/mesos-dns/http-interface/, котрий може допомогти у вирішенні задач автоматизації.
Mesos-DNS, окрім створення записів для працюючих задач, створює записи (A та SRV) також для Mesos Slaves, Mesos Masters (і окремо для лідера серед них), фреймворків. Все для нашої з вами зручності.
MARATHON-LB
Незважаючи на переваги, у Mesos-DNS також є і певні обмеження, серед яких:
- DNS не робить прив’яки до портів на яких працюють сервіси в контейнерах. Їх потрібно або ж обирати статично при постановці задачі (слідкувати за їх використанням може бути не такою вже і простою задачею) або кожен раз дізнаватись новий порт через Marathon задля доступу до кінцевих ресурсів. Mesos-DNS вміє генерувати також SRV-записи в DNS (з вказанням кінцевого хостнейму та порту), проте "з коробки" з цим вміють працювати далеко не всі програми.
- DNS не має швидкого failover (функція перемикання на резервний вузол).
- Записи в локальних DNS-кешах можуть зберігатись досить довго (як мінімум час TTL). Хоча сам Mesos-DNS опитує Mesos Master API досить часто.
- Відсутні Health-check перевірки сервісів у кінцевих контейнерах. Тобто у випадку декількох інстансів, падіння одного із них лишиться непоміченим для Mesos-DNS до повного його перестворення фреймворком Marathon.
- Деякі програми та бібліотеки не працюють коректно з декількома A-записами, що накладає серйозні обмеження на масштабування задач.
Тобто більшість проблем виникають внаслідок самої природи DNS.
Саме задля усунення цих недоліків був розпочатий проект Marathon-lb. Marathon-lb - це скрипт на мові Python, котрий опитує Marathon API і на основі отриманих даних (адреса Mesos слейва, на котрому фізично знаходиться контейнер та порт роботи сервісу) створює конфігураційний файл HAproxy та робить reload його процесу.
Проте варто зазначити, що Marathon-lb працює лише з Marathon, на відміну від Mesos-DNS. Тож у разі інших фреймворків потрібно буде шукати інші програмні рішення.
На малюнку зображене балансування запитів двома пулами балансувальників - Internal (для доступів з внутрішньої мережі) та External (для доступів з мережі Internet). Балансувальники (окрім ELB) - це HAproxy та Marathon-lb. ELB - це балансувальник в термінології Amazon AWS.
Для налаштування Marathon-lb я використав окрему віртуальну машину з адресою 10.0.3.61. В офіційній документації також наведені варіанти запуску Marathon-lb в docker-контейнері, у якості задачі, запущеній з Marathon
Налаштування Marathon-lb та HAproxy досить таки не складне. Потрібний останній стабільний реліз HAproxy і для Ubuntu 14.04 це версія 1.6:
# apt-get install software-properties-common
# add-apt-repository ppa:vbernat/haproxy-1.6
# apt-get update
# apt-get install haproxy
Завантажуємо код проекту Marathon-lb:
# mkdir /marathon-lb/
# cd /marathon-lb/
# git clone https://github.com/mesosphere/marathon-lb .
Установимо необхідні для роботи python-пакети:
# apt install python3-pip
# pip install -r requirements.txt
Генеруємо ключі, котрі потребує по-замовчуванню marathon-lb:
# cd /etc/ssl
# openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes
# cat key.pem >> mesosphere.com.pem
# cat cert.pem >> mesosphere.com.pem
Виконуємо перший запуск marathon-lb:
# /marathon-lb/marathon_lb.py --marathon http://my_marathon_ip:8080 --group internal
Якщо не виникло жодний серйозних помилок - можна переходити до створення задачі в Marathon:
{
"id": "nginx-internal",
"container": {
"type": "DOCKER",
"docker": {
"image": "nginx:1.7.7",
"network": "BRIDGE",
"portMappings": [
{ "hostPort": 0, "containerPort": 80, "servicePort": 10001 }
],
"forcePullImage":true
}
},
"instances": 1,
"cpus": 0.1,
"mem": 65,
"healthChecks": [{
"protocol": "HTTP",
"path": "/",
"portIndex": 0,
"timeoutSeconds": 10,
"gracePeriodSeconds": 10,
"intervalSeconds": 2,
"maxConsecutiveFailures": 10
}],
"labels":{
"HAPROXY_GROUP":"internal"
}
}
Одразу після переходу задачі в режим Running, опитаємо Marathon:
# /marathon-lb/marathon_lb.py --marathon http://10.0.3.11:8080 --group internal
marathon_lb: fetching apps
marathon_lb: GET http://10.0.3.11:8080/v2/apps?embed=apps.tasks
marathon_lb: got apps ['/nginx-internal']
marathon_lb: setting default value for HAPROXY_BACKEND_REDIRECT_HTTP_TO_HTTPS
...
marathon_lb: setting default value for HAPROXY_BACKEND_HTTP_HEALTHCHECK_OPTIONS
marathon_lb: generating config
marathon_lb: HAProxy dir is /etc/haproxy
marathon_lb: configuring app /nginx-internal
marathon_lb: frontend at *:10001 with backend nginx-internal_10001
marathon_lb: adding virtual host for app with id /nginx-internal
marathon_lb: backend server 10.0.3.52:31187 on 10.0.3.52
marathon_lb: reading running config from /etc/haproxy/haproxy.cfg
marathon_lb: running config is different from generated config - reloading
marathon_lb: writing config to temp file /tmp/tmp02nxplxl
marathon_lb: checking config with command: ['haproxy', '-f', '/tmp/tmp02nxplxl', '-c']
Configuration file is valid
marathon_lb: moving temp file /tmp/tmp02nxplxl to /etc/haproxy/haproxy.cfg
marathon_lb: No reload command provided, trying to find out how to reload the configuration
marathon_lb: we seem to be running on a sysvinit based system
marathon_lb: reloading using /etc/init.d/haproxy reload
* Reloading haproxy haproxy
marathon_lb: reload finished, took 0.02593827247619629 seconds
Назва групи має співпадати з ярликом "HAPROXY_GROUP" в поставленій задачі. Це зроблено задля можливості використовувати декілька балансувальників, що обслуговуватимуть різні групи.
З останнього виводу бачимо що до haproxy.cfg було додано проксування з порту 10001 на адресу 10.0.3.52:31187.
І HAproxy дійсно відкрив відповідний порт:
root@mesos-lb# netstat -tulpn
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
...
tcp 0 0 0.0.0.0:10001 0.0.0.0:* LISTEN 10285/haproxy
...
Конфігурація haproxy.cfg виглядатиме так:
# cat /etc/haproxy/haproxy.cfg
...
frontend nginx-internal_10001
bind *:10001
mode http
use_backend nginx-internal_10001
backend nginx-internal_10001
balance roundrobin
mode http
option forwardfor
http-request set-header X-Forwarded-Port %[dst_port]
http-request add-header X-Forwarded-Proto https if { ssl_fc }
option httpchk GET /
timeout check 10s
server 10_0_3_52_31187 10.0.3.52:31187 check inter 2s fall 11
Відповідно якщо буде 2 інстанси в задачі на Marathon - то буде і 2 адреси в бекенді nginx-internal_10001.
Але з ж знову таки, пам’ятати і використовувати номер порту з’єднання - не найприємніша річ. Тому ідеальний варіант - це використання virtual host mapping для HAproxy. Суть цього всього в тому, що в залежності від запитаного домену до HAproxy, буде відбуватись адресація на кінцевий слейв і порт до нього.
Поставимо ще одну задачу для того щоб перевірити все це в ділі:
{
"id": "nginx-external",
"container": {
"type": "DOCKER",
"docker": {
"image": "nginx:1.7.7",
"network": "BRIDGE",
"portMappings": [
{ "hostPort": 0, "containerPort": 80, "servicePort": 10000 }
],
"forcePullImage":true
}
},
"instances": 1,
"cpus": 0.1,
"mem": 65,
"healthChecks": [{
"protocol": "HTTP",
"path": "/",
"portIndex": 0,
"timeoutSeconds": 10,
"gracePeriodSeconds": 10,
"intervalSeconds": 2,
"maxConsecutiveFailures": 10
}],
"labels":{
"HAPROXY_GROUP":"external",
"HAPROXY_0_VHOST":"nginx.external.com"
}
}
Отже, ми додали додатковий ярлик "HAPROXY_0_VHOST" з указанням домену, який має проксувати HAproxy.
Зачекавши хвилину, запускаємо marathon_lb.py:
# /marathon-lb/marathon_lb.py --marathon http://10.0.3.11:8080 --group external
marathon_lb: fetching apps
marathon_lb: GET http://10.0.3.11:8080/v2/apps?embed=apps.tasks
marathon_lb: got apps ['/nginx-internal', '/nginx-external']
marathon_lb: setting default value for HAPROXY_HTTP_FRONTEND_ACL_WITH_AUTH
...
marathon_lb: generating config
marathon_lb: HAProxy dir is /etc/haproxy
marathon_lb: configuring app /nginx-external
marathon_lb: frontend at *:10000 with backend nginx-external_10000
marathon_lb: adding virtual host for app with hostname nginx.external.com
marathon_lb: adding virtual host for app with id /nginx-external
marathon_lb: backend server 10.0.3.53:31980 on 10.0.3.53
marathon_lb: reading running config from /etc/haproxy/haproxy.cfg
marathon_lb: running config is different from generated config - reloading
marathon_lb: writing config to temp file /tmp/tmpcqyorq8x
marathon_lb: checking config with command: ['haproxy', '-f', '/tmp/tmpcqyorq8x', '-c']
Configuration file is valid
marathon_lb: moving temp file /tmp/tmpcqyorq8x to /etc/haproxy/haproxy.cfg
marathon_lb: No reload command provided, trying to find out how to reload the configuration
marathon_lb: we seem to be running on a sysvinit based system
marathon_lb: reloading using /etc/init.d/haproxy reload
* Reloading haproxy haproxy
marathon_lb: reload finished, took 0.02756667137145996 seconds
Та перевіримо конфігураційний файл HAproxy:
# cat /etc/haproxy/haproxy.cfg
...
frontend marathon_http_in
bind *:80
mode http
acl host_nginx_external_com_nginx-external hdr(host) -i nginx.external.com
use_backend nginx-external_10000 if host_nginx_external_com_nginx-external
frontend marathon_http_appid_in
bind *:9091
mode http
acl app__nginx-external hdr(x-marathon-app-id) -i /nginx-external
use_backend nginx-external_10000 if app__nginx-external
frontend marathon_https_in
bind *:443 ssl crt /etc/ssl/mesosphere.com.pem
mode http
use_backend nginx-external_10000 if { ssl_fc_sni nginx.external.com }
frontend nginx-external_10000
bind *:10000
mode http
use_backend nginx-external_10000
backend nginx-external_10000
balance roundrobin
mode http
option forwardfor
http-request set-header X-Forwarded-Port %[dst_port]
http-request add-header X-Forwarded-Proto https if { ssl_fc }
option httpchk GET /
timeout check 10s
server 10_0_3_53_31980 10.0.3.53:31980 check inter 2s fall 11
Тож наразі, якщо буде запитаний домен nginx.external.com, котрий попередньо необхідно прив’язати до сервера HAproxy, запит буде переадресований на порт і хост задачі, на Mesos слейві.
Перевіримо результат в браузері:
Ярлики HAproxy також відображаються в Marathon:
Окрім цього, Marathon-lb підтримує налаштування SSL для HAproxy, sticky SSL, а дані для побудови haproxy.cfg взагалі можна отримувати не опитуванням Marathon по API, а підпискою на Event Bus і т.п.
Той, хто працював з платформою Amazon AWS, знає цю чудову можливість збільшувати чи зменшувати кількість віртуальних машин в залежності від навантаження. Mesos Cluster також має таку функцію.
Перший варіант реалізації - це marathon-autoscale. Скрипт по API Marathon може слідкувати за використанням CPU/пам'яті задачі та піднімати кількість інстанів за заданих умов.
Інший, більш інтелектуальний, - marathon-lb-autoscale. Цей варіант заснований на опитуванні Marathon-lb сервіса. У разі перевищення певного рівня запитів в одиницю часу - скрипт буде піднімати кількість інстансів задачі в Marathon.
Посилання
Getting Stated
https://www.digitalocean.com/community/tutorials/how-to-configure-a-production-ready-mesosphere-cluster-on-ubuntu-14-04
https://open.mesosphere.com/advanced-course/introduction/
https://open.mesosphere.com/getting-started/install/
http://iankent.uk/blog/a-quick-introduction-to-apache-mesos/
http://frankhinek.com/tag/mesos/
https://mesosphere.github.io/marathon/docs/service-discovery-load-balancing.html
https://mesosphere.github.io/marathon/
https://mesosphere.github.io/marathon/docs/ports.html
https://beingasysadmin.wordpress.com/2014/06/27/managing-docker-clusters-using-mesos-and-marathon/
http://mesos.readthedocs.io/en/latest/
Docker
http://mesos.apache.org/documentation/latest/containerizer/#Composing
http://mesos.apache.org/documentation/latest/docker-containerizer/
https://mesosphere.github.io/marathon/docs/native-docker.html
Mesos-DNS
https://tech.plista.com/devops/mesos-dns/
http://programmableinfrastructure.com/guides/service-discovery/mesos-dns-haproxy-marathon/
http://mesosphere.github.io/mesos-dns/docs/tutorial-systemd.html
http://mesosphere.github.io/mesos-dns/docs/configuration-parameters.html
https://mesosphere.github.io/mesos-dns/docs/tutorial.html
https://mesosphere.github.io/mesos-dns/docs/tutorial-forward.html
https://github.com/mesosphere/mesos-dns
Marathon-lb
https://mesosphere.com/blog/2015/12/04/dcos-marathon-lb/
https://mesosphere.com/blog/2015/12/13/service-discovery-and-load-balancing-with-dcos-and-marathon-lb-part-2/
https://docs.mesosphere.com/1.7/usage/service-discovery/marathon-lb/
https://github.com/mesosphere/marathon-lb
https://dcos.io/docs/1.7/usage/service-discovery/marathon-lb/usage/
Autoscaling
https://docs.mesosphere.com/1.7/usage/tutorials/autoscaling/
https://docs.mesosphere.com/1.7/usage/tutorials/autoscaling/cpu-memory/
https://docs.mesosphere.com/1.7/usage/tutorials/autoscaling/requests-second/
https://github.com/mesosphere/marathon-autoscale
Other
https://clusterhq.com/2016/04/15/resilient-riak-mesos/
https://github.com/CiscoCloud/mesos-consul/blob/master/README.md#comparisons-to-other-discovery-software
http://programmableinfrastructure.com/guides/load-balancing/traefik/
https://opensource.com/business/14/8/interview-chris-aniszczyk-twitter-apache-mesos
http://www.slideshare.net/akirillov/data-processing-platforms-architectures-with-spark-mesos-akka-cassandra-and-kafka
http://www.slideshare.net/mesosphere/scaling-like-twitter-with-apache-mesos
http://www.slideshare.net/subicura/mesos-on-coreos
https://www.youtube.com/watch?v=RciM1U_zltM
http://www.slideshare.net/JuliaMateo1/deploying-a-dockerized-distributed-application-in-mesos
Генеруємо ключі, котрі потребує по-замовчуванню marathon-lb:
# cd /etc/ssl
# openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes
# cat key.pem >> mesosphere.com.pem
# cat cert.pem >> mesosphere.com.pem
Виконуємо перший запуск marathon-lb:
# /marathon-lb/marathon_lb.py --marathon http://my_marathon_ip:8080 --group internal
Якщо не виникло жодний серйозних помилок - можна переходити до створення задачі в Marathon:
{
"id": "nginx-internal",
"container": {
"type": "DOCKER",
"docker": {
"image": "nginx:1.7.7",
"network": "BRIDGE",
"portMappings": [
{ "hostPort": 0, "containerPort": 80, "servicePort": 10001 }
],
"forcePullImage":true
}
},
"instances": 1,
"cpus": 0.1,
"mem": 65,
"healthChecks": [{
"protocol": "HTTP",
"path": "/",
"portIndex": 0,
"timeoutSeconds": 10,
"gracePeriodSeconds": 10,
"intervalSeconds": 2,
"maxConsecutiveFailures": 10
}],
"labels":{
"HAPROXY_GROUP":"internal"
}
}
Одразу після переходу задачі в режим Running, опитаємо Marathon:
# /marathon-lb/marathon_lb.py --marathon http://10.0.3.11:8080 --group internal
marathon_lb: fetching apps
marathon_lb: GET http://10.0.3.11:8080/v2/apps?embed=apps.tasks
marathon_lb: got apps ['/nginx-internal']
marathon_lb: setting default value for HAPROXY_BACKEND_REDIRECT_HTTP_TO_HTTPS
...
marathon_lb: setting default value for HAPROXY_BACKEND_HTTP_HEALTHCHECK_OPTIONS
marathon_lb: generating config
marathon_lb: HAProxy dir is /etc/haproxy
marathon_lb: configuring app /nginx-internal
marathon_lb: frontend at *:10001 with backend nginx-internal_10001
marathon_lb: adding virtual host for app with id /nginx-internal
marathon_lb: backend server 10.0.3.52:31187 on 10.0.3.52
marathon_lb: reading running config from /etc/haproxy/haproxy.cfg
marathon_lb: running config is different from generated config - reloading
marathon_lb: writing config to temp file /tmp/tmp02nxplxl
marathon_lb: checking config with command: ['haproxy', '-f', '/tmp/tmp02nxplxl', '-c']
Configuration file is valid
marathon_lb: moving temp file /tmp/tmp02nxplxl to /etc/haproxy/haproxy.cfg
marathon_lb: No reload command provided, trying to find out how to reload the configuration
marathon_lb: we seem to be running on a sysvinit based system
marathon_lb: reloading using /etc/init.d/haproxy reload
* Reloading haproxy haproxy
marathon_lb: reload finished, took 0.02593827247619629 seconds
Назва групи має співпадати з ярликом "HAPROXY_GROUP" в поставленій задачі. Це зроблено задля можливості використовувати декілька балансувальників, що обслуговуватимуть різні групи.
З останнього виводу бачимо що до haproxy.cfg було додано проксування з порту 10001 на адресу 10.0.3.52:31187.
І HAproxy дійсно відкрив відповідний порт:
root@mesos-lb# netstat -tulpn
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
...
tcp 0 0 0.0.0.0:10001 0.0.0.0:* LISTEN 10285/haproxy
...
Конфігурація haproxy.cfg виглядатиме так:
# cat /etc/haproxy/haproxy.cfg
...
frontend nginx-internal_10001
bind *:10001
mode http
use_backend nginx-internal_10001
backend nginx-internal_10001
balance roundrobin
mode http
option forwardfor
http-request set-header X-Forwarded-Port %[dst_port]
http-request add-header X-Forwarded-Proto https if { ssl_fc }
option httpchk GET /
timeout check 10s
server 10_0_3_52_31187 10.0.3.52:31187 check inter 2s fall 11
Відповідно якщо буде 2 інстанси в задачі на Marathon - то буде і 2 адреси в бекенді nginx-internal_10001.
Але з ж знову таки, пам’ятати і використовувати номер порту з’єднання - не найприємніша річ. Тому ідеальний варіант - це використання virtual host mapping для HAproxy. Суть цього всього в тому, що в залежності від запитаного домену до HAproxy, буде відбуватись адресація на кінцевий слейв і порт до нього.
Поставимо ще одну задачу для того щоб перевірити все це в ділі:
{
"id": "nginx-external",
"container": {
"type": "DOCKER",
"docker": {
"image": "nginx:1.7.7",
"network": "BRIDGE",
"portMappings": [
{ "hostPort": 0, "containerPort": 80, "servicePort": 10000 }
],
"forcePullImage":true
}
},
"instances": 1,
"cpus": 0.1,
"mem": 65,
"healthChecks": [{
"protocol": "HTTP",
"path": "/",
"portIndex": 0,
"timeoutSeconds": 10,
"gracePeriodSeconds": 10,
"intervalSeconds": 2,
"maxConsecutiveFailures": 10
}],
"labels":{
"HAPROXY_GROUP":"external",
"HAPROXY_0_VHOST":"nginx.external.com"
}
}
Отже, ми додали додатковий ярлик "HAPROXY_0_VHOST" з указанням домену, який має проксувати HAproxy.
Зачекавши хвилину, запускаємо marathon_lb.py:
# /marathon-lb/marathon_lb.py --marathon http://10.0.3.11:8080 --group external
marathon_lb: fetching apps
marathon_lb: GET http://10.0.3.11:8080/v2/apps?embed=apps.tasks
marathon_lb: got apps ['/nginx-internal', '/nginx-external']
marathon_lb: setting default value for HAPROXY_HTTP_FRONTEND_ACL_WITH_AUTH
...
marathon_lb: generating config
marathon_lb: HAProxy dir is /etc/haproxy
marathon_lb: configuring app /nginx-external
marathon_lb: frontend at *:10000 with backend nginx-external_10000
marathon_lb: adding virtual host for app with hostname nginx.external.com
marathon_lb: adding virtual host for app with id /nginx-external
marathon_lb: backend server 10.0.3.53:31980 on 10.0.3.53
marathon_lb: reading running config from /etc/haproxy/haproxy.cfg
marathon_lb: running config is different from generated config - reloading
marathon_lb: writing config to temp file /tmp/tmpcqyorq8x
marathon_lb: checking config with command: ['haproxy', '-f', '/tmp/tmpcqyorq8x', '-c']
Configuration file is valid
marathon_lb: moving temp file /tmp/tmpcqyorq8x to /etc/haproxy/haproxy.cfg
marathon_lb: No reload command provided, trying to find out how to reload the configuration
marathon_lb: we seem to be running on a sysvinit based system
marathon_lb: reloading using /etc/init.d/haproxy reload
* Reloading haproxy haproxy
marathon_lb: reload finished, took 0.02756667137145996 seconds
Та перевіримо конфігураційний файл HAproxy:
# cat /etc/haproxy/haproxy.cfg
...
frontend marathon_http_in
bind *:80
mode http
acl host_nginx_external_com_nginx-external hdr(host) -i nginx.external.com
use_backend nginx-external_10000 if host_nginx_external_com_nginx-external
frontend marathon_http_appid_in
bind *:9091
mode http
acl app__nginx-external hdr(x-marathon-app-id) -i /nginx-external
use_backend nginx-external_10000 if app__nginx-external
frontend marathon_https_in
bind *:443 ssl crt /etc/ssl/mesosphere.com.pem
mode http
use_backend nginx-external_10000 if { ssl_fc_sni nginx.external.com }
frontend nginx-external_10000
bind *:10000
mode http
use_backend nginx-external_10000
backend nginx-external_10000
balance roundrobin
mode http
option forwardfor
http-request set-header X-Forwarded-Port %[dst_port]
http-request add-header X-Forwarded-Proto https if { ssl_fc }
option httpchk GET /
timeout check 10s
server 10_0_3_53_31980 10.0.3.53:31980 check inter 2s fall 11
Тож наразі, якщо буде запитаний домен nginx.external.com, котрий попередньо необхідно прив’язати до сервера HAproxy, запит буде переадресований на порт і хост задачі, на Mesos слейві.
Перевіримо результат в браузері:
Ярлики HAproxy також відображаються в Marathon:
Окрім цього, Marathon-lb підтримує налаштування SSL для HAproxy, sticky SSL, а дані для побудови haproxy.cfg взагалі можна отримувати не опитуванням Marathon по API, а підпискою на Event Bus і т.п.
AUTOSCALING
Перший варіант реалізації - це marathon-autoscale. Скрипт по API Marathon може слідкувати за використанням CPU/пам'яті задачі та піднімати кількість інстанів за заданих умов.
Інший, більш інтелектуальний, - marathon-lb-autoscale. Цей варіант заснований на опитуванні Marathon-lb сервіса. У разі перевищення певного рівня запитів в одиницю часу - скрипт буде піднімати кількість інстансів задачі в Marathon.
Посилання
Getting Stated
https://www.digitalocean.com/community/tutorials/how-to-configure-a-production-ready-mesosphere-cluster-on-ubuntu-14-04
https://open.mesosphere.com/advanced-course/introduction/
https://open.mesosphere.com/getting-started/install/
http://iankent.uk/blog/a-quick-introduction-to-apache-mesos/
http://frankhinek.com/tag/mesos/
https://mesosphere.github.io/marathon/docs/service-discovery-load-balancing.html
https://mesosphere.github.io/marathon/
https://mesosphere.github.io/marathon/docs/ports.html
https://beingasysadmin.wordpress.com/2014/06/27/managing-docker-clusters-using-mesos-and-marathon/
http://mesos.readthedocs.io/en/latest/
Docker
http://mesos.apache.org/documentation/latest/containerizer/#Composing
http://mesos.apache.org/documentation/latest/docker-containerizer/
https://mesosphere.github.io/marathon/docs/native-docker.html
Mesos-DNS
https://tech.plista.com/devops/mesos-dns/
http://programmableinfrastructure.com/guides/service-discovery/mesos-dns-haproxy-marathon/
http://mesosphere.github.io/mesos-dns/docs/tutorial-systemd.html
http://mesosphere.github.io/mesos-dns/docs/configuration-parameters.html
https://mesosphere.github.io/mesos-dns/docs/tutorial.html
https://mesosphere.github.io/mesos-dns/docs/tutorial-forward.html
https://github.com/mesosphere/mesos-dns
Marathon-lb
https://mesosphere.com/blog/2015/12/04/dcos-marathon-lb/
https://mesosphere.com/blog/2015/12/13/service-discovery-and-load-balancing-with-dcos-and-marathon-lb-part-2/
https://docs.mesosphere.com/1.7/usage/service-discovery/marathon-lb/
https://github.com/mesosphere/marathon-lb
https://dcos.io/docs/1.7/usage/service-discovery/marathon-lb/usage/
Autoscaling
https://docs.mesosphere.com/1.7/usage/tutorials/autoscaling/
https://docs.mesosphere.com/1.7/usage/tutorials/autoscaling/cpu-memory/
https://docs.mesosphere.com/1.7/usage/tutorials/autoscaling/requests-second/
https://github.com/mesosphere/marathon-autoscale
Other
https://clusterhq.com/2016/04/15/resilient-riak-mesos/
https://github.com/CiscoCloud/mesos-consul/blob/master/README.md#comparisons-to-other-discovery-software
http://programmableinfrastructure.com/guides/load-balancing/traefik/
https://opensource.com/business/14/8/interview-chris-aniszczyk-twitter-apache-mesos
http://www.slideshare.net/akirillov/data-processing-platforms-architectures-with-spark-mesos-akka-cassandra-and-kafka
http://www.slideshare.net/mesosphere/scaling-like-twitter-with-apache-mesos
http://www.slideshare.net/subicura/mesos-on-coreos
https://www.youtube.com/watch?v=RciM1U_zltM
http://www.slideshare.net/JuliaMateo1/deploying-a-dockerized-distributed-application-in-mesos
Дякую!
ВідповістиВидалитиБудь ласка.
Видалити