Ansible和Docker Swarm还有MongoDB Replica Set

工作需要,Let’s do it.

策略

  • 事先确定Replica Set的成员数量,因为这跟能Replica Set中能容忍挂掉的成员数量有直接关系
  • Replica Set中的voting成员数量要提前确定,MongoDB的Replica Set最大可以拥有50个成员,其中包括最多7个voting成员
  • 部署奇数成员数的Replica Set
  • 操作系统采用Ubuntu16.04.4 LTS 64bit
  • 原计划五台机器单机跑docker实例,也可以创建replica set ,但是日后还有其他测试要用到swarm环境,所以干脆一步到位,五台机器全部作为swarm node

IP规划

Replica Set 角色 swarm角色 物理机器名 IP CPU 内存
主节点 管理节点 docker-swarm01 192.168.0.233 i5-4590 8G
从节点 工作节点 docker-swarm02 192.168.0.232 Celeron G1840 8G
从节点 工作节点 docker-swarm03 192.168.0.242 i7-4790 16G
从节点 工作节点 docker-swarm04 192.168.0.241 i7-6700 8G
从节点 工作节点 docker-swarm05 192.168.0.230 i5-4590 8G

按照表格规划好的,修改各机器的IP和机器名

这里我们使用五台机器搭建swarm集群,root账号操作,顺序如下:

  1. 准备工作,免密登录,初始化设置等。
  2. 替换系统的默认软件源为网易镜像源。
  3. 增加科技大docker安装源,安装好docker-ce最新稳定版本。
  4. docker hub镜像源替换为国内DaoCloud加速源。(你也可以用已有的docker hub加速器)
  5. 上传MongoDB测试数据(这一步可以提前上传到主节点),这里我上传到了docker-swarm01。
  6. 建立docker swarm,并把各节点加入swarm。
  7. 每个节点建立存放MongoDB的数据volume,并做好命名工作。
  8. 每个节点建立MongoDB Replica Set的节点service,并做好命名工作。
  9. 初始化MongoDB Replica Set。
  10. 还原备份数据到Replica Set中。
  11. 查看数据同步情况,确认同步完毕。
  12. 进行访问测试。

准备工作

1、配置IP地址,ubuntu16.04的话,修改/etc/network/interfaces文件即可。

2、配置Ansible与目标机器的ssh免密登录,这里以swarm01为例。

1
2
3
4
5
6
# 生成ssh key
ssh-keygen -t rsa -b 8192 -C Ansible-key
# 拷贝ssh key到远程主机,ssh的时候就不需要输入密码了
ssh-copy-id root@192.168.0.233
# ssh的时候不会提示是否保存key
ssh-keyscan 192.168.0.233 >> ~/.ssh/known_hosts

3、修改/etc/ansible/hosts和/etc/hosts文件

不修改/etc/hosts的话,Ansible会报错

/etc/hosts文件加入以下内容:

1
2
3
4
5
192.168.0.233 swarm01
192.168.0.232 swarm02
192.168.0.242 swarm03
192.168.0.241 swarm04
192.168.0.230 swarm05

/etc/ansible/hosts文件加入以下内容:

1
2
3
4
5
6
[swarm]
swarm01 ansble_ssh_host=192.168.0.233 ansible_ssh_port=22 ansible_ssh_user="root"
swarm02 ansble_ssh_host=192.168.0.232 ansible_ssh_port=22 ansible_ssh_user="root"
swarm03 ansble_ssh_host=192.168.0.242 ansible_ssh_port=22 ansible_ssh_user="root"
swarm04 ansble_ssh_host=192.168.0.241 ansible_ssh_port=22 ansible_ssh_user="root"
swarm05 ansble_ssh_host=192.168.0.230 ansible_ssh_port=22 ansible_ssh_user="root"

替换系统的默认软件源为网易镜像源

1
2
3
4
5
#这里采用Ansible的copy模块+修改好的sources.list文件上传到对应位置即可
ansible swarm -m copy -a "src=/opt/sources.list dest=/etc/apt/sources.list owner=root group=root mode=0644 backup=yes"

