Kubernetes部署MongoDB副本集最佳实践

问题

  • 为什么要进行mongodb的高可用部署?

在实际的生产环境当中,mongo作为一种非关系型数据库,在项目中是非常重要的角色。 我选择高可用的部署主要初衷是当前项目的mongodb通过kubernetes单个pod部署,而上一任运维居然连admin账户都没有! 最近的一次发现它的日志文件大小达到了20多个GB,CPU利用率持续在100%,而在一些特殊时段开发的操作下,达到300%。 由于没有admin账户,又没有部署文档,不敢随意去修改它的配置甚至重启。

通过kubernetes部署的mongo相比传统单机部署可用性较高一点也有存储以及服务不可用的情况出现,那么有没有存储服务都高可用的模式?

2021-08-11T02:17:09.png

图:运行在多个可用区上的副本集

mongodb高可用模式

此小结内容参考,略有改动《【MongoDB进阶】MongoDB高可用集群配置的几种方案》

mongodb的部署方式有:

  • Standalone单节点部署

此种部署方式就是最简单易用并且常见的部署了,直接使用mongod起来一个进程。

  • Master-Slave主从结构

主从架构一般用于备份或者做读写分离。一般有一主一从设计和一主多从设计。 2021-08-11T02:23:17.png 主(Master)

可读可写,当数据有修改的时候,会将oplog同步到所有连接的salve上去。

从(Slave)

只读不可写,自动从Master同步数据。

特别的,对于Mongodb来说,并不推荐使用Master-Slave架构,因为Master-Slave其中Master宕机后不能自动恢复,推荐使用Replica Set,后面会有介绍,除非Replica的节点数超过50,才需要使用Master-Slave架构,正常情况是不可能用那么多节点的。

还有一点,Master-Slave不支持链式结构,Slave只能直接连接Master。Redis的Master-Slave支持链式结构,Slave可以连接Slave,成为Slave的Slave。

  • Relica Set副本集

Mongodb的Replica Set即副本集方式主要有两个目的,一个是数据冗余做故障恢复使用,当发生硬件故障或者其它原因造成的宕机时,可以使用副本进行恢复。

另一个是做读写分离,读的请求分流到副本上,减轻主(Primary)的读压力。

Primary和Secondary搭建的Replica Set

2021-08-11T02:25:16.png

Replica Set是mongod的实例集合,它们有着同样的数据内容。包含三类角色:

主节点(Primary)

接收所有的写请求,然后把修改同步到所有Secondary。一个Replica Set只能有一个Primary节点,当Primary挂掉后,其他Secondary或者Arbiter节点会重新选举出来一个主节点。默认读请求也是发到Primary节点处理的,需要转发到Secondary需要客户端修改一下连接配置。

副本节点(Secondary)

与主节点保持同样的数据集。当主节点挂掉的时候,参与选主。

仲裁者(Arbiter)

不保有数据,不参与选主,只进行选主投票。使用Arbiter可以减轻数据存储的硬件需求,Arbiter跑起来几乎没什么大的硬件资源需求,但重要的一点是,在生产环境下它和其他数据节点不要部署在同一台机器上。

注意,一个自动failover的Replica Set节点数必须为奇数,目的是选主投票的时候要有一个大多数才能进行选主决策。

选主过程

其中Secondary宕机,不受影响,若Primary宕机,会进行重新选主 2021-08-11T02:33:07.png

使用Arbiter搭建Replica Set

偶数个数据节点,加一个Arbiter构成的Replica Set方式 2021-08-11T02:35:09.png

  • shard Cluster数据分片

当数据量比较大的时候,我们需要把数据分片运行在不同的机器中,以降低CPU、内存和IO的压力,Sharding就是数据库分片技术。

MongoDB分片技术类似MySQL的水平切分和垂直切分,数据库主要由两种方式做Sharding:垂直扩展和横向切分。

垂直扩展的方式就是进行集群扩展,添加更多的CPU,内存,磁盘空间等。

