Select Page

Как написать Linux Daemon с Node.js на VPS

Как написать Linux Daemon с Node.js на VPS

Daemon – это программа, которая работает в фоновом режиме и не имеет управляющего терминала. Их часто используют для предоставления исходного сервиса. Например, веб-сервер или сервер баз данных может работать как daemon.

Эта статья покажет вам, как написать daemon с Node.js и применить его на VPS с Upstart.

Я остановлюсь на реализации стандартного daemon. Я использую upstart для простоты, однако вы можете написать скрипт System-V init или использовать все что угодно, чтобы стартовать свой daemon.

Требования

Для этого урока вам понадобится Linux VPS (желательно Ubuntu или CentOS ), Node.js и Upstart.

Node.js

Есть несколько способов установки Node.js. Самый простой способ, на мой взгляд является использование NVM. Она также позволяет управлять различными версиями узла.

Upstart

Upstart поставляется предварительно установленным на многих Linux дистрибутивах. Если он не установлен на вашем дистрибутиве, вы сможете установить его с официальных репозиториев. Вы также можете скомпилировать его из исходных кодов.

Как работает daemon

В основном запуск daemon выглядит, как нормальный процесс:

  • Он создает свою копию в виде ако его дочерний процесс.
  • Дочерний процесс отделяется от родительского процесса.
  • Дочерний процесс закрывает свои стандартные файловые дескрипты
  • Родительский процесс завершается.
  • Daemon продолжает свою работу в фоновом режиме.

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

Пример daemon

Ради этого урока мы собираемся создать простой HTTP- daemon.

Наш daemon сможет:

  • Стартовать в фоновом режиме (для этого мы собираемся использовать модуль, который называется “daemon”.)
  • Порождать множество задач для HTTP сервера.
  • Перезапустить задачи на SIGHUP.
  • Завершить задачи на SIGTERM.
  • Понижать статус задач после запуска сервера HTTP.

Начальная структура проекта

Ниже приводится первоначальная структура проекта:

