MAATRIX / Блог / Ежегодный пересмотр архитектуры: не пора ли разделить один сервер на два

Ежегодный пересмотр архитектуры: не пора ли разделить один сервер на два

MAATRIX

Год назад один сервер спокойно тянул сайт, базу и фоновые задачи — с запасом, без единой жалобы. Сегодня ночной бэкап роняет отклик веб-сервера на полминуты, очередь задач ест память, которая нужна приложению, а перезапуск одного компонента тянет за собой остальные. Это накапливается незаметно: разбор происходит только когда что-то уже отвалилось посреди рабочего дня. Ежегодный пересмотр архитектуры — способ проверить вопрос «не пора ли разделить нагрузку на два сервера» по фактам, до того как за вас его решит инцидент.

Почему это стоит пересматривать раз в год, а не по ощущению

Держать сайт, базу и фоновые задачи на одном сервере — не ошибка, а нормальная стартовая архитектура для большинства проектов. Разносить всё по отдельным серверам с первого дня — обратная проблема, антипаттерн «архитектура Netflix для проекта на 200 пользователей»: сложность, за которую платите вперёд, не получив взамен ничего, кроме лишних точек отказа.

Но за год состав нагрузки меняется незаметно: появился новый фоновый воркер, база выросла втрое при трафике +20%, ночная выгрузка в аналитику стала занимать два часа вместо десяти минут. По отдельности каждое изменение незаметно; год спустя — это уже другая нагрузка на той же архитектуре, которую никто осознанно не пересматривал.

Это отдельный вопрос от общего годового техосмотра инфраструктуры: там — что уже есть на серверах и по каким тарифам, здесь — правильно ли разложены компоненты. Логично делать оба пересмотра рядом по календарю, но отдельными пунктами — аргументы у них разные.

Признак первый: компоненты дерутся за ресурсы

Веб-серверу нужен быстрый отклик прямо сейчас. Бэкапу или batch-обработке нужна пропускная способность — им всё равно, займёт задача 10 минут или 12, лишь бы завершилась. Когда обоим достаются одни и те же ядра и диск, фоновая задача забирает ресурс у того, кому нужна скорость реакции — и веб-сервер отвечает медленнее ровно в те минуты, когда идёт бэкап.

Типичная картина: ночной pg_dump или borg create насыщает диск, и в это же окно у сайта резко растёт время ответа — совпадение легко заметить, сопоставив расписание crontab -l с временем ответа в логе nginx за то же окно. Кто прямо сейчас ест CPU и диск, показывает systemd-cgtop, а I/O по процессам — pidstat -d 1 5. Один cgroup явно выделяется по нагрузке — это измеренный конфликт, а не гипотеза.

Прежде чем разносить серверы, дешевле ограничить фоновую задачу — например, через systemctl edit backup.service добавить Nice=15, IOSchedulingClass=idle и CPUQuota=50%. Отклик выровнялся, а бэкап уложился в окно — проблема решена без второго сервера. Бэкап после лимитов начинает не успевать или всё равно заметно мешает — конфликт реальный, а не решаемый настройкой приоритетов.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Признак второй: компоненты растут с разной скоростью

Год спустя после запуска редко все части системы выросли одинаково: база увеличилась втрое из-за роста числа записей, а трафик — на 15-20%; или очередь фоновых задач стала прожорливее по CPU, а фронтенд как ел мало ресурсов, так и ест. Проблема в том, что на одном сервере масштабировать можно только весь тариф целиком: если базе не хватает памяти под кэш, а приложению её хватает с запасом — апгрейд ради базы означает переплату за память, которая приложению не нужна.

Смотреть на потребление стоит по компоненту, а не по серверу целиком: если сервисы разведены на systemd-юниты, systemctl show postgresql.service --property=MemoryCurrent даёт цифру для конкретного сервиса. Собранные за несколько месяцев такие цифры обычно показывают, что тянет вверх общую потребность — база и воркер, а веб-приложение едет пассажиром. Разделение здесь не про отказоустойчивость, а про то, чтобы масштабировать именно то, что растёт, не переплачивая за то, что стоит на месте.

