「今話題のローコードツールを触ってみたいけど、意外と月額が高いのよね…」とお悩みの方におすすめするのがおうちn8n。
だいたいこんな構成になると思います。
なぜ自宅サーバーでn8nを?
まず前提としてSaaS版(Webサービスとしてn8nがホストする環境を利用)のn8nはエンタープライズ版でなければマトモに動かせません。
参考: https://n8n.io/pricing/
制約上50ユーロのPro版ですら「15ワークフローを1ヶ月10000回実行」の制約があり、そもそも気軽に試すのもちょっと辛い。
更に加えるとちょっと手の込んだ、ローコード的なJavascriptの実行に関して任意のnpmで公開されているライブラリを利用できないなど、かゆい所に手が届かない場合も多い。
n8nを自前でビルドする場合「Dockerfileをビルドするタイミングでnode_modulesを追加してCodeブロックで何でも出来る」事ができるので、使い倒すにはセルフホスト版を使いたいところ。
ただ、インフラの構成上、外部DB(RDS等)を付けたり、4GB以上のインスタンス、となるとカジュアルに使うには料金が高くなりがち。自宅サーバーで組んだらカジュアルにn8nを使い倒せますよ、という話。
ハードウェア
n8n自体はNode.jsのランタイムだけなので2GBメモリ+SQLiteで十分動く構成ですが、動かすタスクで大きめのバッチ処理をやる余地を考えると、可能なら8GBのPCが手元にあると良いです。
AmazonではIntel N100 + 8GB +256GB SSDのミニPC(-> https://amzn.to/3OVvwyF )が2万円未満で売っているので、初期投資はこれだけでOKです。ラズパイより安い。
ルーターとサーバーのセットアップ
Ubuntu 22.04 LTSのサーバー版が丁度良い。
基本的にはSSHでログインできるようにしつつルーター側ではDHCPで割り振られるサーバーのローカルIPアドレスを固定して、80番ポートがサーバーに届くようにすればOK。SSHのポートを開くのはあんまオススメしない。
後は docker-compose
コマンドを打てるようにしておいて、公式リポジトリをCloneしてdocker-composeのフォルダに行って、 docker-compose up
すれば終わりです。
ただし、色々いじるには不足している所もあるので、以下のようにカスタマイズを行います。
https://github.com/ten-mado/n8n-docker-nginx
主に変更したのは以下の点です。
- postgresのデータをvolumeでマウントする(永続化&バックアップ)
- n8nアプリのコンテナをDockerfileでカスタムビルドして、バージョン指定したりnode_modulesを任意に追加できるようにする
- Traefikを使ってLets’EncryptでSSL対応+HSTS
- 自作のn8nノードを試せるように「n8n-nodes-starter」ディレクトリを配置してコンテナ内で読ませる
- 公式リポジトリ(https://github.com/n8n-io/n8n-nodes-starter)をCloneしてビルドしてこのディレクトリに配置すれば自作のノードが動きます(n8nの再起動は必要)
DNSは今回はRoute53を使っていますが、DDNSサービスに慣れているならそれらと統合して好みのDNSサービスを使っても良いですし、お好きにどうぞといった感じです。安く済ませるならAPIでDNSを更新できるValue-Domainとかかな…
重要なCredentialsを管理しない前提ならCDNも要らないと思うのですが、漏れて困るデータが存在する場合、最低限そのサーバーは80(443)番ポートだけを開放し、なおかつゼロデイ脆弱性に備えて多段の制御(Basic認証するとか、特定のヘッダのみ許可するとか)を入れるべきです。TraefikではBasic認証をサクっと入れられるのですが、WebhookのエンドポイントだけはBasic認証から除外したいものの方法がわからず、ちょっとセキュリティ対策は要検討です…(ガチ運用ならCDNを前段に噛ませて色々フィルタリングすれば良いと思います)
const { Route53Client, ListResourceRecordSetsCommand, ChangeResourceRecordSetsCommand } = require("@aws-sdk/client-route-53");
const hostedZoneId = 'Z0XXXXXXXXXXXXXXXXXXX';
const recordName = 'xxx.xxx.xxx.';
const actualIpAddress = $input.first().json.data.replaceAll(/[^0-9\.]/g, '');
const command = new ListResourceRecordSetsCommand({
HostedZoneId: hostedZoneId,
StartRecordName: recordName,
StartRecordType: 'A',
MaxItems: 1
});
const client = new Route53Client({ region: "ap-northeast-1" });
const data = await client.send(command);
const awsRecordValue = data.ResourceRecordSets[0].ResourceRecords[0].Value;
const isValid = actualIpAddress === awsRecordValue;
let changeCommandResult = null;
if (!isValid) {
const changeCommand = new ChangeResourceRecordSetsCommand({
HostedZoneId: hostedZoneId,
ChangeBatch: {
Comment: 'modified by n8n workflow',
Changes: [
{
Action: 'UPSERT',
ResourceRecordSet: {
Name: recordName,
Type: 'A',
TTL: 300,
ResourceRecords: [
{
Value: actualIpAddress
}
]
}
}
]
}
});
try {
changeCommandResult = await client.send(changeCommand);
} catch (ex) {
console.log(`error`, ex);
changeCommandResult = ex;
}
}
return {
actualIpAddress: actualIpAddress,
awsRecordValue: awsRecordValue,
isValid: isValid,
changeCommandResult: changeCommandResult
};
ついでにウチのネット環境は動的IPのため、上記のようなCodeブロックを組んで定期的に「自宅のIPアドレスが変わったらRoute53のレコードを更新する」ようなスケジュールワークフローを入れています。自宅のIPアドレスを調べるのはHTTP Requestノード等で「http://checkip.amazonaws.com/」を呼べばOK。
死活監視はLambda Functionsで定期的にヘルスのAPIを呼び出して評価してもいいですし、落ちて困らないなら放置しても良い。
後はそもそも2GBのVPS上やらEC2でも十分動いてくれるので自宅サーバーに拘る必要すらも無いのですが、こういうカジュアルなタスクランナーは自宅サーバーを飼っている人たちにこそ必要とされているものなので、是非おすすめしたい所です。