以前にとある人から
 (例えば仮想マシン環境と比較して)コンテナ環境のデメリットはなんですか?
と質問され、一瞬返答に詰まってしまったことがありました。メリットを考えたことはあったけど、積極的にデメリットを考えたことがなく、即答できませんでした。ちゃんと正しく理解していないとなかなか難しい質問だと思っています。

改めて時間のある時に考えてみるといくつか思いつきます。まあ「デメリット」といえるかどうかはともかく、「VMでできてコンテナでできないこと」はいくつかあります。

その一つが "cron" ジョブだと思っています。特定時刻とか、何分おきにとか、実行タイミングのスケジュールを決めた上で実行する機能です。例えば kubernetes であれば CronJob を使って実現するなど、厳密には「コンテナで実現できない」わけではないのですが、そのコンテナ環境に合わせた対応が必要になるのはそれはそれでデメリットになりえますよね。

一方、アプリケーション開発レベルでは、これらのスケジュールジョブを併用することでアプリケーションとしての必要な機能を実現することは珍しくありません。1分毎にどこかからデータを取得して更新するとか、毎日○時に自動的にバックアップを取得するとかといった場合です。それらの機能が必要なアプリケーションをコンテナ環境で動かす可能性がある中で実装する場合、どういった方法を検討する必要があるでしょうか?


その答えの1つが「アプリケーションレベルで(アプリケーションの機能の一部として) cron ジョブを実装する」方法です。Node.js アプリケーションの場合は Node-Schedule ライブラリを使うと簡単に実現できそうだったので、その内容を以下で紹介します:
2020071200



Node-Schedule は Node.js で使えるライブラリで、cron ライクなスケジュールジョブを比較的簡単に(setTimeout とかを意識することなく)実現できます。またスケジュールの定義フォーマットは cron のものと互換性があるので、crontab に1行追加する感覚で、アプリケーション内にスケジュールジョブを追加・更新・削除できるものです。アプリケーションの中でスケジュールジョブを定義できるので、アプリケーションの実行環境(実機とか、VM とか、コンテナとか、・・)を意識する必要もありません。

例を1つ記述しておきます。以下のコードで1分おきにコンソールにメッセージを表示するウェブアプリケーションが作成できます(Node-Schedule に関係している部分のみ赤字):
// app.js
var schedule = require( 'node-schedule' );
var express = require( 'express' );
var app = express();

//. 毎分実行
schedule.scheduleJob( '* * * * *', function(){
  console.log( 'running a task every minute' );
});


app.get( '/', function( req, res ){
  res.write( JSON.stringify( { status: true }, null, 2 ) );
  res.end();
});


var port = process.env.PORT || 8080;
app.listen( port );
console.log( 'server started on ' + port );

上記コードから赤字部分を抜くと、ごくシンプルな Node.js + Express のウェブアプリケーションになるのがわかると思います。つまり Node-Schedule に関係しているのは赤字部分の実質4行だけです。

で、その赤字部分で何をしているのかというと、まず先頭行で require() して Node-Schedule ライブラリのモジュールを呼び出します:
// app.js
var schedule = require( 'node-schedule' );

そして scheduleJob() メソッドを使ってジョブを(イメージとしては cron に)登録します:
//. 毎分実行
schedule.scheduleJob( '* * * * *', function(){
  console.log( 'running a task every minute' );
});

この第一パラメータは cron に登録する時に指定する時刻フォーマットと互換性のある文字列表現を使います。'* * * * *' は「毎分実行」を意味しています。

そして第二パラメータには該当時刻になったら実行するコールバック関数を指定します。上の例では 'running a task every minute' とコンソールに表示するだけの内容にしていますが、実際にはここに crontab の最後に指定するコマンドを登録することになります。これで1分ごとに 'running a task every minute' という文字列がコンソールに表示され続けるジョブが登録できたことになります。


なお、一度登録したジョブをキャンセルするには登録時の scheduleJob() メソッドの実行結果オブジェクトを受け取り、そのオブジェクトの cancel() メソッドを実行します:
//. 毎分実行
var job = schedule.scheduleJob( '* * * * *', function(){
  console.log( 'running a task every minute' );
});

  :

job.cancel(); //. 登録したジョブをキャンセル

ジョブの実行条件や実行内容を更新する場合は一度キャンセルしてから再登録することで実現できます。


最近のクラウド環境は PaaS 化が進み、どういうコンテナ環境を使っているのかよくわからないことがあるかもしれません。アプリケーションを Node.js で記述するという条件はありますが、この方法で実装していればコンテナ環境に依存しないスケジュールジョブが実現できそうです。