Когда вы написали серверное приложение, его нужно где-то развернуть. У нас в компании сейчас это реализовано с помощью VPS на Linux, bash скриптов, и небольшой Java программы. Это эволюционный процесс, и как по мне, получилось весьма неплохо.


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


КДПВ — архитектура системы, для части которой автоматизируем развертывание:



Немного предыстории


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


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


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


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


Архитектура системы


Один из наших продуктов имеет следующую архитектуру:



Есть один главный бекенд — центр. Именно к нему стучатся все фронтенды (их несколько). На главном бекенде хранятся данные юзеров, и сосредоточено большинство бизнес-логики.


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


  • эти сервера запускают внешний непроверенный код. Следовательно, никаких данных там хранить нельзя, потому что рано или поздно внешний код выберется из песочницы;
  • вспомогательные бекенды довольно нагруженные, именно они делают основную работу. Главный бекенд умеет распределять нагрузку, и в случае чего — мы добавляем еще вспомогательные бекенды для размазывания нагрузки.
  • на вспомогательных бекендах используются разные технологии. Есть java, node.js, python.

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


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


Level 1


Все начиналось просто. Захожу по SSH на VPS, выкачиваю изменения с git, делаю mvn build, ну или npm i, дальше java -jar или выполняю другую команду для поднятия сервера.


Ничего не автоматизировано, все вручную. Частота — несколько раз в день.


Level 2


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


Окей, в gihub добавляю SSH ключ VPS. Теперь git pull, не ввожу пароли. Вроде мелочь, но стало быстрее.


Level 3


И все же получается долго. Даже не так долго, как скучно.


Окей, пишу bash скрипт. В скрипте несколько команд:


  • git pull, чтобы вытянуть последние изменения
  • mvn package — делаем fatjar (описываю Java)
  • pkill yourserverprocessname — убиваем текущий процесс
  • java -jar yourfatjar.jar

Теперь мне нужно зайти на VPS, сделать cd ~/git/repository_name, и выполнить скрипт — ./deploy.sh


Level 4


Раз у нас есть один скрипт, почему не вызывать его удаленно?


Не забывайте, что все вращается на дешевых VPS. Поднимать что-то сложное я не хочу. Ищу простой сервер на C — чтобы занимал минимум ресурсов. Нахожу, пытаюсь пофиксить под свои нужды — не получается быстро. Я писал на C десять лет назад, и понимаю, что помню только синтаксис, но работу с сетью, сокетами забыл напрочь.


Окей, делаем возврат к Java. На коленке набрасываю сервер из нескольких десятков строк кода. Использую встроенный HttpServer. Умеет принять GET и POST запрос, вытянуть параметр token, если параметр правильный — запустить указанный bash скрипт.


Запускаю все это чудо.


Теперь на каждом VPS вращается две программы. Одна основная. Другая — вспомогательная, для перезапуска основной.


Итог — когда что-то поменяли на вспомогательном бекенде, просто переходим по определенному URL, выполняется bash скрипт, и сервер перезапускается с обновленным кодом.


Level 5


Остается последний шаг.


Открываю github, нахожу настройки webhook для нужных репозиториев. Смысл в том, что когда мы делаем определенное действие (push, etc) — github умеет дернуть указанный для этого репозитория URL. Точнее — отправить POST запрос по указанному адресу с параметрами события.


Я настроил webhook на любой push. Дергается именно тот URL, который делает обновление и рестарт сервера.


Теперь, если мы делаем git push, через минуту мы имеем обновленный и перезапущенный сервер.


Level 6 (bonus)


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


Для нас падение некритично, при условии что сервер быстро поднимется.


Окей, я начинаю искать сервисы для мониторинга доступности серверов. Нахожу UptimeRobot. По описанию все выглядит весьма прилично:


  • раз в пять минут мониторит доступность указанного адреса
  • если адрес недоступен — делает то или иное действие (отправляет POST/GET запрос, шлет оповещение по электронной почте и т.д.).

Прямо то что нужно! Настраиваю мониторинг, добавляю действие — если сервер упал, то дернуть URL перезагрузки. Также добавляю Телеграм бота, чтобы он оповещал команду о падении и восстановлении серверов.


Какое-то время работает нормально. Потом оказывается, что UptimeRobot мониторит нифига не раз в пять минут. Сервер упал, прошел час или что-то вроде того, и только тогда он обнаруживает падение.


Час — это долго. На коленке на Spring Boot набрасываю решение, аналогичное UptimeRobot, но сильно урезанное. Раз в минуту мониторим указанные адреса, если адрес недоступен — шлем оповещение про падение/поднятие сервера, ну и перезапускаем сам сервер.


В Телеграм канале, где есть все разработчики, видим вот такое:


image


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


Плюсы решения


Главный плюс описанной выше системы — простота. Примитивные bash скрипты с минимумом логики.


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


Минусы решения


Главный минус — хрупкость.


Что, если не при каждом push на github мы хотим перезапускать сервер?


Что, если мы сделали push, а код не компилируется?


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


По нормальному это решается CI/CD системой. Где код вначале проходит тесты, и лишь если все ок — доставляется на production.


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


Куда двигаться дальше?


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


Поэтому нужно слегка дофиксить bash скрипт, чтобы он лишь в случае успешной сборки билда (появился .jar файл после mvn package) убивал текущий процесс и пытался запустить новый. Что-то похожее можно сделать и для node.js — если тесты не прошли, ничего не перезапускаем.


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


Я думал про взрослые CI/CD системы, типо Jenkins, Gitlab, софт вида Ansible. Но пока пришел к выводу, что текущая система более чем достаточна.


Любую систему нужно поддерживать, а чем система проще — тем проще ее поддержка. Я люблю простые и понятные решения, и не хочу слепо использовать технологии лишь из-за того, что они на слуху. Если команда разработки вырастет, и нас перестанет устраивать текущий подход — тогда задумаемся о его изменении, но не раньше.


Путь тимлидера


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


Нужно учитывать временные и финансовые ограничения. Учитывать особенности каждого разработчика. Я сейчас читаю много тематичной литературы, из последних прочитанных за месяц книг — "Как пасти котов", "Я, нерды и гики", "Программист-праграматик", "Роман о управлении проектами".


Это интересный и новый для меня путь. Я прохожу его, описывая свой прогресс в своем Телеграм канале — Программист и бизнес.


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