プロジェクトフォルダ直下に集約した Node-RED 環境を Heroku で動かすメモ

プロジェクトフォルダ直下に集約した Node-RED 環境を Heroku で動かすメモです。

やりたいこと

Node-RED をあるローカルのフォルダだけで動くようにプロジェクトを作るメモ

この記事でプロジェクトフォルダ直下に集約した Node-RED 環境が作れるようになったので、

起動パラメータから Node-RED エディタ画面の有効・無効を出し分けるメモ

この記事で「公開時はエディタ画面が無効で、開発時はエディタ画面で編集できる」状況にして、 Heroku 環境で動かします。

フローを更新するときは開発環境でフローを作ってイケそうだなって思ったら、Heroku にコミットします。

2021/9/26 追記

こちらの記事のままだと、ノードの追加インストールがうまく Heroku で動かなかったので、近日修正予定です。もしノード追加をせずに標準ノードだけで作業する場合は、こちらの記事で問題ありません。

Heroku 用に Node-RED をプロジェクトフォルダ配下で Node-RED エディタ画面でノードの追加インストールも動くようにするメモ

2021/10/12 追記

いろいろ試行錯誤しまして、以下の記事に、修正しつつ追加で盛り込んだものと GitHub リポジトリとともにまとめました。

ローカルの作業データを元に Heroku で動かす Node-RED の仕組みを GitHub に公開したメモ

対応してみる

2021/09/21 時点の情報で進めます。

さきほどの 2 つのナレッジを組み合わせればよいですが Heroku 自体の手順もあるので、一通りなぞっていきます。

image

まず、適当な名前でプロジェクトフォルダを作ります。今回は heroku-node-red-test にしています。

cd heroku-node-red-test

作業をするため、ターミナルでフォルダに入ります。私は Visual Studio Code で作業をしています。

{
  "name": "package.json",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "node-red": "./node_modules/.bin/node-red -p 18801 -u ./node-red_dir -s ./node-red_dir/settings.js",
    "init":"npm run init:mkdir && npm run init:get-settings.js",
    "init:mkdir":"node -e \"console.log('[START] mkdir-node-red_dir');const fs = require('fs');const path_nodered_dir = './node-red_dir';if (fs.existsSync(path_nodered_dir)) {console.log( '[ALERT] ' + path_nodered_dir + ' has existed!');} else {console.log('[OK] mkdir -> ' + path_nodered_dir );fs.mkdirSync(path_nodered_dir);}\"",
    "init:get-settings.js": "node -e \"const url = 'https://raw.githubusercontent.com/node-red/node-red/master/packages/node_modules/node-red/settings.js';const stream_output_path = './node-red_dir/settings.js';if (fs.existsSync(stream_output_path)) {console.log( '[ALERT] ' + stream_output_path + ' has existed!');} else {console.log('[START] download settings.js ...');const https = require('https');const fs = require('fs');const stream = fs.createWriteStream(stream_output_path);https.get(url, function(res){res.pipe(stream);res.on('end', function () {stream.close();console.log('[OK] got settings.js! -> ' + stream_output_path);});});}\""
  },
  "keywords": [],
  "author": "",
  "license": "MIT"
}

こちらの記事の package.json を参考にプロジェクトフォルダ直下で package.json を作ります。

こちらを配置して、

npm run init

で初期設定をします。これで、Node-REDを使うときのユーザーフォルダ node-res_dir や設定ファイルが setting.js が準備されます。

image

プロジェクトフォルダはこのような構成になりました。

開発するフォルダ直下で Node-RED をインストール

プロジェクトフォルダ直下で

npm i node-red

コマンドを実行してインストールします。

image

結果として Node-RED がインストールされたので node_modules のフォルダが登場しました。

Heroku 用に起動パラメータで Node-RED エディタ画面の有効・無効を出し分ける

Heroku 公開時はエディタ画面が無効で、開発時はエディタ画面で編集できる状況にします。

