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) 。
本文環境
- macOS
- OrbStack
- MongoDB 6.0
- MongoDB Compass
MongoDB replica sets 介紹
MongoDB 的叢集(clusters)架構又被稱為 replica sets 。
A 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/
後,按照下圖設定也可以:
連線成功之後,也可以在 mongosh
輸入 rs.status()
查看 replica set 的狀態:
剩下的就跟平常使用 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!