Как запустить приложение узла как службу systemd, когда оно используется вечно?

Я новичок в node и следую отличному руководству для разработчиков AWS от Райана Льюиса. В этом курсе мы учимся развертывать приложение Node.js в AWS EC2 с помощью образа Bitnami Node.js из AWS Marketplace. Чтобы немного попрактиковаться, я хотел превратить приложение в службу с помощью systemd, чтобы оно возвращалось после перезапуска. Однако после долгой отладки я понял, что служба, похоже, все время перезагружается, а приложение никогда не выходит в сеть. Это может быть вызвано способом запуска приложения. Он запускается с помощью инструмента forever CLI. Когда я запускаю npm start вручную, я вижу следующий результат:

npm start

> [email protected] prestart /home/bitnami/hamstercourse
> npm run build


> [email protected] build /home/bitnami/hamstercourse
> webpack

(node:19917) DeprecationWarning: Chunk.modules is deprecated. Use Chunk.getNumberOfModules/mapModules/forEachModule/containsModule instead.
Hash: aa4bec1a367d114f2c7f
Version: webpack 3.3.0
Time: 12349ms
             Asset     Size  Chunks                    Chunk Names
application.min.js   363 kB       0  [emitted]  [big]  application
    stylesheet.css  13.4 kB       0  [emitted]         application
  [10] ./node_modules/react-redux/es/index.js + 14 modules 37.6 kB {0} [built]
  [18] ./node_modules/react-router-dom/es/index.js + 13 modules 11.9 kB {0} [built]
  [59] ./node_modules/redux/es/index.js + 6 modules 21.3 kB {0} [built]
  [62] ./node_modules/react-router-redux/es/index.js + 4 modules 5.87 kB {0} [built]
 [105] ./app/index.jsx 1.86 kB {0} [built]
 [209] ./app/router.jsx 2.85 kB {0} [built]
 [211] ./app/routes/index.jsx 1.65 kB {0} [built]
 [287] ./app/reducers/index.js 316 bytes {0} [built]
 [288] ./app/reducers/hamsters.js 717 bytes {0} [built]
 [289] ./app/reducers/races.js 649 bytes {0} [built]
 [290] ./app/reducers/user.js 2.32 kB {0} [built]
 [291] ./app/reducers/leaderboards.js 355 bytes {0} [built]
 [292] ./app/reducers/status.js 285 bytes {0} [built]
 [293] ./app/index.less 41 bytes {0} [built]
 [326] ./node_modules/css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!./node_modules/less-loader/dist!./app/index.less 247 bytes [built]
    + 312 hidden modules
Child extract-text-webpack-plugin:
       [0] ./node_modules/css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!./node_modules/less-loader/dist!./app/index.less 247 bytes {0} [built]
        + 1 hidden module
Child extract-text-webpack-plugin:
       2 modules
Child extract-text-webpack-plugin:
       2 modules
Child extract-text-webpack-plugin:
       2 modules
Child extract-text-webpack-plugin:
       2 modules
Child extract-text-webpack-plugin:
       2 modules
Child extract-text-webpack-plugin:
       2 modules
Child extract-text-webpack-plugin:
       2 modules
Child extract-text-webpack-plugin:
       [0] ./node_modules/css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!./node_modules/less-loader/dist!./app/scenes/Config/index.less 485 bytes {0} [built]
        + 1 hidden module
Child extract-text-webpack-plugin:
       [0] ./node_modules/css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!./node_modules/less-loader/dist!./app/scenes/User/index.less 275 bytes {0} [built]
        + 1 hidden module
Child extract-text-webpack-plugin:
       [0] ./node_modules/css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!./node_modules/less-loader/dist!./app/scenes/Leaderboards/index.less 485 bytes {0} [built]
        + 1 hidden module
Child extract-text-webpack-plugin:
       [0] ./node_modules/css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!./node_modules/less-loader/dist!./app/scenes/Races/index.less 485 bytes {0} [built]
        + 1 hidden module
Child extract-text-webpack-plugin:
       [0] ./node_modules/css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!./node_modules/less-loader/dist!./app/scenes/Race/index.less 485 bytes {0} [built]
        + 1 hidden module
Child extract-text-webpack-plugin:
       [0] ./node_modules/css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!./node_modules/less-loader/dist!./app/scenes/Main/index.less 501 bytes {0} [built]
        + 1 hidden module
Child extract-text-webpack-plugin:
       [0] ./node_modules/css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!./node_modules/less-loader/dist!./app/scenes/Login/index.less 488 bytes {0} [built]
        + 1 hidden module
Child extract-text-webpack-plugin:
       [0] ./node_modules/css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!./node_modules/less-loader/dist!./app/scenes/Hamster/index.less 485 bytes {0} [built]
        + 1 hidden module
Child extract-text-webpack-plugin:
       [0] ./node_modules/css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!./node_modules/less-loader/dist!./app/scenes/Hamsters/index.less 617 bytes {0} [built]
        + 1 hidden module
Child extract-text-webpack-plugin:
       2 modules
Child extract-text-webpack-plugin:
       2 modules
Child extract-text-webpack-plugin:
       2 modules
Child extract-text-webpack-plugin:
       2 modules
