プロジェクトフォルダ直下に集約した Node-RED 環境を Heroku で動かすメモです。
やりたいこと
この記事でプロジェクトフォルダ直下に集約した Node-RED 環境が作れるようになったので、
この記事で「公開時はエディタ画面が無効で、開発時はエディタ画面で編集できる」状況にして、 Heroku 環境で動かします。
フローを更新するときは開発環境でフローを作ってイケそうだなって思ったら、Heroku にコミットします。
2021/9/26 追記
こちらの記事のままだと、ノードの追加インストールがうまく Heroku で動かなかったので、近日修正予定です。もしノード追加をせずに標準ノードだけで作業する場合は、こちらの記事で問題ありません。
Heroku 用に Node-RED をプロジェクトフォルダ配下で Node-RED エディタ画面でノードの追加インストールも動くようにするメモ
2021/10/12 追記
いろいろ試行錯誤しまして、以下の記事に、修正しつつ追加で盛り込んだものと GitHub リポジトリとともにまとめました。
対応してみる
2021/09/21 時点の情報で進めます。
さきほどの 2 つのナレッジを組み合わせればよいですが Heroku 自体の手順もあるので、一通りなぞっていきます。
まず、適当な名前でプロジェクトフォルダを作ります。今回は 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 が準備されます。
プロジェクトフォルダはこのような構成になりました。
開発するフォルダ直下で Node-RED をインストール
プロジェクトフォルダ直下で
npm i node-red
コマンドを実行してインストールします。
結果として 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 をつくる
このようなフローをデプロイして /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
で、動かしてみます。
http://127.0.0.1:18801/
で表示できました。
http://localhost:18801/api1?heroku=hello!
でアクセスすると、送った heroku=hello!
をそのまま返答しています。
npm run node-red-no-editor
でも、動かしてエディタ無効を確認します。
http://127.0.0.1:18801/
がエディタ無効を確認します。
エディタ無効だからといって 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 が除外されていることを念入りに確認しましょう。
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 で試してみてください。(このあたり、どこでメインブランチの名前が決まるか分かっていないです)
うまくいけば、このようにプッシュされアプリが反映されます。
アプリをブラウザで確認するとアクセスできました。ただこれだと分かりにくいので、
https://heroku-node-red-test.herokuapp.com/api1?heroku=hello!
でもアクセスしてみると、Heroku 公開時にはエディタ画面が無効になり、Node-RED でつくったフローはちゃんと動作していることが確認できました。
フローを更新してアプリに反映したいときは
Heroku ではエディタ画面で編集しないので、フローを更新するときは、手元の開発環境でフローを作ってイケそうだなって思ったら Heroku にコミットします。
これだけでも、ある程度は開発していけそうですね!
ただ、Heroku 反映後に公開先で起こる固有の問題に対応するために直接サーバー側でデバッグするとなると、やはりエディタをパスワードかけてONにしたほうがいいですね。このあたりも追いかけてみます。