Mongo がトランザクションをサポートするための最初の条件は、Mongo のバージョンが 4.x 以上であり、レプリカセットモードであることです。多くの場合、Mongo を使用する際に複製を展開する必要はありませんが、トランザクションをサポートしたい場合は「単一複製モード」を使用できます。これにより、Mongo インスタンスが 1 つだけであることが保証され、かつレプリカセットモードになります。この記事では、Mongo5.0.8 を例として使用します。
この記事は、日常的に遭遇する問題の簡単なメモです。誤りがあれば指摘してください。
まず docker-compose.yml を示します#
version: '3.0'
services:
mongo:
image: mongo:5.0.8
restart: unless-stopped
container_name: mongodb
command: --replSet rs0 --bind_ip_all --keyFile /data/mongodb/keyFile
environment:
TZ: 'Asia/Shanghai'
#ユーザー名とパスワード
MONGO_INITDB_ROOT_USERNAME: 'admin'
MONGO_INITDB_ROOT_PASSWORD: 'password'
ports:
- 27017:27017
volumes:
- ./mongodb/data:/data/db
- ./mongodb/keyFile:/data/mongodb/keyFile
keyFile の準備#
私の粗いテストでは、4.x バージョンでは keyFile を使用する必要はありませんが、5.x バージョンでは KeyFile が必須です。そうしないと、「BadValue: security.keyFile is required when authorization is enabled with replica sets」と表示されます。
keyFile の生成#
openssl rand -base64 128 > ./mongodb/keyFile
ここで、./mongodb/keyFile は生成するファイルの名前とフォルダーを指定します。
権限と所有ユーザーの設定#
keyFile ファイルの権限は 600 でなければなりません。権限が大きすぎると、起動時に「error opening file: /data/mongodb/keyFile: bad file」と表示されます。
権限を 600 に変更した後、keyFile ファイルの所有ユーザーとユーザーグループを mongodb に変更する必要があります。そうしないと、起動時に「permissions on /data/mongodb/keyFile are too open」と表示されます。
コンテナを使用して起動するため、keyFile ファイルの所有ユーザーとユーザーグループを 999 に変更する必要があります。これにより、コンテナは自動的に keyFile ファイルの所有ユーザーとユーザーグループを mongodb に変更します。
sudo chmod 600 keyFile
sudo chown 999:999 keyFile
コンテナ外
コンテナ内
keyFile ファイルの準備が整ったら、上記の docker-compose.yml に従ってコンテナを起動できます。
初期化#
コンテナが起動した後、Mongo はまだ使用できません。コンテナ内に入ってレプリカセットを初期化する必要があります。
コンテナに入る
docker exec -it mongodb /bin/bash
先ほど設定したユーザー名とパスワードで Mongo に入ります。
mongo -u admin --authenticationDatabase admin
初期化を実行します。
rs.initiate()
表示されれば成功です。
これで Mongo は起動完了です。
Spring Boot プロジェクトの設定#
トランザクションを実現するには、プロジェクト内で設定を行う必要があります。
Spring Boot バージョン:2.1.6.RELEASE
@Configuration
@Slf4j
public class MongoTransactionConfig {
@Bean
MongoTransactionManager transactionManager(MongoDbFactory factory){
log.warn("Mongoトランザクションを有効にします");
return new MongoTransactionManager(factory);
}
}
Spring Boot の高バージョンでは MongoDbFactory が廃止され、MongoDatabaseFactory に変更する必要があります。
Spring Boot バージョン:2.6.1
@Configuration
@Slf4j
public class MongoTransactionConfig {
@Bean
MongoTransactionManager transactionManager(MongoDatabaseFactory factory){
log.warn("Mongoトランザクションを有効にします");
return new MongoTransactionManager(factory);
}
}
設定クラスを追加した後、メソッドに @Transactional アノテーションを追加するだけで Mongo トランザクションを実現できます。
!!!新規追加!!!
多くの場合、マウントされたデータを保持し、新しいコンテナを再構築します。Docker は新しいコンテナを構築するたびにランダムにホスト名を割り当てます。しかし、前のコンテナからマウントされたデータを使用するため、Mongo のレプリカセット設定のホスト名が前のコンテナのままになり、新しく起動した Mongo が使用できなくなります。したがって、Mongo のレプリカセット設定のホスト名を変更するだけでこの問題を解決できます。
Mongo を起動した後、直接接続するとこのエラーが表示されます。
まず、コンテナに入り、現在のコンテナのホスト名を確認します。
上記の方法を使用して、admin アカウントで Mongo にログインし、config を確認します。
rs.config()
上記のように、config 内のホスト名が現在のコンテナのものとは異なります。
方法 1:ホスト名を変更する#
- レプリカセットの設定を取得します。
config=rs.conf()
- 上記の設定 JSON の形式に基づいて、ホスト名の位置を特定し、再割り当てします(host を現在のコンテナの host に変更します)。
config.members[0].host="c3e9261f8a04:27017"
- 最後に config を更新します。
rs.reconfig(config, {force : true})
変更後、Mongo は正常に起動します。
方法 2:yaml でホスト名を指定する#
version: '3.0'
services:
mongo:
hostname: mongo501
image: mongo:5.0.8
restart: unless-stopped
container_name: mongodb
command: --replSet rs0 --bind_ip_all --keyFile /data/mongodb/keyFile
environment:
TZ: 'Asia/Shanghai'
#ユーザー名とパスワード
MONGO_INITDB_ROOT_USERNAME: 'admin'
MONGO_INITDB_ROOT_PASSWORD: 'password'
ports:
- 27017:27017
volumes:
- ./mongodb/data:/data/db
- ./mongodb/keyFile:/data/mongodb/keyFile
これにより、新しく作成されたコンテナのホスト名が常に同じになります。