golang-migrateでMongoDBマイグレーションやってみた
概要
MongoDBへの利用を検討しておりマイグレーションはどうしようかなと思っているのですが、golang-migrateが使いやすそうだったので試してみました。
何番煎じかは分かりませんが、執筆練習も兼ねて投稿してみます。
golang-migrateはdockerコンテナで利用します。
検証環境構築
マイグレーション先のMongoDBとgolang-migrateの環境をdocker-composeで構築しておきます。
MongoDBはレプリカセットで構築します。
構築の詳細は本記事の目的と離れるため割愛します。
構築
docker-compose.ymlを作成
version: "3.9"
x-conf: &conf
image: mongo:5.0.7
restart: always
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: root
MONGO_INITDB_DATABASE: sample
volumes:
- ./replicaset.key:/etc/replicaset.key
command: --keyFile /etc/replicaset.key --replSet rs0
services:
mongo-primary:
ports:
- 27017:27017
<<: *conf
mongo-secondary:
ports:
- 27018:27017
<<: *conf
mongo-tertiary:
ports:
- 27019:27017
<<: *conf
mongo-arbiter:
ports:
- 27020:27017
<<: *conf
migrate:
image: migrate/migrate:v4.15.2
entrypoint: sh
tty: true
volumes:
- .:/var/task
working_dir: /var/task
command: ""
認証鍵(replicaset.key
)の作成
$ openssl rand -base64 756 > replicaset.key
$ chmod 600 replicaset.key
$ chown 999 replicaset.key
起動
$ docker compose up -d
DBに接続
$ mongosh -u root -p root
レプリカセット設定を適用
> rs.initiate({
_id: "rs0",
members: [
{_id: 0, host: "mongo-primary:27017", priority: 3},
{_id: 1, host: "mongo-secondary:27017", priority: 2},
{_id: 2, host: "mongo-tertiary:27017", priority: 1},
{_id: 3, host: "mongo-arbiter:27017", arbiterOnly: true},
],
})
{ ok: 1 }
上記構築後、golang-migrate実行環境に入っておきます。
$ docker exec -it migrate-test-migrate-1 sh
/var/task #
使用方法
マイグレーションファイル作成
他のマイグレーションツール同様、up/downの2つ作成します。
ファイル名のフォーマットは
{VERSION}_{任意のファイル名}.{up|down}.json
としておけばいいようです。
公式リポジトリのサンプルに従い、ユーザを作成/削除するファイルを作成します。
versions
というディレクトリを作成し、そこに作成します。
[
{
"createUser": "app",
"pwd": "app",
"roles": [
{
"role": "readWrite",
"db": "sample"
}
]
}
]
[
{
"dropUser": "app"
}
]
マイグレーション
下記フォーマットのコマンドでマイグレーションが実行できます。
migrate -database "{DRIVER}://{USER}:{PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}" -path {DIR} {CMD} {VERSION}
- {CMD}
- 指定バージョンまでup|downしたい場合は
goto
を利用します。 - バージョン指定せずに全てのバージョンを対象にする場合は
up
もしくはdown
を利用します。
- 指定バージョンまでup|downしたい場合は
今回は全てのバージョンを対象とする以下のようなコマンドで実行します。
migrate -database "mongodb://root:root@mongo-primary:27017/sample?replicaSet=rs0&authSource=admin" -path ./versions up
- DRIVERは
mongodb
にします(Atlasの場合はmongodb+srv
でよさそう[1]) - レプリカセット(
rs0
)なのでreplicaSet=rs0
を追加します - レプリカセットに管理者ユーザで接続するので
authSource=admin
を追加します - マイグレーションファイルのバージョン全てを対象としたいので{VERSION}は指定しません
実行結果は以下のようになりました。
up
マイグレーション実行
$ migrate -database "mongodb://root:root@mongo-primary:27017/sample?replicaSet=rs0&authSource=admin" -path ./versions up
1/u create_users (229.5216ms)
確認
rs0 [direct: primary] sample> show users
[
{
_id: 'sample.app',
userId: UUID("564f59d4-9d7e-47a8-9bd3-7eaaea8a8ef7"),
user: 'app',
db: 'sample',
roles: [ { role: 'readWrite', db: 'sample' } ],
mechanisms: [ 'SCRAM-SHA-1', 'SCRAM-SHA-256' ]
}
]
down
マイグレーション実行
$ migrate -database "mongodb://root:root@mongo-primary:27017/sample?replicaSet=rs0&authSource=admin" -path ./versions down
Are you sure you want to apply all down migrations? [y/N]
y
Applying all down migrations
1/d create_users (160.9787ms)
確認
rs0 [direct: primary] sample> show users
[]
無事実行できました!
色々マイグレーションしてみる
次に以下のマイグレーションを試してみます。
- usersコレクションを作成、ageフィールドにインデックス作成
- usersコレクションにデータを登録
実行できる操作はdb.runCommand()で実行できる操作を参考にすればいいようです。
ファイル作成
コレクションとインデックス作成
[
{
"create": "users"
},
{
"createIndexes": "users",
"indexes": [
{
"key": {
"age": 1
},
"name": "index_age"
}
]
}
]
[
{
"drop": "users"
}
]
データの登録(実際の運用でデータを洗い替えするイメージなのでupの最初に削除を入れています)
[
{
"delete": "users",
"deletes": [{"q": {}, "limit": 0}]
},
{
"insert": "users",
"documents": [
{
"name": "Taro",
"age": 26
},
{
"name": "Kota",
"age": 32
}
]
}
]
[
{
"delete": "users",
"deletes": [{"q": {}, "limit": 0}]
}
]
up
実行
$ migrate -database "mongodb://root:root@mongo-primary:27017/sample?replicaSet=rs0&authSource=admin" -path ./versions up
1/u create_users (193.4746ms)
2/u create_collection_users (417.5746ms)
3/u insert_users (591.2593ms)
確認
rs0 [direct: primary] sample> db.users.getIndexes()
[
{ v: 2, key: { _id: 1 }, name: '_id_' },
{ v: 2, key: { age: 1 }, name: 'index_age' }
]
rs0 [direct: primary] sample> db.users.find()
[
{ _id: ObjectId("62873f73711e1795197ffbbf"), name: 'Taro', age: 26 },
{ _id: ObjectId("62873f73711e1795197ffbc0"), name: 'Kota', age: 32 }
]
down
実行
$ migrate -database "mongodb://root:root@mongo-primary:27017/sample?replicaSet=rs0&authSource=admin" -path ./versions down
Are you sure you want to apply all down migrations? [y/N]
y
Applying all down migrations
3/d insert_users (162.5102ms)
2/d create_collection_users (358.8213ms)
1/d create_users (538.2104ms)
確認
rs0 [direct: primary] sample> show collections
migrate_advisory_lock
schema_migrations
まとめ
シンプルに利用でき、db.runCommand()で実行できるコマンドは全て使えそうなので柔軟性もありそうです。
欲を言うとデータ登録のdocumentsを別ファイルの外出しとかできたらなとは思います。
トランザクション利用もできそうなので引き続き検証してみて、何か気づいたこと等あればまた追記・執筆します!
Discussion