RE:NODE

Ресурсы12 мин чтения

CPU или RAM: что на самом деле тормозит ваш сервер

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

Обновлено

0 прочтений

Хостинг продают по памяти, потому что память легко посчитать. Большинство игровых серверов упирается в то, как быстро одно ядро CPU успевает завершить шаг симуляции, а это трудно посчитать и ещё труднее рекламировать. Из-за этого расхождения добавление гигабайтов к тормозящему серверу так часто не меняет ничего, а апгрейд, который действительно помогает, иногда оказывается дешевле того, который не помогает. Для игроков две проблемы выглядят одинаково, а в цифрах не похожи вовсе, поэтому всё мастерство в том, чтобы знать, на какие цифры смотреть. Когда знаешь, где они лежат, это занимает около тридцати секунд.

Что делает каждый из ресурсов#

Память хранит состояние. Загруженный мир, сущности в нём, кэш чанков, данные плагинов, куча JVM. Память определяет, сколько может существовать одновременно. У неё жёсткий край: ниже черты всё в порядке, выше - процесс убивают. Понятия «немного не хватает памяти» для контейнера с лимитом не существует: вы либо под лимитом, либо мертвы.

CPU выполняет работу. На каждом тике сервер продвигает симуляцию, и почти любой игровой движок делает это в одном потоке. CPU определяет, как быстро этот шаг завершается. У него мягкий край: по мере приближения к лимиту тики затягиваются, мир замедляется, и ничего не падает. Он деградирует, а не отказывает, и именно поэтому месяцами остаётся недиагностированным.

Есть и третий ресурс, который обычно относят к одному из этих двух по ошибке. Диск проявляется коротким зависанием по расписанию - сохранение мира, backup, ротация логов - и люди сообщают о нём как о лаге и покупают CPU. И четвёртый, сеть, проявляющийся тем, что одному игроку плохо, а всем остальным нормально.

Ещё одно различие, важное на любом хостинге с панелью. Показатель CPU в тарифе - это процент одного ядра: 100 - одно ядро, 250 - два с половиной. Это троттлинг, а не резерв и не гарантия того, какие именно физические ядра вы получите. На RE:NODE это жёсткий лимит: вашему контейнеру никогда не позволяют выйти за него, а сервер на 100% медленный, но не сломанный и за это никогда не приостанавливается. Для диагностики это важно: сервер, упёршийся в лимит CPU, не ведёт себя неправильно, он сообщает вам, что лимит - это лимит.

Как отличить их за тридцать секунд#

СимптомПочти навернякаНе
Падает с «out of memory» в логеПамятьCPU
Сам перезапускается, в логе пустоПамять, убит снаружиCPU
График памяти растёт до лимита и там ровно держитсяПамятьCPU
Память спокойна, время тика выше бюджетаCPUПамять
Хуже становится с ростом числа сущностей, а не игроковCPUПамять
Короткое зависание раз в несколько минут, в остальное время нормальноДискНи то, ни другое
Долгие паузы в случайные моменты, график памяти резко пилообразныйСборка мусораЧистый CPU
Затронут один игрок, у остальных всё хорошоСетьНи то, ни другое
Нормально при 10 игроках, невозможно играть при 40Обычно CPUИногда память

Две строки люди путают чаще всего - вторую и четвёртую. Сервер, который перезапускается с пустым логом, почти всегда убивают из-за памяти: процесс не успевает оставить прощальную записку. А сервер, у которого график памяти ровный и спокойный, пока игроки жалуются, - это проблема CPU, каким бы соблазнительным ни казался тариф побольше.

Как правильно читать память#

Самая частая ошибка - путать память, которой, по мнению игры, она располагает, с памятью, которую ей даёт контейнер.

На сервере Java куча JVM задаётся через -Xmx, и куча - не весь процесс. Metaspace, стеки потоков, direct byte buffers, кэш кода JIT и собственные структуры сборщика мусора живут за её пределами. Укажите -Xmx4G в контейнере на 4 GB, и процесс превысит 4 GB и будет убит, а в логе игры ничего не окажется, потому что память кончилась не у игры, а у контейнера.

