Mongodb高可用模式
mongodb的部署方式有:
Standalone单节点部署
此种部署方式就是最简单易用并且常见的部署了,直接使用mongod起来一个进程。
Master-Slave主从结构
主从架构一般用于备份或者做读写分离。一般有一主一从设计和一主多从设计。 
可读可写,当数据有修改的时候,会将oplog同步到所有连接的salve上去。
只读不可写,自动从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

Replica Set是mongod的实例集合,它们有着同样的数据内容。包含三类角色:
接收所有的写请求,然后把修改同步到所有Secondary。一个Replica Set只能有一个Primary节点,当Primary挂掉后,其他Secondary或者Arbiter节点会重新选举出来一个主节点。默认读请求也是发到Primary节点处理的,需要转发到Secondary需要客户端修改一下连接配置。
与主节点保持同样的数据集。当主节点挂掉的时候,参与选主。
不保有数据,不参与选主,只进行选主投票。使用Arbiter可以减轻数据存储的硬件需求,Arbiter跑起来几乎没什么大的硬件资源需求,但重要的一点是,在生产环境下它和其他数据节点不要部署在同一台机器上。
注意,一个自动failover的Replica Set节点数必须为奇数,目的是选主投票的时候要有一个大多数才能进行选主决策。
其中Secondary宕机,不受影响,若Primary宕机,会进行重新选主 
使用Arbiter搭建Replica Set
偶数个数据节点,加一个Arbiter构成的Replica Set方式 
当数据量比较大的时候,我们需要把数据分片运行在不同的机器中,以降低CPU、内存和IO的压力,Sharding就是数据库分片技术。
MongoDB分片技术类似MySQL的水平切分和垂直切分,数据库主要由两种方式做Sharding:垂直扩展和横向切分。
垂直扩展的方式就是进行集群扩展,添加更多的CPU,内存,磁盘空间等。
横向切分则是通过数据分片的方式,通过集群统一提供服务:

MongoDB的Sharding架构

MongoDB分片架构中的角色
用来保存数据,保证数据的高可用性和一致性。可以是一个单独的mongod实例,也可以是一个副本集。
在生产环境下Shard一般是一个Replica Set,以防止该数据片的单点故障。所有Shard中有一个PrimaryShard,里面包含未进行划分的数据集合: 
路由就是mongos的实例,客户端直接连接mongos,由mongos把读写请求路由到指定的Shard上去。
一个Sharding集群,可以有一个mongos,也可以有多mongos以减轻客户端请求的压力。
保存集群的元数据(metadata),包含各个Shard的路由规则。
安装部署单节点Mongo
创建 NFS 存储
NFS 存储主要是为了给 MongoDB 提供稳定的后端存储,当 MongoDB 的 Pod 发生故障重启或迁移后,依然能获得原先的数据。
记得保证所有可部署pod的节点都要安装nfs服务端/客户端
mongo默认配置(小记)
| 12
 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
 
 | storage:dbPath: /var/lib/mongodb
 journal:
 enabled: true
 
 
 
 
 systemLog:
 destination: file
 logAppend: true
 path: /var/log/mongodb/mongod.log
 
 net:
 port: 27017
 bindIp: 127.0.0.1
 
 processManagement:
 timeZoneInfo: /usr/share/zoneinfo
 
 
 
 
 
 
 
 
 
 | 
