[Из песочницы] Разбираемся в DevOps и Js на примере Dillinger.io

Я бэкенд Java-разработчик, и до фронтенда руки доходят очень редко. Это большое упущение. Отсутствие представления о фронтенде не даёт мне увидеть полную картину мира. Я не знаю, на что обращают внимание и как пользуются всеми модными средствами вроде Node.js, Gulp, Webpack, то есть тем, что так пугает бекенд разработчиков своим разнообразием и постоянными переменами.

И первой моей -жертвой- целью стал Dillinger.io. Это открытый текстовый редактор для Markdown, в нём есть подсветка синтаксиса и экспорт написанного текста в PDF, HTML, Dropbox, Github и Google Drive, также можно добавить собственный способ, к примеру, статический генератор сайтов или Wordpress.

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

Технологии


В репозитории проекта висит скромный список использованных технологий:

  • AngularJs — фронтенд-фреймворк, позволяющий приносить логику в HTML (да-да, первый);
  • Ace Editor — текстовый редактор;
  • Marked — парсер и компилятор Markdown;
  • Twitter Bootstrap — «Nough said;
  • Node.js — реактивный js на бекенде;
  • Express — web-framework для Node.js;
  • Gulp — потоковая система сборки для фронтенда;
  • Keymaster.js — библиотека для обработки хоткеев;
  • jQuery — ну куда без него?


От себя я могу добавить ещё парочку:

  • Docker — контейнеры (о них и так все говорят);
  • Docker Compose — тулза для запуска контейнеров пачками;
  • Ansible — тулза для автоматизации управления конфигурацией;
  • Heroku — облачный сервер приложений;
  • N|Solid — мониторинг, прокси, оркестрация и управление приложениями на Node.Js;
  • NGinx — http и proxy сервер (в этом проекте);
  • Npm — менеджер пакетов для Node.js;
  • Webpack — сборщик JS файлов для фронтенда;
  • Karma — запускатор тестов;
  • Ejs — движок для шаблонов;


Такой зоопарк технологий меня, мягко говоря, удивил. Из всего этого, я использовал только AngularJs, Bootstrap и Jquery. О большинстве технологий, я, конечно, слышал, но слухами всё ограничивалось.

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


Когда я просматривал список технологий, мне не давал покоя вопрос: «Зачем текстовому редактору, который работает в моём браузере, бекенд?». И тем более мне было непонятно, зачем нужен в этом случае Docker и Enterprise платформа для управления Node.js. Но я узнал больше о создателе приложения и второй вопрос решился сам собой. Joe McCann — сооснователь NodeSource, компании, которая выпустила N|Solid (фреймворк для управления и мониторинга Node.js приложений). Это значит, что он хорошо знаком с созданием инфраструктуры приложения и в этой области у него есть чему поучиться.

Можно ли просто запустить приложения?


Если вы хотите просто протестировать приложение, то можно выбрать простой вариант. Нужны только Node.js и Gulp. Node.js поднимает и настраивает веб-сервер Express. А Gulp следит за изменениями в Js и Sass файлах, преобразует их в вид, понятный браузеру и перезагружает страницу, если какие-то из файлов были изменены. В ридми можно найти гайды по установке и по запуску сервера.

Настройки Node.js лежат в файле app.js. В этом файле подключаются внешние зависимости и внутренние модули (роутинг главной страницы, настройки и плагины), а также настраивается веб-сервер. Он отдаёт статические страницы, компилирует шаблоны ejs, привязывает куки к пользователю, сжимает http-ответы, настраивает логирование и находит обработчики для динамических запросов.

Скрипты для Gulp лежат в папке /gulp. Основной интерес здесь представляют таски: просто по их названиям можно понять, зачем используется Gulp в этом проекте: watch, build, webpack, sass, browserSync. Или на русском: мы сообщаем Gulp за какими файлами нужно следить. Если кто-то из них обновился, то при помощи Webpack он соберёт скрипт из зависимостей и кусочков Js-кода и превратит Sass в Css. Дальше, он попросит браузер обновить страницу при помощи browserSync.

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

6b1bfb90b269498588a383d03de4d122.jpg

Как перенести приложение на сервер?


