Loading...
本文的專案放在:https://github.com/eugenechen0514/demo_mongo_cluster,從那可以下載完整的範例。

現在越來越多的資料類型像是日誌、時間序列的資料,相比儲存在關聯式資料庫,它們更適合待在不需事先定義的綱要(schema)的 NoSQL 資料庫。 MongoDB 提供三種資料庫架構,讓我們可以依照資料保存需求(如:多副本、資料分散)做選擇。

本篇會提及:

  • MongoDB 的三種架構
  • 使用 docker 架構 MongoDB 三種架構
  • 如何存取 MongoDB?

Docker 是什麼?實戰手札帶你認識 Docker

為什麼我們要用 Docker?

MongoDB 可以安裝在Linux/macOS/Windows,它提供水平擴展 (Sharding),垂直擴展 (Replication))的強大機制。 然而,要建置 MongoDB 叢集會有很多工作要做,像是啟動很多台 MongoDB 的服務、組態設定,工作十份繁複。

快速架構出用於測試開發的環境,若正式環境是 sharded cluster 的 MongoDB ,那在測試環境最好也是採用 sharded cluster 的 MongoDB 會比較好。因為有些功能在不同的架構可能會有限制,像是聚合管道(Aggregation Pipeline)的 $lookup 就不能查詢被分片的集合(sharded collection)。

再者,你也可能遭遇實體主機不夠而不能啟動太多 MongoDB(若一台只啟動一個 MongoDB),或者一台機器太強大,有高核心數高記憶本,想要榨乾它的效能。

基於以上理由, Docker 容器化技術為構築 MongoDB 叢集提供很好的解決方案。 Docker Compose File 提供述描執行服務的組態設定,你可以容易在任何 docker 環境下重現你的 MongoDB 叢集。

本文使用 Docker Compose File 來設定 MongoDB 叢集。我們假設您的執行環境已安裝 Docker,且可以下 docker-compose 指令。

想要快速了解 docker 指令請見〈Docker 是什麼?實戰手札帶你認識 Docker〉一文。

MongoDB 架構(Architecture)

MongoDB 有以下架構:

I. Standalone:

  • 建立難易度:低
  • 特色:單一台 MongoDB 服務,只有一個存取資料庫
  • 用途:獨立使用或用於維護 Replica Set/Sharded Cluster 的單一節點

II. Replica Set

  • 建立難易度:中
  • 特色:資料在 Replica Set 中會有副本資料
  • 用途:
  1. 需要 High availability (HA)的環境
  2. 依硬體效能調整副本策略,如:你有較快/較大的硬碟
  3. 不影響 Primary,使用 Read Preference 讀取較近或快的副本或是策略性地讀寫分離

III. Sharded Cluster

  • 建立難易度:高
  • 特色:資料遍佈在不同的分片(shard),shard/config server 都是 Replica Set,所以同時有 HA 的能力
  • 用途:
  1. 依照 Shard Keys 分散式地存取資料
  2. 有效的查找或存取到目標文件(document)
  3. 依硬體效能調整分散策略,如:你有較強/多核的 CPU,常用資料可以放在讀寫快的主機

當 MongoDB 變成叢集就會衍生出以下問題:

  1. 資料怎麼分散,分散的略策如何影響存取效能? -> Shard Keys 資料如何畫分 chunkZones 指定資料存放位置、Balancer 平衡 Shard 中的 chunk 數量
  2. 邏輯上怎樣才算寫入資料庫? -> Write Concern 資料寫入的策略
  3. 邏輯上怎樣才算讀出真正的資料? -> Read Concern 資料讀取的策略
  4. 怎麼決定誰是 Primary ? -> Replica Set Elections 推選投票誰當 Primary
  5. 叢集如何管理? -> Replica Set MaintenanceSharded Cluster Administration

因為本系列只專注建立叢集,不會涉及其它議題,有興趣的人可以自行學習。

若你不想自己架設 MongoDB,可以考慮試用看看 MongoDB Atlas。它架設在 AWS, Azure 或 GCP 提供 DBaaS。

本文不會引入安全性的設定,若需要可以參考 Security Checklist

該用 MySQL 或 MongoDB?選擇資料庫前你該了解的事

Standalone MongoDB

Standalone 是最簡單的 MongoDB 架構,只有一個 MongoDB 的實體,存放資料的資料庫只有一個, Mongo Client 只要直接連到它就可以使用。雖然最簡單的運作方式,但有時在維運叢集中的資料庫節點時,會以 standalone 方式啟動它。

請先建立一個名為 data 資料夾,資料庫的資料會放到這裡面,我們的資料夾目錄如下圖:


