🛩️

n8nのデータベースとしてlitestreamを使ってcloud storageにして格安にした話

2024/03/04に公開

はじめに

以前の記事でn8nというサービスをcloudrunにデプロイする話をしていました。
n8nは本当に使い勝手よく、見つけてよかったプロダクトの一つになっています。
プライベートでガッツリ使っており、今後は職場に実験的に導入していこうとしています。

https://zenn.dev/ksmlo/articles/6ce4a8f5a86b4d

そんな中で、DBとしてpostgresの設定がとても楽だったので、初期無料で使えるelephant sqlを一歩目に選択したのですが、無料枠を2ヶ月目にして超えてしまい$5が発生することに。。
まだ全然安いのですが、よりコストを落とせそうなlitestreamを知っていたので、組み合わせてみたというのが今回の記事の内容です。

コードはこちら↓
https://github.com/ksmsto/n8n-test/tree/test/litestream

READMEもサクッとですがあるので、作りたい方はそちらご覧ください。
n8nについての詳細は前回記事に細かく書いてあるので、そっちをご覧ください。
litestreamについては、詳細な解説はしないのでドキュメントなどご覧ください。今回は組み合わせで考えたことと実装について記載していきます。

litestreamについて

https://litestream.io/

Litestreamとは、サーバー費用を抑えつつ、AWS S3、Azure Blob Storage、Google Cloud Storage、SFTP、NFSにSQLiteの変更を継続的にストリーミングし、サーバー障害時に迅速に復旧できるようにするOSSです。既存のアプリケーションにコード変更なしで統合でき、別のプロセスとして実行されます(GPTサマリー)

ということで、n8nの中で動くSQLiteの変更をlitestreamがwatchし続け、変更があった場合にCloud Storageにストリーミングする感じ。

(↓ GPTにwhimsicaldで作成したイメージ。雑だけどこんなもん)

これによって、データベース費用が必要なくなり、単純なcloud storage, cloudrunのみの金額になります。かつ、10GBまでは無料枠なので、おそらく個人利用ならタダじゃないかな。(1年は検証したい)

もちろん、cloud storage以外にもcloudflare R2とかも使用することもできるので、料金的にはそっちの方が抑えれそうです。

n8n×litestream

n8nはデフォルト設定で~/.n8n/database.sqliteを作成します。パスは変更不可能です。
なので、このsqliteをlitestreamのwatch先にすることになります。

litestreamの設定について

設定はlitestream.ymlに記載していきます。

litestream.yml
dbs:
  - path: .n8n/database.sqlite
    replicas:
      - url: ${GCS_BUCKET_URL}

内容として、pathで.n8n/database.sqliteを設定しています。
replicas.urlが今回のcloud storageのbucketへのURLになります。

litestream導入について

Dockerfile
FROM alpine AS litestream-builer
ARG LITESTREAM_VERSION=0.3.13

ENV LITESTREAM_VERSION=${LITESTREAM_VERSION}
ADD "https://github.com/benbjohnson/litestream/releases/download/v${LITESTREAM_VERSION}/litestream-v${LITESTREAM_VERSION}-linux-amd64.tar.gz" /tmp/litestream.tar.gz
RUN tar -C /usr/local/bin -xzf /tmp/litestream.tar.gz

## 省略
COPY --from=litestream-builer /usr/local/bin/litestream /usr/local/bin/litestream
COPY litestream.yml /etc/litestream.yml
RUN chmod +x /etc/litestream.yml
RUN chmod +x /usr/local/bin/litestream

RUN apk add bash

RUN \
	mkdir .n8n && \
	chmod +x .n8n && \
	chown node:node .n8n
USER node


ENTRYPOINT ["/docker-entrypoint.sh"]

全体のコードはぜひgithubを見てもらえたらと思いますが、マルチステージビルドとしてlitestreamをインストールしたのちに権限をつけるだけになっています。
amd64でDockerのbuildをするので、litestreamもamd64のものを使用することを間違いなく。
(ミスって違うの選んで頭悩ませてました。。w)

entrypointの実装

docker-entrypoint.sh
if [ -f .n8n/database.sqlite ]; then
  echo "Database already exists"
else
  echo "Database does not exist. Creating..."
  litestream restore -if-replica-exists -o .n8n/database.sqlite "${GCS_BUCKET_URL}" 
fi

exec litestream replicate -exec "n8n"

(説明として不要な箇所は消しています)

litestreamを繋いでいることとしては以下です。

  • restore(storageの情報をsqliteに適応)
  • replicate(sqliteの変更をstorageにストリーム)

最初のif文は確実にfalseになる想定ですが、logとして検知する意味で置いています。

litestream replicate -execでフォアグラウンドで実行したいコマンドをかけます。今回はn8nだけで大丈夫だったのでシンプルになっていますが、色々書けちゃいます。

当初サイドカー構成にしようかも考えたのですが、構成としてn8nとlitestreamは何かあったときにちゃんと一緒に落ちて欲しいなという気持ちもあり、まとめて実行するようにしています。

cloudrunの設定

run以外は、前回の設定と変わらないのでスキップします。storageのバケット作りもそんなに説明することないと思うので、はしょります。

runの変数が今回変わっていてシンプルになっています。
N8N_ENCRYPTION_KEYは前回と同じで、GCS_BUCKET_URLが追加です。
GCS_BUCKET_URLは
gcs://xxx/yyy.sqlite
を入れてください。

cloud storageのバケットを作成して、そのバケット名と保存先の名前を入力してください。
ex. gcs://n8n/database.sqlite

注意点として、gs://でgsutil URIとしては表示されると思いますが、litestreamのtype認識の問題としてgcsにしないと動かないようです。

あと、リビジョンのオートスケーリングの上限は必ず1にしてください。
litestreamの都合として、1つのsqliteをwatchして、1つのstorageに情報を流していきます。
オートスケーリングで5つ動いている場合、5つのsqliteをwatchして、1つのstorageに書き込む状態が発生すると、データ競合というか。。破壊する原因になっちゃうので、気をつけてください。
これをうまくやるためにLiteFSとかあるらしいが。。個人利用・職場のちょい利用くらいならええやろと思い、モチベは生まれなかったです。
(ちょい利用じゃなくなるタイミングでちゃんとn8nに課金してくれ!)

まとめ

ということで、今回の記事はとてもざっくりなのですが、結果としてコストはしっかりと落とせそうです!
ですし、職場の導入もSQLサーバーを使って。。いくらかかります!みたいなのもなく、シュッと導入できる状態になりそうです。
litestream神すぎる。。

今後も個人開発の枠を超えないものは、ガンガンlitestream使っていこうと思います。

また来月以降でコスト感を補足として記載します。

今後はnoteでn8nのtipsを書いたり、n8nのちょっと技術いる部分をzennで書いていきます。
次回はどのようなものを実際に作っているかをお伝えできたらと考えておりますので、お楽しみに!

Discussion