1. 程式人生 > 資料庫 >Docker 搭建叢集MongoDB的實現步驟

Docker 搭建叢集MongoDB的實現步驟

前言

由於公司業務需要,我們打算自己搭建 MongoDB 的服務,因為 MongoDB 的雲資料庫好貴,我們這次採用副本集的方式來搭建叢集,三臺伺服器,一主、一副、一仲裁

基本概念

Replica Set 副本集:一個副本集就是一組 MongoDB 例項組成的叢集,由一個主(Primary)伺服器和多個備份(Secondary)伺服器構成

  • 主節點(master):主節點接收所有寫入操作。主節點將對其資料集所做的所有更改記錄到其 oplog。
  • 副節點(secondary):複製主節點的 oplog 並將操作應用到其資料集,如果主節點不可用,一個合格的副節點將被選為新的主節點。
  • 仲裁節點(arbiter):負載選舉,當主節點不可用,它將從副節點中選一個作為主節點。

Sharding 分片:

Master-slave 主備

  • MongoDB 4.0 以上版本執行時提示:[main] Master/slave replication is no longer supported,也就是 MongoDB 4.0 後不在支援主從複製

一、環境準備

使用 CentOS 7.6 64bit 系統,安裝 Docker、Docker-compose、Docker-Swarm

二、生成 KeyFile

  • MongoDB 使用 KeyFile 認證,副本集中的每個 MongoDB 例項使用 KeyFile 內容作為認證其他成員的共享密碼。MongoDB 例項只有擁有正確的 KeyFile 才可以加入副本集。
  • keyFile 的內容必須是 6 到 1024 個字元的長度,且副本集所有成員的 KeyFile 內容必須相同。
  • 有一點要注意是的:在 UNIX 系統中,KeyFile 必須沒有組許可權或完全許可權(也就是許可權要設定成 X00 的形式)。Windows 系統中,keyFile 許可權沒有被檢查。
  • 可以使用任意方法生成 keyFile。例如,如下操作使用 openssl 生成複雜的隨機的 1024 個字串。然後使用 chmod 修改檔案許可權,只給檔案擁有者提供讀許可權。
  • 這是 MongoDB 官方推薦 keyFile 的生成方式:
# 400許可權是要保證安全性,否則mongod啟動會報錯
openssl rand -base64 756 > mongodb.key
chmod 400 mongodb.key

二、建立跨主機網路

搭建叢集我們肯定是跨主機通訊,要搭建 Overlay Network 網路,我們就要用到 Docker Swarm 這個工具了。Docker Swarm 是 Docker 內建的叢集工具,它能夠幫助我們更輕鬆地將服務部署到 Docker daemon 的叢集之中。

Docker 搭建叢集MongoDB的實現步驟

既然要將 Docker 加入到叢集,我們就必須先有一個叢集,我們在任意一個 Docker 例項上都可以通過 docker swarm init 來初始化叢集。

$ sudo docker swarm init

Swarm initialized: current node (t4ydh2o5mwp5io2netepcauyl) is now a manager.

To add a worker to this swarm,run the following command:

  docker swarm join --token SWMTKN-1-4dvxvx4n7magy5zh0g0de0xoues9azekw308jlv6hlvqwpriwy-cb43z26n5jbadk024tx0cqz5r 192.168.1.5:2377

在叢集初始化後,這個 Docker 例項就自動成為了叢集的管理節點,而其他 Docker 例項可以通過執行這裡所列印的 docker swarm join 命令來加入叢集。

加入到叢集的節點預設為普通節點,如果要以管理節點的身份加入到叢集中,我們可以通過 docker swarm join-token 命令來獲得管理節點的加入命令。

$ sudo docker swarm join-token manager
To add a manager to this swarm,run the following command:

  docker swarm join --token SWMTKN-1-60am9y6axwot0angn1e5inxrpzrj5d6aa91gx72f8et94wztm1-7lz0dth35wywekjd1qn30jtes 192.168.1.5:2377

我們通過這些命令來建立用於我們服務開發的 Docker 叢集,並將相關開發同事的 Docker 加入到這個叢集裡,就完成了搭建跨主機網路的第一步。

建立跨主機網路

接下來,我們就通過 docker network create 命令來建立 Overlay 網路。

$ sudo docker network create --driver overlay --attachable mongodbs

在建立 Overlay 網路時,我們要加入 --attachable 選項以便不同機器上的 Docker 容器能夠正常使用到它。

在建立了這個網路之後,我們可以在任何一個加入到叢集的 Docker 例項上使用 docker network ls 檢視一下其下的網路列表。我們會發現這個網路定義已經同步到了所有叢集中的節點上。

$ sudo docker network ls
NETWORK ID     NAME        DRIVER       SCOPE
## ......
y89bt74ld9l8    mongodbs        overlay       swarm
## ......

接下來我們要修改 Docker Compose 的定義,讓它使用這個我們已經定義好的網路,而不是再重新建立網路。