Но как только вы покончили с разработкой и тестированием приложения локально, встаёт вопрос о переносе его на сервер. Конечно, самый простой вариант — сделать всё влоб и установить все нужные утилиты на удалённый компьютер, убедиться, что он настроен так же как локальная машина, запустить приложение и верить в то, что ничего не изменится. Но чтобы быть уверенным в том, что среда настроена правильно и ничего не изменится, нужно как-то изолировать своё приложение. Есть два принципиальных пути: виртуальные машины и контейнеры. И, конечно, грех не собрать все хипстерские технологии, если пишешь на JS =) И выбор в сторону Docker очевиден.

В конфигурации Docker контейнера ничего нет ничего сверхъестественного. В качестве базового контейнера используется образ, на котором установлен Node.Js и все нужные утилиты. В скрипте указываются инструкции для установки зависимостей и запуска сервера, наружу отдаётся 80 порт, и указываются переменные среды.

aaa63c3c40ed4ee1825f8a452e1d9d34.jpg

Но почему именно Docker?


Мода — не единственная причина использования Docker в этом проекте. Как я уже говорил, автор проекта как-то замешан в создании N|Solid — enterprise платформы для приложений на Node.Js. В прошлом пункте я не упомянул, но в качестве базового образа как раз используется образ N|Solid, и пока непонятно, какие преимущества это даёт. Но в корне лежат ещё два подозрительных файла, которые стоит изучить: nsolid.yml и docker-compose.yml.

Эти два файла нужны для конфигурации утилиты Docker Compose. Они описывают, как поднять это приложение в режиме кластера и конфигурируют платформу N|Solid. Все контейнеры поднимаются в одной сети и в итоге стартует 8 контейнеров:

  • N|Solid service registry (регистрирует приложения, участвующие в инфраструктуре),
  • N|Solid proxy (на основе Service Registry распределяет запросы между приложениями),
  • N|Solid console (средство для управления приложением),
  • 4 контейнера Dillinger (stateless, регистрируются в service registry и готовы принимать запросы),
  • NGinx (веб-сервер, не на Node.JS).

    В репозитории не забыли и про конфигурацию NGinx. Он используется в качестве Proxy. NGinx, как и Express.js, сжимает данные и отдаёт наружу статические файлы. Запросы к статическим файлам он обрабатывает самостоятельно, а остальные запросы проксирует между узлами кластера. Получается, что в репозитории находятся две реализации Proxy (N|Solid Proxy и NGinx), и по умолчанию используется NGinx. Статические файлы можно тоже отдать наружу двумя способами: через Express.js сервер или с помощью NGinx. Оба этих способа используются: первый — в случае локальной разработки, а второй — если запускать приложение в режиме кластера и использовать NGinx в качестве сервера.

    Из-за того, что возможности Service Registry, Proxy и Console не используются в этой конфигурации просто закроем на них глаза и рассмотрим на рисунке только проксирование запросов. Да и, вообще, я не ощутил всей важности N|Solid для этого проекта, большую часть функциональности можно воссоздать с помощью NGinx, может быть всё дело в удобстве управления, но до него руки не дошли.


9323348ca42240498dfa69e5fb6bdfcc.jpg

Ещё больше автоматизации


Но на этом автоматизация не заканчивается =) В корне лежат ещё два интересных файла: deploy.yml и dillinger.service. Они тоже связаны с созданием инфраструктуры.

Первый из них — это скрипт для Ansible. Он автоматизирует установку приложения на Debian. А точнее, подключает дополнительные репы, устанавливает rsync и docker, устанавливает образ Dillinger, создаёт и запускает Debian-service с этим образом.

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

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

Фронт-енд


Один из важных подходов первой версии AngularJs — это двухсторонний биндинг. Он позволяет описывать часть логики прямо в HTML, и эта возможность активно используется в этом проекте. Код получился размазанным между JS-файлами и ejs-шаблонами, но проект небольшой и полную картину удаётся держать в голове.

Шаблоны


Для написания HTML был использован шаблонизатор Ejs. В итоге, файлы получились небольшими и легко читаемыми. Все шаблоны можно посмотреть в папке /view. Но если вы просто начнёте читать все файлы подряд, то увидите теги, которые отсутствуют в обычном Html, к примеру, тег switch в файле /views/dropdowns/settings. Это директива, описанная с помощью AngularJs, с ней связана какая-то логика, и это неплохой способ использования ангуляра, в этом случае, код стал более читаемым и понятным. Хотя, если использовать директивы слишком часто, придётся учить собственный Domain Specific Language (DSL) для каждого сайта. Но с появлением кода, шаблоны становятся менее читаемыми, к примеру, в файле /views/sidebar.ejs необходимо разбираться в том, что значит документ в этом контексте и почему нельзя удалить единственный оставшийся документ.

