稀にしか使わないデータベースにLitestreamを使う

2024/06/30 21:55

※ 商品のリンクをクリックして何かを購入すると私に少額の報酬が入ることがあります【広告表示】

文中に登場するコードは、GitHubにリポジトリを作成して公開しています。以下のリンクからコードを参照してください。

Litestreamとは何か

Litestreamは、SQLiteデータベースファイルをリアルタイムでAmazon S3やGoogle Cloud Storageなどのオブジェクトストレージにレプリケーションすることができるオープンソースツールです。

主な特徴

  • セットアップが簡単

  • 低コスト

  • 高可用性

  • WAL(Write Ahead Logging)による効率的なレプリケーション

  • レプリケーション先の多様性(S3、GCS、ファイルシステム、SFTPなど)

  • 指定した時刻に復元可能なポイントインタイムリカバリ

公式サイト: https://litestream.io/

コンテナでSQLiteを使える

使える場面は限られますが、常時起動している必要のない個人向けのWebサービスの場合は運用費用を極限まで抑えることができるかもしれません。

例えば、Cloud RunやECS Fargateのように、リクエストがあったときだけコンテナが起動するサービスを使って、データベースは起動時にgcsやS3から復元し、更新があった場合にはgcsやS3にレプリケーションしておきます。

こうすればCloud RunやECS Fargateが停止してローカルディスクからSQLiteのファイルが消えても、次回起動時にはまたgcsやS3から復元できるので、データの永続性を確保できます。

試しにGrafanaを起動してみる

Grafanaは可視化ツールで、Monitoring/Observabilityの分野でよく使われています。

デフォルトでSQLiteを使っているので、Litestreamを使ってGrafanaを起動してみます。

公式サイト: https://grafana.com

terraformで環境を用意する

main.tf

※ 簡易にしてあるので、専用のGoogle Cloudプロジェクトを作成することをお勧めします。自己責任でお願いします

terraformの main.tf のgoogle_cloud_run_serviceとgoogle_cloud_run_service_iam_memberのresourceをコメントアウトしておき、terraformのフォルダでterraformを実行する。

サービスをEnabledにしろというメッセージでエラーになるので、Cloud Runのコンソールから手動でEnabledにして何度かやり直してください。

  $ terraform init
  $ TF_VAR_project_id=<Google CloudのProject_id> TF_VAR_grafana_settings_bucket_name=<gcsのバケット名> TF_VAR_google_artifact_registry_repository_name=<Artifact Registryに作るRepository名> TF_VAR_image_name=<Dockerのイメージ名> terraform apply

リソースが4つ作成されます。

コンテナイメージの作成

Litestreamの設定ファイルを修正します

Litestreamの設定ファイル にあるGCSのバケット名をterraformで作成したバケット名に変更します。

<GRAFANA_SETTINGS_BUCKET_NAME> を修正します。

  url: gcs://<GRAFANA_SETTINGS_BUCKET_NAME>/grafana.db

Dockerのbuildとpush

Dockerfileのあるディレクトリで以下のコマンドを実行します。

私はApple SiliconのMacを使っているので、 --platform linux/amd64 をつけていますが、IntelのMacやLinuxの場合は不要です。

  $ docker build --platform linux/amd64 . -t <Dockerのイメージ名>
  $ docker tag <Dockerのイメージ名> asia-northeast1-docker.pkg.dev/<Google CloudのProject_id>/<Artifact Registryに作るRepository名>/<Dockerのイメージ名>
  $ docker push asia-northeast1-docker.pkg.dev/<Google CloudのProject_id>/<Artifact Registryに作るRepository名>/<Dockerのイメージ名>:latest

terraformでCloudRunのサービスを起動する

コメントアウトしておいたresourceを有効にして再びterraform applyを実行します。前回と同じコマンドです。

  $ TF_VAR_project_id=<Google CloudのProject_id> TF_VAR_grafana_settings_bucket_name=<gcsのバケット名> TF_VAR_google_artifact_registry_repository_name=<Artifact Registryに作るRepository名> TF_VAR_image_name=<Dockerのイメージ名> terraform apply