demo_mongo_cluster 工作目錄可以執行下操作:

  1. 建立 MongoDB 容器並且在背景執行容器
    docker-compose -f docker-compose-standalone.yml up -d
    就會在背景執行 (-d)。此指令會建立執行 docker file 中設定的所有 service。因此, standalone 容器建立並執行 mongod 指令運行 MongoDB 實體。
  2. 停止執行資料庫容器
    docker-compose -f docker-compose-standalone.yml stop

請注意,此操作是停止容器運作導致 MongoDB process((行程)) 停止,類似 kill process 非正常關閉 MongoDB,在有進行讀寫的情況下可能會產生無法預期的情況。安全的 關閉 MongoDB 需要連入資料庫
use admin

db.shutdownServer()

先關 MongoDB ,再停止容器會比較安全。

  1.  重新執行容器,在背景重新運作
    docker-compose -f docker-compose-standalone.yml start
    或再執行一次
    docker-compose -f docker-compose-standalone.yml up -d
  2. 停止並刪除容器
    docker-compose -f docker-compose-standalone.yml down

使用容器內的 Mongo Shell(mongosh)連接資料庫

因為我們使用 MongoDB 5.0(mongo:5.0) ,所以 Mongo Shell 建議也要使用同樣的版本號,否則可能會有不可預期的事。

從 Mongo 5 以開始 Mongo Shell 改名為 mongosh,目前雖然可以使用 mongo 但未來會可能移除

官方的 mongo:5.0 映像檔本身就放一些工具,像是 mongo, mongoimport, … 等(見:MongoDB Package Components)。因此我們只需要直接使用就可以了。

在終端機輸入:

docker-compose -f docker-compose-standalone.yml exec standalone bash

進入 standalone 容器中,它已裝有 MongoDB shell 終端機,我們就能使用 mongosh 指令連進資料庫

# 容器內

mongosh mongodb://standalone:27017

要離開就一直輸入 exit。

接下來,我們要來構築 Replica Set

Replica Set

它是就像是 standalone 的 HA 版本,一群代表同的個 standalone 的叢集。Primary 為主版本,Secondary 是副本。當 Primary 死掉時會執行選舉推選出新的 Primary。

Replica Set 是 Sharded Cluster 架構中 shardconfig servers 的基本元件,所以水平擴展是以 Replica Set 做為基本元件增加的資料存放節點。

架構

一個最基本的 Replica Set 至少要三個 member,一個 Primary、二個 Secondary,如下圖:


Primary/Secondary 都是有實際存資料的 member,若在硬體不夠的情況下,Mongo 提供 Arbiter 這種形態的 member,它只參與投票,本身不存資料也不能成為 Primary,如下圖:


若使用 Primary-Second-Arbiter 三個節點,有件事需要注意。當存有資料的節點(Primary/Second)死掉且使用 “majority” read concern 時會導致 cache pressure。見:Read Concern majority and Three-Member PSA

啟動三台 MongoDB

我們使用 docker-compose 構築 Primary-Secondary-Secondary 的 Replica Set。


我們的 Replica Set 名稱叫 RS,所以要用到 --replSet。一共建立三個 container (rs1/rs2/rs3),port 分別為 27041/27042/27043

請在 /replica/data 建立 rs1、rs2 和 rs3 資料夾,我們的資料夾目錄如下圖

接下來,執行

$ docker-compose -f docker-compose-replica-set.yml up -d

一次啟動三個 MongoDB 的實體。其它的 docker-compose 操作和 Standalone 章節一樣。

Replica Set 設定

上一節只有啟動三台 MongoDB,但它們彼此還不認識,不知道他們要組成一個副本集(Replica set)。進行組態設定前我們要先確認他們可以連線到對方。

1. 確認資料庫能否相互連線(可選)

在網路設定很復雜的環境中,可能會需要做連線檢查。本節示範在容器內試圖連線到別的容器內的 MongoDB。這步驟是可選的,可以等連接不到再回來檢查。

連入 rs1 container,確認能不能用域名(domain)連線到 rs1/rs2/rs3 資料庫。因為我們使用 docker-compose 預設的網路設定,所以 rs1, rs2, rs3 這三個 docker service name 是可以直接視為主機名,也就是我們可以直接透過 docker 預設的 dns 伺服器查詢到 MongoDB 實體的 IP。

進入(執行 bash) rs1 container

docker-compose -f docker-compose-replica-set.yml exec rs1 bash

確認資料庫連線

mongosh mongodb://rs1:27041 --eval "print('ok')"

