OpenStack, Docker і веб-термінал, або як ми робимо інтерактивні вправи для навчання Linux

статті про онлайн-курсі «Введення в Linux» на освітній платформі Stepic ми обіцяли розповісти про технічної реалізації нового типу інтерактивних завдань, який був вперше застосований в цьому курсі. Цей тип завдань дозволяє створювати на льоту віртуальні сервери з Linux для роботи через веб-термінал прямо у вікні браузера. Автоматична перевіряюча система стежить за коректністю виконання завдань.

Приклад завдання з курсу:


У цій статті я хочу розповісти про проект, який ліг в основу нового типу завдань на Stepic. Я також розповім про те, з яких компонентів складається система, і як вони взаємодіють між собою, як і де створюються віддалені сервера, як працює веб-термінал і автоматична перевіряюча система.

Натхнення
Я один з тих багатьох, хто при пошуку роботи не любить складати резюме і писати десятки мотиваційних листів IT-компаніям, щоб пробитися через фільтр HR-фахівців і нарешті отримати від них заповітне запрошення на співбесіду. Куди більш природно, замість того, щоб писати про себе похвальні слова, показати свій реальний досвід і навички на прикладі вирішення щоденних завдань.

Ще в 2012 році, будучи студентом Матмеха Спбду, я надихнувся проектом InterviewStreet, пізніше эволюционировавшим в проект HackerRank. Хлопці розробили платформу, на якій IT-компанії можуть проводити онлайн-змагання з програмування. За результатами таких змагань компанії запрошують кращих учасників пройти співбесіду. На додаток до рекрутингу метою проекту HackerRank стало створення сайту, на якому будь-який бажаючий може розвивати навички програмування через рішення задач з різних областей Computer Science, таких як теорія алгоритмів, штучний інтелект, машинне навчання і інші. Вже тоді існувала велика кількість інших платформ, на яких проводилися змагання для програмістів. Активно набирали популярність інтерактивні онлайн-платформи для навчання програмуванню, такі як Codecademy і Code School. До того моменту я мав достатній досвід роботи системним адміністратором Linux і хотів бачити подібні ресурси для спрощення процесу працевлаштування системних інженерів, проведення змагань з адміністрування Linux, ресурси для навчання системного адміністрування шляхом вирішення реальних завдань у цій області.

Після наполегливого пошуку схожих проектів знайшовся тільки LinuxZoo, розроблений для академічних цілей в Единбурзькому університеті ім. Непера (Edinburgh Napier University). Мені також попався на очі промо-ролик вельми успішного і амбітного проекту Coderloop, вже тоді занедбаного після покупки компанією Gild. У цьому ролику я побачив саме те, про що тоді мріяв. На жаль, розроблена в Coderloop технологія для створення інтерактивних вправ по системного адміністрування так і не побачила світ. Під час листування з одним із засновників проекту Coderloop я отримав багато теплих слів і побажань по розробці та подальшому розвитку цієї ідеї. Моє натхнення не було меж. Так я почав розробку платформи Root ' Roll, якою став присвячувати практично весь свій вільний час.

Цілі Root ' Roll
Основною метою проекту стало не створення конкретного ресурсу для навчання, проведення змагань або рекрутингу системних інженерів, а щось більше — створення базової технічної платформи, на якій можна було б будувати і розвивати будь-який з цих напрямків.

Ключовими вимогами базової платформи стали наступні:
  • Мати можливість максимально швидко і дешево запускати одночасно сотні або навіть тисячі віртуальних машин з мінімальною Linux-системи, які можна безпечно разом з root-права віддати на розтерзання користувачам. Дешево мінімізувати кількість споживаних системних ресурсів на одну віртуальну машину. Відсутність якого-небудь істотного бюджету передбачає відмову від використання хмарного хостингу зразок Amazon EC2 або Rackspace за принципом «одна віртуальна машина» = «один хмарний инстенс».
  • Дозволяти працювати з віртуальною машиною через веб-термінал прямо у вікні браузера. Ніякі зовнішні програми для цього не потрібні, лише веб-браузер.
  • Нарешті, мати інтерфейс для тестування конфігурації будь-якої віртуальної машини. Тестування конфігурації може включати в себе як перевірку стану файлової системи (лежать потрібні файли на своїх місцях, коректно їх вміст), так і перевірки сервісів і мережевої активності (запущені якісь сервіси, чи правильно вони налаштовані, коректно відповідають на певні мережеві запити і т. д.).
