Страх и ненависть в отдельно взятом стартапе. Часть 1 — Страх
У нас было 2 балансировщика, 7 серверов для приложения, 5 самописных gem«ов, самописный конфиг ядра и целое множество языков программирования и технологий всех сортов и расцветок, база данных, а также списки todo в Basecamp, NodeJS, soсket.io и 2 дюжины ключей на одну сущность в Redis. Не то чтобы это был необходимый запас для стартапа, но если начал писать код, становится трудно остановиться. Единственное, что вызывало у меня опасение — это ZeroMQ. Ничто в мире не бывает более беспомощным, безответственным и порочным, чем самописная шина сообщений на основе ZeroMQ. Я знал, что рано или поздно мы перейдем и на эту дрянь.
А начиналось всё невинно: проект на Ruby on Rails, архитектура микро-сервисов, отдельный балансировщик с «горячим» бэкапом… Только потом я узнал, что: сервера работают на Gentoo; микросервисы используют хитрый алгоритм регистрации себя в приложении, что приводит к взрыву всего приложения если падает хотя бы один сервис; мониторинга нет; документации нет; населена роботами. Но достаточно лирики, давайте посмотрим как жил проект, когда в него пришёл я.
На сегодняшний день 90% написаного ниже неактуально — так это выглядело примерно два года назад. Повествование, однако, ведётся в настоящем времени — мне так удобнее.
Как я уже сказал, имеется Rails-приложение, и некоторое количество сервисов на бэкэнде. Обмен сообщениями между Rails-приложением и сервисами осуществляется через самописную шину сообщений. Рядом крутится Redis, в котором хранится срез текущего состояния сущностей, приложение на NodeJS + socket.io (обмен сообщениями между фронтэндом и бекендом). Упрощённо это выглядит примерно так:
Приложение оперирует некими сущностями, которые хранятся в базе «Rails db». Сервисы оперируют теми же самыми сущностями, что и основное приложение, но имеют каждый свою отдельную базу. Синхронизация сущностей происходит через сообщения в ZeroMQ по сообытиям, которое генерирует основное приложение. Каждый сервис состоит из собственно самого сервиса, и набора event listener«ов (отдельные процессы по сути), которые принимают сообщения (каждый listener обрабатывает сообщения одного типа), и сообщают сервису что нужно сделать (какие поля в базе нужно поменять). Процесс самого сервиса тоже принимает определённый тип сообщений, по которым отдаёт в основное приложение данные из своей базы. И сервисы, и основное приложение активно работают с Redis (читают и пишут); синхронизации/локинга нет (т.е. возможны гонки, и они есть). Это что касается архитектуры приложения. Теперь пройдёмся кратко по серверам, и перейдём к самому интересному — особенностям экслпуатации.
Итак, сервера — обычные выделеные сервера (кто сказал «виртуалка»? обычные железяки). Средняя конфигурация — 2х Core i7, 32–64 GB RAM, 2×1T HDD / RAID1. По количеству — считаем. Два «балансера» (один основной, второй — «горячий» резерв, переключение — плавающий IP). Три сервера для сервисов. Один сервер для БД. Один сервер для брокера и Redis. Один сервер — тестовый (на нём крутятся все компоненты разом). Итого — 8 штук. LAN между серверами нет, весь трафик гуляет по ДЦ (некоторые сервера разнесены по разным стойкам).
Что же с написаным выше не так, спросите вы? Да всё так, но дьявол, как обычно, кроется в деталях, а в нашем случае это — повседневная эксплуатация и поддержка. Дальше просто неструктурировано опишу некоторые моменты, которые я считаю проблемными.
Инструкция по добавлению нового сервера начинается со слов «устанавливаем Gentoo». Да, весь стек крутится на Gentoo, со всеми вытекающими. Provisioning делается баш-скриптом. Для управления конфигурациями используется synctool (такой rsync на стероидах по сути). Деплой приложения — git pull руками на всех семи серверах. В коде основного приложения в десятке мест гвоздями прибиты имена хостов БД, Redis, брокера. Разделение в коде на production/development весьма условное (программисту настроить окружение для разработки занимает 2–3 дня). Сервисы настолько тесно интегрированы с основным приложением, что при падении одного — валится вся платформа. Сам сервис представляет из себя от трёх до пятнадцати процессов, каждый из которых просто пускается руками в screen (да, на одном из серверов постоянно запущено 15–20 сессий screen, и в репозитории с документацией лежит файлик «вот такие скрины должны быть на этом сервере после ребута»).
И самое главное — админка приложения. Рождённая в потаённых закоулках сознания сумрачного тевтонского гения гремучая смесь из bash, Ruby и Falcon. Вот вы знали, что есть такой язык программирования — Falcon? Теперь знаете (пройдите по ссылке, к слову, язык достоин внимания в качестве расширения кругозора). Сама «админка» представляет из себя просто набор SQL запросов, и кусков кода Ruby, которая позволяет оперировать разными параметрами сущностей. В ней нет ни-че-го, что оправдало бы использование чего-то ещё, кроме bash. Тот же falcon — за каким чёртом он там используется я так и не понял; реально всё, что делают falcon скрипты— по определённым условиям формируют примерно такое, и выполняют это через system ():
echo "SELECT stuff FROM entities" | psql -U postgres db_name
Заключение
Всё, что описано выше создавалось одним человеком в течение двух лет. Его программерские скилы оценивать не возьмусь, но админ (или девопс, как хотите) из него, прямо скажем, херовый — чего стоит одна Гента на проде, вместо нормального бинарного дистрибутива. В какой-то момент даже бизнес понял что дальше нормально оно жить не будет, и меня попросили посмотреть, что можно сделать, чтоб со всей этой хернёй попробовать взлететь. В следующей части расскажу, что и как я делал, чтобы приуменьшить количество энтропии во Вселенной.