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默认配置(小记)
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
| 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 的配置文件,文件里的内容如下:
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
| 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)稍等片刻,执行如下命令可以查看是否创建成功:
1 2 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
1 2 3 4 5 6 7 8 9 10
| apiVersion: v1 kind: Service metadata: name: mongodb-rs namespace: middleware spec: publishNotReadyAddresses: false clusterIP: None selector: app: mongodb-rs
|
ConfigMap
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
| apiVersion: v1 kind: 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
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
| apiVersion: v1 kind: 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
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
| apiVersion: apps/v1 kind: 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是没有任何联系的,需要进行一些设置
1 2 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:
1 2 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:
1 2 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类)解决
如下:
1 2 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是否正确,不正确的话重新配置下
1 2 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)执行如下命令创建用户即可:
1 2 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')
|