SQLite3からPostgreSQLへの移行
なお、ActiveStorageのvariantsを使っていたりすると、1つのattachmentを削除しただけで複数のblobの削除が発生して、高確率で同時書き込みが発生する
再現用のコード
並行IOがある以上、SQLite3だと対策のしようが無いようなので、SQLite3からPostgreSQLに移行する 一応運用中のアプリケーションなので、データ移行も手順を用意して真面目にやってみることにした
方針
1. SQLite3で運用中のアプリケーションの横にPostgreSQLを立ち上げる
2. SQLite3からPostgreSQLにデータをコピーする
3. PostgreSQL化したバージョンのアプリケーションをデプロイする
SQLite3で運用中のアプリケーションの横にPostgreSQLを立ち上げる
これはdocker composeでおこなえる
SQLite3からPostgreSQLにデータをコピーする
sequelというツールを使う
移行対象
demo.ytdlor.or6.jp
デモサイト
ytdlor.local
自宅のLANで動かしているNAS
以下、「移行日」「前日」といった表現を使っているが、べつに1日でやってもよい。
移行準備作業
以下の作業を移行日前日までに済ませておく
データ移行ツールの準備
data_migrationブランチを切って、移行用のDockerfileとcomposeファイルを用意する
data_migration/Dockerfile
コンテナが起動したらsequelを起動してデータ移行して終了する
docker-compose-data-migration.yml
メインのdocker-composeに加えて、以下のものを追加する
data_migrationサービス
上記のDockerfileからつくられたコンテナが起動する
dbサービス
postgresqlを動かす
postgresボリューム
data_migrationブランチはmainにマージしない
アプリケーションをPostgreSQLに対応させる
mainブランチから改めてブランチを切り、PostgreSQL化したバージョンを用意しておく
ブランチ名: sqlite3_to_postgresql
このブランチは最終的にはmainにマージする
Railsアプリケーション自体のDBMSの変更は簡単にできる
PostgreSQLを使うとなるとdevelopmentもcomposeで動かしてしまったほうがよさそうなのでそのようにしようとしたら結構めんどくさかった
リハーサル
ダミー環境を用意してデータ移行日当日の手順をリハーサルする
cd ~/project/ytdlor
git checkout data_migration
export SECRET_KEY_BASE=$(cat secret.txt)
docker compose -f docker-compose.yml -p ytdlor_production down -v
docker compose -f docker-compose.yml -p ytdlor_production build
docker compose -f docker-compose.yml -p ytdlor_production run --rm web rails db:migrate
docker compose -f docker-compose.yml -p ytdlor_production up
DOCKER_HOST="unix:///var/run/docker.sock" COMPOSE_PROJECT_NAME="ytdlor_production" ./run_data_migration.sh
git checkout sqlite3_to_postgresql
./docker_compose_production up --build
補足
docker_compose_production は SECRET_KEY_BASE=$(cat secret.txt) docker compose -f docker-compose.yml -f docker-compose-production.yml -p ytdlor_production $@ を実行するスクリプト
テストとdb/seeds.rbも用意しとけばよかったな・・・
データ移行日当日の手順
demo.ytdlor.or6.jp
$ ssh n
n$ git checkout data_migration
n$ DOCKER_HOST="unix:///var/run/docker.sock" COMPOSE_PROJECT_NAME="ytdlor_demo_site" ./run_data_migration.sh
デプロイ先と作業用マシンが同じホスト
マージするとCIからデプロイが行われる
ytdlor.local
$ ssh ytdlor.local
ytdlor.local$ git pull
ytdlor.local$ git checkout data_migration
ytdlor.local$ DOCKER_HOST="unix:///var/run/docker.sock" COMPOSE_PROJECT_NAME="ytdlor" ./run_data_migration.sh
ytdlor.local$ git checkout main
sqlite3_to_postgresql は既にマージ済みのはずなのでmainをcheckoutすればよい
ytdlor.local$ exit
$ ./deploy_ytdlor_local.sh
補足
run_data_migration.sh
実行するとsqlite3からPostgreSQLにデータがコピーされる
実行が終了するとコンテナは終了する
これを実行した時点で rails db:create; rails db:migrate してデータを投入したのと同じ状態になっているはず
postgresqlとsqlite3にアクセスできればいいので、他の環境変数なんかはいらないはず
deploy_ytdlor_local.sh
実行すると ytdlor.local にアプリケーションをデプロイする
db:migrateを省略してアプリケーションのデプロイを実行する必要がある?
実行しても無害なはずなので実行してもいいかも?
db:migrateをバイパスするためだけに今まで通りのデプロイ手順(CI含む)が使えなくなるものやだし
リハーサルで検証しておく
動作確認してみたところ無害だった
結論: 省略はしなくてok
ssh経由のdocker contextだと docker compose down が正常終了しないので、デプロイ先のホストにシェルログインしてからrun_data_migration.shを実行することにした
code:migration.sh
apt update
apt install libpq-dev
gem insall sequel pg
sequel postgres://postgres:password@db/template1
irb> DB.run('CREATE DATABASE "ytdlor_production" ENCODING = \'unicode\'')
exit
sequel -C sqlite:///var/opt/ytdlor/db/production.sqlite3 postgres://postgres:password@db/ytdlor_production
オマケ
データベースの削除
code:ruby
DB.run("drop database ytdlor_production")
所感
NAS上で動かす前提のものなので、今回やった手順は不適な気がする
データマイグレーションが必要になるときは、今回やったような手順で移行するよりも、アプリケーション自体にデータのインポート/エクスポート機能があったほうがいいような気がした
本来は移行作業の前にバックアップをとるべき
しかし、docker volumeのバックアップはかなりめんどくさい
docker cliがデータのコピーをサポートしておらず、コンテナにマウントしたうえでコピーしないといけないので