動作を確認する

Google CloudのコンソールからCloud RunのサービスのURLを確認し、URLにアクセスしてください。

Grafanaの初期ユーザーはユーザー名 admin 、パスワード admin です。初回サインインするとパスワードの変更を求められますので、簡単に推測できないパスワードを設定してください。

しばらく放置するとCloudRunのサービスが終了します。再度アクセスすると起動にしばらく時間がかかりますが、パスワードの変更が反映されたまま起動します。

Litestreamの設定を見る

litestream.yml

  dbs:
    - path: /var/lib/grafana/grafana.db
      replicas:
        - url: gcs://<GRAFANA_SETTINGS_BUCKET_NAME>/grafana.db # GCSのバケット名はグローバルに一意(重複できない)のでそれっぽい名前にする
          retention: 12h
          retention-check-interval: 3m
          snapshot-interval: 4h
          sync-interval: 30s

pathはgrafanaがデータベースファイルを保存している場所です。

replicasはレプリケーション先の設定です。ここではGCSにレプリケーションしています。コンテナは短い時間で終了してしまうので、頻繁にsyncし、また、履歴がどんどん増えていってしまうので定期的に削除するようにしています。

Dockerfile

Dockerfile

Cloud Runのコンテナで動作させるLitestreamのバイナリをダウンロードして展開しています。

  ADD https://github.com/benbjohnson/litestream/releases/download/v0.3.13/litestream-v0.3.13-linux-amd64.tar.gz /tmp/litestream.tar.gz
  RUN tar -C /usr/local/bin -xzf /tmp/litestream.tar.gz

続いて、Grafanaのイメージを使い、Litestreamのバイナリをコピー、Litestreamの設定ファイルをコピーしています。

  FROM grafana/grafana-enterprise
  COPY --from=builder /usr/local/bin/litestream /usr/local/bin/litestream
  COPY litestream.yml /etc/litestream.yml

残りは、Cloud Runが期待しているアプリケーションのポートに設定ファイルを置換したり、LitestreamとGrafanaを起動するスクリプトをコピーしています。

start.sh

start.sh

コンテナの起動時に呼び出されるので、SQLiteのデータベースファイルのリストアを行います。リストア前にデフォルトのデータベースファイルが存在していたら、バックアップをとっておき、リストアするデータベースがなかった場合にはデフォルトのデータベースを元に戻すような処理をしています。関数にしてファイルパスを引数に取るようにすれば他のアプリケーションでも使いまわせると思います。

最後の行で、Litestreamを起動しつつ、Grafanaを起動(/run.sh)しています。

  #!/bin/sh
  set -e # 失敗したら終了する

  # 起動時にすでにデータベースファイルが存在する場合は、一旦バックアップする
  if [ -f /var/lib/grafana/grafana.db ]; then
    mv /var/lib/grafana/grafana.db /var/lib/grafana/grafana.db.bk
  fi

  # replicaが存在したらリストアする
  litestream restore -if-replica-exists -config /etc/litestream.yml /var/lib/grafana/grafana.db

  # replicaのレストアができた && バックアップしたデータベースファイルが存在する場合は削除する
  if [ -f /var/lib/grafana/grafana.db ]; then
    if [ -f /var/lib/grafana/grafana.db.bk ]; then
      rm /var/lib/grafana/grafana.db.bk
    fi
  else
    # replicaのレストアができなかった場合 && バックアップしたデータベースファイルがあれば元に戻す
    if [ -f /var/lib/grafana/grafana.db.bk ]; then
      mv /var/lib/grafana/grafana.db.bk /var/lib/grafana/grafana.db
    fi
  fi

  # Litestreamを起動しつつGrafanaを起動する
  exec litestream replicate -exec "/run.sh"

Prev Entry

Next Entry