Comments 48
Довольно резкая книга. После ее прочтения нет никаких шансов найти оправдания, чтобы не тестировать весь код.
Меньше мутабельности) Вместо Array.prototype.push.apply(arr1, arr2) лучше [...a, ...b]
Если Вы сервис создали сами «с-нуля» (как я в примере), то после апгрейда ему ничего не будет. Если модифициворали существующий, то точно затрет, ну тогда вы дали правильную ссылку.
Если сервис создан с нуля, его нужно помещать в /etc/systemd/system, а не /usr/lib/systemd/system.
Нет. enable только создаёт симлинк, например, в цель multi-user (multi-user.target.wants) и ничего никуда не копирует. Вопрос в том, где лежит то, на что будет симлинк.
ps. Хотя, да, если имеется в виду ложить в корень /etc/systemd/system, то все будет работать аналогично.
Ну, если автор ноду пакует в RPM, то вероятно он и свои сервисы раскатывает из пакетов. Тогда класть юниты в {/usr,}/lib/systemd/systemd как раз правильно. Чтобы локально, per-machine, админ мог перекрыть настройки drop-in-ами.
Мы это делаем путем внесения правок в конфиг и релоада nginx до и после перезагрузки инстанса.

а почему не через pm2 graceful reload? кстати говоря, у них есть ещё keymetrics.io, из которой можно делать ручной graceful reload и прочие полезные штуки
Потому что я не в курсе что там есть такой функционал :), обязательно попробую. И keymetrics.io тоже мне понравился, буду тестить.
Несколько сумбурно написано и структурированности не хватает. По пунктам:
Теперь перед этими 4-ма инстанами нужно установить балансировщик, который будет распределять нагрузку
Тут, есть множество вариантов, например: Запустить еще один инстанс ноды и задействовать модуль cluster

Cluster-модуль содержит балансировку по воркерам внутри себя, ничего дополнительно ставить не нужно. А вот для статики — действительно, неплохо иметь nginx сверху. Но это опять же ничуть не исключает наличия кластер-модуля в апстриме.

Периодичность перезагрузки (в зависимости от разных причин) от 1 раза в час до 1 раза в сутки.

Я могу понять когда сервис жрет память и не отдает (от чего кстати хорошо помогают лимиты по использованию памяти в pm2), но от чего сервис может становиться более медленным с течением времени — непонятно. Так или иначе, выше уже упомянули про graceful reload. И да, перезагружать весь инстанс довольно странно, если есть кластер-модуль и можно, грубо говоря, послать SIGTERM медленному воркеру.

написано множество cli утилит, которые следят за процессом ноды и в случае необходимости его перегружают

Не могу сказать за остальные три, но могу сказать за pm2 — падает как миленький весь God Daemon :) Особенно на node/io от 0.11 до 4.1.2, из-за бага, имевшего место быть в тех версиях в модуле кластера. Так что за pm2 тоже желательно следить, через upstart-скрипт, например. А можно вообще впилить голый кластер-модуль и мониторить/перезагружать его опять же через upstart.

По пункту 10 — чот вообще вcё в кучу смешалось. Да, мониторинг нужен. Да, юнит-тестирование нужно. Но из пункта сложилось впечатление, что вы предлагаете использовать mocha/jasmine на продакшене для проверки окружения о.0 Это довольно странный способ использования этих фреймворков.
Не могу сказать за остальные три, но могу сказать за pm2 — падает как миленький весь God Daemon :) Особенно на node/io от 0.11 до 4.1.2, из-за бага, имевшего место быть в тех версиях в модуле кластера.

Вы говорите о встроенной кластеризации pm2? У меня сейчас на проекте запускаются воркеры через apps.json с записанными апстримами в nginx, ни одного вылета не заметил
Да. Она там правда не совсем встроенная и тоже использует node-cluster, правда с весьма солидным обвесом. Поэтому и падало, собственно. Был даже вот такой замечательный баг: github.com/nodejs/node-v0.x-archive/issues/9261
И я кажется наврал насчет версии, вроде в io v3 фикс уже приземлился :)
Я могу понять когда сервис жрет память и не отдает (от чего кстати хорошо помогают лимиты по использованию памяти в pm2), но от чего сервис может становиться более медленным с течением времени — непонятно. Так или иначе, выше уже упомянули про graceful reload.

