MongoDBをgolang-migrate/migrateでマイグレーションしてみる

golang-migrate/migrateを使ってMongoDBをマイグレーションする試みです。
Dockerでローカル環境作るところも含めて説明します。

ページの最後に、全体のコードをアップロードしたGitHubのリンクを載せておきます。

背景

MongoDBのマイグレーションライブラリは私が調べたところ、2つしかありませんでした。

xakep666/mongo-migrateはREADME.mdをすぐ使い方が分かりました。
しかし、golang-migrate/migrateは分かりにくかったと感じました。
そのため、本記事を作成した次第です。

どちらを使うか迷っている方の参考になればと思っています。

バージョン

利用したバージョンは以下です。

環境バージョン
MongoDB7.0.5
golang-migrate/migrate4.17.0

レプリカセットのキーを作成

レプリカ間で安全に情報をやりとりするために暗号化キーを用意します。
以下のコマンドを実行してください。

$ openssl rand -base64 756 > replicaset.key
$ chmod 600 replicaset.key
$ chown 999 replicaset.key

docker-compose.ymlを用意

今回は以下のようなdocker-compose.ymlファイルを用意しました。
最新のMongoDBのバージョンは公式サイトから確認できます。

version: "3.9"

x-commons: &commons
  environment:
    MONGO_INITDB_ROOT_USERNAME: root
    MONGO_INITDB_ROOT_PASSWORD: root
    MONGO_INITDB_DATABASE: testdb
  image: mongo:7.0.5
  command: mongod --replSet rs0 --keyFile /etc/replicaset.key

services:
  mongodb-primary:
    container_name: go_migrate_mongo_primary
    <<: *commons
    ports:
      - "27017:27017"
    volumes:
      - ./replicaset.key:/etc/replicaset.key
      - go_migrate_mongo_primary_data:/data/db

  mongo-secondary:
    container_name: go_migrate_mongo_secondary
    <<: *commons
    ports:
      - "27018:27017"
    volumes:
      - ./replicaset.key:/etc/replicaset.key
      - go_migrate_mongo_secondary_data:/data/db

  mongo-tertiary:
    container_name: go_migrate_mongo_tertiary
    <<: *commons
    ports:
      - "27019:27017"
    volumes:
      - ./replicaset.key:/etc/replicaset.key
      - go_migrate_mongo_tertiary_data:/data/db

  mongo-arbiter:
    container_name: go_migrate_mongo_arbiter
    <<: *commons
    ports:
      - "27020:27017"
    volumes:
      - ./replicaset.key:/etc/replicaset.key
      - go_migrate_mongo_arbiter_data:/data/db

  ## Docker上でhttps://github.com/golang-migrate/migrateを実行するためのコンテナ
  ## https://hub.docker.com/r/migrate/migrate
  migrate:
    container_name: migrate
    image: migrate/migrate:latest
    entrypoint: sh
    tty: true
    volumes:
      - .:/var/task
    working_dir: /var/task
    command: ""

volumes:
  go_migrate_mongo_primary_data:
  go_migrate_mongo_secondary_data:
  go_migrate_mongo_tertiary_data:
  go_migrate_mongo_arbiter_data:

docker-compose up -dで実行します。

イニシャライズ

MongoDBはデータベースを立ち上げた後、手動で初期化する必要があります。
まずは、primarymongoshを開いてください。

docker exec -it go_migrate_mongo_primary bash
mongosh -u root -p root

次に、レプリカセットの初期化を実行します。

rs.initiate({
  _id: "rs0",
  members: [
    { _id: 0, host: "go_migrate_mongo_primary:27017", priority: 3 },
    { _id: 1, host: "go_migrate_mongo_secondary:27017", priority: 2 },
    { _id: 2, host: "go_migrate_mongo_tertiary:27017", priority: 1 },
    { _id: 3, host: "go_migrate_mongo_arbiter:27017", arbiterOnly: true },
  ],
})

rs.status()rs.conf()を使って、初期化がうまく行ったか確認することができます。

マイグレーションファイルを用意

公式ライブラリがサンプルのマイグレーションファイルを用意してくれています。
これを拝借しましょう。

バージョンダウン用のdown.jsonとバージョンアップ用のup.jsonが必要です。
migrations/001_create_user.down.jsonを作成してください。

[
  {
    "dropUser": "deminem"
  }
]

migrations/001_create_user.up.jsonを作成してください。

[
  {
    "createUser": "deminem",
    "pwd": "gogo",
    "roles": [
      {
        "role": "readWrite",
        "db": "testMigration"
      }
    ]
  }
]

ちなみに、migrate create -ext json -dir ./migrations -seq {ファイル名}を使うことでテンプレートファイルを作成することができます。

マイグレーション

バージョンをアップさせるときは以下のコマンドを実行してください。

migrate -path ./migrations -database "mongodb://root:root@localhost:27017/testdb?authSource=admin&directConnection=true" up 1

バージョンダウンするときは以下です。

migrate -path ./migrations -database "mongodb://root:root@localhost:27017/testdb?authSource=admin&directConnection=true" down 1

ときどきdirty状態になることがあります。その際は以下のコマンドを実行しましょう。

migrate -path ./migrations -database "mongodb://localhost:27017/testdb?directConnection=true" force 1

注意点

migrate/database/mongodb/README.mdによれば、以下のように書かれています。

mongodb://user:password@host:port/dbname?query (mongodb+srv:// also works, but behaves a bit differently. See docs for more information)

言い換えると、MongoDB Atlasで利用すると挙動が少し変化するため注意が必要のようです。

全体のコード

全体のコードはこちらです。

GitHub – taako-502/go-migrate-sample: …

GitHub – taako-502/go-migrate-sample: …

MongoDB migrations using golang-migrate/migrate library – taako-502/go-migrate-sample

MongoDB migrations using golang-migrate/migrate library – taako-502/go-migrate-sample

まとめ

個人的にはあまりおすすめできないと思いました。
xakep666/mongo-migrateのほうが良さそうです。

  • MongoDBのバージョン4.2までしかテストされていない
  • MongoDBに限定すると最終コミットが2年前
  • go.mongodb.org/mongo-driveのバージョンが古い
  • 時々Dirtyモードになる
  • Atlasの挙動がローカルと異なる
  • MongoDB以外のデータベースに対応している分、コード量が多く影響範囲がわかりにくい

一方、xakep666/mongo-migrateも似ていて、あまりメンテがされていないのですが、こちらはコード量が非常に少なくシンプルであるため、その気になれば自分でフォークしてメンテできるというのが利点だと思いました。

また、xakep666/mongo-migrateはMongoDBに特化しているので、無駄なコードもなく読みやすいです。

参考

golang-migrateでMongoDBマイグレーションやってみた

golang-migrateでMongoDBマイグレーションやってみた