Litestreamをローカルで試す
概要
Litestream試した事が無かったので、試してみたいと思います。今回はローカルだけで動作させてみて雰囲気が分かればと思います。
Litestreamとは?
Litestreamは、SQLiteデータベースのストリーミングレプリケーションを提供するオープンソースのツールです
レプリケーション先としてはAWS S3、Google Cloud Storageなどを設定する事ができます。
一覧は👇で確認できます。
仕組み
独立したバックグラウンド プロセスとして実行され、WAL(Write-Ahead Log)ページを非同期に複製します。
-
WALの役割: SQLiteのWALモードでは、データベースの変更が一時的に
-wal
ファイルに記録され、後でメインのデータベースファイルに反映されます - シャドウWAL: Litestreamは長時間の読み取りトランザクションを開始し、他のプロセスによるチェックポイントを防ぎます。新しいWALページはシャドウWALと呼ばれる領域にコピーされ、必要に応じて手動でチェックポイントが実行されます
- スナップショットとジェネレーション: データベースの正確な復元には、スナップショットとその後のすべてのWALフレームが必要です。これらの連続したファイル群をジェネレーションと呼びます
- 保持期間の管理: 復元時間はスナップショット以降のWALファイルの数とサイズに依存するため、Litestreamは定期的に新しいスナップショットを作成し、古いWALファイルを削除して管理します
ローカルで試す環境の作成
MinIO環境作成
Docker環境でレプリケーション先を公式ドキュメントにもある MinIO を使ってLitestreamを試してみたいと思います。
MinIOとは?
Amazon S3と互換性のあるオブジェクトストレージサーバーです。オープンソースで無償利用でき、クラウドやコンテナ、エッジ環境などさまざまな環境で利用できます
早速 compose.yml
を以下内容で作成して、先にMinIOだけ設定しときます。
volumes:
minio_data:
name: litestream_example # 好きな名前で
services:
minio:
image: minio/minio:RELEASE.2025-02-18T16-25-55Z
volumes:
- minio_data:/minio/data
command: server --console-address ':9001' /minio/data
ports:
- 9000:9000
- 9001:9001
起動し、http://localhost:9001 にアクセスするとログイン画面が表示されるかと思います。
今回はローカルで試すだけなのでUsernameとPasswordはデフォルトの minioadmin:minioadmin
でログインします。
「Create a Bucket」からBucketを作成しときます。今回は「litestream-bucket」という名前で作成しました。
Litestream環境作成
次にSQLiteを扱う側の環境を構築していこうと思います。本来は何かしらのアプリケーションが存在し、SQLite DBが更新され、Litestreamでレプリケーションされる想定ですが、今回はSQLite CLIで直接DBを更新し、Litestreamでレプリケーションしようと思います。
LitestreamのDocker Image にSQLiteのバイナリをコピーしてImageを作成してみたいと思います。
FROM keinos/sqlite3:3.49.0 AS builder
FROM litestream/litestream:0.3
COPY /usr/bin/sqlite3 /usr/bin/sqlite3
COPY /run-test.sh /run-test.sh
RUN /run-test.sh
RUN apk update && \
apk add --no-cache git bash
SQLiteのDocker Imageには以下のImageを使わせてもらいました。
最後の正しく sqlite3
コマンドが機能するかテストする run-test.sh
もこちらのImage内で使っているものを使っています。
もしテストが失敗した場合は👇の様なメッセージが表示されImageのビルドに失敗します。
=> ERROR [app stage-1 3/3] RUN /run-test.sh
LitestreamのDocker Imageは以下の公式のImageを使っています。
compose.yml
にLitestreamの設定も追加します。
services:
# ここから追加
app:
build: .
image: litestream
entrypoint: ["/bin/sh", "-c"]
command: ["while sleep 1000; do :; done" ]
working_dir: /usr/src
minio:
# ....
これで docker compose up
すると MinIO と Litestream 環境が立ち上がるかと思います。
動作確認
早速Litestreamの挙動を確認していきたいと思います。まず先ほどのDocker環境を起動しLitestreamのコンテナでSQLite databaseを作成します。
作成する内容はLitestreamのドキュメントのこちらの内容で試してみたいと思います。
$ sqlite3 fruits.db
SQLite version 3.44.4 2025-02-19 00:18:53
Enter ".help" for usage hints.
sqlite> CREATE TABLE fruits (name TEXT, color TEXT);
sqlite> INSERT INTO fruits (name, color) VALUES ('apple', 'red');
sqlite> INSERT INTO fruits (name, color) VALUES ('banana', 'yellow');
sqlite> SELECT * FROM fruits;
apple|red
banana|yellow
sqlite> .quit
次にMinIOにアクセスする為のAccessKeyを作成します。http://localhost:9001 にアクセスしサイドメニューの「Access Keys」を選択し「Create access key +」から新しいAccessKeyを作成しときます。
次に別ターミナルを立ち上げドキュメント通り litestream replicate fruits.db s3://litestream-bucket.minio:9000/fruits.db
の様にコマンドで実行しようと思ったんですが、色々エラーが出てうまくいかなかったので litestream.yml
の設定ファイルを作成しそちらを読み込んで実行する形で試してみたいと思います。
# 以下の様なlitestream.ymlをfruits.dbと同じ場所に作成しとく
$ cat litestream.yml
dbs:
- path: /usr/src/fruits.db
replicas:
- type: s3
bucket: litestream-bucket
path: fruits.db
endpoint: http://minio:9000
region: us-east-1
access-key-id: xxxxxxxx
secret-access-key: xxxxxxx
force-path-style: true
$ litestream replicate -config ./litestream.yml
これで実行されて、👇の様にBucket内に fruits.db
が作成されていれば成功です。
継続的なレプリケーション
次に継続的にMinIOにバックアップが行われているか試してみたいと思います。litestream replicate -config ./litestream.yml
を実行した状態で別ターミナルでレコードを新たに登録してみます。
$ sqlite3 fruits.db
SQLite version 3.44.4 2025-02-19 00:18:53
Enter ".help" for usage hints.
sqlite> INSERT INTO fruits (name, color) VALUES ('grape', 'purple');
sqlite> .quit
この後 litestream replicate -config ./litestream.yml
を実行していたターミナルを Ctrl+Cで replicate
をストップさせ restore_fruits.db
ファイルへMinIOから復元させてみます。
$ litestream restore -o restore_fruits.db -config ./litestream.yml /usr/src/fruits.db
$ ls restore*
restore_fruits.db restore_fruits.db.tmp-shm restore_fruits.db.tmp-wal
$ sqlite3 restore_fruits.db
SQLite version 3.44.4 2025-02-19 00:18:53
Enter ".help" for usage hints.
sqlite> .table
_litestream_lock _litestream_seq fruits
sqlite> SELECT * FROM fruits;
apple|red
banana|yellow
grape|purple
sqlite> .quit
ちゃんと継続的にレプリケーションされていそうです。ちなみに .tmp-shm
と .tmp-wal
ファイルは litestream replicate
を実行すると .shm
.wal
ファイルが作成されますが .tmp-*
ファイルは残ったままになっていました。
試しにMinIOを停止させてみる
実験的にレプリケーションしている状態で、MinIOのコンテナを停止させデータ登録を行ってみます。
$ sqlite3 fruits.db
SQLite version 3.44.4 2025-02-19 00:18:53
Enter ".help" for usage hints.
sqlite> INSERT INTO fruits (name, color) VALUES ('mango', 'orange');
sqlite> .quit
litestream
側で以下のエラーが発生しました。
level=ERROR msg="monitor error" db=/usr/src/fruits.db replica=s3 error="RequestError: send request failed\ncaused by: Get \"http://minio:9000/litestream-bucket?delimiter=%2F&prefix=fruits.db%2Fgenerations%2F5f5a48c771697f1e%2Fsnapshots%2F\": dial tcp: lookup minio on 127.0.0.11:53: no such host"
再度MinIOのコンテナを起動してやると書き込みが完了したログが出たので復元して確認してみます。
$ sqlite3 restore_fruits2.db
SQLite version 3.44.4 2025-02-19 00:18:53
Enter ".help" for usage hints.
sqlite> .table
_litestream_lock _litestream_seq fruits
sqlite> SELECT * FROM fruits;
apple|red
banana|yellow
grape|purple
mango|orange
sqlite> .quit
ちゃんと書き込まれていました 👀 裏でずっとリトライを実施している様子でした。
今回はここまでで、次回は実際にどこかデプロイして挙動を試してみたいと思います。
参考URL
Discussion