Если даже код идеален, сервис теряет приоритет для операционной системы и начинает работать медленее и это есть проблема. В nginx, например, можно задать принудительно приоритет для процесса параметром worker_priority.

И да, перезагружать весь инстанс довольно странно, если есть кластер-модуль и можно, грубо говоря, послать SIGTERM медленному воркеру.


Вы невнимательно читали, можно использовать модуль cluster, и я об этом написал. Это менее стабильное решение чем балансировщик nginx или ha-proxy. Я не могу понять чем так плохо раз в сутки ребутнуть инстанс ноды?

По пункту 10 — чот вообще вcё в кучу смешалось. Да, мониторинг нужен. Да, юнит-тестирование нужно. Но из пункта сложилось впечатление, что вы предлагаете использовать mocha/jasmine на продакшене для проверки окружения о.0 Это довольно странный способ использования этих фреймворков.


Не совсем так, я не нашел время чтоб расписать, это будет в следующих сериях. В каждом проекте, есть папочка ./spec, там лежат тесты, и да перед тем как на продакшине запустить приложение после апдейта именно на продакшине надо запустить npm test и убедиться что все работает.
Я не могу понять чем так плохо раз в сутки ребутнуть инстанс ноды?
Зависит от нагрузки :) Ребут инстанса означает, что некоторые клиенты могут отвалиться от сервиса или не смогут достучаться до него. Если ночью инстанс пустует — почему бы и нет.

именно на продакшине надо запустить npm test и убедиться что все работает
Вы не думали о внедрении continuous integration с прогоном всех тестов на тестовых средах, идентичных боевым?
UFO landed and left these words here
> И мы не нашли ничего лучшего чем с определенной периодичностью ребутить инстансы.
Тоже так приходится делать. А всё потому что у приложения «медленные» утечки. Сколько не мониторил код дебаггером — полностью не избавился.

> Самостоятельно собираем свежие релизы ноды
В этом смысла нет. Берёте из докера дистрибутив ноды mhart/alpine-node и накатываете на него своё приложение.
Молодцы!

Уходите от REST-а, вы его переросли. Переходите на AMQP (а именно RabbitMQ с роутингом). Это даст вам асинхронность и все преимущества event-based loose-coupling системы. В том числе такие приятные вещи, как параллелизация нагруженных узлов, кворумные воутеры, возможность рестарта любого сервиса без потерь, dry-run новых версий сервисов, debug «на ходу» и т.д.

А во вторых — очень любопытно, как вы решаете вопросы совместимости API при внесении breaking changes? Когда у вас десятки сервисов, предполагаю, вам нужно или передепловать всю кучу на новую версию API или поддерживать две версии одновременно. Как именно вы поступаете?
Мы используем RabbitMQ, там где это уместно (точнее RabbitMQ Cluster из 3-х инстансов — так спокойнее).
А во вторых — очень любопытно, как вы решаете вопросы совместимости API при внесении breaking changes?

Поддерживаем и старое и новое API и плавно все сервисы перетекают на новое.
Нода под нагрузкой требует увеличения ограничения nofile limit

Зачем он вам? Вы же не отдаете статику через ноду?

Инстанс под нагрузкой, который работает продолжительное время начинает «тормозить»

Можете примерно сказать нагрузку? У нас тоже есть такое, но начинают тормозить инстансы с говнокодом. За нормальными такого замечено не было.
При асинхронном обращении к большому числу REST-сервисов проблема существует.

У нас проект среднего размера. Мы обслуживаем чуть более полмиллиона пользователей в день. На нагруженых сервисах в пики бывает до 200 req/sec.
9. Не бойтесь использовать ECMAScript 2015 (ES6)

Извините, но пример совершенно не впечатляет, т.к. можно использовать
arr1 = arr1.concat(arr2);