#给swarm各节点系统升级软件包
ansible swarm -m apt -a "force_apt_get=yes state=latest upgrade=yes update_cache=yes autoremove=yes"

增加科技大docker安装源,安装好docker-ce最新稳定版本

一键脚本在此 https://github.com/zhusas/docker-ce.init.git 这个脚本也包含了docker hub镜像源替换为国内自定义加速源。

这是之前写的一个脚本,用ansible的script模块可以使用,其内容也可以写成playbook,这里就直接用脚本操作,日后再完善playbook,放到Github上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#批量运行脚本
ansible swarm -m script -a "/opt/docker-ce.repo.init.sh"

#验证一下安装的docker版本
root@Ansible:/opt# ansible swarm -a "docker -v"
swarm01 | SUCCESS | rc=0 >>
Docker version 18.03.1-ce, build 9ee9f40
swarm03 | SUCCESS | rc=0 >>
Docker version 18.03.1-ce, build 9ee9f40
swarm05 | SUCCESS | rc=0 >>
Docker version 18.03.1-ce, build 9ee9f40
swarm02 | SUCCESS | rc=0 >>
Docker version 18.03.1-ce, build 9ee9f40
swarm04 | SUCCESS | rc=0 >>
Docker version 18.03.1-ce, build 9ee9f40

建立docker swarm,并把各节点加入swarm

以下端口必须保持畅通:

TCP port 2377 :swarm集群管理信息通讯端口

TCP and UDP port 7946 :swarm节点之间的通讯端口

UDP port 4789 : overlay网络通讯端口

如果你建立了一个加密的overlay网络 (–opt encrypted),你还徐确保 ip protocol 50 (ESP) 能够正常通讯。

下面开始操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
root@Ansible:/opt# ansible swarm01 -m shell -a "docker swarm init --advertise-addr 192.168.0.233" 
swarm01 | SUCCESS | rc=0 >>
Swarm initialized: current node (wum4rkn8jk4qb97yib9kggirv) is now a manager.

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

docker swarm join --token SWMTKN-1-5wooyyq98crvrs0zv49ktg0bguua90k7in2ij1me81ljfgpkqt-7o9simk0nit9avq3637gzh0a5 192.168.0.233:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
root@Ansible:/opt# ansible swarm -m shell -a "docker swarm join --token SWMTKN-1-5wooyyq98crvrs0zv49ktg0bguua90k7in2ij1me81ljfgpkqt-7o9simk0nit9avq3637gzh0a5 192.168.0.233:2377"
swarm01 | FAILED | rc=1 >>
Error response from daemon: This node is already part of a swarm. Use "docker swarm leave" to leave this swarm and join another one.non-zero return code
swarm03 | SUCCESS | rc=0 >>
This node joined a swarm as a worker.
swarm04 | SUCCESS | rc=0 >>
This node joined a swarm as a worker.
swarm05 | SUCCESS | rc=0 >>
This node joined a swarm as a worker.
swarm02 | SUCCESS | rc=0 >>
This node joined a swarm as a worker.
root@Ansible:/opt#

可以看到,建立了swarm管理节点之后,运行docker swarm join命令,swarm01会报“Error response from daemon: This node is already part of a swarm. Use “docker swarm leave” to leave this swarm and join another one.non-zero return code”,意思是提醒你这已经是swarm的一部分了,不过没关系,哈哈。swarm02~05已经顺利加入。

我们用命令看看

OK了。

这里提醒一下的是,一般服务器都是网卡,建立swarm时候,最好加上–listen-addr 参数。我这里测试的机器都是单网卡,影响不大。各位切记哦。

明确一下计划

基本计划是将MongoDB副本集的每个成员定义为单独的swarm服务,并使用docker service的约束参数来防止swarm的scaling特性将它们从数据卷移开,因为数据卷存放在每个节点上,没法跟着容器漂移到其他节点, 这保留了Docker提供的所有操作优势,同时消除了scaling故障恢复功能(会影响MongoDB副本集的可用性)。通过命令将MongoDB服务固定到与其数据卷相同的swarm节点,在每个节点上设置标签。 稍后在创建服务时,将在约束中使用这些标签。