В шаблонах также присутствует разделение скриптов для режима разработки и production: в файле /views/footer.ejs, включена асинхронная загрузка скрипта и Google-аналитика.

AngularJs


Мы уже успели затронуть AngularJs, и пришло время подробнее разобраться, как и почему он используется в этом проекте. Если кто-то незнаком с AngularJs, стоит упомянуть, что это целый фреймворк как, например, Spring для Java. То есть, он содержит не только вспомогательные функции, но ещё и подходы к программированию, которые нужно использовать вместе с ним: Dependency Injection (описание зависимостей при создании компонента ангуляра), модульность (паттерн программирования, позволяющий отделять видимость переменных), двухсторонний биндинг (при изменении переменной меняется компонент на экране, при изменении компонента на экране (ввод текста, выбор чекбокса и т. д.) изменяется состояние переменной). Подробнее можно посмотреть здесь (загляните под спойлер).

Код приложения можно найти в папке /public/js. В файле /public/js/app.js описываются зависимости, основной модуль ангуляра и заставку на старте с логотипом и подписью о загрузке.

Сервисы


Вся основная логика приложения расположена в папке /public/js/services.

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

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

Ещё в этой папке лежат два сервиса: wordscount и сервис нотификаций. С первым сервисом всё понятно, он простой. Количество кода во втором сервисе может испугать, но там тоже всё довольно просто: он описывает функцию, с помощью которой можно показать пользователю всплывающее окно. Основная часть кода высчитывает взаимное положение нотификаций, выводит их по порядку и обрабатывает закрытие окна. В качестве шаблона нотификации используется html файл, в нём выводится логотип и сообщение.

Контроллеры


С контроллерами всё ещё легче. Они занимаются обработкой событий, описанных в ejs-шаблонах. К примеру user controller занимается обработкой нажатий на кнопки выпадающего меню, а document controller занимается обработкой событий левого меню, созданием нового файла и автосохранением.

Здесь же есть контроллер экспорта файлов, он использует форму на главной странице для загрузки файлов. И ходит на сервер в core plugin для рендеринга Markdown.

В контроллерах также происходит конфигурирование самого текстового редактора. К моему разочарованию, про сам текстовый редактор написано мало. Основные функции делегированы редактору Ace Editor, и всё, что мне удалось найти — это тему и конфигурацию текстового редактора дважды (из-за дублирования кода тоже могут быть баги).

Директивы


О директивах я уже упомянул, когда говорил о шаблонах ejs. Директива — это произвольный html элемент или атрибут, который имеет собственную логику. Их в проекте много, и я не буду на них останавливаться подробно, они очень простые, заинтересовавшиеся могут поискать их в папке /public/js/components и в других уголках проекта.

Backend


О бекенде я расскажу не так уж много. Я был удивлён встретить его в этом проекте. Бекенд в проекте реализован в виде плагинов и лежит в папке /plugins. Среди них есть плагин core, который занимается рендерингом Markdown в PDF, Html и Markdown. И 4 плагина интеграции с внешними сервисами, их имеет смысл читать в связке с соответствующими плагинами на фронт енде. Это просто адаптеры, которые занимаются авторизацией, рендерерят формочки и собирают дополнительную информацию от пользователя. Так скажем, чтение на любителя, если кому-то покажется интересным, не буду останавливать.

Пожалуй, хватит об этом проекте. Сегодня мы разобрались в архитектуре простого приложения на Node.js, включая инфраструктуру, фронт енд и бек енд. Надеюсь, вы узнали что-то новое и стало понятнее, как работает этот проект и, даже может быть, появилось более чёткое видение, как сделать лучше собственный. Я мог забыть осветить какую-то важную тему или что-то не заметить, пишите, я с радостью исправлюсь.

К сожалению, в рамках текущей версии текстового редактора, реализованы не все задумки Мартина Бродера, но зато есть работа для Open-Source сообщества. Когда я разбирался в этом проекте, я написал пару issue и починил один баг и призываю вас к тому же. Сделайте это хотя бы ради себя, вы наверняка найдёте ещё что-то интересное. Смотрите, сколько работы вас ждёт.

С момента написания статьи могло многое изменится. Последний коммит на момент написания статьи: c3782a8dc0b91e5a6ae2c2ecd528daa1f42b3a9a.

© Habrahabr.ru