部署 MongoDB 应用服务
(1)首先创建一个名为 mongodb.yaml 的配置文件,文件里的内容如下:
| 12
 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
 
 | apiVersion: v1
 kind: PersistentVolume
 metadata:
 name: mongodb-pv
 namespace: middleware
 spec:
 capacity:
 storage: 1Gi
 accessModes:
 - ReadWriteMany
 persistentVolumeReclaimPolicy: Retain
 storageClassName: nfs-mongodb
 nfs:
 path: /data/k8s
 server: 10.1.4.13
 ---
 
 kind: PersistentVolumeClaim
 apiVersion: v1
 metadata:
 name: mongodb-pvc
 namespace: middleware
 spec:
 accessModes:
 - ReadWriteMany
 resources:
 requests:
 storage: 1Gi
 storageClassName: nfs-mongodb
 ---
 
 apiVersion: v1
 kind: Service
 metadata:
 name: mongodb-svc
 namespace: middleware
 spec:
 type: NodePort
 ports:
 - name: mongo
 port: 27017
 targetPort: 27017
 nodePort: 30017
 protocol: TCP
 selector:
 app: mongodb
 ---
 
 apiVersion: apps/v1
 kind: Deployment
 metadata:
 name: mongo-deploy
 namespace: middleware
 spec:
 replicas: 1
 strategy:
 type: Recreate
 selector:
 matchLabels:
 app: mongodb
 template:
 metadata:
 labels:
 app: mongodb
 spec:
 containers:
 - name: mongodb
 image: mongo:4.4.13
 imagePullPolicy: IfNotPresent
 ports:
 - containerPort: 27017
 volumeMounts:
 - name: mongo-pvc
 mountPath: /data/db
 subPath: mongo-data
 - name: mongo-config
 mountPath: /etc/mongod.conf
 subPath: mongo-data
 volumes:
 - name: mongo-pvc
 persistentVolumeClaim:
 claimName: mongodb-pvc
 - name: mongo-config
 persistentVolumeClaim:
 claimName: mongodb-pvc
 
 | 
(2)接着执行如下命令对这个 YAML 文件进行部署:
| 1
 | kubectl apply -f mongodb.yaml
 | 
(3)稍等片刻,执行如下命令可以查看是否创建成功:
| 12
 3
 
 | kubectl get pv kubectl get pods
 kubectl get service
 
 | 

访问测试
(1)我们使用客户端工具连接上我们刚刚创建的 MongoDB,然后创建一个 hangge 数据库以及 test 集合,并给集合中插入一些数据,说明 MongoDB 部署成功。

(2)接着执行如下命令强制重启 pod,重启后再次查看数据库可以发现数据没有丢失,说明数据持久化也是成功的。
| 1
 | kubectl replace --force -f mongodb.yaml
 | 
安装部署Mongo - Relica Set副本集(3节点)
Headless Service
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | apiVersion: v1kind: Service
 metadata:
 name: mongodb-rs
 namespace: middleware
 spec:
 publishNotReadyAddresses: false
 clusterIP: None
 selector:
 app: mongodb-rs
 
 | 
ConfigMap
| 12
 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
 
 | apiVersion: v1kind: ConfigMap
 metadata:
 name: mongodb-rs-cm
 namespace: middleware
 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
 
 | 
PV
| 12
 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
 
 | apiVersion: v1kind: PersistentVolume
 metadata:
 finalizers:
 - kubernetes.io/pv-protection
 labels:
 mongo-pvname: mongodb-pv0
 name: mongodb-pv0
 namespace: middleware
 spec:
 capacity:
 storage: 5Gi
 accessModes:
 - ReadWriteOnce
 persistentVolumeReclaimPolicy: Retain
 storageClassName: mongodb-rs-storage
 nfs:
 path: /data/k8s/mongo-data0
 server: 10.1.4.13
 readOnly: false
 ---
 apiVersion: v1
 kind: PersistentVolume
 metadata:
 finalizers:
 - kubernetes.io/pv-protection
 labels:
 mongo-pvname: mongodb-pv1
 name: mongodb-pv1
 namespace: middleware
 spec:
 capacity:
 storage: 5Gi
 accessModes:
 - ReadWriteOnce
 persistentVolumeReclaimPolicy: Retain
 storageClassName: mongodb-rs-storage
 nfs:
 path: /data/k8s/mongo-data1
 server: 10.1.4.13
 readOnly: false
 ---
 apiVersion: v1
 kind: PersistentVolume
 metadata:
 finalizers:
 - kubernetes.io/pv-protection
 labels:
 mongo-pvname: mongodb-pv2
 name: mongodb-pv2
 namespace: middleware
 spec:
 capacity:
 storage: 5Gi
 accessModes:
 - ReadWriteOnce
 persistentVolumeReclaimPolicy: Retain
 storageClassName: mongodb-rs-storage
 nfs:
 path: /data/k8s/mongo-data2
 server: 10.1.4.13
 readOnly: false
 
 | 
Statefulset
| 12
 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
 
 | apiVersion: apps/v1kind: StatefulSet
 metadata:
 name: mongodb-rs
 namespace: middleware
 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.13
 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
 resources:
 limits:
 cpu: '2'
 memory: 2000Mi
 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: 5Gi
 storageClassName: mongodb-rs-storage
 
 | 