我們只需要在 Docker Compose 配置檔案的網路定義部分,將網路的 external 屬性設定為 true,就可以讓 Docker Compose 將其建立的容器都連線到這個不屬於 Docker Compose 的專案上了。

networks:
 mesh:
  external: true

通過這個實現,我們在開發中就使整個服務都處於一個可以使用別名對映網路中,避免了要對不同功能聯調時切換服務 IP 的煩瑣流程。在這種結構下,我們只需要讓我們開發的 Docker 退出和加入不同的叢集,就能馬上做到切換不同聯調專案。

二、編寫 docker-compose 檔案

主節點

version: "3"
services: 
 master:
  image: mongo:4.1
  container_name: master
  environment:
   MONGO_INITDB_ROOT_USERNAME: root
   MONGO_INITDB_ROOT_PASSWORD: 123456
   TZ: "Asia/Shanghai"
  volumes:
   # 掛載 MongoDB 資料目錄
   - "/data/docker/mongodb/data/mongo:/data/db:rw"
   # 掛載 KeyFile
   - "/data/docker/mongodb/data/mongodb.key:/data/mongodb.key"
  ports:
   - "27018:27017"
  networks:
   - mongodbs
  command:
   # 密碼
   --auth
   # 副本集名稱
   --replSet testSet 
   --oplogSize 128
   --keyFile /data/mongodb.key
# Swarm 跨主機網路網路
networks:
 mongodbs:
  external: true

副節點

version: "3"
services: 
secondary:
 image: mongo:4.1
 container_name: secondary
 environment:
  MONGO_INITDB_ROOT_USERNAME: root
  MONGO_INITDB_ROOT_PASSWORD: 123456
  TZ: "Asia/Shanghai"
 volumes:
  - "/data/docker/mongodb/data/mongo:/data/db:rw"
  - "/data/docker/mongodb/data/mongodb.key:/data/mongodb.key"
 ports:
  - "27018:27017"
 networks:
  - mongodbs
 command:
  --auth
  --replSet testSet 
  --oplogSize 128
  --keyFile /data/mongodb.key
networks:
mongodbs:
 external: true

仲裁節點,因為仲裁節點不需要儲存資料,他只是用來當主節點掛掉後選舉新的主節點,所以不需要密碼、對映埠等操作

version: "3"
services:
arbiter:
 image: mongo:4.1
 container_name: arbiter
 restart: always
 volumes:
  - "/data/docker/mongodb/data/mongo:/data/db:rw"
  - "/data/docker/mongodb/data/mongo_key:/mongo:rw"
 networks:
  - mongodbs
 command:
  mongod --replSet testSet --smallfiles --oplogSize 128
networks:
mongodbs:
 external: true

三、啟動容器

接下來我們分別在三臺伺服器中使用容器編排啟動容器

docker-compose up -d

四、配置副本集

進入主節點容器內部

docker exec -it master mongo

在 mongo shell 裡執行:

> rs.initiate()
{
   "info2" : "no configuration specified. Using a default configuration for the set","me" : "7abd89794aa7:27017","ok" : 1
}

繼續執行:

testSet:SECONDARY> rs.add('secondary:27017')
{
   "ok" : 1,"$clusterTime" : {
       "clusterTime" : Timestamp(1599562800,1),"signature" : {
           "hash" : BinData(0,"wrxMUIX/0bEyLgCVoQqdLvH59T0="),"keyId" : NumberLong("6870069879538450434")
       }
   },"operationTime" : Timestamp(1599562800,1)
}

繼續執行,其中 true 表示這個節點是仲裁節點

