Чеклист по разработке и поддержке серверсайда
26 декабря 2014
В данной заметке я намерен снова напомнить о том, что программирование — это, оказывается, не только выбор правильного языка и набивание кода. И речь идет не о наличии социальной/психологической составляющей процесса, вовсе нет. Речь все о той же, технической, стороне.
Часто можно наблюдать забавную ситуацию. Скажем, нужно добавить в систему полнотекстовый поиск. Программист пишет для этого микросервис, юзает в нем, например, Lucene, прикручивает API (добавить сущность, найти сущность) — готово, полнотекстовый поиск, как и заказывали. Вы же поняли, в чем проблема? Не спешите с ответом, подумайте хорошенько. Я подожду.
Проблема заключается в том, что программист не видит картины в целом. Что будет с остальной частью системы, если сервис поиска окажется недоступен? Как вы будете синхронизировать с ним данные, если они разъехались? Что, если вы внезапно перестали выдерживать нагрузку? Если все-таки горизонтальное масштабирование предусмотрено, правильно ли реализована репликация или решардинг? Будет ли работать репликация, если между мастером и репликой теряется 80% пакетов? А если пересылка пакета в одну сторону занимает 100 миллисекунд?
Разработка любого серьезного серверного приложения — это разработка распределенной системы. Зачастую не допускается, чтобы эта система была недоступна дольше 5 минут в течение года («пять девяток» и более). А потому, как только вы начинаете работать над чем-то серьезнее бложика или небольшого интернет-магазина, нужно непрерывно держать в голове следующее.
Через что вы управляете своими серверами? Chef, Puppet или Ansible? Используются deb-пакеты или вы за каким-то хреном решили заменить их на Docker? Кто занимается написанием рецептов для Chef, программисты (типа devops) или отдельная команда ops? Если при раскладке вашего приложения нужно создать какие-то дополнительные каталоги или смигрировать схему БД, не ваша ли обязанность автоматизировать это?
А будут ли работать два экземпляра приложения? Или у вас всюду write through кэши, которые разъедутся между узлами при первом хождении в приложение на запись? Или, возможно, у вас Erlang-кластер, который при поднятии второго узла сразу грохнется из-за конфликта глобальных имен?
Можно ли поднять приложение в двух разных ДЦ? Если у вас Erlang или Akka и вы используете хождение в удаленные акторы, насколько хорошо это будет работать между двумя ДЦ? А тремя? Если вы используете Cassandra или Riak, есть ли в них репликация между ДЦ? Как при этом происходит разрешение конфликтов, если это master-master репликация, или что нужно делать в случае падения мастера иначе?
Есть ли метрики? Или вы считаете, что remsh в Erlang достаточно? А если выйдя из метро вы получаете звонок от ops, дескать пол часа назад все было очень плохо, но сейчас уже вроде ОК, как вы будете разбираться в проблеме без метрик? Агрегируются ли метрики, пришедшие от разных узлов, или вам нужно просматривать 100 дашбордов один за другим? Возможно ли посмотреть на метрики без агрегации, если проблема произошла только на одном узле? Достаточно ли полны ваши метрики, или вы пишите только load average и забиваете на число ошибок подключения к БД?
Есть ли мониторинг? И понимаете ли вы, в чем отличие мониторинга от метрик? Допустим, все работает быстро, но 10% пользователей не могут войти в систему — получите ли вы об этом SMS? Получите ли вы только один SMS или они будут сыпаться один за другим? Получите ли вы ровно один SMS, когда все снова вернется в норму? Можно ли отключить мониторинг на время деплоя? Отслеживаете ли вы все мыслимые инварианты в стиле «у любого инструмента, имеющего котировку, должна быть информация о типе инструмента»? Или вы свято верите, что кэши никогда не разъедутся с реальными данными, никогда не произойдет рейс кондишен и так далее?
Есть ли централизованные логи? Или для того, чтобы разобраться во внезапно возникшей проблеме, вам нужно по очереди зайти на каждый из десяти серверов? Есть ли поиск по этим логам? Достаточно ли долго они хранятся? Опять же, если сервис, отвечающий за хранение логов, приляжет, будет ли произведена какая-то синхронизация? Разбирались ли вы, чем сбор логов при помощи Syslog отличается от использования решений вроде Logstash и Graylog2, или взяли первое попавшееся решение?
Как вы управляете конфигурацией? Через Consul, ZooKeeper, etcd, srv records? Если вы подняли новую реплику БД, узнают ли об этом экземпляры бэкенда? А если наоборот, стало одной репликой меньше? А если вы свичнули мастер? Не кэширует ли ваш DNS клиент данные дольше, чем нужно? Уверены ли вы, что ваше приложение не упадет во время переключения на новый мастер?
Есть ли бэкапы? И понимаете ли вы, чем бэкапы отличаются от репликации, версионируемости БД и RAID? Проверяете ли вы работоспособность бэкапов, например, накатывая их в stage окружении? Можно ли произвести частичное восстановление из бэкапа, например, данных только заданного множества пользователей? Что будет, если злобный хэкер украдет ваши бэкапы?
Хорошо ли организовано тестирование? Есть ли модульные тесты для простой логики с входными и выходными данными? Есть ли интеграционные тесты для более сложной логики, включающей несколько компонентов системы? Есть ли системные тесты для тестирования всего в целом? Проверили ли вы, что очередная версия RabbitMQ адекватно ведет себя при нетсплитах? Есть ли заглушки, эмулирующие странное поведение сервисов, написанных другими командами? Крутятся ли у вас в продакте chaos monkey, latency monkey, chaos gorilla?
Ну и тому подобное:
- Что будет при падении машины? ДЦ?
- Нет ли в системе единой точки отказа?
- Как вы переживаете нетсплиты, высокие latency и packet loss?
- Как происходит балансировка нагрузки?
- Что вы будете делать в случае DDoS?
- Уверены ли вы в безопасности пользовательских данных?
Вспомните еще о проблеме обратной совместимости и необходимости иметь возможность откатить приложение к старой версии, о том, что данные между Solr и MongoDB могут разъезжаться и нужно их как-то синхронизировать, что нужно уметь выбрать между Amazon и Google Cloud, а также как-то делать транзакции для данных, находящихся в разных шардах, и так далее — продолжать можно до бесконечности.
В общем, писать серверсайд сложно. Здесь приходится много думать, иногда даже головой. Если вы хотите, чтобы админы не будили вас по ночам, и работа приносила удовольствие, приходится помнить о вещах вроде названных ваше, а не перекладывать их на админов, дескать я код написал, дальше пусть сами разбираются. В свете вышесказанного не вызывает удивления хайп вокруг этих ваших DevOps и разговоры о вымирании такой профессии, как системный администратор.
Дополнение: Распределенные системы: что может пойти не так
Метки: Разработка, Распределенные системы.
Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.