Dockerコンテナ上でgolang-migrateを使ってMongoDBのマイグレーション
はじめに
MongoDB のマイグレーション、どうするのが良いかなーと思って調査してみました。
検索してると、Go で書かれたgolang-migrate/migrateがシンプルで良さそうだったので、試すことにしました。
気に入ったポイントは、ここらです。
- 対応してる DB が多い。(PostgreSQL にも MongoDB に対応してた)
- 機能がシンプルそう。
- 情報と Star が多い。(2022/10/17 時点で 9.9k)
- Go で書かれてる(中身を読みたい訳ではないけども、気に入ってる言語で書かれてる方が気分が上がる。)
PostgreSQL と MongoDB で試してみたんですが、MongoDB の情報があまり見つからなかったので、MongoDB 中心に記載します。
特にdocker-compose.yml
とdocker run
を使って Docker コンテナ上で動作確認する情報が見当たらなかったので、そこら辺を中心に記載します。
golang-migrate をコンテナでやりたいんだ!って人の参考になればと思います。
ちなみに調査の初めは、開発に使ってる言語に対応してるツールじゃないといけないかなーと思ってたけど、ORM (Object-Relational Mapping) 使わなかったら、そんな制約ないのかと気づいて、golang-migrate 試すことにしました。
以下では、MongoDB での環境構築・ローカルでの動作確認、コンテナでの動作確認、最後に PostgreSQL での動作確認の順で説明します。
コードはoptimisuke/hello-golang-migrateにおいてます。
MongoDB
環境構築
まず、docker compose を使って、MongoDB と PostgreSQL のコンテナを作ります。
ローカルからアクセスできるように、ポートの設定もしてます。
後半で、公式のコンテナ(migrate/migrate)を立ち上げてマイグレーションする方法も書いてますが、その場合、ポートの設定は不要です。
使ってるイメージは、MongDB の公式のやつを使ってます。
version: "3"
services:
mongo:
image: mongo
ports:
- 27017:27017
networks:
- network
volumes:
- mongo_db_data:/data/db
- ./initdb.d:/docker-entrypoint-initdb.d/
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: password
postgres:
image: postgres:14.1-alpine
ports:
- 5432:5432
networks:
- network
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
POSTGRES_USER: admin
POSTGRES_PASSWORD: admin-password
LANG: ja_JP.utf8
TZ: Asia/Tokyo
volumes:
postgres_data:
mongo_db_data:
networks:
network:
加えて、事前準備として、認証の設定するために、少し真面目にユーザーを設定しています。
下記ファイルをマウントすることで、コンテナ立ち上げ時にユーザーを作成してくれます。
db = db.getSiblingDB("admin");
db.auth("root", "password");
db.createUser({
user: "testuser",
pwd: "password",
roles: [
{
role: "readWrite",
db: "hoge",
},
],
});
これで、動くはずですが、MongoDB のこともうちょい知りたい人は、以前書いた記事(はじめての MongoDB with Docker)を読んでもらえると嬉しいです。
加えて、動作確認のためにローカルでmongosh
を使ったので、下記を参照して MongoDB をインストールしました。
Install MongoDB Community Edition on macOS — MongoDB Manual
また、golang-migrate/migrate
は MacOS を使ってるので、下記コマンドで入れました。
brew install golang-migrate
コンテナのみ使うのであれば、上記不要ですが、いきなりコンテナで確認するには不安があったので入れて確認しました。
他の OS の場合は、下記ドキュメント参照です。
migrate/cmd/migrate at master · golang-migrate/migrate
ローカルでの動作確認
PostgreSQL はチュートリアル(migrate/TUTORIAL.md)がありますが、MongoDB はありません。(README (migrate/database/mongodb)のみ。PR しよかな。)
なので、色々調べながら、試してみました。
基本的には、PostgreSQL と同じなのですが、権限まわりでつまづいてました。
まず、下記コマンドでマイグレーション用の up と down のファイルを作ります。
migrate create -ext json -dir mongo/migrations -seq create_hoge
その後、中身を記載します。
ここらの記事を参考にしました。
- migrate/database/mongodb/examples/migrations
- golang-migrate で MongoDB のマイグレーション
- golang-migrate で MongoDB マイグレーションやってみた)
以下に、コレクション&ドキュメントの追加とコレクションの削除するファイルを記載します。
[
{
"insert": "hoge",
"documents": [
{
"column1": "test1",
"column2": "test2"
}
]
}
]
[
{
"drop": "hoge"
}
]
上記参照先に、他にもインデックスを追加したりする方法が書かれてます。
次に、以下のコマンドでマイグレーションを実施します。データベースへの接続情報とファイルのパスとサブコマンドの up/down を書いてます。
migrate -database "mongodb://testuser:password@localhost:27017/hoge?authSource=admin" -path ./mongo/migrations up
migrate -database "mongodb://testuser:password@localhost:27017/hoge?authSource=admin" -path ./mongo/migrations down
コネクションの URI は下記参照です。上記コマンドでは、ユーザー情報のあるデータベースを指定するauthSource
しか設定してませんが、他にも色々あるみたいです。ユーザーは、initdb.d/init.js
に書いたように、testuser
を指定しています。
Connection String URI Format — MongoDB Manual
コンテナでの動作確認
公式のコンテナ(migrate/migrate)を使ってマイグレーションする方法も記載します。
上記イメージは、migrate/Dockerfileで作ったもののようです。
インストールするだけでなくENTRYPOINT
でmigrate
コマンドを実行してるところが特徴的かなと思います。
コンテナを使って、コマンドを実行する方法は、公式の README.md のここにも書いてました。
ちなみに、最初 docker-compose.yml
でコンテナを立ち上げっぱなしにして、マイグレーションしたい時にコマンド実行する感じにしようかなと思いましたが、ENTRYPOINT
にmigrate
が設定されているので、コマンド実行したら止める系なのかなと思い、やめました。
今回は、docker run
でマイグレーションを実行してみましたが、下記サイトにあるようにdocker-compose.yml
に書いて、docker compose up
したときに Migration するようにしておくのもありだと思います。
database - How to run golang-migrate with docker-compose? - Stack Overflow
docker run
を使ったマイグレーションコマンドはこんな感じになります。
事前にネットワークの設定をdocker network ls
で確認しておく必要があります。
docker run -v $(pwd)/mongo/migrations:/migrations --network=hello-golang-migrate_network migrate/migrate -path=/migrations/ -database "mongodb://testuser:password@mongo:27017/hoge?authSource=admin" up
以下のように、Dockerfile
のENTRYPOINT
で、migrate
コマンドが指定してあるので、それが実行されオプションの--help
部分を上書きしてる感じです。
ENTRYPOINT ["migrate"]
CMD ["--help"]
ついでに、Makefile はこんな感じかなと思います。もうちょい、変数を使った方が良いと思いますが。
up:
docker run -v ${PWD}/mongo/migrations:/migrations --network=hello-golang-migrate_network migrate/migrate -path=/migrations/ -database "mongodb://testuser:password@mongo:27017/hoge?authSource=admin" up
down:
docker run -v ${PWD}/mongo/migrations:/migrations --network=hello-golang-migrate_network migrate/migrate -path=/migrations/ -database "mongodb://testuser:password@mongo:27017/hoge?authSource=admin" down -all
最後の-all
は、途中でインタラクティブに聞いてくる質問を全て yes で進めるためのオプションです。
Add an option to assume "yes" to all prompts · Issue #370 · golang-migrate/migrate
PostgreSQL
PostgreSQL の場合は、チュートリアルが割としっかり書かれてるので、下記ドキュメントを参照するのが良いと思いますが、参考までに記載します。
migrate/TUTORIAL.md at master · golang-migrate/migrate
こちらも、シンプルに書かれてて参考になりました。
golang-migrate での PostgreSQL マイグレーション備忘録
最初に MongoDB と同じように、下記コマンドでファイルを作成します。
migrate create -ext sql -dir postgres/migrations -seq create_bookmarks_table
そのあと、下記コマンドでマイグレーションを実施します。
export POSTGRESQL_URL=postgres://admin:admin-password@localhost:5432/admin?sslmode=disable
migrate -database ${POSTGRESQL_URL} -path postgres/migrations up
docker run
使う場合は MongoDB と同じように下記コマンドで実行できます。
docker run -v $(pwd)/postgres/migrations:/migrations --network=hello-golang-migrate_network migrate/migrate -path=/migrations/ -database "postgres://admin:admin-password@postgres:5432/admin?sslmode=disable" up
docker run -v $(pwd)/postgres/migrations:/migrations --network=hello-golang-migrate_network migrate/migrate -path=/migrations/ -database "postgres://admin:admin-password@postgres:5432/admin?sslmode=disable" down -all
おわりに
MongoDB 使うとき、マイグレーションツールって本当に必要なのか?問題もあるのかなと思います。マイグレーション使わなくてもコレクションを生成できたり、JavaScript で操作できたりするので。
ただ、インデックスとかちょっとしたデータを入れたりを宣言型っぽく管理したいユースケースもあるのかなと思って試してみました。(リレーショナルデータベースの考え方に縛られてるだけなのかもしれませんが。)
使い勝手は、シンプルでとても良い感じでした。
ORM を使ってなくて、RDB と NoSQL のマイグレーションを統一したい等の要件があれば、golang-migrate/migrateは最適な気がします。
実際使う時には、以下の項目をもう少しケアする必要がありそうなので、そのうちまた試してみたいなと思います。
- 設定ファイルとか Makefile にがっつりパスワードを書いちゃってるので、環境変数から取得する
- Kubernetes とかで運用するときに、Migration 用のコンテナを立てる
- Migration を CICD に組み込む
Discussion