横向切分则是通过数据分片的方式,通过集群统一提供服务:

2021-08-11T02:36:16.png

MongoDB的Sharding架构 2021-08-11T02:37:33.png

MongoDB分片架构中的角色 数据分片(Shards)

用来保存数据,保证数据的高可用性和一致性。可以是一个单独的mongod实例,也可以是一个副本集。

在生产环境下Shard一般是一个Replica Set,以防止该数据片的单点故障。所有Shard中有一个PrimaryShard,里面包含未进行划分的数据集合: 2021-08-11T02:38:16.png

查询路由(Query Routers)

路由就是mongos的实例,客户端直接连接mongos,由mongos把读写请求路由到指定的Shard上去。

一个Sharding集群,可以有一个mongos,也可以有多mongos以减轻客户端请求的压力。

配置服务器(Config servers)

保存集群的元数据(metadata),包含各个Shard的路由规则。

部署

  • 选型

根据业务综合考虑我们选择继续在Kubernetes部署的,使用MongoDB副本集(Replica Set)方案,一个Primary,两个Secondary,存储选择阿里云的NAS文件存储。 对于有状态的服务,主要由StatefulSetsPersistent Volumes实现。

对于部署,mongodb官方有文档Deploy a Replica Set。但我最终还是选择了《Kubernetes部署Redis集群最佳实践》类似的方式,简单易用。

pv的建立

需要建立3个pv,这里存储选择了阿里云的NAS,accessModes选择ReadWriteOnce,磁盘回收策略选择Retain。

上次没有解释pv访问模式和回收模式,简单说一下

  1. ReadWriteOnce:读写权限,仅被单个node挂载
  2. ReadOnlyMany:只读,可被多个node挂载
  3. ReadWriteMany:读写权限,可被多个node挂载

在cli中,访问模式被简化为:

  1. RWO - readWriteOnce
  2. ROX - readOnlyMany
  3. RWX - readWriteMany

回收模式:

  1. Retain:不清理保留数据。即删除pvc或者pv后,在插件上的数据不会被删除。这种方式是最常用的,可以避免误删pvc或者pv而造成数据的丢失。
  2. Recycle:不保留数据。经测试pvc删除后,文件服务端的数据也会随机删除。只有hostPath和NFS支持这种方式
  3. Delete:删除存储资源,AWS EBS, GCE PD, Azure Disk, and Cinder volumes支持这种方式。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
[[email protected] mongodb]$ kubectl apply -f mongod-pv.yml
persistentvolume/mongodb-pv1 created
persistentvolume/mongodb-pv2 created
persistentvolume/mongodb-pv3 created
[[email protected] mongodb]$ kubectl get pv 
NAME                                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM                        STORAGECLASS               REASON   AGE
mongodb-pv1                                                120Gi      RWO            Retain           Available                                mongodb-rs-storage                  14s
mongodb-pv2                                                120Gi      RWO            Retain           Available                                mongodb-rs-storage                  14s
mongodb-pv3                                                120Gi      RWO            Retain           Available                                mongodb-rs-storage                  14s
[[email protected] mongodb]$

redis-pv.yaml

 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
apiVersion: v1
kind: PersistentVolume
metadata:
  finalizers:
    - kubernetes.io/pv-protection
  labels:
    alicloud-pvname: mongodb-pv1
  name: mongodb-pv1
spec:
  capacity:
    storage: 300Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: mongodb-rs-storage
  csi:
    driver: nasplugin.csi.alibabacloud.com
    volumeAttributes:
      path: /data/mongodb/pv1
      server: xxxxxxxxxxxx.cn-beijing.nas.aliyuncs.com
      vers: "3"
    volumeHandle: mongodb-pv1
---
apiVersion: v1
kind: PersistentVolume
metadata:
  finalizers:
    - kubernetes.io/pv-protection
  labels:
    alicloud-pvname: mongodb-pv2
  name: mongodb-pv2