Всі перераховані вимоги в якійсь мірі вже вдалося реалізувати. Про все по порядку.

Запуск віртуальних машин
Для запуску віртуальних машин спочатку потрібно було визначитися з технологією віртуалізації. Варіанти з гипервизорной віртуалізацією: апаратної (KVM, VMware ESXi) і паравиртуализацией (Xen) — відпали досить швидко. Ці підходи мають досить великі накладні витрати на системні ресурси, зокрема пам'ять, т. к. для кожної машини запускається своє окреме ядро, і стартують різні підсистеми ОС. Віртуалізації такого типу також вимагають наявності виділеного фізичного сервера, до того ж, при заявленому бажання запускати сотні виртуалок, з досить непоганим «залізом». Для наочності можна подивитися на технічні характеристики серверної інфраструктури, що використовується в проекті LinuxZoo.

Далі фокус упав на системи віртуалізації рівня операційної системи (OpenVZ і Linux Containers (LXC)). Контейнерну віртуалізацію можна дуже грубо порівняти з запуском процесів в ізольованому chroot оточенні. Про порівняння і технічні характеристики різних систем віртуалізації написано вже величезна кількість статей, у тому числі на хабре, тому не зупиняюся на подробицях їх реалізації. Контейнеризація не має таких накладних витрат, як повноцінна віртуалізації, тому що всі контейнери розділяють одне ядро хостової системи.

Якраз до моменту мого вибору віртуалізації встиг вийти в Open Source і нашуміти про себе проект Docker, надає інструменти і середовище для створення і управління LXC-контейнерами. Запуск LXC-контейнера під управлінням Docker (далі docker-контейнер або просто контейнер) порівняємо з запуском процесу в Linux. Як і звичайному linux-процесу, контейнеру не потрібно наперед резервувати оперативну пам'ять. Пам'ять виділяється та очищається по мірі використання. Якщо потрібно, можна встановити гнучкі обмеження на максимальний об'єм використовуваної пам'яті в контейнері. Величезна перевага контейнерів в тому, що для контролю їх пам'яттю використовується загальна хостова підсистема управління сторінками пам'яті (у тому числі механізм copy-on-write і колективні сторінки). Це дозволяє збільшити щільність розміщення контейнерів, тобто можна запускати набагато більше примірників linux-машин, ніж при використанні гипервизорных систем віртуалізації. Завдяки цьому досягається ефективна утилізація ресурсів сервера. Із запуском декількох сотень docker-контейнерів з процесом bash всередині запросто впорається навіть micro-инстенс в хмарі Amazon EC2. Ще один приємний бонус — сам процес запуску контейнера займає мілісекунди.

Таким чином, на перший погляд Docker дешево вирішував завдання запуску великої кількості машин (контейнерів), тому для першого proof-of-concept рішення я вирішив зупинитися саме на ньому. Питання про безпеку гідний окремого обговорення, поки опустимо. До речі, хлопці з Coderloop для створення віртуальних оточень у своїх вправах також використовували LXC-контейнери.

Управління контейнерами
Docker надає програмний REST-інтерфейс для створення і запуску контейнерів. Через цей інтерфейс можна управляти тільки контейнерами, які знаходяться на сервері, на якому запущено сервіс docker.

Якщо зазирнути на один крок вперед, то добре було б мати можливість горизонтально масштабуватися, тобто запускати всі контейнери не на одному сервері, а розподілити їх на кілька серверів. Для цього необхідно мати централізоване управління docker-хостами і планувальник, що дозволяє балансувати навантаження між декількома серверами. Наявність планувальника може бути дуже корисно під час технічного обслуговування сервера, наприклад, установки оновлень, які потребують перезавантаження. В такому випадку сервер позначається як «в обслуговуванні», в результаті чого нові контейнери на ньому не створюються.