node-simple-http-daemon/
|
|-- README.md
|-- bin/
|   `-- node-simple-http-daemon
`-- lib/
    `-- app.js

package.json

Давайте начнем с создания файла package.json:

$ npm init

Установите модуль daemon:

$ npm --save install daemon

А вот как должен выглядеть package.json:

{
  "name": "node-simple-http-daemon",
  "version": "0.0.0",
  "description": "Simple HTTP Daemon written in Node.js",
  "main": "lib/app.js",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1",
    "start": "node lib/app.js"
  },
  "author": "Fardjad Davari",
  "license": "MIT",
  "dependencies": {
    "daemon": "~1.1.0"
  }
}

Обратите внимание, что мы добавили начало скрипта, поэтому мы можем запустить приложение с командой npm start позже.

HTTP-сервер

В настоящее время мы собираемся создать простой HTTP сервер, который начинает прослушивать порт 80 и отвечает “Hello world” для каждого запроса.

Вставьте следующее в lib/app.js файла:

/**
 * lib/app.js
 */

const PORT = 80;
const ADDRESS = '0.0.0.0';

var http = require('http');

var server = http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello Worldn');
});

server.listen(PORT, ADDRESS, function () {
    console.log('Server running at http://%s:%d/', ADDRESS, PORT);
    console.log('Press CTRL+C to exit');
});

Вы можете стартоватьVPS, запустив sudo npm start.

Daemon executable Ниже подается daemon executable код.

Следующий нужно поместить в bin/node-simple-http-daemon:

#!/usr/bin/env node

/**
 * bin/node-simple-http-daemon
 */

// Everything above this line will be executed twice
require('daemon')();

var cluster = require('cluster');

// Number of CPUs
var numCPUs = require('os').cpus().length;

/**
 * Creates a new worker when running as cluster master.
 * Runs the HTTP server otherwise.
 */
function createWorker() {
  if (cluster.isMaster) {
    // Fork a worker if running as cluster master
    var child = cluster.fork();

    // Respawn the child process after exit
    // (ex. in case of an uncaught exception)
    child.on('exit', function (code, signal) {
      createWorker();
    });
  } else {
    // Run the HTTP server if running as worker
    require('../lib/app');
  }
}

/**
 * Creates the specified number of workers.
 * @param  {Number} n Number of workers to create.
 */
function createWorkers(n) {
  while (n-- > 0) {
    createWorker();
  }
}

/**
 * Kills all workers with the given signal.
 * Also removes all event listeners from workers before sending the signal
 * to prevent respawning.
 * @param  {Number} signal
 */
function killAllWorkers(signal) {
  var uniqueID,
      worker;

  for (uniqueID in cluster.workers) {
    if (cluster.workers.hasOwnProperty(uniqueID)) {
      worker = cluster.workers[uniqueID];
      worker.removeAllListeners();
      worker.process.kill(signal);
    }
  }
}

/**
 * Restarts the workers.
 */
process.on('SIGHUP', function () {
  killAllWorkers('SIGTERM');
  createWorkers(numCPUs * 2);
});

/**
 * Gracefully Shuts down the workers.
 */
process.on('SIGTERM', function () {
  killAllWorkers('SIGTERM');
});

// Create two children for each CPU
createWorkers(numCPUs * 2);

Ну и настало время, чтобы стартануть наш daemon! Но перед этим мы изменим наш app.js чтобы справиться SIGTERM.

Добавьте следующие строки в конце app.js файла:

process.on('SIGTERM', function () {
  if (server === undefined) return;
  server.close(function () {
    // Disconnect from cluster master
    process.disconnect && process.disconnect();
  });
});

Сделать daemon скрипт рабочим:

$ chmod +x bin/node-simple-http-daemon

И запустить его (Убедитесь, что ничто другое не работает на порту 80):

$ sudo bin/node-simple-http-daemon

Теперь ваш демон и его задачи должны быть запущены в фоновом режиме. Вы можете подтвердить это, посылая HTTP GET запрос через сURL:

$ curl 127.0.0.1

Перед тем как перейти к следующему шагу, давайте взглянем на идентификаторы процессов:

$ ps -axf | grep [n]ode-simple-http-daemon | 
  awk '{ print "pid:"$2", parent-pid:"$3 }'

Пример вывода:

pid:33811, parent-pid:1
pid:33853, parent-pid:33811
pid:33856, parent-pid:33811
pid:33857, parent-pid:33811
pid:33858, parent-pid:33811
pid:33859, parent-pid:33811
pid:33860, parent-pid:33811
pid:33861, parent-pid:33811
pid:33862, parent-pid:33811

Обратите внимание, что daemon процесс с parent-pid из 1.

Попробуйте перезагрузить задачи, отправив SIGHUP к daemon:

$ kill -HUP 33811 # (replace 33811 with the daemon PID)

Теперь, если вы просмотрите PIDs. снова, вы должны увидеть новые рабочих процессов с новой PIDs. Удивительно, не так ли?

Чтобы остановить daemon, просто запустите:

$ kill -TERM 33811 # (replace 33811 with the daemon PID)

Сброс привилегий

Мы почти закончили. Нам нужно только, чтобы задачи отказались от своих привилегий после запуска VPS.

Изменить server.listen() возврата в app.js:

server.listen(PORT, ADDRESS, function () {
    console.log('Server running at http://%s:%d/', ADDRESS, PORT);
    console.log('Press CTRL+C to exit');

    // Check if we are running as root
    if (process.getgid() === 0) {
      process.setgid('nobody');
      process.setuid('nobody');
    }
});

И это все для части daemon.

Теперь вы можете установить system-wide:

$ sudo npm link

Upstart

Создать Upstart очень легко. Создайте файл в /etc/init/node-simple-http-daemon.conf со следующим контентом:

# /etc/init/node-simple-http-daemon.conf

start on started network
stop on stopping network

respawn
expect daemon

exec https-proxy-daemon

Теперь вы можете:

$ sudo start node-simple-http-daemon # Start the job
$ initctl --system node-simple-http-daemon # Check the job status
$ sudo reload node-simple-http-daemon # Send SIGHUP (restart the workers)
$ sudo stop node-simple-http-daemon # Stop the job

About The Author

Leave a reply

Ваш адрес email не будет опубликован.