mongosh mongodb://rs2:27042 --eval "print('ok')"

mongosh mongodb://rs3:27043 --eval "print('ok')"

確認是否都可以印出如下圖的 ok


這樣 rs1 到 rs1, rs2, rs3 的連線都確認好了,其它留給讀者自行驗証。

接下來開始設定 Replica Set。

2. 設定 Replica Set

我們随便選一個容器進入,然後連入某個資料庫,以 rs1 為例

docker-compose -f docker-compose-replica-set.yml exec rs1 bash

設定 Replica Set 組態

# 容器內

mongosh mongodb://rs1:27041

<p>CODE:https://gist.github.com/ACbosong/44874bc9aec209c37ba4c332ff7a831b.js</p>

印出 {ok: 1} 就設定完成。

3. 檢查組態和確認 Replica Set 的狀態

我們可以輸入rs.conf() 和 rs.status() 可以分別看到細部的 Replica Set 組態和所有資料節點的狀態。下面的指令更容易知道誰是 primary?誰是 secondary?

rs.status().members.map(m => `${m.name}(${m.stateStr})`).join('\n')

除了 rs.status(), rs.printReplicationInfo(), rs.printSlaveReplicationInfo() 也是很常用到的指令。

4. 測試連線

連入 Replica Set 的方法如下:

# 容器內

mongosh mongodb://s1:27041,rs2:27042,rs3:27043/?replicaSet=RS

隨便寫入資料

db.col.insertOne({text: 'hi'})

印出

{
acknowledged: true,
insertedId: ObjectId("60f3d3811b7c3c3381a3bb34")
}

以上就完成了 Replica Set 的架設。

Sharded Cluster


在這架構中,有三個主要的元件 routerconfig servershard server。其中 config server, shard server 都必需以 Replica set 的型式存在。

  1. shard server:實際儲存資料的節點,每個分片(shard)儲存部分資料的子集,或是說資料被分散儲存在各個分片中,每個分片必需是 Replica set 架構。上圖中,一共有兩個分片 Shard1 和 Shard2,它們各自都是 Replica set 架構。
  2. router(mongos):提供用戶端存取 Sharded Cluster 的介面。上圖中,我們只打算建一台 Router。事實上,我們可以使用一個以上的 router 存取同一座 Sharded Cluster (見:Connection String URI Format — MongoDB Manual)。
  3. config server:儲存分片的 metadata (如:狀態)和組態資訊(如:資料分散的組態訊息或 cluster 的組態)。因此,這元件十分的重要,官方要求必需是 Replica set 架構且不能有 Arbiter 的節點存在。上圖中,Config 就是一座 replica set 的 config server。

我們統計一下,要建立一座 Sharded Cluster MonoDB(不使用Arbiter 情況下)至少要有 10 個 docker service,其中有 9 個 MongoDB 資料庫(mongod)和 1 個路由元件(mongos)。

0. 開始前的準備

設定順序如下:

  1. 建立 config server 的 replica set
  2. 建立 shard server 的 replica set
  3. 啟動指定 config server 的 router
  4. 在 router(mongos)中加入所有的分片

跟之前的一樣我們預先開好資料夾:


若主機效能允許是可以直接用 docker-compose -f docker-compose-sharded-cluster.yml up -d,一次起動所有容器,但 routre 會因為還沒設定完成所以結束執行,這是正常的。

若有效能議題, docker-compose-sharded-cluster.yml 中分成了多個 Step 可以用 「#」 註解掉還沒設定的部分。之後,依設定順序一步步從上到下解開註解。

1. 建立 config server 的 replica set

我們先設定「 Step1: config server: config1 ~ config3」,它們的 port 號依序是 27031 ~ 27033。 這裡注意到,除了一般的 replica set 的設定,還需要指定 --configsvr 指定它們是 config server

command: mongod --configsvr --replSet Config --port 27031 --dbpath /data/db --config /resource/mongod.yml

我們開始設定,進到 config1

docker-compose -f docker-compose-sharded-cluster.yml exec config1 bash

開始設定

# 容器內

mongosh mongodb://config1:27031

<p>CODE:https://gist.github.com/ACbosong/b3e417c2ffc54902d2625e33ac0bd550.js"></p>

這裡跟建立 replic set 一樣,但要多加 configsvr: true。

別忘了可以用 rs.status() 檢查狀態

2. 建立 shard server 的 replica set

接來來開始設定 「Step2: shard server Shard1: sh1r1 ~ sh1r3」和 「Step3: shard server Shard2: sh2r1 ~ sh2r3」。 Shard1 的 port 是 27041 ~ 27043,而 Shard2 的 port 是 27051 ~ 27053

