るいすときのこの物語

オタクエンジニアの雑記

Node.jsのプログラムをsystemdで動かすと意図しない動作をどうにかする


背景

Node.jsで書かれたプログラムをsystemdで動かすと 実行ディレクトリが変わってソースによっては意図しない動作をします。

例 コード

const fs = require('fs');
const path = require('path');

setTimeout(() => {
  delete require.cache[path.resolve('date.js')];
  const date = require('./date.js');

  fs.writeFileSync('/tmp/blog-test.log', date.NOW);
}, 100);

setTimeout(() => {
  delete require.cache[path.resolve('date.js')];
  const date = require('./date.js');

  fs.writeFileSync('/tmp/blog-test.log', date.NOW);
}, 101);

setTimeout(() => {
  delete require.cache[path.resolve('date.js')];
  const date = require('./date.js');

  fs.writeFileSync('/tmp/blog-test.log', date.NOW);
}, 102);

例 systemd Unitファイル

[Unit]

[Service]
Type=simple
ExecStart=/usr/bin/node /root/blog-test/test.js
KillMode=control-group

[Install]
WantedBy=multi-user.target

例 結果

1501585612398
1501585612398
1501585612398

requireで読み込んだプログラムはキャッシュされるため 逐一、使用する場面でキャッシュを削除し再度読み込むようなプログラムです。

systemd で動かすと結果の通り、キャッシュを削除していても Unix タイムスタンプが変更されずにいます。

 

原因

ソースコード fs.writeFileSync('/tmp/blog-test.log', path.resolve('date.js'));

node test.js /root/blog-test/date.js

systemd /date.js

path.resolve('date.js')は相対パスから絶対パスを取得する部分ですが これがうまく動いていません。

systemd は cgroups を使って管理されるため プログラムの実行パスが / となり、delete require.cache が動きませんでした。

 

 

解決方法

サーバー上では systemd で起動しています。 各エンジニアのローカル環境でも正常に動いてほしいので サーバー上に環境変数を用意して雑に対応しました。

let os;
if (process.env.SERVER_ENV) {
  os = "server";
}

if (os === "server") {
    delete require.cache['/home/_/_/_.js']; // systemd で動かしてる場合は絶対パスを書いとく
} else {
    delete require.cache[path.resolve('_/_.js')];
}

 

 

思ったこと

「○○エンジニアは、○○だけに精通していれば良い」という人に反対です