spec:
  capacity:
    storage: 300Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: mongodb-rs-storage
  csi:
    driver: nasplugin.csi.alibabacloud.com
    volumeAttributes:
      path: /data/mongodb/pv2
      server: xxxxxxxxxxxx.cn-beijing.nas.aliyuncs.com
      vers: "3"
    volumeHandle: mongodb-pv2
---
apiVersion: v1
kind: PersistentVolume
metadata:
  finalizers:
    - kubernetes.io/pv-protection
  labels:
    alicloud-pvname: mongodb-pv3
  name: mongodb-pv3
spec:
  capacity:
    storage: 300Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: mongodb-rs-storage
  csi:
    driver: nasplugin.csi.alibabacloud.com
    volumeAttributes:
      path: /data/mongodb/pv3
      server: xxxxxxxxxxxx.cn-beijing.nas.aliyuncs.com
      vers: "3"
    volumeHandle: mongodb-pv3

statefulset的建立

configmap也一同建立,这里以默认的namespace进行的部署,实际情况因选择自己的namespace。 这里要注意,keyfile权限设置600否则无法启动mongod。 fork如果配置true的话,在启动命令后我写了sleep infinity,否则容器立马会退出。

1
2
3
4
5
6
7
8
[[email protected] mongodb]$ kubectl apply -f mongod-sts.yml
configmap/mongodb-rs-cm created
statefulset.apps/mongodb-rs created
[[email protected] mongodb]$ kubectl get pod
NAME                                READY   STATUS    RESTARTS   AGE
mongodb-rs-0                        1/1     Running   0          22s
mongodb-rs-1                        1/1     Running   0          20s
mongodb-rs-2                        1/1     Running   0          17s

redis-sts.yaml

 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
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: mongodb-rs-cm
data:
  keyfile: |
        dGhpcyBpcyBycyBzdXBlciBzZWNyZXQga2V5Cg==
  mongod_rs.conf: |+
    systemLog:
      destination: file
      logAppend: true
      path: /data/mongod.log
    storage:
      dbPath: /data
      journal:
        enabled: true
      directoryPerDB: true
      wiredTiger:
        engineConfig:
          cacheSizeGB: 2
          directoryForIndexes: true
    processManagement:
      fork: true
      pidFilePath: /data/mongod.pid
    net:
      port: 27017
      bindIp: 0.0.0.0
      maxIncomingConnections: 5000
    security:
      keyFile: /data/configdb/keyfile
      authorization: enabled
    replication:
      oplogSizeMB: 1024
      replSetName: rs_prod    
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mongodb-rs
spec:
  serviceName: mongodb-rs
  replicas: 3
  selector:
    matchLabels:
      app: mongodb-rs
  template:
    metadata:
      labels:
        app: mongodb-rs
    spec:
      containers:
        - name: mongo
          image: mongo:4.4.1
          ports:
            - containerPort: 27017
              name: client
          command: ["sh"]
          args:
            - "-c"
            - |
              set -ex
              mongod --config /data/configdb/mongod_rs.conf
              sleep infinity              
          env:
            - name: POD_IP
              valueFrom:
                fieldRef:
                  fieldPath: status.podIP
          volumeMounts:
            - name: conf
              mountPath: /data/configdb
              readOnly: false
            - name: data
              mountPath: /data
              readOnly: false
      volumes:
        - name: conf
          configMap:
            name: mongodb-rs-cm
            defaultMode: 0600
  volumeClaimTemplates:
    - metadata:
        name: data
      spec:
        accessModes: ["ReadWriteOnce"]
        resources:
          requests:
            storage: 300Gi
        storageClassName: mongodb-rs-storage

service的建立

