fbpx

はじめまして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を触ってもらうのを目的としています。サイジングする段階で躓いてほしくないため、以下にサイジングの例を示します。サンプルデータを入れることができる最低限の構成としました。

OSDebian
GNU/Linux 12
MongoDB公式で対応しているOSの1つです。RHELやUbuntuにも対応しています。操作に慣れていれば、お好きなもので構いません。
ファイルシステムXFSMongoDBでは強く推奨されています。
OSインストール時の設定が必要です。
インストールバージョンMongoDB Community Edition 7.0(pkg)2024年時点のMongoDB Serverの最新版です。
CPU1-
RAM4GB-
Disk Size16GB-

※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サーバに登録し、名前解決できるようにあります。

FQDNIPアドレス
gaia.mongo.lab192.168.50.64
ortega.mongo.lab192.168.50.72
mash.mongo.lab192.168.50.29
DNSサーバが用意できない場合は、各サーバの/etc/hostsに書き込んで名前解決できる状態にしれおけば大丈夫です。

それでは、こちらの手順にそって進めていきましょう。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を使ってみよう」でお会いしましょう。

Author

MongoDB日本語サポート担当。ITインフラや運用・監視・保守が好きです。
無駄のない構成やアーキテクチャを見てうっとりしています。

k-yamamoriの記事一覧

新規CTA