问题
- 为什么要进行mongodb的高可用部署?
在实际的生产环境当中,mongo作为一种非关系型数据库,在项目中是非常重要的角色。 我选择高可用的部署主要初衷是当前项目的mongodb通过kubernetes单个pod部署,而上一任运维居然连admin账户都没有! 最近的一次发现它的日志文件大小达到了20多个GB,CPU利用率持续在100%,而在一些特殊时段开发的操作下,达到300%。 由于没有admin账户,又没有部署文档,不敢随意去修改它的配置甚至重启。
通过kubernetes部署的mongo相比传统单机部署可用性较高一点也有存储以及服务不可用的情况出现,那么有没有存储服务都高可用的模式?
图:运行在多个可用区上的副本集
mongodb高可用模式
此小结内容参考,略有改动《【MongoDB进阶】MongoDB高可用集群配置的几种方案》
mongodb的部署方式有:
- Standalone单节点部署
此种部署方式就是最简单易用并且常见的部署了,直接使用mongod起来一个进程。
- Master-Slave主从结构
主从架构一般用于备份或者做读写分离。一般有一主一从设计和一主多从设计。 主(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
Replica Set是mongod的实例集合,它们有着同样的数据内容。包含三类角色:
主节点(Primary)
接收所有的写请求,然后把修改同步到所有Secondary。一个Replica Set只能有一个Primary节点,当Primary挂掉后,其他Secondary或者Arbiter节点会重新选举出来一个主节点。默认读请求也是发到Primary节点处理的,需要转发到Secondary需要客户端修改一下连接配置。
副本节点(Secondary)
与主节点保持同样的数据集。当主节点挂掉的时候,参与选主。
仲裁者(Arbiter)
不保有数据,不参与选主,只进行选主投票。使用Arbiter可以减轻数据存储的硬件需求,Arbiter跑起来几乎没什么大的硬件资源需求,但重要的一点是,在生产环境下它和其他数据节点不要部署在同一台机器上。
注意,一个自动failover的Replica Set节点数必须为奇数,目的是选主投票的时候要有一个大多数才能进行选主决策。
选主过程
其中Secondary宕机,不受影响,若Primary宕机,会进行重新选主
使用Arbiter搭建Replica Set
偶数个数据节点,加一个Arbiter构成的Replica Set方式
- shard Cluster数据分片
当数据量比较大的时候,我们需要把数据分片运行在不同的机器中,以降低CPU、内存和IO的压力,Sharding就是数据库分片技术。
MongoDB分片技术类似MySQL的水平切分和垂直切分,数据库主要由两种方式做Sharding:垂直扩展和横向切分。
垂直扩展的方式就是进行集群扩展,添加更多的CPU,内存,磁盘空间等。
横向切分则是通过数据分片的方式,通过集群统一提供服务:
MongoDB的Sharding架构
MongoDB分片架构中的角色 数据分片(Shards)
用来保存数据,保证数据的高可用性和一致性。可以是一个单独的mongod实例,也可以是一个副本集。
在生产环境下Shard一般是一个Replica Set,以防止该数据片的单点故障。所有Shard中有一个PrimaryShard,里面包含未进行划分的数据集合:
查询路由(Query Routers)
路由就是mongos的实例,客户端直接连接mongos,由mongos把读写请求路由到指定的Shard上去。
一个Sharding集群,可以有一个mongos,也可以有多mongos以减轻客户端请求的压力。
配置服务器(Config servers)
保存集群的元数据(metadata),包含各个Shard的路由规则。
部署
- 选型
根据业务综合考虑我们选择继续在Kubernetes部署的,使用MongoDB副本集(Replica Set)方案,一个Primary,两个Secondary,存储选择阿里云的NAS文件存储。 对于有状态的服务,主要由StatefulSets和Persistent Volumes实现。
对于部署,mongodb官方有文档Deploy a Replica Set。但我最终还是选择了《Kubernetes部署Redis集群最佳实践》类似的方式,简单易用。
pv的建立
需要建立3个pv,这里存储选择了阿里云的NAS,accessModes选择ReadWriteOnce,磁盘回收策略选择Retain。
上次没有解释pv访问模式和回收模式,简单说一下
- ReadWriteOnce:读写权限,仅被单个node挂载
- ReadOnlyMany:只读,可被多个node挂载
- ReadWriteMany:读写权限,可被多个node挂载
在cli中,访问模式被简化为:
- RWO - readWriteOnce
- ROX - readOnlyMany
- RWX - readWriteMany
回收模式:
- Retain:不清理保留数据。即删除pvc或者pv后,在插件上的数据不会被删除。这种方式是最常用的,可以避免误删pvc或者pv而造成数据的丢失。
- Recycle:不保留数据。经测试pvc删除后,文件服务端的数据也会随机删除。只有hostPath和NFS支持这种方式
- Delete:删除存储资源,AWS EBS, GCE PD, Azure Disk, and Cinder volumes支持这种方式。
|
|
redis-pv.yaml
|
|
statefulset的建立
configmap也一同建立,这里以默认的namespace进行的部署,实际情况因选择自己的namespace。
这里要注意,keyfile
权限设置600否则无法启动mongod。
fork
如果配置true
的话,在启动命令后我写了sleep infinity
,否则容器立马会退出。
|
|
redis-sts.yaml
|
|
service的建立
经过上面的步骤,已经建立了pod了,此时pod还不能被外部访问。 先创建一个ClusterIP类型的Service,用以达到被其他pod访问的目的。
|
|
可以看到Endpoints是有三个节点的。
现在我们可以通过域名的方式来设置replica set,也不用关心pod会变化的ip地址了。
域名为:$(podname).$(service name).$(namespace).svc.cluster.local
可以进行一次验证,mongodb-rs-0
通过域名连接mongodb-rs-2
|
|
可以看到是连通的。
设置
- 建立rs
上面已经完成了rs的部署,其实是通过StatefulSet建立了三个pod内运行mongod服务,此时三个mongod是没有任何联系的,需要进行一些设置,我的配置如下。
|
|
可以在任意的pod内进行初始化设置,这里已mongodb-rs-0
为例
|
|
如上返回的信息一切都正常!
- 设置账户 创建admin管理账户,并赋予权限。
|
|
至此完成了admin账户的设置。
还需要设置日常使用的账户
|
|
完成设置。
测试
- 读写
默认情况下,PRIMARY
节点有读写权限,SECONDARY
节点没有读写权限。可以分别验证一下。
|
|
PRIMARY
节点成功读写,再来看看SECONDARY
节点。
|
|
可以看到SECONDARY
节点可以成功使用账户密码登录,而进行查询时提示不是master并且slaveOk也没打开。
此时使用rs.secondaryOk();
可以临时打开读权限。
- 模拟故障 待更新…