這篇文章說明如何在 Docker Swarm 上面使用 Mongodb 自身的 Replica Set 以及環境建置,雖然 Docker Service 已經可以使用內建的 replica 了,不過使用 Mongodb 自身的支援度較高也較完整,本文參考自 Medium 的這篇,寫得非常清楚詳細。
初始化 Docker Swarm Mode
我們的架構長這樣,一台 manager (primary),兩台 worker (secondary),三台機器在同一個 swarm 。如果還沒有初始化 swarm,請在 manager 的機器上先初始化,另外再將兩台 worker 的機器加入這個 manager,成為一組 swarm。
## 在 manager 機器上初始化 swarm mode
docker swarm init --listen-addr MANAGER_IP_ADDRESS:2377 --advertise-addr MANAGER_IP_ADDRESS:2377
## 輸出
To add a worker to this swarm, run the following command:
docker swarm join --token SWMTKN-1-3fhjqnlewg78kcb0hkoc2v9o3zwmotqgurhy42jgkrnhpvmgh8-5qylz0finkxjpaiulli0yfs4w MANAGER_IP_ADDRESS:2377
## ----------
## 在 worker_1 機器上加入 swarm,成為 node
docker swarm join --token SWMTKN-1-3fhjqnlewg78kcb0hkoc2v9o3zwmotqgurhy42jgkrnhpvmgh8-5qylz0finkxjpaiulli0yfs4w --listen-addr WORKER_1_IP_ADDRESS:2377 --advertise-addr=WORKER_1_IP_ADDRESS:2377 MANAGER_IP_ADDRESS:2377
## ----------
## 在 worker_2 機器上加入 swarm,成為 node
docker swarm join --token SWMTKN-1-3fhjqnlewg78kcb0hkoc2v9o3zwmotqgurhy42jgkrnhpvmgh8-5qylz0finkxjpaiulli0yfs4w --listen-addr WORKER_2_IP_ADDRESS:2377 --advertise-addr=WORKER_2_IP_ADDRESS:2377 MANAGER_IP_ADDRESS:2377
這時候切回 manager 機器,利用指令
## 在 manager 上查看 docker 資訊
docker info
可以看到 swarm node 的資訊,如果看到下面的資訊代表 swarm 已經建立成功了。
Swarm: active
NodeID: 3nrg063zylzxfe6ij92c6safp
Is Manager: true <---------------- 註明這個 node 為 manager
ClusterID: s3rgodac8w7qdp3la0gwuu620
Managers: 1
Nodes: 3 <--------------- 有三個 node 在這個 swarm 中
Orchestration:
Task History Retention Limit: 5
Raft:
Snapshot Interval: 10000
Number of Old Snapshots to Retain: 0
Heartbeat Tick: 1
Election Tick: 10
Dispatcher:
Heartbeat Period: 5 seconds
CA Configuration:
Expiry Duration: 3 months
Force Rotate: 0
Autolock Managers: false
Root Rotation In Progress: false
Node Address: MANAGER_IP_ADDRESS
Manager Addresses:
MANAGER_IP_ADDRESS:2377
初始化 MongaDB 環境
為了要更好的控制或是讓不同的 mongodb 部署在不同機器上,可以在 node 上設定 label。
## 在 manager 機器上設定 node 的 label
## 先查看 node 資訊,找出 node ID
docker node ls
## 輸出
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
sv8gp5uyfe471iohkfwu8cyv2 WORKER_2 Ready Active 18.06.0-ce-rc1
34dizj0kic58kplkjsdhpgjv8 WORKER_1 Ready Active 17.06.0-ce
3nrg063zylzxfe6ij92c6safp * MANAGER Ready Active Leader 18.03.1-ce
## 為 manager 設定 label mongo.replica=1
docker node update --label-add mongo.replica=1 3nrg063zylzxfe6ij92c6safp
## 為 worker_1 設定 label mongo.replica=2
docker node update --label-add mongo.replica=2 34dizj0kic58kplkjsdhpgjv8
## 為 worker_2 設定 label mongo.replica=3
docker node update --label-add mongo.replica=3 sv8gp5uyfe471iohkfwu8cyv2
Replica Set 需要一個 overlap 的網路環境互相溝通,建立新的網路環境
## 在 manager 機器上新增網路
docker network create --driver overlay mongo
## 查看目前網路
docker network ls
## 輸出
NETWORK ID NAME DRIVER SCOPE
2ef63bb7d7ba bridge bridge local
6469b890cf65 docker_gwbridge bridge local
567d39d1ac4c host host local
kgt6db91iko1 ingress overlay swarm
l136q8h5hzfl mongo overlay swarm ## <---- 這個是新增的
e0bf52c71f72 none null local
另外這邊我們可以決定要把 db 放在哪邊:
-
放在 docker 環境下,利用 volume 掛載一個 docker 空間給 container
-
放在硬碟中,利用 bind mount 掛載一個實體位置給 container
詳細的差異可以參考官方文件
我們選擇的是第二個方式,想要在硬碟中可以看到實際的 db raw data,另外我們也把 mongodb config 放在硬碟中,以便日後的修改;如果選擇的是第一種方式,那就要在三台機器上面分別新增 volume:
## 在 manager 機器上新增 db volume 以及 config volume
docker volume create --name data
docker volume create --name config
## 在 worker_1 機器上新增 db volume 以及 config volume
docker volume create --name data
docker volume create --name config
## 在 worker_2 機器上新增 db volume 以及 config volume
docker volume create --name data
docker volume create --name config
當然也可以混用,在 manager 上面採用第二種做法,直接 bind mount 一個硬碟空間,在 worker 上採用第二種做法,mount volume 到 container 中。
啟動 Mongodb service
我們使用 docker-compose.yml
啟動 mongodb service,這份 yml
使用混合模式,檔案內容如下:
version: '3.3'
services:
db_manager:
image: mongo:3.6.4
volumes:
## 利用 bind mount 掛載硬碟空間以及存放在硬碟上的 config 給 container
- "PATH_TO_DIRECTORY_FOR_DB:/data/db"
- "PATH_TO_CONFIG_FILE:/etc/mongo.conf"
environment:
- MONGO_INITDB_DATABASE=YOUR_DB_NAME
command: mongod --config /etc/mongo.conf
networks:
- mongo
deploy:
replicas: 1
placement:
constraints:
## 指定這個 service 被 deploy 至 label mongo.replica == 1 的 node
- node.labels.mongo.replica == 1
db_worker_1:
image: mongo:3.6.4
volumes:
## 利用 volume 掛載 docker 空間給 container
- type: volume
source: data
target: /data/db
## 利用 bind mount 掛載存放在硬碟上的 config 給 container
- "PATH_TO_CONFIG_FILE:/etc/mongo.conf"
environment:
- MONGO_INITDB_DATABASE=YOUR_DB_NAME
command: mongod --config /etc/mongo.conf
networks:
- mongo
deploy:
replicas: 1
placement:
constraints:
## 指定這個 service 被 deploy 至 label mongo.replica == 2 的 node
- node.labels.mongo.replica == 2
db_worker_2:
image: mongo:3.6.4
volumes:
## 利用 volume 掛載 docker 空間給 container
- type: volume
source: data
target: /data/db
## 利用 bind mount 掛載存放在硬碟上的 config 給 container
- "PATH_TO_CONFIG_FILE:/etc/mongo.conf"
environment:
- MONGO_INITDB_DATABASE=YOUR_DB_NAME
command: mongod --config /etc/mongo.conf
networks:
- mongo
deploy:
replicas: 1
placement:
constraints:
## 指定這個 service 被 deploy 至 label mongo.replica == 2 的 node
- node.labels.mongo.replica == 3
networks:
mercury:
external:
## 使用 external 的網路,否則 docker stack 會自動產生網路,網路名稱前綴 service 名字
name: mongo
volumes:
data:
另外我們的 mongo.conf
如下,最重要的設定就是 replSetName,等等在 mongodb console 會用到,按照上面的 yml
,請記得把這個 config 放到三台機器的指定路徑 (PATH_TO_CONFIG_FILE) 下。
# mongod.conf
# for documentation of all options, see:
# http://docs.mongodb.org/manual/reference/configuration-options/
# Where and how to store data.
storage:
dbPath: /data/db
# journal:
# enabled: true
# engine:
# mmapv1:
# wiredTiger:
# where to write logging data.
#systemLog:
# destination: file
# logAppend: true
# path: /var/log/mongodb/mongod.log
# network interfaces
net:
port: 27017
bindIp: 0.0.0.0
# how the process runs
processManagement:
timeZoneInfo: /usr/share/zoneinfo
#security:
# keyFile: /etc/mongodb_keyfile
#operationProfiling:
replication:
replSetName: rs0
#sharding:
## Enterprise-Only Options:
#auditLog:
啟動 docker-compose.yml
中的 service:
## 啟動 service
docker stack deploy -c docker-compose.yml SERVICE_NAME
## 查看 service 資訊
docker service ls
## 輸出
ID NAME MODE REPLICAS IMAGE PORTS
z6q4z2646b20 db_manager replicated 1/1 mongo:3.6.4
7uptth7xnpuj db_worker_1 replicated 1/1 mongo:3.6.4
kuq8djlsoszc db_worker_2 replicated 1/1 mongo:3.6.4
看到上面資訊代表 service 都已經成功起來了,最後就是設定 replica set 啦。
初始化 Replica Set
為求方便起見,我們直接進到 mongodb console 操作,先查看一下 manager 上面的 mongodb container 資訊
## 在 manager 機器上查看 container 資訊
docker ps
## 輸出
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b578b7f6c539 mongo:3.6.4 "docker-entrypoint.s…" 2 days ago Up 2 days 27017/tcp db_manager.1.mim1o3ojh92jhcxvc2ugboam7
## 進入 container mongodb console
docker exec -it db_manager.1.mim1o3ojh92jhcxvc2ugboam7 mongo
在 mongodb console 中,我們利用 mongodb 的指令來初始化以及管理 replica set,詳細可以參考官方文件。使用下面指令可以初始化 replica set,方便的是可以直接用 service name 當作 host name,docker 會自動連結到正確的機器上。
rs.initiate( {
_id : "rs0",
members: [
{ _id: 0, host: "db_manager:27017" },
{ _id: 1, host: "db_worker_1:27017" },
{ _id: 2, host: "db_worker_2:27017" }
]
})
再來可以在 manager 上面查看 replica set 資訊
// 在 manager (primary) 上查看 replica set 資訊
rs.status()
直接看 members 那欄,可以看到三個 service 已經被加到一組 replica set,一個 Primary,兩個 secondary。
// rs.status() 的輸出
{
"set" : "rs0",
"date" : ISODate("2018-07-04T06:46:49.280Z"),
"myState" : 1,
"term" : NumberLong(5),
"heartbeatIntervalMillis" : NumberLong(2000),
"optimes" : {
"lastCommittedOpTime" : {
"ts" : Timestamp(1530686803, 1),
"t" : NumberLong(5)
},
"readConcernMajorityOpTime" : {
"ts" : Timestamp(1530686803, 1),
"t" : NumberLong(5)
},
"appliedOpTime" : {
"ts" : Timestamp(1530686803, 1),
"t" : NumberLong(5)
},
"durableOpTime" : {
"ts" : Timestamp(1530686803, 1),
"t" : NumberLong(5)
}
},
"members" : [
{
"_id" : 1,
"name" : "db_manager:27017",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 175074,
"optime" : {
"ts" : Timestamp(1530686803, 1),
"t" : NumberLong(5)
},
"optimeDate" : ISODate("2018-07-04T06:46:43Z"),
"electionTime" : Timestamp(1530531998, 2),
"electionDate" : ISODate("2018-07-02T11:46:38Z"),
"configVersion" : 1,
"self" : true
},
{
"_id" : 2,
"name" : "db_worker_1:27017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 175071,
"optime" : {
"ts" : Timestamp(1530686803, 1),
"t" : NumberLong(5)
},
"optimeDurable" : {
"ts" : Timestamp(1530686803, 1),
"t" : NumberLong(5)
},
"optimeDate" : ISODate("2018-07-04T06:46:43Z"),
"optimeDurableDate" : ISODate("2018-07-04T06:46:43Z"),
"lastHeartbeat" : ISODate("2018-07-04T06:46:48.850Z"),
"lastHeartbeatRecv" : ISODate("2018-07-04T06:46:47.593Z"),
"pingMs" : NumberLong(0),
"syncingTo" : "db_manager:27017",
"configVersion" : 1
},
{
"_id" : 3,
"name" : "db_worker_2:27017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 175071,
"optime" : {
"ts" : Timestamp(1530686803, 1),
"t" : NumberLong(5)
},
"optimeDurable" : {
"ts" : Timestamp(1530686803, 1),
"t" : NumberLong(5)
},
"optimeDate" : ISODate("2018-07-04T06:46:43Z"),
"optimeDurableDate" : ISODate("2018-07-04T06:46:43Z"),
"lastHeartbeat" : ISODate("2018-07-04T06:46:49.078Z"),
"lastHeartbeatRecv" : ISODate("2018-07-04T06:46:49Z"),
"pingMs" : NumberLong(0),
"syncingTo" : "db_manager:27017",
"configVersion" : 1
}
],
"ok" : 1,
"operationTime" : Timestamp(1530686803, 1),
"$clusterTime" : {
"clusterTime" : Timestamp(1530686803, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
不放心的話可以測試一下,新增一筆資料,看看 secondary 有沒有也同時新增,另外多久 sync 一次也都可以直接在這邊設定。
這次剛好有找到不錯的文章,在資訊蒐集上面省了很多時間,不過也是第一次設定,相信之後還有機會可做一些最佳化,或是資料量增加之後可能可以做 sharding,學到很多東西,Docker 這東西越用越覺得厲害,應該還有很多功能是還沒有碰到的。有問題請發問,看到都會回。