起動パラメータから Node-RED エディタ画面の有効・無効を出し分けるメモ を元に、Heroku 用にエディタ画面を無効にするコマンドをpackage.json の npm scripts に反映します。

{
  "name": "package.json",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "node-red-no-editor":"node-red -p 18801 -u ./node-red_dir -s ./node-red_dir/settings.js -D httpAdminRoot=false",
    "node-red": "./node_modules/.bin/node-red -p 18801 -u ./node-red_dir -s ./node-red_dir/settings.js",
    "init":"npm run init:mkdir && npm run init:get-settings.js",
    "init:mkdir":"node -e \"console.log('[START] mkdir-node-red_dir');const fs = require('fs');const path_nodered_dir = './node-red_dir';if (fs.existsSync(path_nodered_dir)) {console.log( '[ALERT] ' + path_nodered_dir + ' has existed!');} else {console.log('[OK] mkdir -> ' + path_nodered_dir );fs.mkdirSync(path_nodered_dir);}\"",
    "init:get-settings.js": "node -e \"const url = 'https://raw.githubusercontent.com/node-red/node-red/master/packages/node_modules/node-red/settings.js';const stream_output_path = './node-red_dir/settings.js';if (fs.existsSync(stream_output_path)) {console.log( '[ALERT] ' + stream_output_path + ' has existed!');} else {console.log('[START] download settings.js ...');const https = require('https');const fs = require('fs');const stream = fs.createWriteStream(stream_output_path);https.get(url, function(res){res.pipe(stream);res.on('end', function () {stream.close();console.log('[OK] got settings.js! -> ' + stream_output_path);});});}\""
  },
  "keywords": [],
  "author": "",
  "license": "MIT",
  "dependencies": {
    "node-red": "^2.0.6"
  }
}

node-red-no-editor という名前で追加しました。

簡単な HTTP in をつくる

image

このようなフローをデプロイして /api1 でアクセスすると、返答するものをつくります。

[{"id":"0a02324a2c7b357e","type":"http in","z":"2c233af411932df1","name":"","url":"/api1","method":"get","upload":false,"swaggerDoc":"","x":170,"y":240,"wires":[["f40e5c6c4cbfd700","0d089f742d65a107"]]},{"id":"f40e5c6c4cbfd700","type":"http response","z":"2c233af411932df1","name":"","statusCode":"","headers":{},"x":410,"y":240,"wires":[]},{"id":"0d089f742d65a107","type":"debug","z":"2c233af411932df1","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":430,"y":180,"wires":[]}]

このようなフローです。

ひとまず動かしてみる

npm run node-red

で、動かしてみます。

image

http://127.0.0.1:18801/ で表示できました。

image

http://localhost:18801/api1?heroku=hello! でアクセスすると、送った heroku=hello! をそのまま返答しています。

npm run node-red-no-editor

でも、動かしてエディタ無効を確認します。

image

http://127.0.0.1:18801/ がエディタ無効を確認します。

image

エディタ無効だからといって Node-RED の仕組みは動いています。試してみましょう。さきほどと同様に http://localhost:18801/api1?heroku=hello! でアクセスしても、つくったフローは動作しています。

node-red-no-editor コマンドのポート指定は削除

Heroku の場合は内部的に与えられたポート番号が割り振られるので -p 18801 の部分は削除して、 Heroku 側に任せるようにします。