testSet:PRIMARY> rs.add('arbiter:27017',true)
{
   "ok" : 1,"$clusterTime" : {
       "clusterTime" : Timestamp(1599562838,"p9ub49lLD8ij8nkxpfu2l/AvRRY="),"operationTime" : Timestamp(1599562838,1)
}

檢視配置

testSet:PRIMARY> rs.conf()
{
   "_id" : "testSet","version" : 3,"protocolVersion" : NumberLong(1),"writeConcernMajorityJournalDefault" : true,"members" : [
       {
           "_id" : 0,"host" : "7abd89794aa7:27017","arbiterOnly" : false,"buildIndexes" : true,"hidden" : false,"priority" : 1,"tags" : {

           },"slaveDelay" : NumberLong(0),"votes" : 1
       },{
           "_id" : 1,"host" : "secondary:27017",{
           "_id" : 2,"host" : "arbiter:27017","arbiterOnly" : true,"priority" : 0,"votes" : 1
       }
   ],"settings" : {
       "chainingAllowed" : true,"heartbeatIntervalMillis" : 2000,"heartbeatTimeoutSecs" : 10,"electionTimeoutMillis" : 10000,"catchUpTimeoutMillis" : -1,"catchUpTakeoverDelayMillis" : 30000,"getLastErrorModes" : {

       },"getLastErrorDefaults" : {
           "w" : 1,"wtimeout" : 0
       },"replicaSetId" : ObjectId("5f576426fe90ef2dd8cd2700")
   }
}

檢視狀態

testSet:PRIMARY> rs.status()
{
   "set" : "testSet","date" : ISODate("2020-09-08T11:45:12.096Z"),"myState" : 1,"term" : NumberLong(1),"syncingTo" : "","syncSourceHost" : "","syncSourceId" : -1,"heartbeatIntervalMillis" : NumberLong(2000),"optimes" : {
       "lastCommittedOpTime" : {
           "ts" : Timestamp(1599565502,"t" : NumberLong(1)
       },"lastCommittedWallTime" : ISODate("2020-09-08T11:45:02.775Z"),"readConcernMajorityOpTime" : {
           "ts" : Timestamp(1599565502,"readConcernMajorityWallTime" : ISODate("2020-09-08T11:45:02.775Z"),"appliedOpTime" : {
           "ts" : Timestamp(1599565502,"durableOpTime" : {
           "ts" : Timestamp(1599565502,"lastAppliedWallTime" : ISODate("2020-09-08T11:45:02.775Z"),"lastDurableWallTime" : ISODate("2020-09-08T11:45:02.775Z")
   },"lastStableRecoveryTimestamp" : Timestamp(1599565492,"lastStableCheckpointTimestamp" : Timestamp(1599565492,"name" : "7abd89794aa7:27017","ip" : "10.0.1.41","health" : 1,"state" : 1,"stateStr" : "PRIMARY","uptime" : 2784,"optime" : {
               "ts" : Timestamp(1599565502,"t" : NumberLong(1)
           },"optimeDate" : ISODate("2020-09-08T11:45:02Z"),"infoMessage" : "","electionTime" : Timestamp(1599562790,2),"electionDate" : ISODate("2020-09-08T10:59:50Z"),"configVersion" : 3,"self" : true,"lastHeartbeatMessage" : ""
       },"name" : "secondary:27017","ip" : "10.0.1.233","state" : 2,"stateStr" : "SECONDARY","uptime" : 2711,"optimeDurable" : {
               "ts" : Timestamp(1599565502,"optimeDurableDate" : ISODate("2020-09-08T11:45:02Z"),"lastHeartbeat" : ISODate("2020-09-08T11:45:11.494Z"),"lastHeartbeatRecv" : ISODate("2020-09-08T11:45:11.475Z"),"pingMs" : NumberLong(0),"lastHeartbeatMessage" : "","syncingTo" : "7abd89794aa7:27017","syncSourceHost" : "7abd89794aa7:27017","syncSourceId" : 0,"configVersion" : 3
       },"name" : "arbiter:27017","ip" : null,"health" : 0,"state" : 8,"stateStr" : "(not reachable/healthy)","uptime" : 0,"lastHeartbeat" : ISODate("2020-09-08T11:45:10.463Z"),"lastHeartbeatRecv" : ISODate("1970-01-01T00:00:00Z"),"lastHeartbeatMessage" : "Error connecting to arbiter:27017 :: caused by :: Could not find address for arbiter SocketException: Host not found (authoritative)","configVersion" : -1
       }
   ],"ok" : 1,"$clusterTime" : {
       "clusterTime" : Timestamp(1599565502,"7/ei+8UrhlpIny9zKeWuAFpn46c="),"operationTime" : Timestamp(1599565502,1)
}

五、驗證 MongoDB 可用性

先進入主節點伺服器新增一條資料

docker exec -it master mongo
use admin
db.auth('root','123456')
use test
db.test.insert({name:"muyang",age:20})

在來副節點伺服器檢視是否已經同步了這條資料

[root@linux secondary] docker exec -it secondary mongo
testSet:SECONDARY> use admin
testSet:SECONDARY> db.auth('root','123456')
testSet:SECONDARY> use test
testSet:SECONDARY> db.test.find()
2020-09-08T19:03:02.295+0800 E QUERY  [js] uncaught exception: Error: listCollections failed: {
   "operationTime" : Timestamp(1599562972,"ok" : 0,"errmsg" : "not master and slaveOk=false","code" : 13435,"codeName" : "NotMasterNoSlaveOk","$clusterTime" : {
       "clusterTime" : Timestamp(1599562972,"mhsrpGHRl7qZg2QOjyS3RbBb/Yc="),"keyId" : NumberLong("6870069879538450434")
       }
   }
} :
testSet:SECONDARY> rs.slaveOk()
testSet:SECONDARY> db.users.find()
{ "_id" : ObjectId("5f5764b1f909544b783696c2"),"name" : "muyang","age" : 20 }

在 secondary 查詢時報如下錯誤:

not master and slaveok=false

這是正常的,因為 secondary 是不允許讀寫的,如果非要解決,方法如下:

testSet:SECONDARY> rs.slaveOk()

到此這篇關於Docker 搭建叢集MongoDB的實現步驟的文章就介紹到這了,更多相關Docker 搭建叢集MongoDB內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!