执行rs的初始化操作
上面已经完成了rs的部署,其实是通过StatefulSet建立了三个pod内运行mongod服务,此时三个mongod是没有任何联系的,需要进行一些设置
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | mongo
 config = { _id:"rs_prod", members:[
 {_id:0,host:"mongodb-rs-0.mongodb-rs.middleware.svc.cluster.local:27017",priority:90},
 {_id:1,host:"mongodb-rs-1.mongodb-rs.middleware.svc.cluster.local:27017",priority:80},
 {_id:2,host:"mongodb-rs-2.mongodb-rs.middleware.svc.cluster.local:27017",priority:70}
 ]
 }
 
 rs.initiate(config);
 
 | 
读写
默认情况下,PRIMARY节点有读写权限,SECONDARY节点没有读写权限。
SECONDARY节点可以成功使用账户密码登录,而进行查询时(报错NotMasterNoSlaveOk)提示不是master并且slaveOk也没打开。 此时使用rs.secondaryOk();可以临时打开读权限。
问题1:MongoSocketException: mongodb-rs-0.mongodb-rs.middleware.svc.cluster.local
解决1:
参考:https://stackoverflow.com/questions/60413876/kubernetes-mongodb-connection-issue-from-java
I have found the problem, when you deploy a repSet, you must launch this command:
| 12
 3
 4
 5
 
 | rs.initiate({_id: "MainRepSet", version: 1, members: [{ _id: 0, host : "ip1:27017" },
 { _id: 1, host : "ip2:27017" }
 { _id: 2, host : "ip3:27017" }
 ]})
 
 | 
And I launched this command with the following hostanmes:
| 12
 3
 
 | mongod-0.mongodb-service.default.svc.cluster.local;mongod-1.mongodb-service.default.svc.cluster.local;
 mongod-2.mongodb-service.default.svc.cluster.local
 
 | 
解决2:
配合read_preference=pymongo.ReadPreference.PRIMARY, directConnection=True(DirectConnection类)解决
如下:
| 12
 3
 4
 
 | REPLICASET_URL = 'mongodb://root:root@xxx:27017/admin?replicaSet=rs_prod&slaveOk=true&connectTimeoutMS=3000&socketTimeoutMS=3000&maxPoolSize=1'
 
 client = MongoClient(REPLICASET_URL, read_preference=pymongo.ReadPreference.PRIMARY, directConnection=True)
 
 | 
问题2:rs.status(): “Our replica set config is invalid or we are not a member of it
解决:
使用rs.status()查看下各host是否正确,不正确的话重新配置下
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | reConfig = { _id:"rs_prod", members:[{_id:0,host:"mongodb-rs-0.mongodb-rs.middleware.svc.cluster.local:27017",priority:90},
 {_id:1,host:"mongodb-rs-1.mongodb-rs.middleware.svc.cluster.local:27017",priority:80},
 {_id:2,host:"mongodb-rs-2.mongodb-rs.middleware.svc.cluster.local:27017",priority:70}
 ]
 }
 
 # force -> 非主节点强制请求重新分配主节点
 rs.reconfig(reConfig, { force:true })
 
 | 
附:开启用户权限认证
如果是集群部署,记得连接到primarty节点
rs.isMaster() #rs主节点判断
(1)注意如果之前已经使用没有鉴权的方式部署过 MongoDB,并且做了持久化,那么再改动 YMAL 文件重新部署是不会起作用的。这种情况我们可以进入执行如下命令进入容器:
| 1
 | kubectl -n middleware exec -it xxx /bin/bash
 | 
(2)进入 mongodb 客户端:
(3)执行如下命令创建用户即可:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 
 | use admin
 # 创建root账户
 db.createUser({"user":"root","pwd":"EIEU#@(#234werq","roles":["root"]})
 
 # 登录root
 db.auth("root","EIEU#@(#234werq")
 
 # 创建admin账户
 db.createUser({user:"admin", pwd:"EIEU#@(#234werq", roles:[{role: "userAdminAnyDatabase", db:"admin" }]})
 
 # 创建日常使用账户
 db.createUser(
 {
 user: "mongouser",
 pwd: "EIEU#@(#234werq",
 roles: [ { role: "userAdminAnyDatabase", db: "admin" }, "readWriteAnyDatabase" ]
 }
 )
 
 # 补充 - 删除用户
 use {账号数据所在的数据库}
 db.dropUser('python')
 
 |