開発者ドキュメント

VPSでNode.jsを使用してLinuxデーモンを作成する方法

ステータス:非推奨

この記事では、サポートされなくなったバージョンのUbuntuについて説明します。 現在Ubuntu12.04を実行しているサーバーを運用している場合は、サポートされているバージョンのUbuntuにアップグレードまたは移行することを強くお勧めします。

理由: Ubuntu 12.04は2017年4月28日に保守終了(EOL)に達し、セキュリティパッチまたはアップデートを受信しなくなりました。 このガイドはもう維持されていません。

代わりに参照してください:このガイドは参照として役立つ場合がありますが、他のUbuntuリリースでは機能しない場合があります。 可能な場合は、使用しているUbuntuのバージョン用に作成されたガイドを使用することを強くお勧めします。 ページ上部の検索機能を使用して、より新しいバージョンを見つけることができます。

序章


デーモンは、バックグラウンドで実行され、制御端末を持たないプログラムです。 これらは、バックグラウンドサービスを提供するためによく使用されます。 たとえば、Webサーバーまたはデータベースサーバーをデーモンとして実行できます。

このチュートリアルでは、 Node.js を使用してデーモンを作成し、Upstartを使用してVPSにデプロイする方法を説明します。

標準デーモンの実装に焦点を当てます。 簡単にするためにupstartを使用していますが、 System-V init スクリプトを記述したり、デーモンを起動するために好きなものを使用したりできます。

要件

このチュートリアルでは、 Linux VPS(UbuntuまたはCentOSが望ましい)、 Node.js 、およびUpstartが必要です。

Node.js

Node.jsをインストールするにはいくつかの方法があります。 私の意見では、nvmを使用するのが最も簡単な方法です。 また、ノードのさまざまなバージョンを管理することもできます。

または、次のいずれかのガイドに従うこともできます。

  1. Ubuntu12.04にNode.jsのアップストリームバージョンをインストールする方法
  2. パッケージマネージャーを介したNode.jsのインストール

アップスタート

Upstart は、多くのLinuxディストリビューションにプリインストールされています。 選択したディストリビューションにインストールされていない場合は、公式リポジトリからインストールできるはずです。 ソースコードからコンパイルすることもできます。 詳細については、スタートアップスタートページを参照してください。

デーモンのしくみ


基本的に、デーモンは通常のプロセスのように起動します。 その後、次のことが発生します。

  1. 子プロセスとして、それ自体のコピーを作成します。
  2. 子は親プロセスから自分自身を切り離します。
  3. 子プロセスは、標準のファイル記述子を閉じます(以下を参照)。
  4. 親プロセスが終了します。
  5. デーモンはバックグラウンドで作業を続行します。

標準ファイル記述子を閉じる代わりに、親プロセスは nullデバイスを開いて、それを子の標準ファイル記述子にアタッチできます。

デーモンの例


このチュートリアルのために、単純なHTTPデーモンを作成します。

私たちのデーモンは次のことができるようになります:

  1. バックグラウンドで開始します(これには、「デーモン」というモジュールを使用します)。
  2. HTTPサーバーに複数のワーカーを生成します。
  3. SIGHUPでワーカーを再起動します。
  4. SIGTERMでワーカーを終了します。
  5. HTTP サーバーが起動した後、ワーカーの特権を削除します。

初期プロジェクト構造

以下は、初期のプロジェクト構造です。

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

package.json

package.jsonファイルを作成することから始めましょう。

$ npm init

デーモンモジュールをインストールします。

$ 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サーバー

ここでは、ポート 80 でリッスンを開始し、すべての要求に対して「Helloworld」で応答する単純なHTTPサーバーを作成します。

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 World\n');
});

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.

デーモン実行可能ファイル

以下は、デーモンの実行可能コードです。

以下を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);

デーモンを起動する時が来ました! ただし、その前に、app.jsを変更してSIGTERMを処理する必要があります。

app.jsファイルの最後に以下を追加します。

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

デーモンスクリプトを実行可能にします。

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

そしてそれを実行します(ポート80 で他に何も実行されていないことを確認してください):

$ sudo bin/node-simple-http-daemon

これで、デーモンとそのワーカーはバックグラウンドで実行されているはずです。 cURLを介してHTTPGETリクエストを送信することで確認できます。

$ curl 127.0.0.1

次のステップに進む前に、プロセスIDを見てみましょう。

$ 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

デーモンは、1parent-pidを持つプロセスであることに注意してください。

SIGHUP をデーモンに送信して、ワーカーを再起動してみてください。

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

ここで、PIDを再度リストすると、新しいPIDを持つ新しいワーカープロセスを確認できるはずです。 すごいですね。

デーモンを停止するには、次を実行します。

$ 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');
    }
});

デーモンの部分は以上です。

これで、システム全体にインストールできます。

$ sudo npm link

アップスタート


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

次のステップ


デーモンを変更して、スポーンワーカーが頻繁に殺される(スピンする)ときに、スポーンワーカーをあきらめることができます。

アプリケーションに適切なログを記録することがどれほど重要かを強調することはできません。 node-bunyanを必ずチェックしてください。

モバイルバージョンを終了