[Node.js & Docker] node-cronをDockerコンテナから使ってスケジュール実行をさせてみる


※ 当ページには【広告/PR】を含む場合があります。
2020/09/06
[ラズパイ] cronをつかってDockerコンテナのシェルスクリプトを定期実行する

首題のように
node-cronを常駐させたDockerコンテナから定期実行されるか実験してみます。


合同会社タコスキングダム|蛸壺の技術ブログ【効果的学習法】Dockerをこれから学びたい人のためのオススメ書籍&教材特集

はじめに

前回はホスト側のcronによってdockerコンテナ内のプログラムを定期実行させる事例を紹介しました。

ですがこれだと、ホストOSがcronの使えないWindowsなどのLinux以外のOSではこの方法が利用できません。

そんなときにはnode-cronを利用することで、コンテナ内のnode.jsで実装したプログラムをcronのように定期実行できるようになります。

この場合には、ホストOSのデーモン機能が無くてもコンテナを常時起動しているだけで簡単なスケジューラーとすることが可能です。

合同会社タコスキングダム|蛸壺の技術ブログ
[ラズパイ] cronをつかってDockerコンテナのシェルスクリプトを定期実行する

ラズパイ上でDockerコンテナのコマンドをcronで動かしたいときの応用テクをご紹介します。


合同会社タコスキングダム|蛸壺の技術ブログ【効果的学習法】Dockerをこれから学びたい人のためのオススメ書籍&教材特集

Dockerコンテナの構築

例のように例のごとくDockerとdocker-composeのインストール済みの環境であることが前提です。

これらのインストール方法などは割愛させていただきます。

手元の環境は以下の通りですが、ホストOSはDockerが動作すればOKです。

            
            $ lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description: Debian GNU/Linux 10 (buster)
Release: 10
Codename: buster
$ docker -v
Docker version 19.03.8, build afacb8b7f0
$ docker-compose -v
docker-compose version 1.16.1, build 6d1ac21
        

プロジェクトのファイル構造の下準備

まず今回の内容を試すのに必要最低限のファイル・フォルダを以下のように与えておきましょう。

どこかの適当なフォルダに移って、以下のようにします。

            
            $ touch Dockerfile docker-compose.yml index.js package.json && mkdir tmp
$ tree
.
├── Dockerfile
├── docker-compose.yml
├── index.js
├── package.json
└── tmp #👈空のフォルダ
        
以下ではこのリソースファイルの中身を編集していきます。

Dockerfile

ベースイメージは
node-alpineですが、nodeのバージョンやalpineのリビジョンはなんでもOKです。

            
            FROM node:12-alpine
ENV NODE_ENV development
WORKDIR /usr/src/app

COPY package.json ./

RUN yarn install && yarn cache clean

CMD ["node", "index.js"]
        

docker-compose.yml

Dockerイメージのビルドにdocker-composeを使います。

docker-composeのサービス名は
cronで登録してみます。

            
            version: '3'

services:
  cron:
    image: node-cron-dckr:12-alpine
    build: .
    container_name: my-node-cron
    user: 'node:node'
    environment:
      NODE_ENV: development
    volumes:
        - ./:/usr/src/app
    tty: true
        

index.js

cronのスケジュール文法に関しては後述しています。

毎分実行する相当の
* * * * *を使い、tmpフォルダに文字を書き込んでみます。

            
            const cron = require('node-cron');
const fs = require("fs");

cron.schedule('* * * * *', () => {
    try{
        console.log('running a task every minute');
        fs.appendFileSync("tmp/file.txt", "Hello Node-Cron!\n");
    }
    catch(e){
        console.log(e.message);
    }
});
        

package.json

package.jsonを以下の内容で与えておきます。

            
            {
    "name": "node-cron-dckr",
    "version": "0.0.1",
    "devDependencies": {
        "node-cron": "^2.0.0"
    }
}
        
ここは、devDependenciesをなくして、Dockerfile側でRUN yarn add node-cron -Dとしてもコンテナにnode-cronがインストールされます。