docker files 類似 config server,我們要加 --shardsvr 指定它們是 shard server

command: mongod --shardsvr --replSet Shard1 --port 27041 --dbpath /data/db --config /resource/mongod.yml

我們開始設定,進到 sh1r1 中,開始設定 Shard1 這分片的 replica set,

docker-compose -f docker-compose-sharded-cluster.yml exec sh1r1 bash

# 容器內

mongosh mongodb://sh1r1:27041

<p>CODE:https://gist.github.com/ACbosong/f2ef6ffef5c443dae41238f4ee0c50f8.js"></p>

同理,Shard2 這分片的 replica set 也是類似的方式,我們不再贅述。

<p>CODE:https://gist.github.com/ACbosong/00fdbb73cf6ce1ad6d5eae26e11a4fe2.js"></p>

3. 啟動指定 config server 的 router


前面我們完成了 config server 和 shard server(紅框的部分),但他們目前是各自獨立的 replica set。接下來,我們要重新啟動 router,讓它連接 config server。

在 docker file 中的 router service command 需要使用 --configdb 指定要採用的 config server。

command: mongos --port 27017 --configdb Config/config1:27031,config2:27032,config3:27033 --config /resource/mongos.yml

我們只要再 docker-compose -f docker-compose-sharded-cluster.yml up -d 一次,就會重新執行之前自己停掉的 router。

我們檢查一下啟動狀態,下指令 docker-compose -f docker-compose-sharded-cluster.yml ps ,應該要全部都是 Up,這樣我們就完成把 router 接上 config server 了,此時我們的 sharded cluster 就形成了。


除錯:用 docker-compose -f docker-compose-sharded-cluster.yml logs -f router  查看 log

4. 在 router(mongos)中加入所有的分片

上一步我們只完成建立 sharded cluster,但還沒有儲存資料的分片,所以這步驟我們要把分片加入。

進到 router 容器中,連接上 mongos

docker-compose -f docker-compose-sharded-cluster.yml exec router bash

在容器中連到 router

# 容器內

mongosh mongodb://router:27017

接著加入分片 Shard1 和 Shard2

sh.addShard("Shard1/sh1r1:27041,sh1r2:27042,sh1r3:27043")

sh.addShard("Shard2/sh2r1:27051,sh2r2:27052,sh2r3:27053")

分別出現下圖

確認分片狀態

可以用 sh.status() 查詢分片狀態

就會看到 Shard1 和 Shard2 這兩個分片,已經加入 cluster 中了。

使用 sharded cluster

我們已經完成建立 sharded cluster,現在連接上 router 就可以當做一般的 standalone 使用。

但如果要開始使用分片的功能還要做一些設定。

對資料庫開啟 sharding 功能

針對 playground 資料庫開啟 sharding 功能

sh.enableSharding("playground")

對集合(collection)開啟分片

再開啟資料庫分片功能後,接下來對需要使用分片的集合 sh.shardCollection() 就可以了。

sh.shardCollection(namespace, key) 是最基本的使用方式,namespace 表示是哪個集合要被分片,而 key 是指 Shard Key,其中 shard key 的選擇對於資料的分散策略是很重要的,見:Choose a Shard Key

我們簡單示範,users 這集合用 hashed index 做為 shard key。

sh.shardCollection('playground.users', {name: 'hashed'})

接下來,放入兩筆資料 {name: 'user1'}, {name: 'user2'}

use playground

db.users.insertMany([{name: 'user1'}, {name: 'user2'}])

再用 db.users.getShardDistribution() 查詢分片狀態


Totals 區塊中顯示 Shard1, Shard1 分別個放了 50% 的資料,也就剛剛放的資料被平均分散到這二個分片中。

總結

本篇介紹了 MongoDB 的 StandaloneReplica SetSharded cluster 三種架構,並在 docker-compose 幫助下,容易地啟動它們。不同的架構並不是互相獨立的,反而是相輔相成的,要先會操作 Standalone,再成為 HA 架構的 Replica Set,然後再堆疊成更大的 Sharded cluster,變成同時具有水平擴展和垂直擴展的架構。若是需要維運時(如:升級),就有可能需要把焦點放在基礎 Replica Set 元件或是更基本的 Standalone MongoDB。

參考連結

----

更多技術相關主題文章介紹

前端:JavaScript | HTML / CSS | Bootstrap | RWD | DOM | API | AJAX | Postman | jQuery 

後端:HTTP / HTTPS | Node.js  | MongoDB | Git | SQL / NoSQL | Docker

其他:VSCode | Web App | Leetcode