Clik here to view.

SQLite on Railsシリーズ(04)LitestreamでSQLiteをバックアップしよう(翻訳)
本記事は、Railsアプリケーションの主要な機能やパワーを犠牲にせずに、データベースエンジンとしてSQLiteを使う方法を紹介するシリーズ記事の第4弾です。今回は、SQLiteで最もよく引き合いに出される欠点の1つであるディザスタリカバリー(災害復旧)と、その対処方法について説明したいと思います。
Image may be NSFW.
Clik here to view.
SQLiteをproduction環境で使うには
私がSQLiteエコシステムの中で好きな人物であるBen Johnsonは、この問題を的確に指摘しています。
アプリケーション開発の世界で、SQLiteがproduftion運用に耐えるデータベースではなく「おもちゃ」扱いされている理由は何だろうか?SQLiteをproduction環境で使う場合の最大の問題は、ディザスタリカバリーだ。サーバーが死んだらデータも死ぬのでは困る。
SQLiteがローカルのファイルシステム上にあるファイルを利用するという事実は、SQLiteの評価が真っ二つに割れる原因となる大きな諸刃の剣の1つです。
その一方、ファイルがローカルに置かれていることで、クエリのネットワークレイテンシを完全に解消できます(クエリの遅延は多くのWebアプリケーションで主要なパフォーマンスボトルネックとなります)。
さらに、データベースを1つのファイルにすることで、アプリケーションデータベースをブランチ固有のデータベースとして管理できるなど、独自の可能性も生まれます。
さらに、データベース用に別のサーバーを実行せずに済むため、Webアプリケーション運用の複雑さが著しく軽減されます。
本シリーズの今後の記事では、データベースをファイルシステム上の通常のファイルとして扱えるSQLiteのシンプルさから得られるその他のメリットについても解説する予定です。
ただし、ファイルシステム上の単なるファイルであることは、そのままSQLiteの主な弱点でもあります。永続的なデータストレージは永続的であってくれなければ、あまり役に立ちません。
Image may be NSFW.
Clik here to view.
本当にあった怖い話
これについて、私が個人的に体験した危機的状況をお話ししたいと思います。
あるとき私は、別のアプリケーションをデプロイ待ちしている片手間に、とあるプラットフォームプロバイダのダッシュボード上で、命名を一貫させるだけのために、アプリケーションの名前を変更しました。
このサードパーティサービスのWebダッシュボードでアプリケーションのリネーム操作を行うと、サーバー上のアプリケーションを含むフォルダに対してrm -rf
を実行してから新しいフォルダ名で再デプロイを実行することを、私は気づいていませんでした。
このアプリではSQLiteをproductionデータベースとして利用しており、デプロイ中にデータベースを安全に保持するためにstorage/
ディレクトリに保存していたのですが、親ディレクトリごと削除されてしまえば手も足も出ませんでした。
Web UIでたった1回、何の気なしに更新をかけたせいで、約1年分のproductionデータが完全に吹っ飛びました。さまざまな回復方法を試したものの、どれひとつとして成功せず、データは永久に失われました。
私がこの話をしたのは、この点を軽視したくないからです。明確かつ強力な災害復旧計画を準備せずにSQLiteをproductionデータベースとして利用するのは危険であり、おそらく愚かなことです。
SQLiteには多くのメリットがありますし、私は今も上記を含む多くのアプリケーションで SQLiteをproduction用データベースとして喜んで運用しています。ただし、災害復旧設定は常に確実に行うようにしています。
ここからは、私が利用しているバックアップと復旧の設定について解説し、これを自分で設定するためのツールを皆さんに提供したいと思います。
Image may be NSFW.
Clik here to view.
Litestreamとは
先ほどのBen Johnsonの引用は、彼がLitestream
ツールを作成した理由に関するブログ記事からのものです。Litestream
は、WebアプリケーションでSQLiteのパワーを解き放つ興味がある人にとっては必須のツールです。
Image may be NSFW.
Clik here to view.
Litestream
というツールを一言で説明するならば、こうです。
Litestreamは、SQLiteデータベース用のレプリケーションツールです。
これが具体的に何を意味するのかを詳しく見てみましょう。Benの紹介ブログ記事に詳しい説明があります。
Litestreamは、別プロセスで実行されるツールで、SQLiteデータベースを Amazon S3(または別のストレージプロバイダ)に継続的に複製します。数行の設定を書くだけで起動して実行できます。後のことは何も気にせずに、コードを書くことに専念できます。
手短に言えば、Litestream
はSQLiteの災害復旧プランであり、シンプルで堅牢かつレジリエンス(回復力)に優れています。クラウドストレージのわずかなバケット料金で、SQLiteデータベースのポイントインタイムバックアップを効果的に取得できます1。セットアップも簡単です。
Litestreamのドキュメントには、macOS、Linux(Debian)、またはソースからビルドにLitestream
をインストールするガイドが記載されています。私は個人的にLinux(Debian)サーバーにデプロイしているので、本記事ではこのアプローチを共有します。
productionサーバーにLitestreamパッケージをインストールしたら、それを実行する必要があります。ここでも、複数のオプションがあり、それぞれドキュメントでわかりやすく説明されています。プロセスは以下で実行できます。
私の場合は、Litestream
をSystemdサービスとして実行しているので、この方法で進めます。
最後に、パッケージをインストールして実行したら、ストレージプロバイダと通信するように設定する必要があります。 Litestream
は以下を含む幅広いプロバイダをサポートしています。
- Amazon S3
- Azure Blob Storage
- Backblaze B2
- DigitalOcean Spaces
- Scaleway Object Storage
- Google Cloud Storage
- Linode Object Storage
- SFTPサーバー
私の場合は、DigitalOcean Spacesを利用しています。
ここまでで、Litestream
は非常に柔軟性が高く、さまざまなデプロイ状況やストレージプロバイダで利用できることがおわかりいただけたと思います。ただし、私は自分のセットアップしか経験がないので、方法をできるだけ詳しく共有して、皆さんがすべてを自分でセットアップできるようにお手伝いしたいと思います。
Image may be NSFW.
Clik here to view.
SQLiteを使う場合のRailsホスティング
私は、RailsアプリケーションをホストするためにHatchbox.ioを使っています。
Herokuでは、SQLiteをproductionデータベースとして利用できません。しかし、Salesforce経営のもとで10年間過ごした後の私はHeroku全般に愛着を感じなくなってしまいました。
Hatchboxで気に入っている点は、「自分が所有するサーバーにデプロイ」できる(つまり多くのPaaSプロバイダのようなコストを軽減できる)ことと、Railsアプリケーション向けにカスタマイズされていることです。
Hatchboxは、GoRailsのChris OliverとBilal Budhaniによって運営されており、迅速で役立つカスタマーサポートを提供しています。
セールストークはさておき(私はHatchboxと提携関係にないので単なる冗談です)、Hatchboxは素晴らしいサービスですが、ほとんどのものは自分たちが持ち込んだサーバー上に置かれるため、Litestream
にはHatchbox固有のセットアップは何もありません。
私の場合、Hatchbox経由でDigitalOceanのドロップレットとHetznerサーバーを両方使いました。どちらの場合も、DebianベースのOSであるUbuntuを実行するLinuxマシンを使います。
Litestream
はDebianパッケージファイルを提供するため、dpkg
ユーティリティを使用してLitestream
をインストールするのは簡単です2。さらに、DebianベースのOSを使用すると、systemd
を使用してLitestream
プロセスを自動的に実行できます。
Image may be NSFW.
Clik here to view.
Litestream用スクリプト
以下は、すべてのサーバーにLitestream
をインストールして実行するのに使うBashスクリプトです3。
#!/usr/bin/env bash
set -e
# 環境変数を読み込む
source /home/deploy/.bashrc
# 現在の環境のアーキテクチャを決定する
arch=$(dpkg --print-architecture)
# 利用するLitestreamバージョンを手動で設定する
version="v0.3.11"
# 最新の.debファイルをダウンロードする
wget "https://github.com/benbjohnson/litestream/releases/download/$version/litestream-$version-linux-$arch.deb"
# dpkgで.debファイルをインストールする
sudo dpkg -i "litestream-$version-linux-$arch.deb"
# インストール済みであることを検証する
echo "Litestream version:"
litestream version
# Litestreamを有効にしてバックグラウンドサービスとして継続的に実行可能にする
sudo systemctl enable litestream
# Litestreamをバックグラウンドサービスとして継続的に実行する
sudo systemctl start litestream
# サービスが実行中であることを検証する
echo "Litestream service logs:"
sudo journalctl -u litestream
見てのとおりセットアップは簡単です。このスクリプトをDigitalOceanとHetznerの両方のサーバーで問題なく実行できました。
dpkg
、wget
、systemd
などのプリインストールされたシステムユーティリティを使っているので、追加のセットアップは不要です。しかもスクリプトが短いため、共有・利用・理解も簡単です。
DebianベースのOSを使っていない場合は別のセットアップスクリプトが必要になりますが、LinuxとUbuntuは非常に人気があるため、このスクリプトで役に立つでしょう。
Image may be NSFW.
Clik here to view.
設定
Litestream
をインストールして実行したら、後はデータベースのレプリケーションを開始するように設定するだけです。繰り返しになりますが、ドキュメントにはLitestream
の設定専用のページもあります。
要約すると、/etc/litestream.yml
ファイルを作成してYAML設定を入力する必要があります。設定の基本構造は単純明快です(そろそろお気づきと思いますが、単純なことを単純明快に実行するのがLitestream
のもう1つの素晴らしい点です)。
YAMLでは、dbs
の配列を指定して、そこに各replicas
の配列を指定します。これで、サーバーのLitestream
プロセスで複数の SQLiteデータベースファイルをバックアップして、各データベースファイルを複数のストレージプロバイダにストリーミングできるようになります。
私の場合、1個のアプリに1個のサーバーを使い、それを1個のストレージプロバイダにバックアップします(最も単純なケースでは、RailsアプリのActive Recordモデルのストレージバックエンドに1個のSQLiteファイルを利用します。したがって、データベースは 1 つだけです)。
より興味深いケースとしては、ActionCable
、ActiveSupport::Cache
、ActiveJob
の背後で実際にSQLiteを動かして、それらをLitestackでバックアップすることで、アプリごとに4個以上のSQLiteデータベースファイルをバックアップします(今後詳しく説明する予定です)。
本記事ではクラウドストレージバケットの作成方法については説明しません(Litestream
ドキュメントで独自に説明されています)。
ここでは代わりに、サーバー上の設定に焦点を当てましょう。
私はストレージプロバイダとしてDigitalOcean Spacesを使っているため、Litestream
ドキュメントの 手順に沿って進めました。Litestream
がストレージプロバイダに接続するには、access-key-id
とsecret-access-key
が必要です。設定ファイルでは、これらの値をグローバルに設定することも、レプリカごとに設定することも可能です。私の設定ファイルでは、将来レプリカを簡単に追加できるように、デフォルトでは以下のようにレプリカごとに設定しています。
# /etc/litestream.yml
dbs:
- path: /home/deploy/application-name/current/storage/production.sqlite3
replicas:
- url: s3://bucket-name.litestream.region.digitaloceanspaces.com/production
access-key-id: xxxxxxxxxxxxxxxxxxxx
secret-access-key: xxxxxxxxx/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
これも簡単です。必要なのは、Litestream
のパスにデータベースファイルを指定し、ストレージバケットのURL(とアクセス認証情報)を指定することだけです。後はすべてLitestreamが処理します。
Image may be NSFW.
Clik here to view.
Litestream設定を読み込む
インストールスクリプトはインストール後すぐにLitestream
プロセスを開始するので、次は設定ファイルを作成することになります。設定ファイルを適切にセットアップしたら、新しい設定ファイルを読み込むためにLitestream
プロセスを再起動する必要があります。systemd
を使っているので、次のように簡単に行えます。
sudo systemctl restart litestream
Litestream
をインストール・設定して、実行すれば準備完了です。データベースに書き込んでからストレージバケットにアクセスすると、Litestream
がすでにデータを書き込んでいることがわかります。
Image may be NSFW.
Clik here to view.
復旧をテストする
また、復旧できることを確認しなければバックアップは無意味なので、災害をシミュレートして復旧手順を実行してみましょう。
私の場合、データベースファイルの名前を変更することで削除を模倣するのが好みです。/home/deploy/application-name/current/storage/production.sqlite3
データベースファイルを/home/deploy/application-name/current/storage/-deleted.sqlite3
にリネームしたと仮定すると、Litestream
でproduction.sqlite3
ファイルをどのように復旧できるでしょうか。
サーバーのコマンドラインで以下を実行する必要があります。
litestream restore production.sqlite3
これで完了です。ファイルパス全体を指定する必要もなければ、難解なコマンドライン引数を渡す必要もなく、litestream restore
だけで済みます。
このコマンドは、設定ファイル内のデータベースを検索し、そのストレージレプリカから最新のコピーを復元します。ファイルが存在することを確認してから、以下を実行します。
sqlite3 production.sqlite3
これでデータベースの内容を検査し、「削除」前の最新のデータが存在することを確認します。
リカバリプロセスが機能することを確認したら完了です。
Litestream
のおかげで、production環境でWebアプリケーションの永続化レイヤにSQLiteを使う場合の主な弱点が軽減されました。これで、ディザスタリカバリーについて常に心配することなしに、SQLiteを採用することで得られる開発体験のメリットを享受できるようになります4。
関連記事
-
Litestream
の動作に興味がある方のために、ドキュメントに非常にわかりやすいページがあります。ぜひ一度隅々までじっくり読んでみることをおすすめします。 ↩ -
dpkg
についてはこの記事がおすすめです。それ以外はman
ページの「dpkgは、Debianパッケージをインストール・ビルド・削除・管理するためのツールです」という短い説明で十分でしょう。 ↩ -
本記事公開時点(2023年9月9日)の
Litestream
の最新リリースバージョンは0.3.11です。今後このスクリプトを実行するときに、新しいリリースが出ている場合は、version
変数をそのバージョン番号に置き換えてください。最新リリースはlitestreamプロジェクトのGitHubリリースページでいつでも見つけられます。 ↩ -
ディザスタリカバリー計画の別案の1つとして、AWS EBSなどのリモート接続ストレージを利用する方法もあります。これはアプリケーションの観点では引き続きローカルファイルシステムですが、サーバーがダウンしてもデータはダウンしません。その後でストレージを別のサーバーに再接続するだけでデータを「回復」できます。このソリューションの重要な詳細は、メモリマップが十分に大きいため、読み取りが基本的に本物のローカルストレージ並みに高速になることです。また、接続されたストレージでは書き込みの
fsync
呼び出しが大幅に遅くなるため、SQLite設定のsynchronous
プラグマをNORMAL
に設定して書き込み時のfsync
呼び出しを最小限に抑えるようにしてください。ただし、おそらく最も重要なのは、NFSなどのリモートファイルシステムを使うというアイデアに誘惑されないことです。しかし、私が崇拝している、あるSQLiteの導師 はこう語っています: 「アタッチトストレージ(attached storage)を使えば耐久性と可用性が得られるので、SQLiteをローカルストレージ並みのパフォーマンスに調整できます。さらに、GoogleとAWSは自動インスタンスリカバリを提供しているので、インスタンスがダウンすると別のインスタンスが生成され、ストレージが再アタッチされます。これは数秒で完了し、SQLiteで動作するアプリに非常に高いレベルの可用性を提供します。」 ↩
The post SQLite on Railsシリーズ(04)LitestreamでSQLiteをバックアップしよう(翻訳) first appeared on TechRacho.
概要
原著者の許諾を得て翻訳・公開いたします。
参考: Rails 8はSQLiteで大幅に強化された「個人が扱えるフレームワーク」(翻訳)|YassLab 株式会社
日本語タイトルは内容に即したものにしました。
また、見出しを適宜追加しています。