MongoDBは、JSONライクなドキュメントを使用する動的スキーマのNoSQLデータベースです。一般に、データベースを運用する際には、データベースサーバーの1つが故障したときの対応策を常に用意しておくのがベストプラクティス。補足ですが、WordPressサイトで優れた管理ツールを活用することで、このような事態が発生する可能性を軽減することができます。

したがって、データベースでは、データのコピーを複数確保しておくのが賢明です。これによって、読み込みのレイテンシが削減されると同時に、スケーラビリティや可用性も向上します。ここで登場するのが、レプリケーション。レプリケーションとは、複数のデータベース間でデータを同期することです。

MongoDBのレプリケーション

MongoDBでは、レプリカセットがレプリケーションを実行します。レプリカセットとは、レプリケーションを介して同じデータセットを維持するサーバー群です。またレプリケーションは、ロードバランシングの一部としても使用することができます。書き込みと読み込みの操作は、ユースケースに応じてすべてのインスタンスに分散されます。

MongoDBレプリカセットとは

レプリカセットに含まれるすべてのMongoDBインスタンスは、レプリカセットのメンバーになります。レプリカセットにはプライマリメンバーと、最低1つのセカンダリメンバーが必要です。

プライマリメンバーは、レプリカセットとのトランザクションにおけるメインのアクセスポイントであり、書き込み操作を受け付ける唯一の存在です。レプリケーションはまず、プライマリのoplog(操作ログ)をコピーします。次に、セカンダリのそれぞれのデータセット上で、ログに記録された変更を繰り返します。したがって、どのレプリカセットにも、一度に1つのプライマリメンバーしか存在しません。様々なプライマリが書き込み操作を受け付ければ、データの競合が発生する可能性があります。

通常、アプリケーションは、プライマリメンバーに対してのみ書き込みと読み込みを行います。1つまたは複数のセカンダリメンバーから読み込むように構成を設計できますが、非同期のデータ転送では、セカンダリノードで古いデータが読み取られます。したがってこのような配置が理想的でない場合もあります。

レプリカセットの機能

MongoDBレプリカセットの自動フェイルオーバーメカニズムは、ほかの競合製品と一線を画します。プライマリが存在しなければ、セカンダリノード間で自動的に投票が行われ、新たにプライマリが選出されます。

MongoDBレプリカセットとMongoDBクラスタの違い

MongoDBレプリカセットは、レプリカセットのノード間で同じデータセットの様々なコピーを作成します。レプリカセットの主な目的は次のとおりです。

  • 組み込みのバックアップ機能の提供
  • データの可用性の向上

MongoDBクラスタはまったくの別物で、シャードキーを使用して多くのノードにデータを分散します。このプロセスでデータは、「シャード」と呼ばれる多くの断片に分割されます。それぞれのシャードは、異なるノードにコピーされます。クラスタの目的は、大規模なデータセットと高スループット処理のサポート。ワークロードを水平方向にスケーリングすることで、これを実現します。

レプリカセットとクラスタの違いを簡単に説明すると、以下のようになります。

  • クラスタはワークロードを分散し、データの断片(シャード)を多数のサーバーに保存する。
  • レプリカセットはデータセットを完全に複製。

MongoDBのシャードクラスタでは、この2つの機能を組み合わせることができます。セカンダリサーバーにすべてのシャードを複製し、高い冗長性とデータの可用性を実現可能です。

ただし、レプリカセットのメンテナンスとセットアップは技術的な負担が大きく、手間もかかります。また、適切なホスティングサービスを見つけるのも簡単ではありません。多くの選択肢があるため、ビジネスを構築する前に、事前調査に多くの時間を費やすことになりかねません。

Kinstaでは、このような課題を解消し、サービスや製品の開発に専念できるホスティングサービスを提供しています。

5万5,000人以上の開発者が利用しているKinstaのアプリケーションホスティングは、たった3つの簡単なステップで使い始めることができます。他にも、以下のような機能が強みです。

  • 高いパフォーマンスを実現する内部接続:共有データベースの制限とはお別れ。クエリ数や行数に制限のない内部接続された専用データベースを利用することができます。高速かつ安全な上、内部帯域やアクセス数による課金は一切ありません。
  • 開発者向けの機能一式:堅牢なプラットフォームでアプリケーションを拡張。Gmail、YouTube、Google 検索もサポートしています。
  • 好きなデータセンターを選んで超高速配信:顧客に近いデーターセンターを世界25箇所以上の中から選択可能。260以上を誇るPoPで、サイトを世界中に超高速で届けることができます。

まずはリスクなしで、アプリケーションホスティングの無料利用枠をお試しください。

MongoDBレプリケーションの仕組み

MongoDBでは、書き込み操作をプライマリサーバー(ノード)に送信します。プライマリはセカンダリサーバーに操作を割り振り、データを複製します。

MongoDBのレプリケーションプロセス
MongoDBのレプリケーションプロセス(出典: MongoDB

MongoDBノードの3つのタイプ

MongoDBノードの3つのタイプのうち、プライマリノードとセカンダリノードの2つにはすでに触れました。3つ目のタイプは、レプリケーションの際に機能する「アービター」です。アービターノードは、データセットのコピーを持たないため、プライマリになることはできませんが、プライマリの投票には参加可能です。

プライマリノードがダウンすると、何が起きるかは前述しましたが、セカンダリノードがダウンした際には、プライマリノードがセカンダリになり、データベースにアクセスできなくなります。

メンバーの投票

投票は以下のような状況で発生する可能性があります。

  • レプリカセットの初期化
  • プライマリノードとの接続の消失(ハートビートで検出)
  • reconfigまたはstepDownメソッドを使用したレプリカセットのメンテナンス
  • 既存のレプリカセットへの新しいノードの追加

レプリカセットは最大50台のメンバーを持つことができますが、投票できるのは7台まで。

クラスタがプライマリを選出するまでの平均時間は、12秒を超えてはいけません。投票アルゴリズムでは、最も優先順位の高いセカンダリが選出されます。同時にプライオリティ値「0」のメンバーはプライマリになれず、投票にも参加することはありません。

セカンダリノードがプライマリに
セカンダリノードがプライマリに(出典:Medium

書き込み保証

永続性のため書き込み操作には、指定されたノード数でデータをコピーするフレームワークがあり、クライアントへのフィードバックにも使用されます。このフレームワークは、「書き込み保証」とも呼ばれます。操作を成功として返す前に、データを保持したメンバーが書き込み保証を確認します。一般にレプリカセットは、書き込み保証として値「1」を持ちます。したがって、書き込み保証の確認を返す前に、プライマリのみが書き込みを確認します。

書き込み操作の確認に必要なメンバー数は、増やすことができます。メンバー数に上限はありませんが、数が多いと、クライアントがすべてのメンバーの確認を待たなければならず、高レイテンシが発生します。また「過半数」の書き込み保証も設定可能です。この設定では、確認を受け取った後、メンバーの半数以上が確認したかどうかを計算します。

読み込みの優先順位

読み込み操作に対して優先順位を指定することができます。この指定によって、データベースがレプリカセットのメンバーにどのようにクエリを送信するかが決まります。通常はプライマリノードが読み込み操作を受け取りますが、クライアントは読み込み優先順位を指定することで、セカンダリノードに読み込み操作を送信可能です。優先読み込みのオプションは以下の通りです。

  • primaryPreferred:通常、読み込み操作はプライマリノードで行われますが、プライマリノードが利用できない場合、データはセカンダリノードから取得されます。
  • primary:すべての読み込み操作がプライマリノードで実行されます。
  • secondary:すべての読み込み操作がセカンダリノードで実行されます。
  • nearestpingコマンドの実行で検出される、到達可能な最も近いノードに読み込み要求がルーティングされます。読み込み操作の結果は、プライマリかセカンダリかに関係なく、レプリカセットの任意のメンバーから取得されます。
  • secondaryPreferred:ほとんどの読み込み操作がセカンダリノードで行われますが、利用可能なノードがない場合は、プライマリノードからデータが取得されます。

レプリケーションセットのデータ同期

共有データセットの最新のコピーの維持のため、レプリカセットのセカンダリメンバーは他のメンバーからデータを複製、または同期します。

MongoDBには、2種類のデータ同期があります。新しいメンバーに完全なデータセットをコピーする初期同期と、完全なデータセットへの継続的な変更を実行するレプリケーションです。

初期同期

初期同期の実行中、セカンダリノードはinit syncコマンドを実行して、プライマリノードからセカンダリノードに送信される最新データを同期します。セカンダリノードは、終始tailable cursor機能を利用して、プライマリノードのlocal.oplog.rsコレクション内の最新のoplogエントリを照会し、oplogエントリ内の操作を適用します。

MongoDB 5.2から初期同期は、ファイルコピーベース同期、または論理同期になりました。

論理同期

論理同期の手順は以下のようになります。

  1. 各コレクションのドキュメントをコピーし、すべてのコレクションのインデックスを作成。
  2. ローカルデータベース以外のすべてのデータベースを複製。mongodはすべてのソースデータベースのすべてのコレクションをスキャンし、コレクションの複製内にすべてのデータを挿入。
  3. データセットですべての変更を実行。mongodはソースからのoplogを利用してデータセットをアップグレードし、レプリカセットの現在の状態を再現。
  4. データコピー中に新しく追加されたoplogレコードを抽出。ターゲットメンバーのローカルデータベース内に、データコピー処理中のoplogレコードを一時的に保存できる、十分なディスク容量があることを確認する。

初期同期が完了すると、メンバーはSTARTUP2からSECONDARYに移行します。

ファイルコピーベースの初期同期

ファイルコピーベースの初期同期は、MongoDB Enterpriseを使用している場合にのみ実行できます。この方法は、ファイルシステム上のファイルを複製し移動することで、初期同期を実行するものです。ユースケースによっては、論理同期よりも高速ですが、クエリ述語を指定せずにcount()メソッドを実行すると、正確にカウントできない可能性があるため注意してください。

このファイルコピーベースの初期同期にも、以下のような制限があります。

  • ファイルコピーベースの初期同期中は、同期対象のメンバーのローカルデータベースに書き込めない。また、同期先、同期元のメンバーではバックアップも実行できない。
  • 暗号化ストレージエンジンを利用する場合、ソース鍵によって同期先が暗号化される。
  • 一度に実行できる初期同期は、指定した1つのメンバーからのみ。

レプリケーション

セカンダリメンバーは、初期同期後も一貫してデータを複製します。セカンダリメンバーはソースの同期からoplogを複製し、非同期プロセスで操作を実行します。

セカンダリには、他のメンバーのレプリケーションのping時間や状態の変化に基づき、必要に応じてソースからの同期を自動で修正する機能があります。

ストリーミングレプリケーション

MongoDB 4.4以降、ソースからの同期は同期中のセカンダリにoplogエントリの連続ストリームを送信します。ストリーミングレプリケーションは、高負荷、高レイテンシネットワークでのレプリケーションラグを低減します。また、以下のような特長もあります。

  • プライマリフェイルオーバーのために、「w:1」での書き込み操作を失うリスクが低減する。
  • セカンダリから古いデータを読み込む可能性が下がる。
  • w:majority」および「w:>1」での書き込み操作の待ち時間、すなわち、レプリケーションの書き込み操作で発生する、任意の待ち時間が短縮される。
マルチスレッドレプリケーション

MongoDBは従来、同時実行性を高めるために、複数のスレッドを使用してバッチで書き込み操作を実行していました。このとき、MongoDBはドキュメントIDごとにバッチをグループ化し、各グループの操作を異なるスレッドで実行します。

また、ドキュメントに対する書き込み操作を常に元の書き込み順で実行していましたが、この動きはMongoDB 4.0で変更されました。

MongoDB 4.0のセカンダリを対象とした読み込み操作は、読み込み保証レベルが「majority」または「local」に設定されていると、レプリケーションバッチの適用中、データのWiredTigerスナップショットから読み込むようになりました。スナップショットからの読み込みは、ロックなしでもデータの一貫したビューが保証され、進行中のレプリケーションと同時に読み込み操作を行えます。

したがって、上の読み込み保証レベルを必要とするセカンダリからの読み込みは、レプリケーションバッチの適用を待たずに、受信と同時に処理できます。

MongoDBレプリカセットの作成方法

前述したように、MongoDBはレプリカセットを使用してレプリケーションを実行します。ここからは、ユースケースに合わせたレプリカセットの作成方法をご紹介していきます。

方法1. Ubuntu上にMongoDBレプリカセットを作成する

レプリカセットを作成する前に、Ubuntu 20.04の稼働するサーバーが少なくとも3台あり、各サーバーにMongoDBがインストールされていることを確認してください。

レプリカセットのセットアップで重要な点は、レプリカセットの各メンバーに他のメンバーからアクセス可能なアドレスを指定すること。今回のケースでは、レプリカセットに3台のメンバーを用意します。IPアドレスも使用できますが、アドレスが予期せず変更される可能性があるため推奨できません。論理DNSホスト名を使用してレプリカセットを構成するのが得策です。

DNSホスト名は、各レプリケーションメンバーのサブドメインを構成することで使用できます。これは本番環境では理想的な方法ですが、ここではDNSの解決に各サーバーのhostsファイルを編集して設定する方法を説明します。このファイルを使用すると、数値のIPアドレスに分かりやすいホスト名を割り当てられます。IPアドレスが変わっても、最初からレプリカセットを再構成する必要はなく、3つのサーバーのhostsファイルを更新するだけで済みます。

通常、hosts/etc/ディレクトリ内にあります。3台のサーバーそれぞれで、以下のコマンドを実行してください。

sudo nano /etc/hosts

上のコマンドでは、テキストエディターとしてnanoを使用していますが、任意のものでOKです。冒頭のlocalhostを設定する行の後に、レプリカセットの各メンバーのエントリを追加します。IPアドレスの後にわかりやすいお好きな名前をつけてください。ただし、各メンバーを区別できるように、必ず説明的な名前を選択してください。この例では、以下のホスト名を使用します。

  • mongo0.replset.member
  • mongo1.replset.member
  • mongo2.replset.member

/etc/hostsファイルは以下のようになります。

ホスト名の表示例
ホスト名の表示例

ファイルを保存して、テキストエディターを閉じます。

レプリカセットのDNS解決を構成したら、ファイアウォールのルールを更新して、相互に通信できるようにします。mongo0で次のufwコマンドを実行して、mongo1がmongo0のポート27017にアクセスできるようにしましょう。

sudo ufw allow from mongo1_server_ip to any port 27017

mongo1_server_ipパラメータの代わりに、mongo1サーバーの実際のIPアドレスを指定します。また、このサーバーのMongoインスタンスをデフォルト以外のポートを使うように更新した場合は、MongoDBインスタンスが使用するポート27017を変更してください。

別のファイアウォールルールを追加して、mongo2が同じポートにアクセスできるようにします。

sudo ufw allow from mongo2_server_ip to any port 27017

mongo2_server_ipパラメータの代わりに、mongo2サーバーの実際のIPアドレスを指定します。次に、他の2つのサーバーのファイアウォールルールを更新します。以下のコマンドをmongo1サーバーで実行します。このとき、server_ipパラメーターの代わりに、それぞれmongo0とmongo2のIPアドレスを指定します。

sudo ufw allow from mongo0_server_ip to any port 27017
sudo ufw allow from mongo2_server_ip to any port 27017

最後に、以下の2つのコマンドをmongo2で実行します。ここでも各サーバーの正しいIPアドレスを指定してください。

sudo ufw allow from mongo0_server_ip to any port 27017
sudo ufw allow from mongo1_server_ip to any port 27017

次に、各MongoDBインスタンスの構成ファイルを更新して外部からの接続を許可します。これには各サーバーの構成ファイルを変更して、レプリカセットを指すIPアドレスを指定します。任意のテキストエディターを使用してください(この例ではnano)。各mongod.confファイルを以下のように修正してください。

mongo0:

# network interfaces
net:
port: 27017
bindIp: 127.0.0.1,mongo0.replset.member# replica set
replication:
replSetName: "rs0"

mongo1:

# network interfaces
net:
port: 27017
bindIp: 127.0.0.1,mongo1.replset.member
replication:
replSetName: "rs0"

mongo2:

# network interfaces
net:
port: 27017
bindIp: 127.0.0.1,mongo2.replset.member
replication:
replSetName: "rs0"
sudo systemctl restart mongod

これで各サーバーのMongoDBインスタンスのレプリケーションが有効になりました。

レプリカセットは、rs.initiate()メソッドを使用して初期化できます。このメソッドは、レプリカセット内の単一のMongoDBインスタンスに対してのみ実行してください。レプリカセットの名前とメンバーが、以前に各構成ファイルで行った設定と一致していることを確認してください。

rs.initiate(
  {
    _id: "rs0",
    members: [
      { _id: 0, host: "mongo0.replset.member" },
      { _id: 1, host: "mongo1.replset.member" },
      { _id: 2, host: "mongo2.replset.member" }
    ]
  }
)

このメソッドが出力に「“ok”: 1」を返せば、レプリカセットは正しく開始されています。以下は出力例です。

 "ok": 1,
  "$clusterTime": {
    "clusterTime": Timestamp(1612389071, 1),
    "signature": {
      "hash": BinData(0, "AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
      "keyId": NumberLong(0)
    }
  },
  "operationTime": Timestamp(1612389071, 1)
}

MongoDBサーバーのシャットダウン

MongoDBサーバーをシャットダウンするには、db.shutdownServer()メソッドを使用します(以下参照)。forcetimeoutsecsはどちらも任意のパラメータです。

db.shutdownServer({
  force: <boolean>,
  timeoutSecs: <int>
})

mongodレプリカセットメンバーが、インデックスの構築で特定の操作を実行していると、このメソッドは失敗する可能性があります。操作を中断して強制的にメンバーをシャットダウンするには、ブール値パラメータforceにtrueを指定します。

MongoDBを「–replSet」付きで再起動

構成をリセットするには、まずレプリカセットのすべてのノードが停止していることを確認してください。その後、すべてのノードのローカルデータベースを削除します。--replSetフラグを指定して再起動し、レプリカセットの1つのmongodインスタンスに対してのみrs.initiate()を実行します。

mongod --replSet "rs0"

rs.initiate()は任意で、レプリカセット構成ドキュメントを受け取ります。

  • replSetNameまたは、_idフィールドにレプリカセット名を指定した--replSetオプション
  • メンバーごとに1つのドキュメントを含む、レプリカセットメンバーの配列

rs.initiate()メソッドは投票をトリガーし、メンバーの1つをプライマリに選出します。

レプリカセットへのメンバーの追加

レプリカセットにメンバーを追加するには、それぞれのサーバーでmongodインスタンスを起動します。次にmongoクライアントを起動し、rs.add()コマンドを使用します。

rs.add()コマンドの基本構文は次のとおりです。

rs.add(HOST_NAME:PORT)

たとえば、mongodインスタンス「mongo1」が、ポート27017で待ち受けているとします。このインスタンスをレプリカセットに追加するには、mongoクライアントコマンドrs.add()を使用します。

rs.add("mongo1:27017")

プライマリノードに接続して初めて、レプリカセットにmongodインスタンスを追加することができます。プライマリに接続されているかどうかを確認するには、db.isMaster()コマンドを使用します。

メンバーの削除

メンバーを削除するには、rs.remove()を使用します。

まずは、削除するmongodインスタンスを上で見たdb.shutdownServer()メソッドでシャットダウンします。

次に、レプリカセットの現在のプライマリに接続します。現在のプライマリを調べるには、レプリカセットの任意のメンバーに接続した状態で、db.hello()を使用します。プライマリがわかったら、以下のいずれかのコマンドを実行してください。

rs.remove("mongodb-node-04:27017")
rs.remove("mongodb-node-04")
レプリカセットからノードが正常に削除された様子
レプリカセットからノードが正常に削除された様子(出典:Bmc

MongoDBは、レプリカセットで新しいプライマリの投票が必要であれば、シェルを短時間切断する可能性があります。このときシェルは、自動的に再接続します。またコマンドが成功しても、DBClientCursor::init call()の失敗エラーが表示される場合があります。

方法2. デプロイとテスト用のMongoDBレプリカセットを作成する

一般にテスト用のレプリカセットは、ロールベースのアクセス制御(RBAC)を有効にしても、無効にしても設定することができます。今回は、テスト環境へのデプロイを想定し、アクセス制御を無効にしてレプリカセットをセットアップする方法をご紹介します。

まずは以下のコマンドを使用して、レプリカセットの一部であるすべてのインスタンスにディレクトリを作成します。

mkdir -p /srv/mongodb/replicaset0-0  /srv/mongodb/replicaset0-1 /srv/mongodb/replicaset0-2

このコマンドで、3つのMongoDBインスタンスreplicaset0-0、replicaset0-1、replicaset0-2のディレクトリが作成されます。それぞれのMongoDBインスタンスを次のコマンドで起動します。

Server 1

mongod --replSet replicaset --port 27017 --bind_ip localhost,<hostname(s)|ip address(es)> --dbpath /srv/mongodb/replicaset0-0  --oplogSize 128

Server 2

mongod --replSet replicaset --port 27018 --bind_ip localhost,<hostname(s)|ip address(es)> --dbpath /srv/mongodb/replicaset0-0  --oplogSize 128

Server 3:

mongod --replSet replicaset --port 27019 --bind_ip localhost,<hostname(s)|ip address(es)> --dbpath /srv/mongodb/replicaset0-0  --oplogSize 128

テスト段階でのマシンの過負荷を防ぐため、各ディスクが消費するディスク容量を減らす--oplogSizeパラメータを使用します。

次にインスタンスの1つに接続します。Mongoシェルを使用して以下のポート番号に接続してください。

mongo --port 27017

レプリケーションプロセスを開始するには、rs.initiate()コマンドを使用します。hostnameパラメータは実際のシステム名で置き換えてください。

rs conf = {

  _id: "replicaset0",

  members: [

    {  _id: 0,  host: "<hostname>:27017},

    {  _id: 1,  host: "<hostname>:27018"},

    {  _id: 2,  host: "<hostname>:27019"}

   ] }

これで、initiateコマンドのパラメーターとして構成オブジェクトファイルを指定できます。以下のように使用します。

rs.initiate(rsconf)

以上で、開発およびテスト用のMongoDBレプリカセットが完成です。

方法3. MongoDBのスタンドアロンインスタンスをレプリカセットに変換する

MongoDBでは、スタンドアロンインスタンスをレプリカセットに変換することができます。スタンドアロンインスタンスは、主にテストや開発フェーズで使用され、レプリカセットは本番環境の一部になります。

まず始めに、次のコマンドでmongodインスタンスをシャットダウンします。

db.adminCommand({"shutdown":"1"})

コマンドの--repelSetパラメータに使用するレプリカセットを指定して、インスタンスを再起動します。

mongod --port 27017 – dbpath /var/lib/mongodb  --replSet replicaSet1 --bind_ip localhost,<hostname(s)|ip address(es)>

コマンドにはサーバー名と一意のアドレスを指定してください。

シェルとMongoDBインスタンスを接続し、 initiateコマンドでレプリケーションを開始し、インスタンスをレプリカセットに変換します。インスタンスの追加や削除など、基本的な操作はすべて以下のコマンドで実行可能です。

rs.add(“<host_name:port>”)
rs.remove(“host-name”)

rs.status()コマンドやrs.conf()コマンドを使用して、MongoDBレプリカセットのステータスをチェックすることもできます。

方法4. シンプルな代替としてMongoDB Atlasを使用する

レプリケーションとシャーディングは、連携して「シャードクラスタ」を形成します。セットアップや構成は簡単ですが、その手間を考慮すれば、MongoDB Atlasを使用するのが得策です。

MongoDB Atlasはレプリカセットを自動化し、実装プロセスを簡素化します。グローバルにシャーディングされたレプリカセットを数クリックでデプロイでき、ディザスタリカバリ、容易な管理、データの局所性、マルチリージョン展開を実現することができます。

MongoDB Atlasでは、クラスタを作成する必要があります。このクラスタは、レプリカセットでもシャードクラスタでも構いません。ただし、他のリージョン内にあるクラスタのノード数は、プロジェクトで合計40までに制限されています。

無料クラスタや共有クラスタ、Googleクラウドリージョン間での通信を除外した、任意の2つのリージョン間のノード数の合計がこの制約を満たさなければなりません。たとえば、以下のプロジェクトがあるとします。

  • リージョンAには15ノードがある
  • リージョンBには25ノードがある
  • リージョンCには10ノードがある

このとき追加で割り当てられるのは、リージョンCの5ノードのみ。

  1. リージョンA+リージョンB=40。許可される最大ノード数40の制約を満たす。
  2. リージョンB+リージョンC=25+10+5=40(5は、Cに割り当てられた追加ノード)。許可される最大ノード数40の制約を満たす。
  3. リージョンA+リージョンC=15+10+5=30(5は、Cに割り当てられた追加ノード)。許容される最大ノード数40の制約を満たす。

リージョンCにさらに10ノードを割り当て、リージョンCのノード数を20にすると、45ノードになり、与えられた制約を超えるため、マルチリージョンクラスタを作成できない可能性があります。

クラスタを作成すると、Atlasはプロジェクトにネットワークコンテナを作成します。MongoDB Atlasでレプリカセットクラスタを作成するには、Atlas CLIで次のコマンドを実行します。

atlas clusters create [name] [options]

クラスタ名は、必ずわかりやすいものにしてください。作成後は変更することができません。引数には、ASCII文字、数字、ハイフンが使用可能です。

MongoDBクラスタの作成では、要件に応じて複数のオプションがあります。たとえば、継続的にクラスタのクラウドバックアップを行うには、--backupにtrueを設定してください。

レプリケーション遅延への対処

レプリケーションの遅延は非常に厄介です。遅延はプライマリでの操作と、その操作がoplogからセカンダリに適用される間で発生します。大きなデータセットを扱う場合、ある程度の遅延は予想されますが、その遅延は外的要因によっても増大します。最新のレプリケーションで恩恵を得るには、以下の点を考慮してください。

  1. 安定した十分な帯域幅で、ネットワークの通信をルーティングする─ネットワークのレイテンシは、レプリケーションに大きな影響を与えます。レプリケーションプロセスの要求にネットワークが対応できなければ、レプリカセット全体のデータ複製が遅延します。
  2. 十分なディスクスループットを確保する─セカンダリのファイルシステムとディスクデバイスが、プライマリと同じ速さでディスクにデータをフラッシュできなければ、セカンダリの追従は困難になり、セカンダリノードはプライマリノードよりも書き込みクエリの処理が遅くなります。これは仮想化インスタンスや大規模なデプロイを含む、ほとんどのマルチテナントシステムでよく見られます。
  3. 間隔を空けて書き込み保証の確認を要求する─セカンダリがプライマリに追いつく機会を与えると、特にプライマリへ大量に書き込むバルクロード操作やデータ取り込みに対応できます。セカンダリは未確認の書き込み保証があると、変更に追従するだけの十分な速さでoplogを読み込むことができません。
  4. 実行中のバックグラウンドタスクを特定する─cronジョブのような特定のタスク、サーバーのアップデート、セキュリティチェックなどは、ネットワークやディスクの使用に予期せぬ影響を与え、レプリケーションプロセスに遅延を引き起こす可能性があります。

アプリケーションにレプリケーションの遅延があるかどうかが不明な場合もご安心を。次のセクションで、トラブルシューティング方法をご紹介します。

MongoDBレプリカセットのトラブルシューティング

レプリカセットのセットアップに成功したものの、サーバー間でデータの整合性が取れていない。これは大規模なビジネスにとって、重要な警告です。しかし、簡単なトラブルシューティングで原因を特定し、解決できる可能性があります。以下、レプリカセットのデプロイメントに関する主なトラブルシューティング方法を見ていきましょう。

レプリカのステータスの確認

レプリカセットの現在のステータスと各メンバーのステータスを確認しましょう。レプリカセットのプライマリに接続したmongoshセッションで、以下のコマンドを実行してください。

 rs.status()

レプリケーションラグの確認

先に触れた通り、レプリケーションの遅延は深刻な問題。これは、「遅延した」メンバーはすぐにプライマリになる資格がなくなり、分散された読み込み操作に一貫性のない可能性が高まるためです。現在のレプリケーションログの長さは、以下のコマンドで確認できます。

rs.printSecondaryReplicationInfo()

これは各メンバーについて、最後のoplogエントリーがセカンダリに書き込まれた時刻、syncedTo値を返します。以下はその例です。

source: m1.example.net:27017
    syncedTo: Mon Oct 10 2022 10:19:35 GMT-0400 (EDT)
    0 secs (0 hrs) behind the primary
source: m2.example.net:27017
    syncedTo: Mon Oct 10 2022 10:19:35 GMT-0400 (EDT)
    0 secs (0 hrs) behind the primary

プライマリの非アクティブ時間がmembers[n].secondaryDelaySecs値よりも大きいと、遅延メンバーはプライマリより0秒遅れていると表示される場合があります。

すべてのメンバー間のテスト接続

レプリカセットの各メンバーは、他のすべてのメンバーと接続できなければなりません。必ず双方向の接続を確認してください。多くのエラーでは、ファイアウォールの設定やネットワークのトポロジーによって正常の接続が阻害され、レプリケーションがブロックされます。

たとえば、mongodインスタンスがlocalhostと、IPアドレス198.41.110.1に関連付けられたホスト「ExampleHostname」の両方にバインドしているとします。

mongod --bind_ip localhost, ExampleHostname

このインスタンスに接続するには、リモートクライアントはホスト名またはIPアドレスを指定しなければなりません。

mongosh --host ExampleHostname
mongosh --host 198.41.110.1

もしレプリカセットが3台のメンバー(m1、m2、m3)で構成され、デフォルトのポート27017を使用するなら、接続は以下のようにテストします。

m1

mongosh --host m2 --port 27017
mongosh --host m3 --port 27017

m2

mongosh --host m1 --port 27017
mongosh --host m3 --port 27017

m3

mongosh --host m1 --port 27017
mongosh --host m2 --port 27017

どれか1つでも接続に失敗する場合は、ファイアウォールの構成を確認し、接続を許可するように再構成してください。

キーファイル認証による安全な通信の確保

MongoDBのキーファイル認証は、デフォルトでSCRAM(Salted challenge response authentication mechanism)に依存します。MongoDBは認証のため、提供された認証情報を読み込み、検証しなければなりません。認証情報にはユーザー名とパスワード、そしてMongoDBインスタンスが認識する認証データベースの組み合わせが含まれます。SCRAMはユーザーがデータベースに接続する際に、パスワード認証で使用される仕組みと同じメカニズムです。

MongoDBで認証を有効にすると、自動的にレプリカセットでロールベースのアクセス制御(RBAC)が有効になり、ユーザーには、データベースリソースへのアクセスを決定する、1つ以上のロールが付与されます。RBACが有効なため、適切な権限を持つ認証済みのMongoユーザーだけが、システム上のリソースにアクセスできます。

キーファイルはクラスタ内の各メンバーにおける、共有パスワードのように動作します。レプリカセット内の各mongodインスタンスは、デプロイメント内の他のメンバーを認証する共有パスワードとして、キーファイルの内容を使用できます。

正しいキーファイルを持つmongodインスタンスのみが、レプリカセットに参加できます。キーの長さは6文字から1024文字の間で、base64の文字だけを使用できます。空白文字は、キーを読み込む際に排除されます。

キーファイルを生成する方法は色々あります。ここでは、opensslを使用して複雑な1024文字のランダム文字列を生成し、共有パスワードとして使用します。次にchmodを使用してファイルパーミッションを変更し、ファイル所有者のみに読み込み権限を付与します。なお、mongodインスタンスを稼働するハードウェアから簡単に切り離せる、USBドライブやネットワークに接続された記憶装置などの記憶媒体には、キーファイルを保存しないでください。キーファイルを生成するコマンドは以下の通りです。

openssl rand -base64 756 > <path-to-keyfile>
chmod 400 <path-to-keyfile>

次に、各レプリカセットメンバーにキーファイルをコピーします。mongodインスタンスを実行しているユーザーがファイルの所有者であり、キーファイルにアクセスできることを確認してください。以上の作業を終えたら、レプリカセットの全メンバーを、セカンダリから順にシャットダウンします。すべてのセカンダリがオフラインになったら、プライマリをシャットダウンします。潜在的なロールバックを防ぐため、必ずこの順序で実行してください。次のコマンドを実行して、mongodインスタンスをシャットダウンします。

use admin
db.shutdownServer()

コマンドの実行後、レプリカセットの全メンバーはオフラインになります。ここで、アクセス制御を有効にしてレプリカセットの各メンバーを再起動します。

レプリカセットの各メンバーに対して、security.keyFile構成ファイルか、または--keyFileコマンドラインオプションを指定して、mongodインスタンスを起動します。

構成ファイルを使用する場合は以下を設定してください。

  • keyFile:キーファイルのパス
  • replSetName:レプリカセット名
security:
  keyFile: <path-to-keyfile>
replication:
  replSetName: <replicaSetName>
net:
   bindIp: localhost,<hostname(s)|ip address(es)>

構成ファイルを使用してmongodインスタンスを開始します。

mongod --config <path-to-config-file>

コマンドラインオプションを使用する場合は、以下のオプションを付けて、mongodインスタンスを開始してください。

  • –keyFile:キーファイルのパス
  • –replSet:レプリカセット名
mongod --keyFile <path-to-keyfile> --replSet <replicaSetName> --bind_ip localhost,<hostname(s)|ip address(es)>

構成に必要なオプションを追加します。たとえば、リモートクライアントからデプロイメントに接続する場合や、デプロイメントメンバーが異なるホストで実行されている場合は、–bind_ipを指定します。詳細は公式ドキュメントをご覧ください。

次に、localhostインターフェースを使用して、レプリカセットのメンバーに接続します。mongodインスタンスと同じ物理マシン上でmongoshを実行しなければなりません。このインターフェースは、デプロイメント用のユーザーをまだ作成していない場合にのみ利用可能で、最初のユーザーを作成すると自動的に閉じられます。

次にレプリカセットを開始します。mongoshからrs.initiate()メソッドを実行します。

rs.initiate(
  {
    _id: "myReplSet",
    members: [
      { _id: 0, host: "mongo1:27017" },
      { _id: 1, host: "mongo2:27017" },
      { _id: 2, host: "mongo3:27017" }
    ]
  }
)

前述のように、このメソッドは、メンバーの1つをレプリカセットのプライマリメンバーとして選出します。プライマリメンバーを見つけるには、rs.status()を使用してください。手順を進める前に、プライマリに接続します。

次に、管理者ユーザーを作成します。db.createUser()メソッドを使用してユーザーを追加します。このユーザーには、少なくともadminデータベースのuserAdminAnyDatabaseロールがあることを確認してください。

以下の例では、adminデータベースにuserAdminAnyDatabaseロールを持つユーザー「batman」を作成します。

admin = db.getSiblingDB("admin")
admin.createUser(
  {
    user: "batman",
    pwd: passwordPrompt(), // or cleartext password
    roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]
  }
)

プロンプトが表示されたら、先に作成したパスワードを入力します。

次に、db.auth()を使用して、管理者ユーザーとして認証します。

db.getSiblingDB(“admin”).auth(“batman”, passwordPrompt()) // または平文のパスワード

あるいは、新しいmongoshインスタンスをプライマリのレプリカセットメンバーに接続できます。-u <ユーザー名>、-p <パスワード>、--authenticationDatabaseパラメータを使用します。

mongosh -u "batman" -p  --authenticationDatabase "admin"

-pコマンドラインフィールドでパスワードを指定しなければ、mongoshはパスワードの入力を要求します。

最後に、クラスタ管理者を作成します。clusterAdminロールは、レプリカセットの設定など、レプリケーション操作へのアクセスを許可します。

クラスタ管理者ユーザーを作成し、adminデータベースでclusterAdminロールを割り当てます。

db.getSiblingDB("admin").createUser(
  {
    "user": "robin",
    "pwd": passwordPrompt(),     // or cleartext password
    roles: [ { "role" : "clusterAdmin", "db" : "admin" } ]
  }
)

プロンプトが表示されたらパスワードを入力します。

必要に応じて、クライアントとして許可する追加ユーザーを作成し、レプリカセットと通信することができます。

以上で、キーファイル認証が有効になりました。

まとめ

特にビジネス規模が拡大する状況では、レプリケーションはデータベースに必要不可欠な要件です。レプリケーションによって、システムのパフォーマンス、データセキュリティ、可用性を大幅に向上することができます。また、Kinsta APM、Jetpack、Freshpingなどを使用して、パフォーマンスの問題を監視し、即座に修正することもWordPressデータベースにとって極めて重要です。

レプリケーションは、複数のサーバーにまたがるデータ保護に役立ち、サーバーがダウンした際にデータの損失を回避することができます。この記事では、レプリケーションの重要性とともに、レプリカセットの作成とトラブルシューティングのヒントを見てきました。あなたは、MongoDBのレプリケーションを使用していますか?以下のコメント欄でぜひお聞かせください。

Jeremy Holcombe Kinsta

Content & Marketing Editor at Kinsta, WordPress Web Developer, and Content Writer. Outside of all things WordPress, I enjoy the beach, golf, and movies. I also have tall people problems ;).