经过上面的步骤,已经建立了pod了,此时pod还不能被外部访问。 先创建一个ClusterIP类型的Service,用以达到被其他pod访问的目的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
[[email protected] mongodb]$ kubectl apply -f mongod-svc.yml
service/mongodb-rs created
[[email protected] mongodb]$ kubectl describe svc mongodb-rs
Name:              mongodb-rs
Namespace:         default
Labels:            <none>
Annotations:       <none>
Selector:          app=mongodb-rs
Type:              ClusterIP
IP Families:       <none>
IP:                172.21.7.221
IPs:               <none>
Port:              client  27017/TCP
TargetPort:        27017/TCP
Endpoints:         10.0.0.144:27017,10.0.0.204:27017,10.0.1.22:27017
Session Affinity:  None
Events:            <none>

可以看到Endpoints是有三个节点的。

现在我们可以通过域名的方式来设置replica set,也不用关心pod会变化的ip地址了。 域名为:$(podname).$(service name).$(namespace).svc.cluster.local 可以进行一次验证,mongodb-rs-0通过域名连接mongodb-rs-2

1
2
3
4
5
6
[[email protected] mongodb]$ kubectl exec -it mongodb-rs-0 -- mongo mongodb-rs-2.mongodb-rs.default.svc.cluster.local
MongoDB shell version v4.4.1
connecting to: mongodb://mongodb-rs-2.mongodb-rs.default.svc.cluster.local:27017/test?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("e39c8cc1-dbbc-465b-9c85-744b5d9052e4") }
MongoDB server version: 4.4.1
>

可以看到是连通的。

设置

  • 建立rs

上面已经完成了rs的部署,其实是通过StatefulSet建立了三个pod内运行mongod服务,此时三个mongod是没有任何联系的,需要进行一些设置,我的配置如下。

1
2
3
4
5
6
7
8
config = { _id:"rs_prod", members:[
                     {_id:0,host:"mongodb-rs-0.mongodb-rs.default.svc.cluster.local:27017",priority:90}, 
                     {_id:1,host:"mongodb-rs-1.mongodb-rs.default.svc.cluster.local:27017",priority:80}, 
                     {_id:2,host:"mongodb-rs-2.mongodb-rs.default.svc.cluster.local:27017",priority:70}
    ]
}
#初始化
rs.initiate(config);

可以在任意的pod内进行初始化设置,这里已mongodb-rs-0为例

  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
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
[[email protected] mongodb]$ kubectl exec -it mongodb-rs-0 -- mongo
MongoDB shell version v4.4.1
connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("4dc55a20-3b9b-4eba-8c24-615502208339") }
MongoDB server version: 4.4.1
Welcome to the MongoDB shell.
For interactive help, type "help".
For more comprehensive documentation, see
        https://docs.mongodb.com/
Questions? Try the MongoDB Developer Community Forums
        https://community.mongodb.com