без всяких ES6
Вы потеряли ссылку на изначальный массив, и если в нём были объекты, на которые вы где-то ещё ссылаетесь, массив не удалится и будет лежать в памяти. При использовании push из примера автора просто происходит выделение дополнительной памяти под текущий массив.
По поводу шестого пункта.
Может кто-нибудь посоветовать пример проекта с грамотно построенной микросервисной архитектурой?
Из последнего Берлинского митапа по node.js могу порекомендовать отличную презентацию от автора coyno.
Хотя там ориентация была в сторону контейнеризации и использования tutum сервиса для CI
По поводу утечек памяти и перезагрузок. В конечном счёте обычно выясняется, что виноват код, либо ваш, либо npm. У нас игра на nodejs, очень интенсивная, сетевое взаимодействие через websocket с клиентами. Были «детские» проблемы, которые тоже время от времени забивали память и приходилось перезагружать инстанс, но под пристальным анализом они все были выявлены и исправлены, и да, все они были в коде приложения, хотя далеко не все были так очевидны, т.е. на первый взгляд казалось, что всё в коде хорошо. Но анализ дампов heap и примерных ситуаций, в которых они возникали, помог их выявить. Сейчас инстансы работают спокойно столько, сколько нужно. Неделю недавно работали, т.к. долго не апдейтили код.
У меня ещё есть сайт написанный на node.js, там тоже всё отлажено, писал давно его и долго мучился сначала, но после того, как все проблемы устранил — работает без перезагрузок вообще, поскольку код там не обновляется, аптайм там месяцами измеряется. Ну и все относительно популярные модули npm довольно серьёзно относятся к проблемам утечек памяти, в целом с ними там ситуация хорошая, их либо нет, либо их быстро исправляют.
Все ситуации разные. Подход один: когда видим, что память расходуется не так, как должна, делаем дамп heap и разбираем его в профайлере (Google Chrome). Когда нормальный расход процесса, например, 100Мб, а фактический — гигабайт, не сложно будет увидеть, что за объекты наполняют heap. Ну а далее — думаем, почему GC их не собрал. Причины появления в программе разные, но причина не сбора обычно одна — где-то осталась ссылка на объект, возможно и не явная. Мог где-то быть запущен setInterval или установлен EventListener, в область видимости которого попадает объект. Может быть взаимная ссылка между объектами — очень коварный баг. Ещё могут заполняться буферы, если куда-то пишут быстрее, чем читают. Вообще ситуаций полно, каждую раскручивать нужно исходя из того, что именно утекло.
Каков масштаб проекта? У нас в проекте использование профайлера весьма затруднено из-за чудовищной вложенности вызовов, которая в свою очередь является следствием огромной кодобазы и массы библиотек, пробовали профилировать — погрязли в цепочках вызовов, плюнули, оставили как есть. Очевидно, что нужен рефакторинг, но непонятно в какую сторону двигаться.

В общем, присоединюсь к просьбе поделиться опытом, возможно даже в виде статьи, а не комментария :)
Я конечно JS только начал изучать и могу ляпнуть чушь, но не помогут ли для профилирования проблемных инстансов FlameGraphs? Я понимаю, что они как бы для профилирования onCPU/offCPU, но может быть имеет смысл сравнить срезы нормальных и проблемных инстансов на предмет подозрительных изменений цепочек вызовов?
Пожалуй, но проблема тут скорее в том, что нормальных инстансов нет, есть только проблемные %)
Это ж какой у вас QPS к каждому процессу ноды, что нужно 131070? Или это просто «с потолка», и реально хватило бы 5000-8000? Замеряли реальное потребление fd?
8000 не хватает. Для большинства случаев хватает 16000 я уже об этом писал.
Да тут указан лимит «с запасом», а в чем проблема увеличения этого лимита?
Проблем нет, интересна нагрузка, требующая такого количества дескрипторов, и при этом вытягиваемая нодой.
Паралельное обращение к микросервисам, любой внешний request это открытый файловый дескриптор.
В следующих сериях мы рассмотрим тюнинг ядра для нужд ноды, грабли виртуализации, как правильно закэшировать nginx-ом ответы от инстансов nodejs.

"..., а Германа всё нет."

Only those users with full accounts are able to leave comments. Log in, please.