はじめましてMongoDB #2 MongoDBを建ててみよう
はじめに
MongoDBテクニカルサポートの山森です。こちらの記事は、「はじめましてMongoDB」シリーズのうちの2つ目です。#1より少し間が空いてしまいましたね。
- はじめましてMongoDB #1 MongoDBに触れてみよう
- はじめましてMongoDB #2 MongoDBを建ててみよう(本記事)
- はじめましてMongoDB #3 MongoDBを使ってみよう
- より本番環境に近いPoC環境を作るためのアイデアやドキュメントを紹介します。
さっそくやっていきましょう。
MongoDB Server Community EditionでP1S2のレプリカセットを構築
プラットフォームから自前で用意するので、Atlasよりは難易度が上がります。
タイトルにある「P1S2」は、MongoDBの最も基本的な構成を表したものです。プライマリ1台、セカンダリ2台という意味です。このような構成を「レプリカセット」と呼びます。
3台には、同じデータが入ります。つまり冗長化のための構成です。プライマリに障害が発生してダウンすると、フェイルオーバーしてセカンダリがプライマリに成り代わります。詳しくは、MongoDBのReplicationのドキュメントも読んでみてください。
ハードウェアの選定とサイジング
MongoDBをインストールするには、器の大きさを見積もって用意しなくてはいけません。
今回はプラットフォームとしてESXiを使用し、サーバ3台をVirtual Machineとして構築します。もちろん、オンプレミスのサーバやAmazon Web ServiceのAmazon EC2、AzureのAzure Virtual Machines、Google CloudのCompute Engineを使用しても構いません。3台のサーバが、FQDNで互いの27017ポートにアクセス可能なネットワーク構成にしてください。
MongoDBは最低動作環境が提示されていません(※1)。本来ならばProduction Notesに従い、ユースケースに合わせてサイジングする必要があります。
しかし、この記事は気軽にMongoDBを触ってもらうのを目的としています。サイジングする段階で躓いてほしくないため、以下にサイジングの例を示します。サンプルデータを入れることができる最低限の構成としました。
OS | Debian GNU/Linux 12 | MongoDB公式で対応しているOSの1つです。RHELやUbuntuにも対応しています。操作に慣れていれば、お好きなもので構いません。 |
ファイルシステム | XFS | MongoDBでは強く推奨されています。 OSインストール時の設定が必要です。 |
インストールバージョン | MongoDB Community Edition 7.0(pkg) | 2024年時点のMongoDB Serverの最新版です。 |
CPU | 1 | - |
RAM | 4GB | - |
Disk Size | 16GB | - |
※1…なぜかMongoDB Cloud Manager(MongoDBサーバを管理・監視できるクラウドサービス)のドキュメントにMongoDBデプロイメントの要件が記載されていますが、Production Notesを参照してから設計・構築する方が無難です。
OSインストール時の考慮事項
OSインストールは基本的にデフォルトで進めていってもらって構いませんが、ファイルシステムはXFSを選択する必要があります。
Production Notes内の「Platform Specific Considerations」によると、Linux上で構築する場合のファイルシステムはXFSを強く推奨しています。今回使用したDebian GNU/Linuxの場合、OSインストールの段階でデフォルトのext4から変更する必要があるので注意してください。
Debian GNU/Linux では、ディスクのパーティショニングの設定で、ext4からXFSに変更できます。
以下の画面は、ガイドによるオススメパーティションを選んだ直後です。基本パーティションがext4になっていますね。
利用方法をクリックすると他のファイルシステムが選べるようになりました。XFSを選びます。
これでXFSになりました。
今回の構成ではGUIを入れると重くなるので、デスクトップ環境はインストール対象から外しておきましょう。MongoDBのインストール作業はCLIで完結します。
MongoDB Server Community Edition のインストール
色々な入れ方がありますが、今回はディストリビューションパッケージのMongoDBをインストールします。以下の手順に従って進めていきます。
Install MongoDB Community Edition on Debian
nika@mash:~$ sudo apt-get install gnupg curl -y … nika@mash:~$ curl -fsSL https://www.mongodb.org/static/pgp/server-7.0.asc | \ sudo gpg -o /usr/share/keyrings/mongodb-server-7.0.gpg \ --dearmor
nika@mash:~$ echo "deb [ signed-by=/usr/share/keyrings/mongodb-server-7.0.gpg ] http://repo.mongodb.org/apt/debian bookworm/mongodb-org/7.0 main" | sudo tee /etc/apt/sources.list.d/mongodb-org-7.0.list deb [ signed-by=/usr/share/keyrings/mongodb-server-7.0.gpg ] http://repo.mongodb.org/apt/debian bookworm/mongodb-org/7.0 main nika@mash:~$ sudo apt-get update ヒット:1 http://ftp.jaist.ac.jp/debian bookworm InRelease ヒット:2 http://ftp.jaist.ac.jp/debian bookworm-updates InRelease ヒット:3 http://security.debian.org/debian-security bookworm-security InRelease 無視:4 http://repo.mongodb.org/apt/debian bookworm/mongodb-org/7.0 InRelease 取得:5 http://repo.mongodb.org/apt/debian bookworm/mongodb-org/7.0 Release [1,991 B] 取得:6 http://repo.mongodb.org/apt/debian bookworm/mongodb-org/7.0 Release.gpg [866 B] 取得:7 http://repo.mongodb.org/apt/debian bookworm/mongodb-org/7.0/main amd64 Packages [23.6 kB] 26.4 kB を 2秒 で取得しました (15.9 kB/s) パッケージリストを読み込んでいます... 完了
以下のコマンドでインストールされます。
nika@mash:~$ sudo apt-get install -y mongodb-org インストールされたか確認 nika@mash:~$ dpkg -l | grep mongo ii mongodb-database-tools 100.9.4 amd64 mongodb-database-tools package provides tools for working with the MongoDB server: ii mongodb-mongosh 2.2.3 amd64 MongoDB Shell CLI REPL Package ii mongodb-org 7.0.8 amd64 MongoDB open source document-oriented database system (metapackage) ii mongodb-org-database 7.0.8 amd64 MongoDB open source document-oriented database system (metapackage) ii mongodb-org-database-tools-extra 7.0.8 amd64 Extra MongoDB database tools ii mongodb-org-mongos 7.0.8 amd64 MongoDB sharded cluster query router ii mongodb-org-server 7.0.8 amd64 MongoDB database server ii mongodb-org-shell 7.0.8 amd64 MongoDB shell client ii mongodb-org-tools 7.0.8 amd64 MongoDB tools
今回のように、とりあえず触ってみるための環境では特に問題ないのですが、公式の手順にしたがって「UNIX ulimit Settings — MongoDB Manual」の通りにulimitの設定を変更することをお勧めします。
本番利用時にここでボトルネックになることがあります。本番環境構築時に「こういう設定をいじる必要があったな」と覚えておくために、今サクッと作業してていきましょう。
現在のulimitの設定を確認します。
nika@mash:~$ ulimit -a real-time non-blocking time (microseconds, -R) unlimited core file size (blocks, -c) 0 data seg size (kbytes, -d) unlimited scheduling priority (-e) 0 file size (blocks, -f) unlimited pending signals (-i) 15483 max locked memory (kbytes, -l) 501180 max memory size (kbytes, -m) unlimited open files (-n) 1024 pipe size (512 bytes, -p) 8 POSIX message queues (bytes, -q) 819200 real-time priority (-r) 0 stack size (kbytes, -s) 8192 cpu time (seconds, -t) unlimited max user processes (-u) 15483 virtual memory (kbytes, -v) unlimited file locks (-x) unlimited
UNIX ulimit Settings — MongoDB Manual に従って変更していきます。
nika@mash:~$ su - root root@mash:~# ulimit -l unlimited root@mash:~# ulimit -n 64000 root@mash:~# ulimit -u 64000
最後にもう一度 ulimit -aを叩いて値が変わったことを確認しましょう。
root@mash:~# ulimit -a real-time non-blocking time (microseconds, -R) unlimited core file size (blocks, -c) 0 data seg size (kbytes, -d) unlimited scheduling priority (-e) 0 file size (blocks, -f) unlimited pending signals (-i) 15483 max locked memory (kbytes, -l) unlimited max memory size (kbytes, -m) unlimited open files (-n) 64000 pipe size (512 bytes, -p) 8 POSIX message queues (bytes, -q) 819200 real-time priority (-r) 0 stack size (kbytes, -s) 8192 cpu time (seconds, -t) unlimited max user processes (-u) 64000 virtual memory (kbytes, -v) unlimited file locks (-x) unlimited
MongoDB起動の準備ができたので、起動してみましょう。今回のようにパッケージでインストールした場合は、MongoDBのデータ保存領域やsystemctl用のserviceファイルが自動で用意されます。
nika@mash:~$ sudo systemctl restart mongod
起動しているか確認しましょう。Acitveの項目がactive(running)になっていれば正しく起動しています。これでインストールは完了です。
nika@mash:~$ sudo systemctl status mongod ● mongod.service - MongoDB Database Server Loaded: loaded (/lib/systemd/system/mongod.service; disabled; preset: enabled) Active: active (running) since Tue 2024-04-09 11:13:50 JST; 1s ago Docs: https://docs.mongodb.org/manual Main PID: 2371 (mongod) Memory: 172.7M CPU: 390ms CGroup: /system.slice/mongod.service mq2371 /usr/bin/mongod --config /etc/mongod.conf 4月 09 11:13:50 mash systemd[1]: Started mongod.service - MongoDB Database Server. 4月 09 11:13:50 mash mongod[2371]: {"t":{"$date":"2024-04-09T02:13:50.965Z"},"s":"I", "c":"CONTROL", "id":7484500>
セカンダリ2台も同じようにMongoDBのインストールと起動確認まで完了してください。
レプリカセットを構成する
レプリカセットを構築する前に、構成する3台のサーバが互いに27017ポートでFQDNで通信可能であることを事前に確かめておきます。今回用いる3台は以下の内容でDNSサーバに登録し、名前解決できるようにあります。
FQDN | IPアドレス |
gaia.mongo.lab | 192.168.50.64 |
ortega.mongo.lab | 192.168.50.72 |
mash.mongo.lab | 192.168.50.29 |
それでは、こちらの手順にそって進めていきましょう。3台のmongod.confに設定を追加します。以下はgaia.mongo.labの例ですが、他の2台でも同じです。
# mongod.conf # for documentation of all options, see: # http://docs.mongodb.org/manual/reference/configuration-options/ # Where and how to store data. storage: dbPath: /var/lib/mongodb # engine: # wiredTiger: # where to write logging data. systemLog: destination: file logAppend: true path: /var/log/mongodb/mongod.log # network interfaces net: port: 27017 bindIp: 0.0.0.0 # how the process runs processManagement: timeZoneInfo: /usr/share/zoneinfo #security: #operationProfiling: replication: replSetName: "blacktriplestar" #sharding: ## Enterprise-Only Options: #auditLog:
ここでいったん3台ともmongodを再起動します。
nika@mongodb-rep1:~$ systemctl status mongod ● mongod.service - MongoDB Database Server Loaded: loaded (/lib/systemd/system/mongod.service; disabled; vendor preset: enabled) Active: active (running) since Mon 2024-04-01 16:17:13 JST; 6 days ago Docs: https://docs.mongodb.org/manual Main PID: 2459 (mongod) Memory: 165.8M CPU: 30min 20.797s CGroup: /system.slice/mongod.service mq2459 /usr/bin/mongod --config /etc/mongod.conf nika@mongodb-rep1:~$ sudo systemctl restart mongod nika@mongodb-rep1:~$ systemctl status mongod ● mongod.service - MongoDB Database Server Loaded: loaded (/lib/systemd/system/mongod.service; disabled; vendor preset: enabled) Active: active (running) since Mon 2024-04-08 14:03:04 JST; 1s ago Docs: https://docs.mongodb.org/manual Main PID: 6355 (mongod) Memory: 180.0M CPU: 385ms CGroup: /system.slice/mongod.service mq6355 /usr/bin/mongod --config /etc/mongod.conf
3台のどのサーバでも良いので以下のコマンドでmongodに接続します。
補足:mongoshコマンドで接続先を指定しない場合は、ローカルホストのmongodに接続しに行きます。
mongosh
以下のように入力します。
rs.initiate( { _id : "blacktriplestar", members: [ { _id: 0, host: "mash.mongo.lab:27017" }, { _id: 1, host: "gaia.mongo.lab:27017" }, { _id: 2, host: "ortega.mongo.lab:27017" } ] })
問題なくレプリカセットが構成されれば、 ok:1と表示されます。id:0のサーバーがプライマリになります。
test> rs.initiate( { ... _id : "blacktriplestar", ... members: [ ... { _id: 0, host: "mash.mongo.lab:27017" }, ... { _id: 1, host: "gaia.mongo.lab:27017" }, ... { _id: 2, host: "ortega.mongo.lab:27017" } ... ] ... }) { ok: 1, '$clusterTime': { clusterTime: Timestamp({ t: 1712630566, i: 1 }), signature: { hash: Binary.createFromBase64('AAAAAAAAAAAAAAAAAAAAAAAAAAA=', 0), keyId: Long('0') } }, operationTime: Timestamp({ t: 1712630566, i: 1 }) }
構成を確認するには、rs.status()を実行します。
blacktriplestar [direct: primary] test> rs.status() { set: 'blacktriplestar', date: ISODate('2024-04-09T02:45:05.384Z'), myState: 1, term: Long('1'), syncSourceHost: '', syncSourceId: -1, heartbeatIntervalMillis: Long('2000'), majorityVoteCount: 2, writeMajorityCount: 2, votingMembersCount: 3, writableVotingMembersCount: 3, optimes: { lastCommittedOpTime: { ts: Timestamp({ t: 1712630697, i: 1 }), t: Long('1') }, lastCommittedWallTime: ISODate('2024-04-09T02:44:57.429Z'), readConcernMajorityOpTime: { ts: Timestamp({ t: 1712630697, i: 1 }), t: Long('1') }, appliedOpTime: { ts: Timestamp({ t: 1712630697, i: 1 }), t: Long('1') }, durableOpTime: { ts: Timestamp({ t: 1712630697, i: 1 }), t: Long('1') }, lastAppliedWallTime: ISODate('2024-04-09T02:44:57.429Z'), lastDurableWallTime: ISODate('2024-04-09T02:44:57.429Z') }, lastStableRecoveryTimestamp: Timestamp({ t: 1712630677, i: 1 }), electionCandidateMetrics: { lastElectionReason: 'electionTimeout', lastElectionDate: ISODate('2024-04-09T02:42:57.396Z'), electionTerm: Long('1'), lastCommittedOpTimeAtElection: { ts: Timestamp({ t: 1712630566, i: 1 }), t: Long('-1') }, lastSeenOpTimeAtElection: { ts: Timestamp({ t: 1712630566, i: 1 }), t: Long('-1') }, numVotesNeeded: 2, priorityAtElection: 1, electionTimeoutMillis: Long('10000'), numCatchUpOps: Long('0'), newTermStartDate: ISODate('2024-04-09T02:42:57.411Z'), wMajorityWriteAvailabilityDate: ISODate('2024-04-09T02:42:57.918Z') }, members: [ { _id: 0, name: 'mash.mongo.lab:27017', health: 1, state: 1, stateStr: 'PRIMARY', uptime: 1543, optime: { ts: Timestamp({ t: 1712630697, i: 1 }), t: Long('1') }, optimeDate: ISODate('2024-04-09T02:44:57.000Z'), lastAppliedWallTime: ISODate('2024-04-09T02:44:57.429Z'), lastDurableWallTime: ISODate('2024-04-09T02:44:57.429Z'), syncSourceHost: '', syncSourceId: -1, infoMessage: '', electionTime: Timestamp({ t: 1712630577, i: 1 }), electionDate: ISODate('2024-04-09T02:42:57.000Z'), configVersion: 1, configTerm: 1, self: true, lastHeartbeatMessage: '' }, { _id: 1, name: 'gaia.mongo.lab:27017', health: 1, state: 2, stateStr: 'SECONDARY', uptime: 138, optime: { ts: Timestamp({ t: 1712630697, i: 1 }), t: Long('1') }, optimeDurable: { ts: Timestamp({ t: 1712630697, i: 1 }), t: Long('1') }, optimeDate: ISODate('2024-04-09T02:44:57.000Z'), optimeDurableDate: ISODate('2024-04-09T02:44:57.000Z'), lastAppliedWallTime: ISODate('2024-04-09T02:44:57.429Z'), lastDurableWallTime: ISODate('2024-04-09T02:44:57.429Z'), lastHeartbeat: ISODate('2024-04-09T02:45:03.405Z'), lastHeartbeatRecv: ISODate('2024-04-09T02:45:04.405Z'), pingMs: Long('0'), lastHeartbeatMessage: '', syncSourceHost: 'mash.mongo.lab:27017', syncSourceId: 0, infoMessage: '', configVersion: 1, configTerm: 1 }, { _id: 2, name: 'ortega.mongo.lab:27017', health: 1, state: 2, stateStr: 'SECONDARY', uptime: 138, optime: { ts: Timestamp({ t: 1712630697, i: 1 }), t: Long('1') }, optimeDurable: { ts: Timestamp({ t: 1712630697, i: 1 }), t: Long('1') }, optimeDate: ISODate('2024-04-09T02:44:57.000Z'), optimeDurableDate: ISODate('2024-04-09T02:44:57.000Z'), lastAppliedWallTime: ISODate('2024-04-09T02:44:57.429Z'), lastDurableWallTime: ISODate('2024-04-09T02:44:57.429Z'), lastHeartbeat: ISODate('2024-04-09T02:45:03.408Z'), lastHeartbeatRecv: ISODate('2024-04-09T02:45:04.407Z'), pingMs: Long('0'), lastHeartbeatMessage: '', syncSourceHost: 'mash.mongo.lab:27017', syncSourceId: 0, infoMessage: '', configVersion: 1, configTerm: 1 } ], ok: 1, '$clusterTime': { clusterTime: Timestamp({ t: 1712630697, i: 1 }), signature: { hash: Binary.createFromBase64('AAAAAAAAAAAAAAAAAAAAAAAAAAA=', 0), keyId: Long('0') } }, operationTime: Timestamp({ t: 1712630697, i: 1 }) }
mashに接続している場合はプロンプトに[direct: primary]が表示され、今プライマリで作業していることが分かります。他の2台でmongoshするとどうなるのか見てみましょう。
nika@ortega:~$ mongosh Current Mongosh Log ID: 6614ac35210a30fd3cef634a Connecting to: mongodb://127.0.0.1:27017/?directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+2.2.3 Using MongoDB: 7.0.8 Using Mongosh: 2.2.3 For mongosh info see: https://docs.mongodb.com/mongodb-shell/ To help improve our products, anonymous usage data is collected and sent to MongoDB periodically (https://www.mongodb.com/legal/privacy-policy). You can opt-out by running the disableTelemetry() command. ------ The server generated these startup warnings when booting 2024-04-09T11:19:22.850+09:00: Access control is not enabled for the database. Read and write access to data and configuration is unrestricted 2024-04-09T11:19:22.850+09:00: /sys/kernel/mm/transparent_hugepage/enabled is 'always'. We suggest setting it to 'never' in this binary version 2024-04-09T11:19:22.850+09:00: vm.max_map_count is too low ------ blacktriplestar [direct: secondary] test>
nika@gaia:~$ mongosh Current Mongosh Log ID: 6614ac4c26a81c4571ef634a Connecting to: mongodb://127.0.0.1:27017/?directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+2.2.3 Using MongoDB: 7.0.8 Using Mongosh: 2.2.3 For mongosh info see: https://docs.mongodb.com/mongodb-shell/ To help improve our products, anonymous usage data is collected and sent to MongoDB periodically (https://www.mongodb.com/legal/privacy-policy). You can opt-out by running the disableTelemetry() command. ------ The server generated these startup warnings when booting 2024-04-09T11:19:22.845+09:00: Access control is not enabled for the database. Read and write access to data and configuration is unrestricted 2024-04-09T11:19:22.845+09:00: /sys/kernel/mm/transparent_hugepage/enabled is 'always'. We suggest setting it to 'never' in this binary version 2024-04-09T11:19:22.845+09:00: vm.max_map_count is too low ------ blacktriplestar [direct: secondary] test>
プロンプトに[direct: secondary]と表示されていることが分かります。これでレプリカセットを構築することができました!
レプリカセットにおける冗長性を観察してみよう
レプリカセットは冗長性と高可用性を向上させるためのものなので、3台には同じデータが入るはずです。つまり、プライマリにデータを書き込むと、それがセカンダリに反映されるということです。
実際にやってみましょう。
データ追加の前に、3台のサーバのいずれにも、データがないことを確認します。admin,config,localは管理用データベースなので最初から存在します。
blacktriplestar [direct: primary] test> db.serverStatus().host mash blacktriplestar [direct: primary] test> show dbs admin 80.00 KiB config 200.00 KiB local 460.00 KiB
blacktriplestar [direct: secondary] test> db.serverStatus().host ortega blacktriplestar [direct: secondary] test> show dbs admin 80.00 KiB config 200.00 KiB local 476.00 KiB
blacktriplestar [direct: secondary] test> db.serverStatus().host gaia blacktriplestar [direct: secondary] test> show dbs admin 80.00 KiB config 200.00 KiB local 476.00 KiB
プライマリにデータを追加してみましょう。mongoshで接続した状態で、MongoDBのクエリを使ってinsertしてみます。「use データベース名」は操作するデータベースをスイッチするコマンドですが、データベース名が存在しない場合は新しく作られます。
blacktriplestar [direct: primary] test> use mobilesuits switched to db mobilesuits blacktriplestar [direct: primary] mobilesuits> db.firstgundam.insertOne( { item: "RX-78", pilot: "Amuro Ray", boss:"bright"} ) { acknowledged: true, insertedId: ObjectId('6614ce9279a4a93d1def634c') }
その後、セカンダリ2台でもう一度データベース一覧を出してみます。mobilesuitsというデータベースが見えます。firstgundamはコレクション名です。
findOneを使って挿入されたデータが見えました。セカンダリにもしっかりコピーされていることが分かりました。
blacktriplestar [direct: secondary] test> show dbs admin 80.00 KiB config 244.00 KiB local 484.00 KiB mobilesuits 40.00 KiB blacktriplestar [direct: secondary] mobilesuits> show collections firstgundam blacktriplestar [direct: secondary] mobilesuits> db.firstgundam.findOne() { _id: ObjectId('6614ce8379a4a93d1def634b'), item: 'RX-78', pilot: 'Amuro Ray', boss: 'bright' }
レプリカセット内でデータをコピーする仕組みについては、過去のブログ「MongoDBにおけるレプリケーションの仕組み ~oplogってなに?~」で解説していますので、こちらも読んでみてください。
フェイルオーバーを発生させてみよう
レプリカセットを組んで得られるメリットの一つは、可用性の向上です。今回のようにP1S2の構成の場合、平常時はプライマリがアプリケーションからのデータの操作を受け付けるのですが、プライマリが障害によってダウンしてしまった場合はどうなるのでしょうか。
その時はセカンダリが選挙を行い、2台のうちのどちらかがプライマリに成り代わります。(フェイルオーバー)、実際にフェイルオーバーが起きる様子を見てみましょう。
それでは、プライマリであるmashを止めます。
nika@mash:~$ sudo systemctl stop mongod nika@mash:~$ sudo systemctl status mongod ○ mongod.service - MongoDB Database Server Loaded: loaded (/lib/systemd/system/mongod.service; disabled; preset: enabled) Active: inactive (dead) Docs: https://docs.mongodb.org/manual 4月 09 11:19:22 mash systemd[1]: Stopping mongod.service - MongoDB Database Server... 4月 09 11:19:22 mash systemd[1]: mongod.service: Deactivated successfully. 4月 09 11:19:22 mash systemd[1]: Stopped mongod.service - MongoDB Database Server. 4月 09 11:19:22 mash systemd[1]: mongod.service: Consumed 1.537s CPU time. 4月 09 11:19:22 mash systemd[1]: Started mongod.service - MongoDB Database Server. 4月 09 11:19:22 mash mongod[2476]: {"t":{"$date":"2024-04-09T02:19:22.478Z"},"s":"I", "c":"CONTROL", "id":7484500> 4月 09 14:30:51 mash systemd[1]: Stopping mongod.service - MongoDB Database Server... 4月 09 14:31:08 mash systemd[1]: mongod.service: Deactivated successfully. 4月 09 14:31:08 mash systemd[1]: Stopped mongod.service - MongoDB Database Server. 4月 09 14:31:08 mash systemd[1]: mongod.service: Consumed 1min 8.842s CPU time.
gaiaとortegaにmongoshしてみましょう。gaiaが新しいプライマリになっています!
blacktriplestar [direct: primary] test> db.serverStatus().host gaia
blacktriplestar [direct: secondary] test> db.serverStatus().host ortega
gaiaでrs.status()を見てみましょう。mashがnot reachableになり、gaiaがPRIMARYになっていることが分かります。
blacktriplestar [direct: primary] test> rs.status() { set: 'blacktriplestar', date: ISODate('2024-04-09T05:43:45.544Z'), myState: 1, term: Long('2'), syncSourceHost: '', syncSourceId: -1, heartbeatIntervalMillis: Long('2000'), majorityVoteCount: 2, writeMajorityCount: 2, votingMembersCount: 3, writableVotingMembersCount: 3, optimes: { lastCommittedOpTime: { ts: Timestamp({ t: 1712641421, i: 1 }), t: Long('2') }, lastCommittedWallTime: ISODate('2024-04-09T05:43:41.995Z'), readConcernMajorityOpTime: { ts: Timestamp({ t: 1712641421, i: 1 }), t: Long('2') }, appliedOpTime: { ts: Timestamp({ t: 1712641421, i: 1 }), t: Long('2') }, durableOpTime: { ts: Timestamp({ t: 1712641421, i: 1 }), t: Long('2') }, lastAppliedWallTime: ISODate('2024-04-09T05:43:41.995Z'), lastDurableWallTime: ISODate('2024-04-09T05:43:41.995Z') }, lastStableRecoveryTimestamp: Timestamp({ t: 1712641371, i: 1 }), electionCandidateMetrics: { lastElectionReason: 'stepUpRequestSkipDryRun', lastElectionDate: ISODate('2024-04-09T05:30:51.970Z'), electionTerm: Long('2'), lastCommittedOpTimeAtElection: { ts: Timestamp({ t: 1712640647, i: 1 }), t: Long('1') }, lastSeenOpTimeAtElection: { ts: Timestamp({ t: 1712640647, i: 1 }), t: Long('1') }, numVotesNeeded: 2, priorityAtElection: 1, electionTimeoutMillis: Long('10000'), priorPrimaryMemberId: 0, numCatchUpOps: Long('0'), newTermStartDate: ISODate('2024-04-09T05:30:51.977Z'), wMajorityWriteAvailabilityDate: ISODate('2024-04-09T05:30:51.983Z') }, electionParticipantMetrics: { votedForCandidate: true, electionTerm: Long('1'), lastVoteDate: ISODate('2024-04-09T02:42:57.396Z'), electionCandidateMemberId: 0, voteReason: '', lastAppliedOpTimeAtElection: { ts: Timestamp({ t: 1712630566, i: 1 }), t: Long('-1') }, maxAppliedOpTimeInSet: { ts: Timestamp({ t: 1712630566, i: 1 }), t: Long('-1') }, priorityAtElection: 1 }, members: [ { _id: 0, name: 'mash.mongo.lab:27017', health: 0, state: 8, stateStr: '(not reachable/healthy)', ←mashがnot reachableになってる uptime: 0, optime: { ts: Timestamp({ t: 0, i: 0 }), t: Long('-1') }, optimeDurable: { ts: Timestamp({ t: 0, i: 0 }), t: Long('-1') }, optimeDate: ISODate('1970-01-01T00:00:00.000Z'), optimeDurableDate: ISODate('1970-01-01T00:00:00.000Z'), lastAppliedWallTime: ISODate('2024-04-09T05:31:01.981Z'), lastDurableWallTime: ISODate('2024-04-09T05:31:01.981Z'), lastHeartbeat: ISODate('2024-04-09T05:43:44.108Z'), lastHeartbeatRecv: ISODate('2024-04-09T05:31:06.479Z'), pingMs: Long('0'), lastHeartbeatMessage: 'Error connecting to mash.mongo.lab:27017 (192.168.50.29:27017) :: caused by :: onInvoke :: caused by :: Connection refused', syncSourceHost: '', syncSourceId: -1, infoMessage: '', configVersion: 1, configTerm: 2 }, { _id: 1, name: 'gaia.mongo.lab:27017', health: 1, state: 1, stateStr: 'PRIMARY', ←gaiaがPRIMARYになっている uptime: 12263, optime: { ts: Timestamp({ t: 1712641421, i: 1 }), t: Long('2') }, optimeDate: ISODate('2024-04-09T05:43:41.000Z'), lastAppliedWallTime: ISODate('2024-04-09T05:43:41.995Z'), lastDurableWallTime: ISODate('2024-04-09T05:43:41.995Z'), syncSourceHost: '', syncSourceId: -1, infoMessage: '', electionTime: Timestamp({ t: 1712640651, i: 1 }), electionDate: ISODate('2024-04-09T05:30:51.000Z'), configVersion: 1, configTerm: 2, self: true, lastHeartbeatMessage: '' }, { _id: 2, name: 'ortega.mongo.lab:27017', health: 1, state: 2, stateStr: 'SECONDARY', uptime: 10858, optime: { ts: Timestamp({ t: 1712641421, i: 1 }), t: Long('2') }, optimeDurable: { ts: Timestamp({ t: 1712641421, i: 1 }), t: Long('2') }, optimeDate: ISODate('2024-04-09T05:43:41.000Z'), optimeDurableDate: ISODate('2024-04-09T05:43:41.000Z'), lastAppliedWallTime: ISODate('2024-04-09T05:43:41.995Z'), lastDurableWallTime: ISODate('2024-04-09T05:43:41.995Z'), lastHeartbeat: ISODate('2024-04-09T05:43:43.985Z'), lastHeartbeatRecv: ISODate('2024-04-09T05:43:44.490Z'), pingMs: Long('0'), lastHeartbeatMessage: '', syncSourceHost: 'gaia.mongo.lab:27017', syncSourceId: 1, infoMessage: '', configVersion: 1, configTerm: 2 } ], ok: 1, '$clusterTime': { clusterTime: Timestamp({ t: 1712641421, i: 1 }), signature: { hash: Binary.createFromBase64('AAAAAAAAAAAAAAAAAAAAAAAAAAA=', 0), keyId: Long('0') } }, operationTime: Timestamp({ t: 1712641421, i: 1 }) }
ここでmashを復活させてみましょう。どうなるのでしょうか。再びプライマリの座に返り咲くのでしょうか。mashに接続してrs.status()を実行してみましょう。
(情報が多くて見にくくなるので、rs.status().membersでレプリカセットのメンバの情報だけ見えるようにしています。)
blacktriplestar [direct: secondary] test> rs.status().members [ { _id: 0, name: 'mash.mongo.lab:27017', health: 1, state: 2, stateStr: 'SECONDARY', uptime: 132, optime: { ts: Timestamp({ t: 1712641702, i: 1 }), t: Long('2') }, optimeDate: ISODate('2024-04-09T05:48:22.000Z'), lastAppliedWallTime: ISODate('2024-04-09T05:48:22.001Z'), lastDurableWallTime: ISODate('2024-04-09T05:48:22.001Z'), syncSourceHost: 'ortega.mongo.lab:27017', syncSourceId: 2, infoMessage: '', configVersion: 1, configTerm: 2, self: true, lastHeartbeatMessage: '' }, { _id: 1, name: 'gaia.mongo.lab:27017', health: 1, state: 1, stateStr: 'PRIMARY', uptime: 131, optime: { ts: Timestamp({ t: 1712641702, i: 1 }), t: Long('2') }, optimeDurable: { ts: Timestamp({ t: 1712641702, i: 1 }), t: Long('2') }, optimeDate: ISODate('2024-04-09T05:48:22.000Z'), optimeDurableDate: ISODate('2024-04-09T05:48:22.000Z'), lastAppliedWallTime: ISODate('2024-04-09T05:48:22.001Z'), lastDurableWallTime: ISODate('2024-04-09T05:48:22.001Z'), lastHeartbeat: ISODate('2024-04-09T05:48:22.651Z'), lastHeartbeatRecv: ISODate('2024-04-09T05:48:22.126Z'), pingMs: Long('0'), lastHeartbeatMessage: '', syncSourceHost: '', syncSourceId: -1, infoMessage: '', electionTime: Timestamp({ t: 1712640651, i: 1 }), electionDate: ISODate('2024-04-09T05:30:51.000Z'), configVersion: 1, configTerm: 2 }, { _id: 2, name: 'ortega.mongo.lab:27017', health: 1, state: 2, stateStr: 'SECONDARY', uptime: 131, optime: { ts: Timestamp({ t: 1712641702, i: 1 }), t: Long('2') }, optimeDurable: { ts: Timestamp({ t: 1712641702, i: 1 }), t: Long('2') }, optimeDate: ISODate('2024-04-09T05:48:22.000Z'), optimeDurableDate: ISODate('2024-04-09T05:48:22.000Z'), lastAppliedWallTime: ISODate('2024-04-09T05:48:22.001Z'), lastDurableWallTime: ISODate('2024-04-09T05:48:22.001Z'), lastHeartbeat: ISODate('2024-04-09T05:48:22.652Z'), lastHeartbeatRecv: ISODate('2024-04-09T05:48:22.081Z'), pingMs: Long('0'), lastHeartbeatMessage: '', syncSourceHost: 'gaia.mongo.lab:27017', syncSourceId: 1, infoMessage: '', configVersion: 1, configTerm: 2 } ]
なんとgaiaはプライマリのままです。mashはセカンダリとして復旧しました。このことから、MongoDBのレプリカセットのメンバー内に正副の関係はなく、平等であることが分かります。
世の中にはシステムで冗長構成を組むときに、副系統のスペックを抑えめにする構成もありますが、MongoDBの場合は(セカンダリがプライマリになってもそのまま運用できるように)メンバーをすべて同スペックにしたほうが良いでしょう。
おわりに
お疲れ様でした。次回は「はじめましてMongoDB #3 MongoDBを使ってみよう」でお会いしましょう。