{
  "name": "package.json",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "node-red-no-editor": "node-red -u ./node-red_dir -s ./node-red_dir/settings.js -D httpAdminRoot=false",
    "node-red": "./node_modules/.bin/node-red -p 18801 -u ./node-red_dir -s ./node-red_dir/settings.js",
    "init": "npm run init:mkdir && npm run init:get-settings.js",
    "init:mkdir": "node -e \"console.log('[START] mkdir-node-red_dir');const fs = require('fs');const path_nodered_dir = './node-red_dir';if (fs.existsSync(path_nodered_dir)) {console.log( '[ALERT] ' + path_nodered_dir + ' has existed!');} else {console.log('[OK] mkdir -> ' + path_nodered_dir );fs.mkdirSync(path_nodered_dir);}\"",
    "init:get-settings.js": "node -e \"const url = 'https://raw.githubusercontent.com/node-red/node-red/master/packages/node_modules/node-red/settings.js';const stream_output_path = './node-red_dir/settings.js';if (fs.existsSync(stream_output_path)) {console.log( '[ALERT] ' + stream_output_path + ' has existed!');} else {console.log('[START] download settings.js ...');const https = require('https');const fs = require('fs');const stream = fs.createWriteStream(stream_output_path);https.get(url, function(res){res.pipe(stream);res.on('end', function () {stream.close();console.log('[OK] got settings.js! -> ' + stream_output_path);});});}\""
  },
  "keywords": [],
  "author": "",
  "license": "MIT",
  "dependencies": {
    "node-red": "^2.0.6"
  }
}

実際の動作は Node-RED の設定ファイルであるsetting.js (node-red_dir/setting.js) の中にある uiPort の設定で、

/** the tcp port that the Node-RED web server is listening on */
    uiPort: process.env.PORT || 1880,

実行されているポート process.env.PORT あれば使うという挙動をしてます。分かりやすい。

Procfile ファイルで Heroku の動作を決める

Heroku CLI はインストールされ Heroku CLI は Heroku アカウントでログインして使える状態になっている前提で進めます。

Procfile をプロジェクトフォルダ直下に作成して以下のようにします。

web: npm run node-red-no-editor

これで Heroku 公開時はエディタ画面が無効になります。

.gitinnore を設定

Git を初期化する前に、プロジェクトフォルダ直下で .gitinnore を以下の内容で作ります。

node_modules

node_modules の一行だけでひとまず。これで Node-RED 関連でインストールされたライブラリ群が Git の対象から除外されます。

Git の初期化

Git はインストールされ Git は使える状態になっている前提で進めます。

git init

まず、プロジェクトフォルダ直下で Git の初期化をします。

変更内容をインデックスに追加してコミット対象にする

.gitinnore で node_modules が除外されていることを念入りに確認しましょう。

image

Visual Studio Code の場合は SOURCE CONTROL で分かりやすいですね。ちなみに、Git の初期化 をしてから、後付けで .gitignore を入れると、Visual Studio Code で一度更新しないと無視されたファイル一覧にならないので注意が必要です。今回の説明では、Git の初期化する前に .gitignore にしてます。

git add .

で、変更内容をインデックスに追加してコミット対象にします。

git commit -m "init"

コミット対象にしたらコミットします。

もちろん、Visual Studio Code の場合は SOURCE CONTROL のほうで GUI からインデックス追加とコミット対応してもOKです。

Heroku にアプリ作成

プロジェクトフォルダ直下で Herolu CLI のコマンドを実行して Heroku にアプリ作成します。

heroku create <サーバー名>

<サーバー名> は自分の作りたい名前にしましょう。

Heroku に反映

git push heroku master

で Heroku 側に push してアプリに反映します。master でうまく行かない場合は main で試してみてください。(このあたり、どこでメインブランチの名前が決まるか分かっていないです)

image

うまくいけば、このようにプッシュされアプリが反映されます。

image

アプリをブラウザで確認するとアクセスできました。ただこれだと分かりにくいので、

image

https://heroku-node-red-test.herokuapp.com/api1?heroku=hello! でもアクセスしてみると、Heroku 公開時にはエディタ画面が無効になり、Node-RED でつくったフローはちゃんと動作していることが確認できました。

フローを更新してアプリに反映したいときは

Heroku ではエディタ画面で編集しないので、フローを更新するときは、手元の開発環境でフローを作ってイケそうだなって思ったら Heroku にコミットします。

これだけでも、ある程度は開発していけそうですね!

ただ、Heroku 反映後に公開先で起こる固有の問題に対応するために直接サーバー側でデバッグするとなると、やはりエディタをパスワードかけてONにしたほうがいいですね。このあたりも追いかけてみます。