bash
# Leave the JVM headroom. On a 6 GB plan:$ java -Xms5G -Xmx5G -XX:+UseG1GC -XX:+ParallelRefProcEnabled \    -XX:MaxGCPauseMillis=200 -XX:+AlwaysPreTouch -XX:+DisableExplicitGC \    -jar paper.jar nogui

Разумное правило - -Xmx на уровне 80-85% лимита контейнера, с запасом минимум 512 MB и целым гигабайтом при модах. Если задать -Xms равным -Xmx вместе с -XX:+AlwaysPreTouch, куча занимается сразу, и график памяти выглядит тревожно ровным с первой минуты: это правильно и задумано, а не утечка. Остальные флаги разобраны в статье флаги JVM для Minecraft и версии Java, а аналогичная проблема для приложений Node, у которых свой отдельный потолок кучи, - в статье лимиты памяти узла простыми словами.

На машине, которую вы контролируете, улики лежат в трёх местах:

bash
$ free -h                                  # what the system has left$ dmesg -T | grep -i "killed process"      # the kernel's own record of a kill$ journalctl -k | grep -i oom              # same, on a systemd box# With cgroup v2, which is what a container limit actually is:$ cat /sys/fs/cgroup/memory.max            # the limit, in bytes$ cat /sys/fs/cgroup/memory.current        # what is in use right now$ cat /sys/fs/cgroup/memory.events         # oom_kill counts, cumulative

memory.events - то, что нужно знать. Если oom_kill больше нуля, что-то в этом контейнере убили из-за памяти, и теперь у вас факт, а не теория. Длинная версия - в статье swap в Linux и OOM killer.

Форма графика памяти подсказывает, какая именно у вас проблема с памятью:

  • Растёт, затем ровно, заметно ниже лимита. Здоровое состояние. Так выглядит правильно подобранный сервер.
  • Растёт до лимита и остаётся на нём. Либо куча, которой сказали занять всё, либо настоящая нехватка. Проверьте, не плохо ли и время тика; график, упёршийся в потолок при хорошем времени тика, - обычно просто -Xms.
  • Пила, глубокая и частая. Сборка мусора работает на износ, потому что куча мала для рабочего набора. Это стоит CPU и проявляется рваным временем тика.
  • Медленно растёт днями и не опускается. Утечка или мир, который действительно продолжает расти. Еженедельный перезапуск маскирует это; профилировщик находит.

Как правильно читать CPU#

Смотреть нужно не на «загрузку CPU», а на загрузку того потока, который важен, относительно купленного вами лимита.

bash
$ top -H -p $(pgrep -f paper.jar)   # per-thread, not per-process$ uptime                            # load average over 1, 5, 15 minutes$ vmstat 1 5                        # r = runnable, st = stolen by the hypervisor# cgroup v2 again - this is the one that proves throttling:$ cat /sys/fs/cgroup/cpu.max        # quota and period, e.g. "250000 100000"$ cat /sys/fs/cgroup/cpu.stat       # nr_throttled, throttled_usec

Рост nr_throttled, пока сервер работает плохо, - неопровержимый признак: ваш контейнер снова и снова останавливают на квоте посреди тика. Так лимит CPU выглядит изнутри, и никакие настройки внутри игры его не снимают.

Здесь людей путают три вещи:

Выделение четырёх ядер при 25% загрузки всё равно может упираться в CPU. Если один поток загружен на 100% одного ядра, а три ядра простаивают, вид на уровне процесса покажет 25%. top -H показывает правду. Это нормальное состояние загруженного игрового сервера, потому что симуляция идёт в одном потоке.

Steal time - это не ваш CPU. Столбец st в vmstat - время, которое ваш виртуальный CPU хотел работать, а гипервизор отдал кому-то другому. Стабильно ненулевой steal означает, что хост перегружен, и никакая настройка с вашей стороны не поможет. Что тут можно сделать, описано в статье общий CPU и шумные соседи.

Load average - не процент. Нагрузка 4.0 на четырёхъядерной машине означает полную занятость; те же 4.0 на контейнере с одним ядром означают, что три задачи постоянно ждут. Читайте её относительно вашего выделения.