> config = { _id:"rs_prod", members:[
...                      {_id:0,host:"mongodb-rs-0.mongodb-rs.default.svc.cluster.local:27017",priority:90},
...                      {_id:1,host:"mongodb-rs-1.mongodb-rs.default.svc.cluster.local:27017",priority:80},
...                      {_id:2,host:"mongodb-rs-2.mongodb-rs.default.svc.cluster.local:27017",priority:70}
...     ]
... }
{
        "_id" : "rs_prod",
        "members" : [
                {
                        "_id" : 0,
                        "host" : "mongodb-rs-0.mongodb-rs.default.svc.cluster.local:27017",
                        "priority" : 90
                },
                {
                        "_id" : 1,
                        "host" : "mongodb-rs-1.mongodb-rs.default.svc.cluster.local:27017",
                        "priority" : 80
                },
                {
                        "_id" : 2,
                        "host" : "mongodb-rs-2.mongodb-rs.default.svc.cluster.local:27017",
                        "priority" : 70
                }
        ]
}
> rs.initiate(config);#初始化
{ "ok" : 1 }
rs_prod:SECONDARY>  rs.status()#rs状态查看
{
        "set" : "rs_prod",
        "date" : ISODate("2021-08-11T05:57:44.038Z"),
        "myState" : 1,
        "term" : NumberLong(1),
        "syncSourceHost" : "",
        "syncSourceId" : -1,
        "heartbeatIntervalMillis" : NumberLong(2000),
        "majorityVoteCount" : 2,
        "writeMajorityCount" : 2,
        "votingMembersCount" : 3,
        "writableVotingMembersCount" : 3,
        "optimes" : {
                "lastCommittedOpTime" : {
                        "ts" : Timestamp(1628661451, 2),
                        "t" : NumberLong(1)
                },
                "lastCommittedWallTime" : ISODate("2021-08-11T05:57:31.747Z"),
                "readConcernMajorityOpTime" : {
                        "ts" : Timestamp(1628661451, 2),
                        "t" : NumberLong(1)
                },
                "readConcernMajorityWallTime" : ISODate("2021-08-11T05:57:31.747Z"),
                "appliedOpTime" : {
                        "ts" : Timestamp(1628661451, 2),
                        "t" : NumberLong(1)
                },
                "durableOpTime" : {
                        "ts" : Timestamp(1628661451, 2),
                        "t" : NumberLong(1)
                },
                "lastAppliedWallTime" : ISODate("2021-08-11T05:57:31.747Z"),
                "lastDurableWallTime" : ISODate("2021-08-11T05:57:31.747Z")
        },
        "lastStableRecoveryTimestamp" : Timestamp(1628661450, 3),
        "electionCandidateMetrics" : {
                "lastElectionReason" : "electionTimeout",
                "lastElectionDate" : ISODate("2021-08-11T05:57:30.718Z"),
                "electionTerm" : NumberLong(1),
                "lastCommittedOpTimeAtElection" : {
                        "ts" : Timestamp(0, 0),
                        "t" : NumberLong(-1)
                },
                "lastSeenOpTimeAtElection" : {
                        "ts" : Timestamp(1628661440, 1),
                        "t" : NumberLong(-1)
                },
                "numVotesNeeded" : 2,
                "priorityAtElection" : 90,
                "electionTimeoutMillis" : NumberLong(10000),
                "numCatchUpOps" : NumberLong(0),
                "newTermStartDate" : ISODate("2021-08-11T05:57:30.910Z"),
                "wMajorityWriteAvailabilityDate" : ISODate("2021-08-11T05:57:31.597Z")
        },
        "members" : [
                {
                        "_id" : 0,
                        "name" : "mongodb-rs-0.mongodb-rs.default.svc.cluster.local:27017",
                        "health" : 1,
                        "state" : 1,
                        "stateStr" : "PRIMARY",
                        "uptime" : 69,
                        "optime" : {
                                "ts" : Timestamp(1628661451, 2),
                                "t" : NumberLong(1)
                        },
                        "optimeDate" : ISODate("2021-08-11T05:57:31Z"),
                        "syncSourceHost" : "",
                        "syncSourceId" : -1,
                        "infoMessage" : "Could not find member to sync from",
                        "electionTime" : Timestamp(1628661450, 1),
                        "electionDate" : ISODate("2021-08-11T05:57:30Z"),
                        "configVersion" : 1,
                        "configTerm" : 1,
                        "self" : true,
                        "lastHeartbeatMessage" : ""
                },
                {
                        "_id" : 1,
                        "name" : "mongodb-rs-1.mongodb-rs.default.svc.cluster.local:27017",
                        "health" : 1,
                        "state" : 2,
                        "stateStr" : "SECONDARY",
                        "uptime" : 23,
                        "optime" : {
                                "ts" : Timestamp(1628661451, 2),
                                "t" : NumberLong(1)
                        },
                        "optimeDurable" : {
                                "ts" : Timestamp(1628661451, 2),
                                "t" : NumberLong(1)
                        },
                        "optimeDate" : ISODate("2021-08-11T05:57:31Z"),
                        "optimeDurableDate" : ISODate("2021-08-11T05:57:31Z"),
                        "lastHeartbeat" : ISODate("2021-08-11T05:57:42.794Z"),
                        "lastHeartbeatRecv" : ISODate("2021-08-11T05:57:42.357Z"),
                        "pingMs" : NumberLong(1),
                        "lastHeartbeatMessage" : "",
                        "syncSourceHost" : "mongodb-rs-2.mongodb-rs.default.svc.cluster.local:27017",
                        "syncSourceId" : 2,
                        "infoMessage" : "",
                        "configVersion" : 1,
                        "configTerm" : 1
                },
                {
                        "_id" : 2,
                        "name" : "mongodb-rs-2.mongodb-rs.default.svc.cluster.local:27017",
                        "health" : 1,
                        "state" : 2,
                        "stateStr" : "SECONDARY",
                        "uptime" : 23,
                        "optime" : {
                                "ts" : Timestamp(1628661451, 2),
                                "t" : NumberLong(1)
                        },
                        "optimeDurable" : {
                                "ts" : Timestamp(1628661451, 2),
                                "t" : NumberLong(1)
                        },
                        "optimeDate" : ISODate("2021-08-11T05:57:31Z"),
                        "optimeDurableDate" : ISODate("2021-08-11T05:57:31Z"),
                        "lastHeartbeat" : ISODate("2021-08-11T05:57:42.781Z"),
                        "lastHeartbeatRecv" : ISODate("2021-08-11T05:57:43.864Z"),
                        "pingMs" : NumberLong(0),
                        "lastHeartbeatMessage" : "",
                        "syncSourceHost" : "mongodb-rs-0.mongodb-rs.default.svc.cluster.local:27017",
                        "syncSourceId" : 0,
                        "infoMessage" : "",
                        "configVersion" : 1,
                        "configTerm" : 1
                }
        ],
        "ok" : 1,
        "$clusterTime" : {
                "clusterTime" : Timestamp(1628661451, 2),
                "signature" : {
                        "hash" : BinData(0,"PogzvNGUxIB2BZuoDYEL7Cus4ws="),
                        "keyId" : NumberLong("6995047664005939203")
                }
        },
        "operationTime" : Timestamp(1628661451, 2)
}
rs_prod:PRIMARY>  rs.isMaster()#rs主节点判断
{
        "topologyVersion" : {
                "processId" : ObjectId("61136693184d475955573a94"),
                "counter" : NumberLong(6)
        },
        "hosts" : [
                "mongodb-rs-0.mongodb-rs.default.svc.cluster.local:27017",
                "mongodb-rs-1.mongodb-rs.default.svc.cluster.local:27017",
                "mongodb-rs-2.mongodb-rs.default.svc.cluster.local:27017"
        ],
        "setName" : "rs_prod",
        "setVersion" : 1,
        "ismaster" : true,
        "secondary" : false,
        "primary" : "mongodb-rs-0.mongodb-rs.default.svc.cluster.local:27017",
        "me" : "mongodb-rs-0.mongodb-rs.default.svc.cluster.local:27017",
        "electionId" : ObjectId("7fffffff0000000000000001"),
        "lastWrite" : {
                "opTime" : {
                        "ts" : Timestamp(1628661470, 1),
                        "t" : NumberLong(1)
                },
                "lastWriteDate" : ISODate("2021-08-11T05:57:50Z"),
                "majorityOpTime" : {
                        "ts" : Timestamp(1628661470, 1),
                        "t" : NumberLong(1)
                },
                "majorityWriteDate" : ISODate("2021-08-11T05:57:50Z")
        },
        "maxBsonObjectSize" : 16777216,
        "maxMessageSizeBytes" : 48000000,
        "maxWriteBatchSize" : 100000,
        "localTime" : ISODate("2021-08-11T05:57:54.335Z"),
        "logicalSessionTimeoutMinutes" : 30,
        "connectionId" : 1,
        "minWireVersion" : 0,
        "maxWireVersion" : 9,
        "readOnly" : false,
        "ok" : 1,
        "$clusterTime" : {
                "clusterTime" : Timestamp(1628661470, 1),
                "signature" : {
                        "hash" : BinData(0,"d41oUe+J2tOqraTS8BF5khzM7rA="),
                        "keyId" : NumberLong("6995047664005939203")
                }
        },
        "operationTime" : Timestamp(1628661470, 1)
}
rs_prod:PRIMARY>