Признак третий: падение одного компонента роняет всё остальное

Это самый серьёзный из трёх признаков — цена ошибки не медленный отклик, а полный простой. На общем сервере у всех компонентов одна память, один диск и один IP, поэтому утечка памяти или растущий без ротации лог в одном компоненте бьёт не только по нему.

Классика: фоновая задача съедает память, OOM killer убивает процессы по своей эвристике — и не обязательно виновника (journalctl -k --since "-7 days" | grep -i "killed process" покажет, кого убили). Второй сценарий — переполнение диска: разросшиеся логи одного сервиса забивают раздел, и писать перестаёт всё на нём, включая WAL базы данных. Отдельная разновидность — мониторинг на том же сервере, что и то, за чем он наблюдает: сервер падает целиком, и о падении вы узнаёте не от алерта, а от пользователей.

Ключевой вопрос здесь — не «может ли так случиться в теории» (может почти всегда), а «случалось ли за год хотя бы раз, что сбой одного компонента задел остальные, и сколько раз». Один случай — повод присмотреться. Два-три — уже свойство архитектуры, которое повторится снова.

Когда делить рано: цена сложности "на всякий случай"

У разделения есть своя реальная цена: два набора обновлений безопасности, два бэкапа вместо одного, сетевой канал между приложением и базой со своей задержкой и точкой отказа, который нужно защитить (приватная сеть, VPN или файрвол), плюс базовая стоимость второго тарифа, даже если он используется на 20%. Для небольшой команды это конкретные дополнительные часы на обслуживание уже двух систем вместо одной.

Делить «про запас», под гипотетический рост, который может случиться, а может и нет — случай, когда сложность покупается заранее, а отдача не гарантирована. Практическое правило: разделяйте, когда по трём признакам выше находите факт за прошедший год, а не гипотезу. Не разделяйте, если:

  • конфликт заметен только в мониторинге, но не сказывается на пользователях, и решается через Nice=/CPUQuota=;
  • рост всех компонентов примерно одинаковый, и тариф держит нагрузку с запасом на ближайшие кварталы;
  • падений «домино» за год не было ни разу, а риск существует только в теории.

Алгоритм ежегодной проверки

Пересмотр удобно делать как чек-лист на час-полтора раз в год, привязав к циклу остальных годовых регламентов:

  1. Собрать данные за год. Нет истории метрик по компонентам — уже находка: начните вести журнал (systemd-cgtop, Prometheus/Netdata или еженедельная запись df/free).
  2. Поднять инциденты за год. Сколько раз бэкап или batch-задача роняли отклик? Сколько раз падение одного сервиса утаскивало за собой другие?
  3. Пройти три признака по порядку и прикинуть стоимость обоих вариантов: апгрейд тарифа под прожорливый компонент против второго сервера меньшей конфигурации плюс канал между ними — здесь пригодится разбор стоимости масштабирования вверх или вширь.
  4. Решили делить — выносите первым то, что сильнее нагружает ресурсами или рискует для соседей, а не то, что проще: чаще это база данных или самая тяжёлая batch/cron-задача; веб-сервер остаётся последним. Спланируйте перенос как отдельную задачу с окном обслуживания — практический план разобран в статье разделение общего сервера на клиентские, механика там применима и к разделению по компонентам.

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

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Как отличить конфликт ресурсов от того, что серверу просто не хватает мощности?

Ограничьте фоновую задачу через CPUQuota=/MemoryMax=. Отклик выровнялся — был конфликт за общий ресурс. Веб-сервер всё равно медленный без фоновой нагрузки — тарифа не хватает независимо от архитектуры.

Можно ли решить проблему лимитами cgroup вместо второго физического сервера?

Да, это правильный первый шаг почти всегда, но он не помогает против третьего признака: риска, что падение одного компонента заденет остальные через общую точку отказа — сеть, диск, ядро ОС.

С чего начинать разделение — с базы данных или с веб-сервера?

Обычно с базы: предсказуемая нагрузка, перенос завершается синхронизацией и переключением строки подключения. Веб-сервер разносить сложнее и обычно нет смысла.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →