Docker Compose 架設 MongoDB clusters / replica sets / 叢集

Posted on  Dec 30, 2023  in  Docker  by  Amo Chen  ‐ 3 min read

本文紀錄如何用 Docker 與 Docker Compose 架設 MongoDB replica set (或稱 MongoDB 叢集/Cluster) 。

本文環境

MongoDB replica sets 介紹

MongoDB 的叢集(clusters)架構又被稱為 replica sets 。

replica set in MongoDB is a group of mongod processes that maintain the same data set.

簡單來說,在同 1 個叢集底下的 MongoDB 都擁有相同資料,其中有 1 個會擔任 Primary Node 的角色,剩下的則是 Secondary Node 。

p.s. 1 個 Replica set 只能有 1 個 Primary Node

Replication 同步機制

Primary Node 負責接受所有寫資料的操作,然後紀錄資料的變更(此機制稱為 oplog )並傳給 Secondary Nodes, Secondary Nodes 會負責套用 Primary Node 所傳來的資料變更操作,藉此達到同步的目的。

舉個例子 Primary Node 增加 document A 的欄位 B 之後,它也會告訴 Secondary Nodes 為 document A 增加 B 欄位,可以認為 Secondary Nodes 藉著模仿 Primary Node 的所有動作,以達到同步/備份的目的,比起每次都整份文件或者資料庫都複製到 Secondary Nodes 的做法,用 oplog 重演操作的同步方式更加即時有效率。

高可用性

MongoDB 的 replica sets 是為了提高可用性(availability),因為 Secondary Nodes 都有與 Primary Node 相同的資料,一旦 Primary Node 因事故無法提供服務時,就會有 1 個 Secondary Node 會被選為新的 Primary Node 繼續提供服務。

Docker Compose

初步認識 MongoDB replica sets 之後,接下來實際用 Docker Compose 架設 1 組 MongoDB 的 replica set 。

以下是 Docker Compose file 。

version: "3"
services:
  mongo01:
    image: mongo:6.0
    container_name: mongo01
    expose:
      - 27017
    ports:
      - 27017:27017
    entrypoint: [ "/usr/bin/mongod", "--bind_ip_all", "--replSet", "mongo-replica-set" ]
  mongo02:
    image: mongo:6.0
    container_name: mongo02
    expose:
      - 27017
    depends_on:
      - mongo01
    entrypoint: [ "/usr/bin/mongod", "--bind_ip_all", "--replSet", "mongo-replica-set" ]
  mongo03:
    image: mongo:6.0
    container_name: mongo03
    expose:
      - 27017
    depends_on:
      - mongo01
    entrypoint: [ "/usr/bin/mongod", "--bind_ip_all", "--replSet", "mongo-replica-set" ]
networks:
  default:
    name: mongo-replica-set-network

上述 Docker Compose file 定義 3 個 services, 分別是 mongo01, mongo02, mongo03, 以及設定 3 個 services 使用相同的網路 mongo-replica-set-network , 其中只開放 mongo01 的 27017 port 給 host machine 連線,也就是 posts 的設定,所有 services 只開放 27017 給同 1 個網路內的 services 連線,也就是 expose 的設定,最後在每個 services 的 entrypoint 設定 Replica Set Id, 也就是參數中的 --replSet mongo-replica-set

p.s. 同 1 組的 replica set 要使用同 1 個 replica set Id

設定好 docker compose file 之後,可以先執行以下指令,把 3 個 MongoDB services 啟動:

$ docker compose up

啟動之後,應該會看到很多類似以下的錯誤訊息:

mongo01  | {"t":{"$date":"2023-12-30T14:50:52.827+00:00"},"s":"I",  "c":"-",        "id":4939300, "ctx":"monitoring-keys-for-HMAC","msg":"Failed to refresh key cache","attr":{"error":"ReadConcernMajorityNotAvailableYet: Read concern majority reads are currently not possible.","nextWakeupMillis":1000}}

這個錯誤訊息是因為 replica set 還沒設定好的原因,所以要額外執行 1 個指令,用 mongosh 設定 1 組 repica set, 其指令如下:

$ docker exec -it mongo01 mongosh --eval "rs.initiate({
 _id: \"mongo-replica-set\",
 members: [
   {_id: 0, host: \"mongo01\"},
   {_id: 1, host: \"mongo02\"},
   {_id: 2, host: \"mongo03\"}
 ]
})"

上述指令中的 mongo-replica-set 對應到 docker compose file 中的 --replSet 參數,而 members 設定中的 host 對應到 docker compose file 中的 container name, 如果你有修改這些值,就修改上述指令中對應的設定。

上述指令執行成功之後,就會出現類似以下的訊息提示 ok: 1 代表 replica set 已設定成功:

Current Mongosh Log ID:	658f971112af95d51004a8be
Connecting to:		mongodb://127.0.0.1:27017/?directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+2.1.1
Using MongoDB:		6.0.12
Using Mongosh:		2.1.1

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
   2023-12-30T04:05:20.893+00:00: Access control is not enabled for the database. Read and write access to data and configuration is unrestricted
   2023-12-30T04:05:20.893+00:00: You are running this process as the root user, which is not recommended
   2023-12-30T04:05:20.893+00:00: vm.max_map_count is too low
------

{
  ok: 1,
  '$clusterTime': {
    clusterTime: Timestamp({ t: 1703909138, i: 1 }),
    signature: {
      hash: Binary.createFromBase64('AAAAAAAAAAAAAAAAAAAAAAAAAAA=', 0),
      keyId: Long('0')
    }
  },
  operationTime: Timestamp({ t: 1703909138, i: 1 })
}

接下來,可以用 MongoDB Compass 試著連線進 MongoDB cluster 看看。

在此之前需要編輯 /etc/hosts, 將 mongo01 mongo02 mongo03 的 IP 位置指向 127.0.0.1, 否則會讓 MongoDB Compass 無法正常連線,因為 MongoDB Compass 沒有與 mongo01 mongo02 mongo03 3 個 services 在相同的 Docker Network 之中,所以需要做此設定:

$ vim /etc/hosts

添加以下內容:

127.0.0.1	mongo01 mongo02 mongo03

設定完之後,就可以打開 MongoDB Compass 進行連線, URI 可以輸入 mongodb://localhost:27017/?readPreference=primaryPreferred&replicaSet=mongo-replica-set, 或者輸入 mongodb://localhost:27017/ 後,按照下圖設定也可以:

compass-config.png

連線成功之後,也可以在 mongosh 輸入 rs.status() 查看 replica set 的狀態:

rs status

剩下的就跟平常使用 MongoDB ㄧ樣囉~

同場加映

如果要讓每 1 台都可以用 MongoCompass 連線的話,可以改用以下 docker compose file 與指令:

docker-compose.yml

version: "3"
services:
  mongo01:
    image: mongo:6.0
    container_name: mongo01
    expose:
      - 27021
    ports:
      - 27021:27021
    entrypoint: [ "/usr/bin/mongod", "--bind_ip_all", "--replSet", "rs0", "--port", "27021" ]
  mongo02:
    image: mongo:6.0
    container_name: mongo02
    expose:
      - 27022
    ports:
      - 27022:27022
    depends_on:
      - mongo01
    entrypoint: [ "/usr/bin/mongod", "--bind_ip_all", "--replSet", "rs0", "--port", "27022" ]
  mongo03:
    image: mongo:6.0
    container_name: mongo03
    expose:
      - 27023
    ports:
      - 27023:27023
    depends_on:
      - mongo01
    entrypoint: [ "/usr/bin/mongod", "--bind_ip_all", "--replSet", "rs0", "--port", "27023" ]
networks:
  default:
    name: rs0-network

mongosh 指令

docker exec -it mongo01 mongosh --port 27021 --eval "rs.initiate({
 _id: \"rs0\",
 members: [
   {_id: 0, host: \"mongo01:27021\"},
   {_id: 1, host: \"mongo02:27022\"},
   {_id: 2, host: \"mongo03:27023\"}
 ]
})"

p.s. 27017 - 27020 都是 MongoDB 預設使用的 ports, 所以不可以設定 27017 - 27020 之間的通訊埠

總結

MongoDB 的 replica set 是提高 availability 的方式,對於在乎可用性的生產環境,是相當重要的功能。此外, MongoDB 的 transaction 也需要使用 replica set 才能使用,如果需要在本地開發的話,就勢必要架設 replica set 環境。

需要在本地環境架設 replica set 的話,歡迎使用本文所提供的方法!

以上!

Enjoy!

References

Replication

對抗久坐職業傷害

研究指出每天增加 2 小時坐著的時間,會增加大腸癌、心臟疾病、肺癌的風險,也造成肩頸、腰背疼痛等常見問題。

然而對抗這些問題,卻只需要工作時定期休息跟伸展身體即可!

你想輕鬆改變現狀嗎?試試看我們的 PomodoRoll 番茄鐘吧! PomodoRoll 番茄鐘會根據你所設定的專注時間,定期建議你 1 項辦公族適用的伸展運動,幫助你打敗久坐所帶來的傷害!

贊助我們的創作

看完這篇文章了嗎? 休息一下,喝杯咖啡吧!

如果你覺得 MyApollo 有讓你獲得實用的資訊,希望能看到更多的技術分享,邀請你贊助我們一杯咖啡,讓我們有更多的動力與精力繼續提供高品質的文章,感謝你的支持!