Когда вы написали серверное приложение, его нужно где-то развернуть. У нас в компании сейчас это реализовано с помощью 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, но сильно урезанное. Раз в минуту мониторим указанные адреса, если адрес недоступен шлем оповещение про падение/поднятие сервера, ну и перезапускаем сам сервер.
В Телеграм канале, где есть все разработчики, видим вот такое:
Такое наколенное решение работает уже больше месяца, пока проблем не замечал.
Плюсы решения
Главный плюс описанной выше системы простота. Примитивные bash скрипты с минимумом логики.
Все уровни автоматизации накладываются один на другой, и верхние слои зависят от нижних, но не наоборот. В любой момент можно откатиться на уровень вниз, и ничего не поломается.
Минусы решения
Главный минус хрупкость.
Что, если не при каждом push на github мы хотим перезапускать сервер?
Что, если мы сделали push, а код не компилируется?
В некоторой мере эти минуса нивелируются малым количеством разработчиков, работающим над бекендом. Мы привыкли к этой системе, знаем ограничения. Поскольку даже такая автоматизация сильно упрощает работу, мы живем с этим.
По нормальному это решается CI/CD системой. Где код вначале проходит тесты, и лишь если все ок доставляется на production.
Следуюет учесть, что мы начинали, и до сих пор частично находимся в стадии MVP. То есть, на данном этапе важно быстро выкатить продукт, собрать фидбек. Но сейчас потихоньку переходим в стадию, когда продукт начинает немного зарабатывать деньги, и ощутимо экономить. Поэтому я задумываюсь про описанные выше минусы, и прикидываю способы их устранения.
Куда двигаться дальше?
Скорей всего, указанные выше минуса решатся довольно просто. В Java, при сборке Maven проекта, сначала прогоняются юнит тесты, потом собирается jar. Если тесты не проходят, либо же ошибка компиляции и билд не собрался мы про это узнаем.
Поэтому нужно слегка дофиксить bash скрипт, чтобы он лишь в случае успешной сборки билда (появился .jar файл после mvn package) убивал текущий процесс и пытался запустить новый. Что-то похожее можно сделать и для node.js если тесты не прошли, ничего не перезапускаем.
Также нужно вынести вебхук из github на внешний сервер, и обновлять сервер по определенным условиям (например, запланировать обновление на ночь, когда пользователей меньше).
Я думал про взрослые CI/CD системы, типо Jenkins, Gitlab, софт вида Ansible. Но пока пришел к выводу, что текущая система более чем достаточна.
Любую систему нужно поддерживать, а чем система проще тем проще ее поддержка. Я люблю простые и понятные решения, и не хочу слепо использовать технологии лишь из-за того, что они на слуху. Если команда разработки вырастет, и нас перестанет устраивать текущий подход тогда задумаемся о его изменении, но не раньше.
Путь тимлидера
Как я уже упоминал, я сейчас руковожу отделом разработки. Это очень отличается от того, когда просто пишешь код. Нужно ставить задачи, определять их приоритетность, задумываться не только о технической, но и о бизнес части. Код при этом писать тоже нужно, пусть и меньше.
Нужно учитывать временные и финансовые ограничения. Учитывать особенности каждого разработчика. Я сейчас читаю много тематичной литературы, из последних прочитанных за месяц книг "Как пасти котов", "Я, нерды и гики", "Программист-праграматик", "Роман о управлении проектами".
Это интересный и новый для меня путь. Я прохожу его, описывая свой прогресс в своем Телеграм канале Программист и бизнес.
Пишу о бизнесе с точки зрения разработчика. Туда выкладываю короткие заметки, которые не подходят хабру по формату.