如上返回的信息一切都正常!

  • 设置账户 创建admin管理账户,并赋予权限。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
rs_prod:PRIMARY> use admin
switched to db admin
rs_prod:PRIMARY> db.createUser({user:"admin", pwd:"YOU_PASSWORD", roles:[{role: "userAdminAnyDatabase", db:"admin" }]})
Successfully added user: {
        "user" : "admin",
        "roles" : [
                {
                        "role" : "userAdminAnyDatabase",
                        "db" : "admin"
                }
        ]
}
rs_prod:PRIMARY> db.auth('admin','YOU_PASSWORD')
1
rs_prod:PRIMARY> db.grantRolesToUser("admin", ["clusterAdmin"])
rs_prod:PRIMARY>

至此完成了admin账户的设置。

还需要设置日常使用的账户

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
rs_prod:PRIMARY> use course
switched to db course
rs_prod:PRIMARY> db.createUser(
...    {
...      user: "course",
...      pwd: "course_passwd",
...      roles: [ { role: "readWrite", db: "course" } ]
...    }
...  )
Successfully added user: {
        "user" : "course",
        "roles" : [
                {
                        "role" : "readWrite",
                        "db" : "course"
                }
        ]
}

完成设置。

测试

  • 读写

默认情况下,PRIMARY节点有读写权限,SECONDARY节点没有读写权限。可以分别验证一下。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
[[email protected] ~]$ kubectl exec -it mongodb-rs-0 -- mongo
MongoDB shell version v4.4.1
connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("04f552a7-8898-4e97-b99a-c8c9431f4c71") }
MongoDB server version: 4.4.1
rs_prod:PRIMARY> use course
switched to db course
rs_prod:PRIMARY> db.auth('course','course2020!')
1
rs_prod:PRIMARY> db.test.insert({"KEY":"VALUE"})
WriteResult({ "nInserted" : 1 })
rs_prod:PRIMARY> db.test.find()
{ "_id" : ObjectId("6114879d86cabff797a948a9"), "KEY" : "VALUE" }
rs_prod:PRIMARY>