Какие игры вообще могут использовать больше одного ядра, полезно знать до того, как платить за ядра:

НагрузкаДополнительные ядра используются дляОстаётся однопоточным
Minecraft (Paper)Генерация чанков, сеть, ввод-выводТик мира
Игры на движке SourceОчень малоВесь игровой цикл
Серверы Unreal (Squad, Satisfactory)Работа, близкая к рендерингу, и ввод-выводФизика и игровая логика
FactorioЧасть обновлений сущностейДетерминированный шаг обновления
Приложение на Node или PythonНичего, пока вы не запустите воркерыЦикл событий
База данныхДействительно параллельно по соединениямОдин долгий запрос

Закономерность такая: второе ядро стоит покупки, восьмое - обычно нет, а более высокая частота ценнее и того, и другого. Заметное исключение в Minecraft - форк Paper под названием Folia: он разбивает мир на независимо тикающие регионы и действительно масштабируется по ядрам, но ему нужны плагины, написанные под него, так что это выбор, который делают в начале проекта, а не исправление, применяемое к уже мучающемуся серверу.

Что делать, когда это память#

  1. Сначала сверьте `-Xmx` с лимитом контейнера. Больше серверов убивает неправильно настроенная куча, чем настоящая нехватка. Исправление бесплатно.
  2. Уменьшите то, что загружено. Понизьте view-distance и simulation-distance, сократите границу мира, используйте предварительную генерацию вместо генерации на лету, уберите накопившиеся сущности. Каждый чанк, который вы не держите, - это память, которая вам не нужна.
  3. Найдите утечку, прежде чем её кормить. Память, которая растёт бесконечно, - это плагин или мод. Профилировщик называет виновника за минуты; тариф побольше просто отодвигает срок.
  4. И только потом покупайте память. Это единственная проблема, которую тариф побольше действительно решает, чисто и навсегда.

Проблемы с памятью - в известном смысле хорошие: у них определённая причина, определённое решение, и решение масштабируется деньгами. Подбор объёма разобран в статьях сколько RAM нужно серверу Minecraft и сколько игроков помещается на сервере.

Что делать, когда это CPU#

Порядок здесь другой, потому что самое дешёвое решение почти никогда не железо.

  1. Найдите дорогую вещь. У большинства серверов, упирающихся в CPU, одна причина, а не общая нехватка: ферма мобов, плагин, работающий на каждом игровом тике, цепочка воронок, скрипт, выполняющий запрос к базе прямо в основном потоке. Сначала профилируйте - профилировщик spark для Minecraft, аналогичный инструмент вашей игры в остальных случаях.
  2. Делайте меньше работы за тик. Понизьте дальность симуляции, ограничьте число сущностей, увеличьте интервал всего запланированного, уберите неиспользуемые плагины. Здесь лежат самые большие выигрыши.
  3. Уберите блокирующую работу из основного потока. Синхронная запись на диск или встроенный вызов базы данных тратят бюджет тика впустую. Асинхронные сохранения и пулы соединений - это конфигурация, а не железо.
  4. Покупайте частоту, а не ядра. Если шаги с первого по третий исчерпаны, остаётся более быстрое ядро. Обычно это значит другая машина, а не более крупное выделение на той же: выделенные машины - там, где действительно указана модель CPU, а это единственный способ сравнить частоту до покупки.
  5. Смиритесь с меньшим числом игроков. Непопулярно, честно и иногда правильно.

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

Разобранный пример#

Сервер Paper на тарифе 4 GB, 2 vCPU. Двадцать постоянных игроков, годовалый мир, каждый вечер жалобы на «лаг».

code
/mspt   -> 48.6/312.4, 44.1/312.4, 29.8/486.0memory  -> pinned at 3.9 GB of 4 GB all dayCPU     -> 185% of an allowed 200%, all evening

Прочитайте три строки вместе. График памяти упёрся в потолок, и это выглядит ответом, - но куча была задана как -Xms3G -Xmx3G, так что, конечно, она упёрлась, а событий OOM нет. CPU занят на 92% выделения, и всю работу делает один поток. Среднее время тика - на границе бюджета в 50 мс, а максимальное - почти в десять раз выше.