给swarm各节点加标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
root@Ansible:~# ansible swarm01 -m shell -a "docker node update --label-add mongo.rs=1 docker-swarm01"
swarm01 | SUCCESS | rc=0 >>
docker-swarm01
root@Ansible:~# ansible swarm01 -m shell -a "docker node update --label-add mongo.rs=2 docker-swarm02"
swarm01 | SUCCESS | rc=0 >>
docker-swarm02
root@Ansible:~# ansible swarm01 -m shell -a "docker node update --label-add mongo.rs=3 docker-swarm03"
swarm01 | SUCCESS | rc=0 >>
docker-swarm03
root@Ansible:~# ansible swarm01 -m shell -a "docker node update --label-add mongo.rs=4 docker-swarm04"
swarm01 | SUCCESS | rc=0 >>
docker-swarm04
root@Ansible:~# ansible swarm01 -m shell -a "docker node update --label-add mongo.rs=5 docker-swarm05"
swarm01 | SUCCESS | rc=0 >>
docker-swarm05

这步感觉不够优雅,此时playbook的价值就体现出来了。我这是为了展现详细步骤才这样。

建立MongoDB replica set在docker swarm的overlay专用网络

swarm跨节点通讯必备

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
root@Ansible:~# ansible swarm01 -m docker_network -a "name=mongo_network driver=overlay"
swarm01 | SUCCESS => {
"ansible_facts": {
"docker_network": {
"Attachable": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": null,
"Created": "2018-07-16T08:29:15.88390955Z",
"Driver": "",
"EnableIPv6": false,
"IPAM": {
"Config": [],
"Driver": "default",
"Options": null
},
"Id": "ku3waawi7hqrj02zh9sfvgcfk",
"Ingress": false,
"Internal": false,
"Labels": null,
"Name": "mongo_network",
"Options": null,
"Scope": "swarm"
}
},
"changed": true
}

root@Ansible:~# ansible swarm01 -a "docker network ls"
swarm01 | SUCCESS | rc=0 >>
NETWORK ID NAME DRIVER SCOPE
4127d48c4ee3 bridge bridge local
898954254fae docker_gwbridge bridge local
ce65005a6830 host host local
sqsf18n9lco7 ingress overlay swarm
ku3waawi7hqr mongo_network overlay swarm
d7d752f1abcc none null local

建立存放MongoDB的数据Volume

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
root@Ansible:~# ansible swarm01 -m docker_volume -a "name=rsdata1"
swarm01 | SUCCESS => {
"ansible_facts": {
"docker_volume": {
"CreatedAt": "2018-07-16T16:40:16+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/rsdata1/_data",
"Name": "rsdata1",
"Options": {},
"Scope": "local"
}
},
"changed": true
}

其他四台以此类推,建立volume。

建立完毕后,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
root@Ansible:/opt# ansible swarm -a "docker volume ls"
swarm03 | SUCCESS | rc=0 >>
DRIVER VOLUME NAME
local rsdata3
swarm05 | SUCCESS | rc=0 >>
DRIVER VOLUME NAME
local rsdata5
swarm01 | SUCCESS | rc=0 >>
DRIVER VOLUME NAME
local rsdata1
swarm04 | SUCCESS | rc=0 >>
DRIVER VOLUME NAME
local rsdata4
swarm02 | SUCCESS | rc=0 >>
DRIVER VOLUME NAME
local rsdata2

在swarm01(管理节点)上建立replica set各个节点服务

1
2
3
4
5
6
7
8
9
docker service create --replicas 1 --network mongo_network --mount type=volume,source=rsdata1,target=/data/db --constraint 'node.labels.mongo.rs==1' -p 27017:27017 --name mongo_rs1 mongo:3.6 mongod --replSet "whmallRS"

