はじめに
昨今の円安でAWSコストを抑えるためにLitestreamを試してみました。
環境
MacOS Monterey 12.5
Docker 20.10.21
Compose v2.13.0
Rails 7.0.4(Ruby 3.1)
SQLite 3.34.1
Litestream v0.3.8
Rails(Docker)環境の下準備
FROM ruby:3.1
ENV APP_ROOT /usr/src/app
ARG RUBYGEMS_VERSION=3.3.20
RUN apt-get update -qq \
&& apt-get install -y vim sqlite3 nodejs npm \
&& rm -rf /var/lib/apt/lists/* \
&& npm install --global yarn
# Download the static build of Litestream directly into the path & make it executable.
# This is done in the builder and copied as the chmod doubles the size.
ADD https://github.com/benbjohnson/litestream/releases/download/v0.3.8/litestream-v0.3.8-linux-amd64-static.tar.gz /tmp/litestream.tar.gz
RUN tar -C /usr/local/bin -xzf /tmp/litestream.tar.gz
WORKDIR $APP_ROOT
COPY Gemfile $APP_ROOT/
RUN gem update --system ${RUBYGEMS_VERSION} && \
bundle install
COPY . $APP_ROOT
CMD ["rails", "server", "-b", "0.0.0.0"]
version: '3'
services:
web:
build: .
environment:
RAILS_ENV: development
TZ: Asia/Tokyo
ports:
- '3000:3000'
volumes:
- .:/usr/src/app
source 'https://rubygems.org'
gem 'rails', '~>7.0.3'
Rails環境構築
$ docker compose run --rm web rails new . --api
$ docker compose build
$ docker compose run --rm web rails db:create
$ docker compose up -d
http://localhost:3000
へアクセスして起動確認
S3の準備
litestreamのガイドに従いAWSアクセスキーとS3バケットを準備する。ガイド中にもあるがIAMポリシーは AmazonS3FullAccess
もしくは以下のカスタムポリシーを指定する。
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetBucketLocation",
"s3:ListBucket"
],
"Resource": "arn:aws:s3:::<BUCKET>"
},
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:DeleteObject",
"s3:GetObject"
],
"Resource": [
"arn:aws:s3:::<BUCKET>/*",
"arn:aws:s3:::<BUCKET>"
]
}
]
Litestreamの設定
dbs:
- path: ./db/development.sqlite3
replicas:
- url: s3://litestream-sample-f
※litestream.ymlファイル内で環境変数の展開が出来ないようなのでdocker-compose.yml内で LITESTREAM_ACCESS_KEY_ID
と LITESTREAM_SECRET_ACCESS_KEY
へ値を渡すようにしました。
version: '3'
services:
web:
build: .
environment:
RAILS_ENV: development
TZ: Asia/Tokyo
LITESTREAM_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID}
LITESTREAM_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY}
ports:
- '3000:3000'
volumes:
- .:/usr/src/app
S3へレプリケート
$ docker compose run --rm web litestream replicate
litestream v0.3.8
initialized db: /usr/src/app/db/development.sqlite3
replicating to: name="s3" type="s3" bucket="litestream-sample-f" path="." region="" endpoint="" sync-interval=1s
/usr/src/app/db/development.sqlite3: sync: new generation "f8b26dafeae57bf6", no generation exists
/usr/src/app/db/development.sqlite3(s3): snapshot written f8b26dafeae57bf6/00000000
無事にレプリケーション出来ました
レプリケーション先(S3)の階層
litestream-sample-f/
└─generations/
└─f8b26dafeae57bf6/
├─snapshots/
│ 00000000.snapshot.lz4
│
└─wal/
00000000_00000000.wal.lz4
00000001_00000000.wal.lz4
まとめ
設定自体はさほど難しくなくサクッと出来ましたが、FargateやApp Runnerといったフルマネージドサービスでの実運用を考えると色々と配慮する点が多そうだなと感じました。例えば、
コンテナ起動時にはS3→コンテナへのリストア
コンテナ起動中はコンテナ→S3へのレプリケート
といったコンテナ状態におけるDB挙動の操作やコンテナ複数台起動時には整合性が取れなくなるのでEFSマウントの必要性など考えられるかなと思います。
逆にSLAが低く、多少落としても怒られないような小規模システムであればRDSやAuroraを使うよりもかなりお安くなるので選択肢の一つとして持っておいても損はないかなと思います