Когда вы написали серверное приложение, его нужно где-то развернуть. У нас в компании сейчас это реализовано с помощью 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. Но пока пришел к выводу, что текущая система более чем достаточна.
Любую систему нужно поддерживать, а чем система проще — тем проще ее поддержка. Я люблю простые и понятные решения, и не хочу слепо использовать технологии лишь из-за того, что они на слуху. Если команда разработки вырастет, и нас перестанет устраивать текущий подход — тогда задумаемся о его изменении, но не раньше.
Путь тимлидера
Как я уже упоминал, я сейчас руковожу отделом разработки. Это очень отличается от того, когда просто пишешь код. Нужно ставить задачи, определять их приоритетность, задумываться не только о технической, но и о бизнес части. Код при этом писать тоже нужно, пусть и меньше.
Нужно учитывать временные и финансовые ограничения. Учитывать особенности каждого разработчика. Я сейчас читаю много тематичной литературы, из последних прочитанных за месяц книг — "Как пасти котов", "Я, нерды и гики", "Программист-праграматик", "Роман о управлении проектами".
Это интересный и новый для меня путь. Я прохожу его, описывая свой прогресс в своем Телеграм канале — Программист и бизнес.
Пишу о бизнесе с точки зрения разработчика. Туда выкладываю короткие заметки, которые не подходят хабру по формату.
vdshat
Проходили похожий путь и поверьте использование готового специализированногл софта в разы дешевле, т.к. не нужно еще и велосипед изобретать и чинить его.
Есть разного уровня решения.
Для сборки Jenkins.
Для разворачивания его же можно использовать или Rundeck, Ansible, Puppet etc
Не поленитесь потратить немного времени и оно вернется десятикратно
1nt3g3r Автор
Почитал комментарии, и понимаю что да, костыльное решение получилось. Могу сказать, что поскольку никто не знал как правильно — делали как умеем.
Я так понимаю, что у вас была такая же ситуация (проходили похожий путь).
К чему вы все же пришли, к какому стеку?
Я склоняюсь пока к варианту поднять Jenkins, и делать что-то вроде:
1) Jenkins из jenkinsfile собирает на той же машине, где запущен jenkins билд — это jar файл.
2) Дальше jar файл копируется на целевую машину
3) Каким-то скриптом убиваем старый процесс, запускаем новый.
Пока до конца не понимаю, зачем использовать ansible, например, если процесс билда — это mvn package.
vdshat
Jenkins для сборки пакетов или Docker контейнеров, которые кладутся в соответствующие репозитории.
Для деплоймента использовали Rubdeck и Ansible. В результате сейчас для всего используется Jenkins + Git + Helm, но не от хорошей жизни, а отсутствия ресурсов. Все таки деплоймент должен выполняться предназначенным для этого софтом, который знает топологию и умеет работать с инфраструктурой.
Nikobraz
2) деплой ансиблом:
остановить сервис
положить файл
запустить сервис
3) напишите systemd юнит для вашего софта
killeralex
Иван, в чем отличие рис. 1 и рис. 2? Спасибо