docker service create --replicas 1 --network mongo_network --mount type=volume,source=rsdata2,target=/data/db --constraint 'node.labels.mongo.rs==2' -p 27018:27017 --name mongo_rs2 mongo:3.6 mongod --replSet "whmallRS"

docker service create --replicas 1 --network mongo_network --mount type=volume,source=rsdata3,target=/data/db --constraint 'node.labels.mongo.rs==3' -p 27019:27017 --name mongo_rs3 mongo:3.6 mongod --replSet "whmallRS"

docker service create --replicas 1 --network mongo_network --mount type=volume,source=rsdata4,target=/data/db --constraint 'node.labels.mongo.rs==4' -p 27020:27017 --name mongo_rs4 mongo:3.6 mongod --replSet "whmallRS"

docker service create --replicas 1 --network mongo_network --mount type=volume,source=rsdata5,target=/data/db --constraint 'node.labels.mongo.rs==5' -p 27021:27017 --name mongo_rs5 mongo:3.6 mongod --replSet "whmallRS"

-p <发布端口>:<目标端口>
<目标端口>为Docker容器中所监听的端口,<发布端口>为Swarm集群中使得服务可以访问的端口。

–constraint ‘node.labels.mongo.rs==2’
这个参数作用是通过定义约束表达式来限制可以调度任务的节点,因为没做分布式存储,万一docker-swarm02上mongo.rs2这个服务飘到了docker-swarm05上去了,服务读取不到数据,那就懵逼了。
必须找行政妹子多搞几台PC做分布式存储,GlusterFS就不错。。。。。 必须的~~

看看状况

初始化MongoDB Replica Set

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
#初始化命令
docker exec -it $(docker ps -qf label=com.docker.swarm.service.name=mongo_rs1) mongo --eval 'rs.initiate( { _id : "whmallRS", members: [{ _id: 1, host: "mongo_rs1:27017" }, { _id: 2, host: "mongo_rs2:27017" }, { _id: 3, host: "mongo_rs3:27017" }, { _id: 4, host: "mongo_rs4:27017" }, { _id: 5, host: "mongo_rs5:27017" }], settings: { getLastErrorDefaults: { w: "majority", wtimeout: 30000 }}})'