Додаткові вимоги до централізованої системи управління — налаштування мережі в контейнерах і управління квотами системних ресурсів (процесор, пам'ять, диск). А адже всі ці вимоги — це не що інше, як завдання, які успішно вирішуються хмарами (IaaS). Своєчасно влітку 2013 вийшов посада від розробників Docker про інтеграцію Docker з хмарної платформою OpenStack. Новий nova-docker драйвер дозволяє з допомогою openstack-хмари управляти парком docker-хостів: запускати контейнери, піднімати мережа, контролювати та регулювати споживання системних ресурсів — саме те, що нам потрібно!

На жаль, навіть на сьогоднішній день nova-docker драйвер все ще досить «сирий». Часто з'являються зміни, несумісні навіть з останньою стабільною версією openstack'а. Доводиться самостійно підтримувати окрему стабільну гілку драйвера. Довелося також написати кілька патчів, що поліпшують продуктивність. Наприклад, для отримання статусу одного docker-контейнера драйвер запитував статуси всіх запущених контейнерів (посилав N http-запитів до docker-хосту, де N — число всіх контейнерів). У разі запуску декількох сотень контейнерів створювалася непотрібне навантаження на docker-хости.

Незважаючи на певні незручності, вибір OpenStack'а в якості оркестратора контейнерів у моєму випадку все ж варто: з'явилася централізована система управління віртуальними машинами (контейнерами) і обчислювальними ресурсами з єдиним API. Великий бонус єдиного інтерфейсу полягає ще в тому, що додавання в Root ' Roll підтримки повноцінних віртуальних машин на базі KVM не зажадає ніяких істотних змін в архітектурі і коді самої платформи.

З недоліків OpenStack можна відзначити лише досить високу складність розгортання і адміністрування приватного хмари. Зовсім недавно заявив про собі гідний уваги проект Virtkick — така собі спрощена альтернатива OpenStack. З нетерпінням чекаю його успішного розвитку.

Вибір веб-терміналу
Ще на початковому етапі складання вимог до платформи Root ' Roll основний фичей, яку я хотів бачити в першу чергу, була можливість роботи з віддаленим linux-сервером через вікно веб-терміналу прямо у себе в браузері. Саме з вибору терміналу і почалася розробка, а точніше вивчення і вибір технічних рішень для платформи. Веб-термінал є чи не єдиною точкою входу користувача в систему. Це саме те, що він в першу чергу бачить і з чим працює.

Одним з небагатьох в той час онлайн-проектів, в яких активно використовувався веб-термінал був PythonAnywhere. Він став еталоном, на який я періодично поглядав. Це зараз вже з'явилася величезна кількість веб-проектів і хмарних середовищ розробки, в яких можна побачити термінали: Koding, Nitrous, Codebox, Runnable та ін

Будь-який веб-термінал складається з двох основних частин:
  • Клієнтської: динамічне JavaScript додаток, що займається перехоплення натискань клавіш і їх відсиланням серверної частини, прийомом даних від серверної частини та їх відображенням у веб-браузері користувача.
  • Серверної: веб-сервіс, який приймає повідомлення про натискання клавіш від клієнта і відправляє їх у керуюче термінальний пристрій (pseudo terminal або PTY) пов'язаного з терміналом процесу, наприклад bash. Сирої висновок терміналу з pty-пристрої пересилається незмінним клієнтську частину або обробляється на стороні сервера, у цьому разі в клієнтську частину передається вже перетвореним, наприклад, у форматі HTML.
Було розглянуто безліч емуляторів терміналу: Anyterm, Ajaxterm, Shellinabox (використовується в PythonAnywhere), Secure Shell, GateOne і tty.js. Останні два виявилися найбільш функціональними і активно розвиваються. Через поширення під вільною ліцензією MIT вибір зупинився на tty.js. Tty.js є client-side емулятором терміналу (розбір сирого виведення терміналу, в тому числі керуючих послідовностей, що виконується на стороні клієнта JavaScript).

Серверна частина tty.js написана на Node.js, була нещадно відламана і переписана на Python. Транспорт Socket.IO був замінений на SockJS, про подібному вдалому досвіді вже писали в блог PythonAnywhere.

Політ «Світлячка»
Нарешті дійшли до движка платформи Root ' Roll. Проект побудований за принципами микросервисной архітектури. Основна мова розробки серверної частини — Python.

Діаграма зв'язків сервісів платформи Root ' Roll:
Діаграма зв'язків сервісів платформи Root ' Roll

Микросервисы носять імена головних героїв науково-фантастичного телесеріалу «Світлячок» (Firefly). Основні герої фільму — екіпаж міжпланетного космічного корабля «Сереніті» класу Світлячок. Призначення та місце персонажа на кораблі в деякій мірі відображає призначення і функціонал відповідного його імені сервісу.

Mal — backend API
Mal — власник і капітан корабля. На нашому кораблі Mal є ключовим сервісом, який координує роботу всіх інших сервісів. Це додаток на Django, яке реалізує бізнес-логіку платформи Root ' Roll. Mal виконує роль API-клієнта приватного OpenStack-хмари і в свою чергу надає високорівневий REST інтерфейс для виконання наступних дій:
  • Створення/видалення віртуальної машини (контейнера). Запит перетворюється і делегується хмари.
  • Створення і підключення терміналу до контейнера.
  • Запуск тестового сценарію для перевірки конфігурації віртуальної машини. Запит делегується микросервису перевіряє системи.
  • Отримання результатів перевірки конфігурації.
  • Автентифікація клієнтів і авторизація різних дій.
Kaylee — мультиплексор терміналів
Kaylee — механік корабля. Сервіс Kaylee є двигуном процесу комунікації веб-терміналу з віддаленої віртуальною машиною. Це асинхронний веб-сервер на Python і Tornado, що реалізує серверну частину tty.js.

З одного боку, клієнтська частина tty.js (термінальне вікно) встановлює з'єднання з Kaylee по протоколу SockJS. З іншого боку, Kaylee встановлює з'єднання з термінальним пристроєм віртуальної машини. У випадку з docker-контейнерами встановлюється з'єднання по протоколу HTTPS з керуючим pty-пристроєм, що працює в контейнері процесу, як правило, це процес bash. Після чого Kaylee виконує просту функцію проксі-сервера між двома встановленими сполуками.

Для аутентифікації клієнта і отримання даних про віртуальній машині Kaylee спілкується з Mal за REST API.

Zoe — check-система
Zoe — помічник капітана на борту, у всьому йому беззастережно довіряє. Zoe є автоматичною перевіряє системою, що виконує тестування конфігурацій віртуальних машин. Сервіс Zoe у вигляді celery-таски отримує від Mal'а завдання на запуск тестового сценарію. По закінченню перевірки повідомляє REST API назад Mal'у результати тестування. Як правило, Zoe помилок не прощає (багато учасників курсу з Linux на Stepic'е вже встигли в цьому переконатися).

Тестовий сценарій являє собою не що інше, як Python-скрипт з набором тестів, написаних з використанням фреймворку для тестування py.test. Для py.test був розроблений спеціальний плагін, який обробляє результати запуску тестів і посилає їх Mal'за REST API.

Приклад сценарію для вправи, в якому потрібно написати і запустити простий односторінковий сайт на веб-фреймворку Django:

import re
import requests

def test_connection(s):
assert s.run('true').succeeded, "Could not connect to server"

def test_is_django_installed(s):
assert s.run('python-c "import django"').succeeded, "Django is not installed"

def test_is_project_created(s):
assert s.run('[ -x /home/quiz/llama/manage.py ]').succeeded, "Project is not created"

def test_hello_lama(s):
try:
r = requests.get("http://%s:8080/" % s.ip)
except:
assert False "Service is not running"
if r.status_code != 200 or not re.match(".*Hello, lama.*", r.text):
assert False "Incorrect response"

Для віддаленого виконання команд на досліджуваній віртуальній машині Zoe використовує бібліотеку Fabric.

Висновок
Незабаром на платформі Stepic будь-який бажаючий зможе створювати свої курси та інтерактивні вправи по роботі в Linux з використанням всіх тих технологій, про які я розповів у статті.

У наступній статті я напишу про своє успішне участі в JetBrains EdTech хакатоне, про особливості і проблеми інтеграції Root ' Roll в Stepic і, за вашим бажанням, розкрию глибше будь-яку з порушених тем.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.