Недавно мне посчастливилось развернуть Next.js на сервере с помощью PM2. Этот способ я не нашел в документации фреймворка, хотя считаю его довольно удобным, при этом гайдов по теме оказалось очень мало. Рассказываю, как всё сделать, и привожу рабочие примеры.
Заманчивое предложение
В конце весны в команде появилась задача на разработку веб-приложения с игровыми механиками для одного крупного клиента. К тому моменту давно хотел попробовать Next.js, но руки всё никак не доходили. В этот раз появился триггер — в официальной документации по React с недавних пор настойчиво предлагают начинать проекты сразу на фреймворке, и теперь я не мог отказаться.
Администраторы нашего севера сразу же предложили уйти от способов деплоя из документации (там их всего три вместе со статическим экспортом), и вместо них распробовать демонизацию кода при помощи PM2. Про эту технологию раньше никогда не слышал, а поиск в интернете не дал ясных инструкций, как связать её с Next.js. От этого изучить заморскую штуковину стало только интереснее, и теперь я могу поделиться всеми настройками и болями от их создания.
Работаем с PM2
Для тех людей, вроде меня, кто никогда не слышал про PM2, — это менеджер процессов, с помощью которого можно запускать приложения на NodeJS. Каждое такое приложение-процесс можно перезапускать, останавливать и мониторить отдельно от другого, а ещё с PM2 удобно распределять нагрузку: есть режим кластера.
После того, как поставили пакет PM2 на сервере (в моём случае под пользователя, с которого будет выполняться деплой), заходим в директорию нашего Next.js приложения и создаём в корне файл ecosystem.config.js
. Там будут лежать настройки, которые PM2 возьмёт для запуска процесса. Внутрь добавляем два главных поля:
module.exports = {
apps: [
{
name: 'Моё прекрасное приложение',
script: 'node_modules/next/dist/bin/next'
}
]
}
В name
указываем название приложения, оно появится в списке процессов. Следующий затем script
содержит путь, куда PM2 постучится для запуска Next.js. В своём конфиге я дополнительно указал порт 4000 вместо 3000 — это перестраховка, чтобы не случилось конфликтов за дефолтный. Дополнительно можно указать exec_mode: 'cluster'
для распределения нагрузки, подробнее об этом можно почитать тут. Вот финальный вид:
module.exports = {
apps: [
{
name: 'Моё прекрасное приложение',
script: 'node_modules/next/dist/bin/next',
args: '-p 4000',
exec_mode: 'cluster',
instances: 'max'
}
]
}
На этом всё, теперь команда pm2 start
приведет к запуску того и с теми настройками, что мы указали в корне внутри ecosystem.config.js
.
Nginx — заводись!
С Nginx пришлось повозиться: сперва казалось непонятным, на какой файл вести пользователя. Но чтобы всё завелось, вести нужно на порт, а не на файл. Поскольку я выбрал 4000 вместо дефолтного 3000, то два важных нам кусочка nginx.conf
обрели такой вид (всё остальное опустил в троеточиях):
...
upstream nextjs_upstream {
server 127.0.0.1:4000;
keepalive 64;
}
...
location / {
...
proxy_pass http://nextjs_upstream/;
proxy_redirect off;
proxy_read_timeout 240s;
}
...
С этими настройками 502-я ошибка навсегда ушла, и Nginx стал корректно отрабатывать запросы.
Запускаем Next.js
В документации по деплою приложений на Next.js с помощью Node.js сервера нам приводят две главные команды:
npm run build
npm run start
После start
запускается сервер с поддержкой всех Next.js фич. Но для нас это не актуально, потому что запускаем мы процесс PM2:
npm run build
pm2 start
Здесь важно вот что: если мы заливаем код повторно и снова выполняем npm run build
, то PM2 процесс нужно остановить командой pm2 stop <название приложения>
. Без этого у меня жил коварный баг, который я не мог отловить несколько дней: пайплайн то успешно переходил в passed, то спонтанно падал по таймауту на этапе npm run build
. Причём изменения подгружались на сайт, что ещё больше путало.
Пока искал в интернете, наткнулся на множество тем в духе Next build hangs forever, но моего решения не нашел. Позже вспомнил, что PM2 процесс как работал, так и работает во время сборки, и фикс этого нюанса навсегда избавил меня от проблемы.
Вот и всё! Хотя поначалу деплой Next.js таким способом казался тернистым, сейчас я считаю это довольно удобным и быстрым вариантом запускать будущие приложения на сервере. Надеюсь, эта статья поможет вам комфортно развернуть свои работы на готовых примерах.