MongoDB shell version v3.6.6
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 3.6.6
{
"ok" : 1,
"operationTime" : Timestamp(1531928549, 1),
"$clusterTime" : {
"clusterTime" : Timestamp(1531928549, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}


#查看replica set状态
docker exec -it $(docker ps -qf label=com.docker.swarm.service.name=mongo_rs1) mongo --eval 'rs.status()'

MongoDB shell version v3.6.6
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 3.6.6
{
"set" : "whmallRS",
"date" : ISODate("2018-07-18T15:44:54.605Z"),
"myState" : 1,
"term" : NumberLong(1),
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"heartbeatIntervalMillis" : NumberLong(2000),
"optimes" : {
"lastCommittedOpTime" : {
"ts" : Timestamp(1531928691, 1),
"t" : NumberLong(1)
},
"readConcernMajorityOpTime" : {
"ts" : Timestamp(1531928691, 1),
"t" : NumberLong(1)
},
"appliedOpTime" : {
"ts" : Timestamp(1531928691, 1),
"t" : NumberLong(1)
},
"durableOpTime" : {
"ts" : Timestamp(1531928691, 1),
"t" : NumberLong(1)
}
},
"members" : [
{
"_id" : 1,
"name" : "mongo_rs1:27017",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 1103,
"optime" : {
"ts" : Timestamp(1531928691, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2018-07-18T15:44:51Z"),
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"infoMessage" : "",
"electionTime" : Timestamp(1531928560, 1),
"electionDate" : ISODate("2018-07-18T15:42:40Z"),
"configVersion" : 1,
"self" : true,
"lastHeartbeatMessage" : ""
},
{
"_id" : 2,
"name" : "mongo_rs2:27017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 145,
"optime" : {
"ts" : Timestamp(1531928691, 1),
"t" : NumberLong(1)
},
"optimeDurable" : {
"ts" : Timestamp(1531928691, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2018-07-18T15:44:51Z"),
"optimeDurableDate" : ISODate("2018-07-18T15:44:51Z"),
"lastHeartbeat" : ISODate("2018-07-18T15:44:54.127Z"),
"lastHeartbeatRecv" : ISODate("2018-07-18T15:44:54.464Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "",
"syncingTo" : "mongo_rs1:27017",
"syncSourceHost" : "mongo_rs1:27017",
"syncSourceId" : 1,
"infoMessage" : "",
"configVersion" : 1
},
{
"_id" : 3,
"name" : "mongo_rs3:27017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 145,
"optime" : {
"ts" : Timestamp(1531928691, 1),
"t" : NumberLong(1)
},
"optimeDurable" : {
"ts" : Timestamp(1531928691, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2018-07-18T15:44:51Z"),
"optimeDurableDate" : ISODate("2018-07-18T15:44:51Z"),
"lastHeartbeat" : ISODate("2018-07-18T15:44:54.117Z"),
"lastHeartbeatRecv" : ISODate("2018-07-18T15:44:54.276Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "",
"syncingTo" : "mongo_rs1:27017",
"syncSourceHost" : "mongo_rs1:27017",
"syncSourceId" : 1,
"infoMessage" : "",
"configVersion" : 1
},
{
"_id" : 4,
"name" : "mongo_rs4:27017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 145,
"optime" : {
"ts" : Timestamp(1531928691, 1),
"t" : NumberLong(1)
},
"optimeDurable" : {
"ts" : Timestamp(1531928691, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2018-07-18T15:44:51Z"),
"optimeDurableDate" : ISODate("2018-07-18T15:44:51Z"),
"lastHeartbeat" : ISODate("2018-07-18T15:44:54.186Z"),
"lastHeartbeatRecv" : ISODate("2018-07-18T15:44:54.583Z"),
"pingMs" : NumberLong(1),
"lastHeartbeatMessage" : "",
"syncingTo" : "mongo_rs1:27017",
"syncSourceHost" : "mongo_rs1:27017",
"syncSourceId" : 1,
"infoMessage" : "",
"configVersion" : 1
},
{
"_id" : 5,
"name" : "mongo_rs5:27017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 145,
"optime" : {
"ts" : Timestamp(1531928691, 1),
"t" : NumberLong(1)
},
"optimeDurable" : {
"ts" : Timestamp(1531928691, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2018-07-18T15:44:51Z"),
"optimeDurableDate" : ISODate("2018-07-18T15:44:51Z"),
"lastHeartbeat" : ISODate("2018-07-18T15:44:54.119Z"),
"lastHeartbeatRecv" : ISODate("2018-07-18T15:44:54.401Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "",
"syncingTo" : "mongo_rs1:27017",
"syncSourceHost" : "mongo_rs1:27017",
"syncSourceId" : 1,
"infoMessage" : "",
"configVersion" : 1
}
],
"ok" : 1,
"operationTime" : Timestamp(1531928691, 1),
"$clusterTime" : {
"clusterTime" : Timestamp(1531928691, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}

这一个基于docker swarm的MongoDB Replica Set就成了。

以上适合新建MongoDB副本集的情况,要是原来就是单实例,数据也备份了,要升级为副本集怎么办啊,那就继续往下看吧。

还原备份数据到Replica Set中

备份数据先准备好,解压到volume在宿主机上的路径

启动一个单节点的副本集

先删除原来的所有服务,回到docker swarm刚搭建完毕的时候

1
2
3
4
5
6
7
8
9
10
11
12
13
#删除服务
docker service rm mongo_rs1 mongo_rs2 mongo_rs3 mongo_rs4 mongo_rs5

#确认swarm的节点状态

docker node ls

ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
wum4rkn8jk4qb97yib9kggirv * docker-swarm01 Ready Active Leader 18.03.1-ce
bimxst6zhx732ckule02fozrb docker-swarm02 Ready Active 18.03.1-ce
wnb80zj4ltg5fvfpdfel8dmoe docker-swarm03 Ready Active 18.03.1-ce
8hnm8uht550vuvljwk4zqym8u docker-swarm04 Ready Active 18.03.1-ce
9i2q8ovsca5lwfzkympnmdcm0 docker-swarm05 Ready Active 18.03.1-ce

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#启动一个单节点的副本集
docker service create --replicas 1 --network mongo_network --mount type=volume,src=rsdata1,dst=/data/db --constraint 'node.labels.mongo.rs==1' -p 27017:27017 --name mongo_rs1 mongo:3.6 mongod --replSet "whmallRS"

3idwgelktbji869q10z31fi0e
overall progress: 1 out of 1 tasks
1/1: running [==================================================>]
verify: Service converged


#初始化副本集
docker exec -it $(docker ps -qf label=com.docker.swarm.service.name=mongo_rs1) mongo --eval 'rs.initiate( { _id : "whmallRS", members: [ { _id : 1, host: "mongo_rs1:27017" } ], settings: { getLastErrorDefaults: { w: "majority", wtimeout: 30000 }}})'

MongoDB shell version v3.6.6
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 3.6.6
{
"ok" : 1,
"operationTime" : Timestamp(1531987463, 1),
"$clusterTime" : {
"clusterTime" : Timestamp(1531987463, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}

#查看副本集状态
docker exec -it $(docker ps -qf label=com.docker.swarm.service.name=mongo_rs1) mongo --eval 'rs.status()'

MongoDB shell version v3.6.6
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 3.6.6
{
"set" : "whmallRS",
"date" : ISODate("2018-07-19T08:06:30.690Z"),
"myState" : 1,
"term" : NumberLong(1),
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"heartbeatIntervalMillis" : NumberLong(2000),
"optimes" : {
"lastCommittedOpTime" : {
"ts" : Timestamp(1531987585, 1),
"t" : NumberLong(1)
},
"readConcernMajorityOpTime" : {
"ts" : Timestamp(1531987585, 1),
"t" : NumberLong(1)
},
"appliedOpTime" : {
"ts" : Timestamp(1531987585, 1),
"t" : NumberLong(1)
},
"durableOpTime" : {
"ts" : Timestamp(1531987585, 1),
"t" : NumberLong(1)
}
},
"members" : [
{
"_id" : 1,
"name" : "mongo_rs1:27017",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 875,
"optime" : {
"ts" : Timestamp(1531987585, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2018-07-19T08:06:25Z"),
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"infoMessage" : "",
"electionTime" : Timestamp(1531987463, 2),
"electionDate" : ISODate("2018-07-19T08:04:23Z"),
"configVersion" : 1,
"self" : true,
"lastHeartbeatMessage" : ""
}
],
"ok" : 1,
"operationTime" : Timestamp(1531987585, 1),
"$clusterTime" : {
"clusterTime" : Timestamp(1531987585, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}


#查看副本集配置
docker exec -it $(docker ps -qf label=com.docker.swarm.service.name=mongo_rs1) mongo --eval 'rs.config()'

MongoDB shell version v3.6.6
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 3.6.6
{
"_id" : "whmallRS",
"version" : 1,
"protocolVersion" : NumberLong(1),
"members" : [
{
"_id" : 1,
"host" : "mongo_rs1:27017",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 1,
"tags" : {


},
"slaveDelay" : NumberLong(0),
"votes" : 1
}
],
"settings" : {
"chainingAllowed" : true,
"heartbeatIntervalMillis" : 2000,
"heartbeatTimeoutSecs" : 10,
"electionTimeoutMillis" : 10000,
"catchUpTimeoutMillis" : -1,
"catchUpTakeoverDelayMillis" : 30000,
"getLastErrorModes" : {

},
"getLastErrorDefaults" : {
"w" : "majority",
"wtimeout" : 30000
},
"replicaSetId" : ObjectId("5b504606b5471294515460d0")
}
}

OK,现在往副本集里面添加新成员,不过要注意两点:

1、当新添加的辅助节点的投票和优先级设置大于零时,在其初始同步期间,辅助节点仍然计为投票成员,即使它不能提供读取也不能成为主节点,因为其数据尚未一致。

2、这可能导致大多数投票成员在线但不能选出主要成员的情况。要避免这种情况,请考虑最初新添加的成员的priority的值为0,同样votes的值也为0。然后,一旦成员转换到SECONDARY状态,使用rs.reconfig()更新其priority和votes的值。

好,余下四个节点,依次建立副本集节点的swarm service

1
2
3
4
5
6
7
docker service create --replicas 1 --network mongo_network --mount type=volume,src=rsdata2,dst=/data/db --constraint 'node.labels.mongo.rs==2' -p 27018:27017 --name mongo_rs2 mongo:3.6 mongod --replSet "whmallRS"

docker service create --replicas 1 --network mongo_network --mount type=volume,src=rsdata3,dst=/data/db --constraint 'node.labels.mongo.rs==3' -p 27019:27017 --name mongo_rs3 mongo:3.6 mongod --replSet "whmallRS"

docker service create --replicas 1 --network mongo_network --mount type=volume,src=rsdata4,dst=/data/db --constraint 'node.labels.mongo.rs==4' -p 27020:27017 --name mongo_rs4 mongo:3.6 mongod --replSet "whmallRS"

docker service create --replicas 1 --network mongo_network --mount type=volume,src=rsdata5,dst=/data/db --constraint 'node.labels.mongo.rs==5' -p 27021:27017 --name mongo_rs5 mongo:3.6 mongod --replSet "whmallRS"

添加mongo_rs2节点,数据自动开始同步

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#添加mongo_rs2到副本集中
docker exec -it $(docker ps -qf label=com.docker.swarm.service.name=mongo_rs1) mongo --eval 'rs.add( { host: "mongo_rs2:27017", priority: 0, votes: 0 } )'

MongoDB shell version v3.6.6
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 3.6.6
{
"ok" : 1,
"operationTime" : Timestamp(1531990143, 1),
"$clusterTime" : {
"clusterTime" : Timestamp(1531990143, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}

余下节点按照以上方法依次加入。这要等一段时间,四个节点要同步数据,够呛。

重新配置副本集成员的权重与投票参数

这里有几点要注意:
1、数据全部同步完毕后,注意这是节点的priority和votes的值都为零,需要做调整,调整过程可能会引起主节点重新选举,所以建议此步骤在维护窗口期进行哦。
2、建议在维护窗口期间调整优先级设置。重新配置优先级可以强制当前主服务器降级,从而导致选举。在选举之前,副本集的主节点会关闭所有打开的客户端连接。除非你更改了副本集的默认读策略。
3、rs.reconfig()shell方法可以强制当前主节点降级,从而导致选举。当副本集主节点关闭时,mongod将关闭所有客户端连接。虽然这通常只需要10-20秒,但是建议在计划的维护期间进行这些更改。 避免重新配置包含不同MongoDB版本成员的副本集,因为验证规则可能因MongoDB版本而异。

1
2
3
4
cfg = rs.conf();
cfg.members[1].priority = 2;
cfg.members[1].votes = 1;
rs.reconfig(cfg);

第一个语句使用rs.conf()方法检索包含副本集的当前配置的文档,并将文档设置为本地变量cfg。
第二个语句将members [n] .priority值设置为members数组中的第二个文档。有关其他设置,请参阅副本集配置设置。 要访问数组中的成员配置文档,该语句使用数组索引而不是副本集成员的成员[n] ._ id字段。
最后一条语句使用修改过的cfg调用rs.reconfig()方法来初始化此新配置。

总结

  1. 使用分布式存储文件系统是一个好主意,如ceph、Gluster等。
  2. overlay网络中要确保运行副本集的跨主机容器能互相ping通,副本集依靠这个来检查节点心跳。
  3. 官方文档是必须看的。
0%