PRIMARY节点成功读写,再来看看SECONDARY节点。

 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
[[email protected] ~]$ kubectl exec -it mongodb-rs-0 -- mongo --host mongodb-rs-2.mongodb-rs.default.svc.cluster.local
MongoDB shell version v4.4.1
connecting to: mongodb://mongodb-rs-2.mongodb-rs.default.svc.cluster.local:27017/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("1373b7b7-b6dc-45f9-96c2-d6fdc15dfc1b") }
MongoDB server version: 4.4.1
rs_prod:SECONDARY> use course
switched to db course
rs_prod:SECONDARY> db.auth('course','course2020!')
1
rs_prod:SECONDARY> db.mongo.find()
Error: error: {
        "topologyVersion" : {
                "processId" : ObjectId("611366966100f176bb97579a"),
                "counter" : NumberLong(16)
        },
        "operationTime" : Timestamp(1628735559, 1),
        "ok" : 0,
        "errmsg" : "not master and slaveOk=false",
        "code" : 13435,
        "codeName" : "NotMasterNoSlaveOk",
        "$clusterTime" : {
                "clusterTime" : Timestamp(1628735559, 1),
                "signature" : {
                        "hash" : BinData(0,"aYGB7cJqIn0BeFD2ZoTLzND2B9E="),
                        "keyId" : NumberLong("6995047664005939203")
                }
        }
}

可以看到SECONDARY节点可以成功使用账户密码登录,而进行查询时提示不是master并且slaveOk也没打开。 此时使用rs.secondaryOk();可以临时打开读权限。

  • 模拟故障 待更新…
Licensed under CC BY-NC-SA 4.0
最后更新于 Aug 12, 2021 14:27 UTC
点击刷新🚌