Litestream で思ったよりお金が掛かっているとき、もしかしたら昔のデータが消えていないかもよ
これはなに
RDB 使いたいけど予算がない!!!ってときに便利な litestream ですが、なんかレプリケーションのコストが高かったので調査したログです。
以下 GCP 環境ですが、ほかでも類似の事例があるかも。
TL;DR
generation の retention が効いていなかった。対策は以下の通り。
- 利用する IAM ロールにデータ削除の権限が付いているか確認する
-
retention-check-interval
を短くする -
retention
を短くする
そもそも Litestream on GCP の使い方
下記参照。
本題: GCS で思った以上にお金がかかる
対象のシステムは以下の構成です。
- Cloud Run 上にデプロイされたアプリケーションで SQLite を読み書きする
- SQLite は Litestream で GCS にレプリケーションされる
- 1時間に1回 Cloud Scheduler で Cloud Run エンドポイントが叩かれる
Cloud Run の起動時間も大したことなく、データ量もほとんどありません (スナップショットが ~100MB くらい)。
しかし、実際に稼働させていると以下の課題が出てきました。
- 毎日 GCS (Regional Standard Class A Operations) に対して 10円程度ずつ課金される
- 1日 0.3円ずつぐらいコストが上昇傾向にある
Regional Standard Class A Operations ってなんやねん
https://cloud.google.com/storage/pricing?hl=ja によると、以下のオペレーションが該当するようです。
けっこうな操作が該当しており、そもそも何の操作をしているのかを掴まないと厳しそうです。
ちなみに 1000 ops ごとに $0.005, だいたい 0.7 円くらいです。
1日10円くらい掛かっているので、15000 ops くらい行われていそうです。そんなに???
Audit Logs を有効にする
ということで、Audit Logs を有効にし、どんなオペレーションが走っているのか確認してみます。
やることは
- IAM と管理 → 監査ログ を開く
- Google Cloud Storage をチェック、「管理読み取り」「データ読み取り」「データ書き込み」にチェックを入れて保存をクリック
GCS の読み書きオペレーションごとに Cloud Logging にログが流れるので、その分お金が掛かります。後で止めるのを忘れずに。
Audit Logs を確認する
ログが貯まった頃合いで Cloud Logging を確認します。
大半が storage.object.list
です。どういうことだ?
GCS を見に行く
ここでハッとして GCS を見に行ったのですが、レプリケーション先のバケット内のディレクトリ (Litestream の用語としては generations
と呼ぶやつ) が大量に存在していることに気付きました。
ファイル構造としては、たとえば以下のようになっています。
[レプリケーション先ファイル名]/generations/
├── 150037d2828fc967/
│ ├── snapshots/
│ │ └── 00000000.snapshot.lz4
│ └── wal/
│ ├── 00000000_00000000.wal.lz4
│ ...
├── 1c85baf85db8e169/
│ ....
├── 45ab8cbb0e236995/
│ ....
├── 468b4bf4c44ecd32/
...
上記の 150037d2828fc967
みたいなディレクトリが 500 個くらいありました。
もし Cloud Run がコールドスタートするたびに毎回上記のディレクトリを舐めているとすると、確かにオペレーション回数が嵩むのも納得できます。
Litestream の generation の取扱
これってそもそも正しい動作なのか?ということで Litestream のドキュメントを見に行くと、
Replicas maintain a snapshot of the database as well as a contiguous sequence of SQLite WAL page updates. These updates take up space so new snapshots are created and old WAL files are dropped through a process called “retention”.
The default retention period is 24h. You can change that with the retention field. Retention is enforced periodically and defaults to every 1h. This can be changed with the retention-check-interval field.
ということで retention
というプロセスがあり、しかも 24H を超えると削除してくれるらしいです。これが働いていれば何も問題がないわけですが、どういうわけか動いていないですね?
retention が働かないのはなぜ?
様々調べていくと、概ね以下のような要因がありそうです。
- 削除権限がない
- そもそもオブジェクトの削除権限がなければ削除できない、それはそう
-
retention-check-interval
が長すぎる- 起動後から
retention-check-interval
が経つまで削除されない?
- 起動後から
-
retention
が長すぎる- retention period が長すぎる?
今回のシステムでは GCS に対する強めの権限を付けていたので、retention-check-interval
と retention
かなぁというアタリを付けました。
特に今回のシステムでは Cloud Run インスタンスは Cloud Scheduler に kick されたあとはすぐに動作を停止するため、retention-check-interval
は大いに関係しそうだなと感じました。
litestream.yml
を編集して解決
以下のように設定を編集しました。
dbs:
- path: {データベースファイルのパス}
replicas:
- url: gcs://{バケット名}/{パス}
+ retention: 12h
+ retention-check-interval: 3m
+ snapshot-interval: 4h
+ sync-interval: 30s
本題の retention-check-interval
は、少なくとも 3 分くらいはコンテナが立ち上がっていそうだなということで 3m
にセットしました。その他のパラメータは気分です。
上記設定を入れ込んでデプロイしたあと、次の Cloud Scheduler 発火タイミングで古い generation がごそっと削除され、最終的にバケット内の generation が 20 未満まで減少しました。
おわりに
ということで、立ってすぐ死ぬような環境だったら retention-check-interval
を気にしてみるといいかもよというお話でした。
また、冒頭で説明したとおり、当然 IAM ロールにストレージオブジェクトの削除権限が必要です。併せて確認をどうぞ。
Discussion