Child extract-text-webpack-plugin:
       2 modules
Child extract-text-webpack-plugin:
       2 modules
Child extract-text-webpack-plugin:
       2 modules
Child extract-text-webpack-plugin:
       2 modules
Child extract-text-webpack-plugin:
       [0] ./node_modules/css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!./node_modules/less-loader/dist!./app/scenes/Main/Hero/index.less 827 bytes {0} [built]
        + 1 hidden module
Child extract-text-webpack-plugin:
       2 modules
Child extract-text-webpack-plugin:
       2 modules
Child extract-text-webpack-plugin:
       2 modules
Child extract-text-webpack-plugin:
       2 modules

> [email protected] start /home/bitnami/hamstercourse
> forever stopall && ./node_modules/.bin/forever start index.js

info:    No forever processes running
warn:    --minUptime not set. Defaulting to: 1000ms
warn:    --spinSleepTime not set. Your script will exit if it does not stay up for at least 1000ms
info:    Forever processing file: index.js

Затем процесс переходит в фоновый режим, и им можно управлять с помощью forever list, forever stop и т. Д. Как объяснено здесь systemd убивает фоновые процессы (и есть веские причины для этого).

Чтобы подтвердить свои подозрения, я попробовал запустить сервис следующим образом:

[Unit]
Description=Node.js Hamster Http Server

[Service]
PIDFile=~/hamster-99.pid
User=bitnami
Group=bitnami
Restart=always
RestartSec=10
StandardOutput=syslog
StandardError=syslog
KillSignal=SIGQUIT
WorkingDirectory=/home/bitnami/hamstercourse
ExecStart=/opt/bitnami/nodejs/bin/node /home/bitnami/hamstercourse/index.js

[Install]
WantedBy=multi-user.target

И после включения службы и перезагрузки демона вывод:

hamster.service - Node.js Hamster Http Server
   Loaded: loaded (/etc/systemd/system/hamster.service; enabled; vendor preset: enabled)
   Active: active (running) since Wed 2019-06-26 10:57:46 UTC; 28min ago
 Main PID: 19847 (.node.bin)
    Tasks: 11
   Memory: 26.8M
      CPU: 2.019s
   CGroup: /system.slice/hamster.service
           +-19847 /opt/bitnami/nodejs/bin/.node.bin /home/bitnami/hamstercourse/index.js

Jun 26 10:57:46 ip-172-31-35-115 systemd[1]: hamster.service: Main process exited, code=dumped, status=3/QUIT
Jun 26 10:57:46 ip-172-31-35-115 systemd[1]: Stopped Node.js Hamster Http Server.
Jun 26 10:57:46 ip-172-31-35-115 systemd[1]: hamster.service: Unit entered failed state.
Jun 26 10:57:46 ip-172-31-35-115 systemd[1]: hamster.service: Failed with result 'core-dump'.
Jun 26 10:57:46 ip-172-31-35-115 systemd[1]: Started Node.js Hamster Http Server.
Jun 26 10:57:48 ip-172-31-35-115 node[19847]: Server started at http://localhost:3000

Итак, хорошая новость: это действительно запускает службу. Ура! Однако он пропускает множество важных шагов, которые npm start действительно выполняются (например, минимизация приложения), и, конечно же, он не запускает приложение через forever. Можно обсудить, желательно ли использовать инструмент управления, такой как forever, при запуске в качестве службы, но должна ли быть возможность запускать его из systemd без изменения приложения, верно? Так как бы мне это сделать?

ОБНОВИТЬ:

Я только что нашел https://unix.stackexchange.com/questions/308311/systemd-service-runs-without-exiting и попытался использовать Type=forking в файле модуля. Кажется, это действительно работает. Но так ли это ПУТЬ? Или есть еще одна лучшая практика?


person titusn    schedule 26.06.2019    source источник
comment
Я бы сказал, не используйте вечно, поскольку службу можно настроить так, чтобы она выполняла в основном то же самое.   -  person Antoine Bolvy    schedule 22.01.2020


Ответы (1)


Я считаю Type=forking правильным.

https://www.freedesktop.org/software/systemd/man/systemd.service.html

Если задано разветвление, ожидается, что процесс, настроенный с помощью ExecStart =, вызовет fork () как часть своего запуска. Ожидается, что родительский процесс завершится после завершения запуска и настройки всех каналов связи. Дочерний процесс продолжает работать как основной сервисный процесс, и менеджер сервиса будет считать, что модуль запущен, когда родительский процесс завершится. Это поведение традиционных служб UNIX. Если используется этот параметр, рекомендуется также использовать параметр PIDFile =, чтобы systemd могла надежно идентифицировать основной процесс службы. systemd продолжит запуск последующих модулей, как только родительский процесс завершится.

Так работает forever. Также обратите внимание, что служба apache2 также использует Type=forking.

person jimp    schedule 21.01.2020
comment
Вам не нужна эта опция, если ваша программа не демонизируется. Это не нужно только потому, что программа использует fork. Во время демонизации запущенный процесс запускает другой процесс и затем завершается. Нет необходимости делать это под руководством супервизора, который может сделать за вас всю настройку демонизации. - person Dan D.; 22.01.2020