イメージのビルド

以上のDockerイメージをビルドします。

            
            $ docker-compose build
$ docker images
REPOSITORY       TAG         IMAGE ID      SIZE
node-cron-dckr   12-alpine   76bed01fec57  104MB
        
これで今回のイメージがスタンバイできました。


合同会社タコスキングダム|蛸壺の技術ブログ【効果的学習法】Dockerをこれから学びたい人のためのオススメ書籍&教材特集

Cronのスケジュール文法

cronを扱ったことのある方なら分かっていらっしゃると思います。

まずはちょこっとだけcronのスケジュール表記の復習です。

cronのタスクをスケジュールするのに、スペース切りの5つの数字かアスタリスクの羅列する箇所(
* * * * *みたいなところ)があります。

この5つの数値でcronのスケジュールが管理されます。

cat /etc/crontabと叩くとおおよそのスケジュール文法の概要がコメントで書かれいますが、抜粋すると

            
            Example of job definition:
 .---------------- minute (0 - 59)
 |  .------------- hour (0 - 23)
 |  |  .---------- day of month (1 - 31)
 |  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...
 |  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
 |  |  |  |  |
 *  *  *  *  * user-name command to be executed
        
と説明書きがあります。

ざっと左から、分、時、日、月、曜日、となります。

anyを意味する*を使うと、毎〜として解釈されます。

例えば、よくあるパターンの
* * * * *は回りくどくいうと、毎曜日毎月毎日毎時毎分...つまり毎分実行するという意味です。

            
            * * * * 1:
    月曜日限定で毎分実行
10 * * * *:
    毎時間、時計の針が10分になったら実行
    (..., 8:10, 9:10, 10:10, ...)
20 8 * * *:
    毎日午前8時20分になったら実行
        
あと当然ですが、秒単位での実行指定はcronではできません。

なので、別のタイマーライブラリなどを利用することになります。


合同会社タコスキングダム|蛸壺の技術ブログ【効果的学習法】Dockerをこれから学びたい人のためのオススメ書籍&教材特集

コンテナの起動

ではデタッチモード(バックグラウンド)で起動します。

            
            $ docker-compose run -d cron
        
しばらく経ってから、tmpフォルダにファイルが書き込まれているのを確認したら、cronが定期実行されています。

            
            $ cat tmp/file.txt
Hello Node-Cron!
Hello Node-Cron!
Hello Node-Cron!
Hello Node-Cron!
#...以降一分毎に更新
        

止め方・消し方

今回はnode-cronのテスト程度ですのでいつまでも動かしている必要がないため、終わったら止めておきます。

まずは
docker psで動いているコンテナのID値か名前を探します。

            
            $ docker ps
CONTAINER ID        IMAGE                      COMMAND                  STATUS   NAMES
0639a40822ed        node-cron-dckr:12-alpine   "docker-entrypoint.s…"  Up       nodecrondckr_cron_run_1
        
なおdocker-composeで起動したコンテナ名は、<イメージ名>_<サービス名>_run_<起動しているコンテナ数>という命名ルールで自動登録されているはずです。

            
            #👇コンテナを止める場合
$ docker stop nodecrondckr_cron_run_1
nodecrondckr_cron_run_1
#👇コンテナを消す場合
$ docker rm nodecrondckr_cron_run_1
nodecrondckr_cron_run_1
        

合同会社タコスキングダム|蛸壺の技術ブログ【効果的学習法】Dockerをこれから学びたい人のためのオススメ書籍&教材特集

まとめ

node-cronを使えば、cronデーモンのdocker版のようなプログラムが簡単に作成できます。

shellプログラミングが苦手な方も簡単にスケジューラーがカスタマイズできます。

また今回紹介した方法ならばdockerが使える環境は必要ですがホストOSは問わないので、共通したプログラムで同じ結果が得られるのがメリットです。

参考サイト

Scheduling Cron Jobs in Node.js with Node-Cron(英語サイト)