Страх и ненависть в отдельно взятом стартапе. Часть 1 — Cтрах

в 9:28, , рубрики: architecture design, devops, ruby on rails, ит-инфраструктура

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

У нас было 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, 2x 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

Заключение

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

Автор: br0ziliy

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js