Диагноз - CPU, плюс проблема со всплесками. Максимум в 486 мс - это отдельная проблема от среднего в 29,8 мс: что-то иногда занимает полсекунды, и обычно это генерация чанков у границы мира или автосохранение. Профилировщик нашёл настоящих виновников примерно за десять минут: view-distance=12 на сервере, где никто не смотрит дальше шести, и 3000 накопившихся сущностей-предметов в одной базе.

Исправление ничего не стоило. view-distance=8, simulation-distance=6, таймер исчезновения предметов, и среднее время тика упало до 18 мс. Тариф на 4 GB никогда не был проблемой. Если бы владелец купил 8 GB, график выглядел бы здоровее, а сервер ощущался бы точно так же.

Как купить то, что нужно#

Вы наблюдаетеПокупайтеНе покупайте
OOM-убийства, куча настроена правильноБольше памятиБольше CPU
Время тика высокое, память спокойнаЧастоту или делайте меньше работыБольше памяти
CPU упирается только в часы пикБольше доли CPU или проверьте stealБольше памяти
Периодические зависания при сохраненииБолее быстрое хранилище или более короткое сохранениеНичего из вышеперечисленного
Жалуется один игрокНичегоНичего
Всё нормально, мир всё ещё растётПока ничего, но следите за памятьюНичего

Перед любой покупкой получите две точки данных с разницей в неделю, измеренные одинаково. О том, на что вы смотрите, рассказано в статье как читать график нагрузки сервера, о порогах, оправдывающих покупку, - в статье когда пора повышать тариф, а третий ресурс, который путают с первыми двумя, разобран в статье что на самом деле меняет NVMe. Если ответ окажется «целая машина», компромиссы описаны в статье выбор между VDS и игровой панелью.

FAQ#

Сделает ли больше RAM мой сервер быстрее?

Только если ему не хватало памяти. На сервере, у которого график памяти ровный, а время тика плохое, больше памяти не меняет ничего измеримого. Память поднимает потолок того, сколько может существовать; она не заставляет существующее обновляться быстрее.

Что на самом деле значит 2 vCPU?

Двести процентов одного ядра в виде троттлинга. Это не значит, что за вами зарезервированы два ядра, а поскольку большинство игровых серверов симулирует в одном потоке, один поток по-прежнему может использовать только 100% этого выделения. Второе ядро покрывает сеть, сохранения, сборку мусора и всё остальное вокруг симуляции.

Почему мой сервер всё время использует 100% CPU?

Потому что что-то постоянно просит работы, а лимит делает своё дело. На RE:NODE сервер на 100% медленный, но не в беде и никогда не приостанавливается за это. Спрашивать нужно, укладывается ли время тика в бюджет; если да, высокая загрузка CPU - просто эффективный сервер.

Хостинг говорит 8 GB, а Minecraft видит только 6. Почему?

Потому что -Xmx намеренно задан ниже лимита контейнера. JVM нужна память вне кучи для потоков, metaspace и буферов, а куча, равная всему контейнеру, приводит к убийству процесса. 6 GB кучи в контейнере на 8 GB - это правильно.

Всегда ли сбой без сообщения об ошибке - это память?

Не всегда, но ставить стоит на это. Процесс, убитый ядром за превышение лимита памяти, не имеет возможности ничего записать в лог. Проверьте счётчик OOM или лог ядра, прежде чем подозревать баг. Другие причины разобраны в статье почему ваш игровой сервер постоянно перезапускается.

Покупать больше ядер или более быстрое ядро?

Более быстрое ядро - почти для любой игры. Симуляция идёт в одном потоке, поэтому частота задаёт потолок, а лишние ядра помогают только с работой вокруг него. Два быстрых ядра лучше восьми медленных для игрового сервера, а для базы данных или сервера сборки верно обратное.


Комментарии

Полностью анонимно: без аккаунта, без почты, без cookie. Мы храним имя, которое вы ввели, текст и время - больше ничего. Количество ссылок ограничено, разметка не отображается.

0/2000