Kivi

没有什么远大理想,只是永远都不会满足而已


  • 首页

  • 关于

  • 标签

  • 归档

基于docker搭建mongodb副本集

发表于 2016-08-15 更新于 2017-07-02 分类于 mongodb 阅读次数:
本文字数: 6.2k 阅读时长 ≈ 6 分钟

什么是mognodb副本集

官网上这么描述

A replica set in MongoDB is a group of mongod processes that maintain the same data set. Replica sets provide redundancy and high availability, and are the basis for all production deployments. This section introduces replication in MongoDB as well as the components and architecture of replica sets. The section also provides tutorials for common tasks related to replica sets.

为什么需要副本集

  • 先看看为什么mongodb官方不推荐使用主从复制

    1. 主节点不可用之后,无法自动切换到从节点,无法确保业务访问的不间断性
    2. 所有的读写操作都是对主节点的,造成主节点的访问压力较大
  • 因此,使用副本集的意义在于

    1. 自动故障转移
    2. 负载均衡和读写分离
  • 官方给出的特点: Redundancy and Data Availability

    Replication provides redundancy and increases data availability. With multiple copies of data on different database servers, replication provides a level of fault tolerance against the loss of a single database server.
    In some cases, replication can provide increased read capacity as clients can send read operations to different servers. Maintaining copies of data in different data centers can increase data locality and availability for distributed applications. You can also maintain additional copies for dedicated purposes, such as disaster recovery, reporting, or backup.

副本集复制原理

参考这个博客MongoDB 副本集的原理、搭建、应用
副本集中数据同步过程:Primary节点写入数据,Secondary通过读取Primary的oplog得到复制信息,开始复制数据并且将复制信息写入到自己的oplog。如果某个操作失败,则备份节点停止从当前数据源复制数据。如果某个备份节点由于某些原因挂掉了,当重新启动后,就会自动从oplog的最后一个操作开始同步,同步完成后,将信息写入自己的oplog,由于复制操作是先复制数据,复制完成后再写入oplog,有可能相同的操作会同步两份,不过MongoDB在设计之初就考虑到这个问题,将oplog的同一个操作执行多次,与执行一次的效果是一样的。简单的说就是:

当Primary节点完成数据操作后,Secondary会做出一系列的动作保证数据的同步:
1:检查自己local库的oplog.rs集合找出最近的时间戳。
2:检查Primary节点local库oplog.rs集合,找出大于此时间戳的记录。
3:将找到的记录插入到自己的oplog.rs集合中,并执行这些操作。

副本集的同步和主从同步一样,都是异步同步的过程,不同的是副本集有个自动故障转移的功能。其原理是:slave端从primary端获取日志,然后在自己身上完全顺序的执行日志所记录的各种操作(该日志是不记录查询操作的),这个日志就是local数据 库中的oplog.rs表,默认在64位机器上这个表是比较大的,占磁盘大小的5%,oplog.rs的大小可以在启动参数中设 定:–oplogSize 1000,单位是M。

注意:在副本集的环境中,要是所有的Secondary都宕机了,只剩下Primary。最后Primary会变成Secondary,不能提供服务。

基于docker搭建最简单的副本集

基本思路是这个

mongodb副本集

以下所有文件的项目地址在这里

配置docker环境

这里省略,我当前的环境如下:
docker version

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Client:
Version: 1.12.0
API version: 1.24
Go version: go1.6.3
Git commit: 8eab29e
Built: Thu Jul 28 21:15:28 2016
OS/Arch: darwin/amd64

Server:
Version: 1.12.0
API version: 1.24
Go version: go1.6.3
Git commit: 8eab29e
Built: Thu Jul 28 23:54:00 2016
OS/Arch: linux/amd64

docker-compose version

1
2
3
4
docker-compose version 1.8.0, build f3628c7
docker-py version: 1.9.0
CPython version: 2.7.9
OpenSSL version: OpenSSL 1.0.2h 3 May 2016

mongo, 用的版本较新,稳定版是3.2

1
2
MongoDB shell version: 3.3.10
MongoDB server version: 3.3.10

编写Dockerfile

思路是把配置过程通过一个docker container执行,不用自己动手了,先build自己的image,源项目是这个mongo-replset-config
Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
FROM mongo:3.3
MAINTAINER "kivi"

RUN mkdir -p /opt/mongo-replset-script
COPY ./replset.sh /opt/mongo-replset-script
WORKDIR /opt/mongo-replset-script
RUN chmod +x replset.sh

ENV MONGO_REPLSET_NAME=mongoreplset
ENV MONGO_MASTER=mongo1
ENV MONGO_SECONDARY=mongo2
ENV MONGO_ARBITER=mongo3

CMD ["/bin/bash", "./replset.sh"]

replset.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/bin/bash

mongo $MONGO_MASTER:27017/admin --eval "rs.initiate({\"_id\": \"$MONGO_REPLSET_NAME\", \"members\": [{\"_id\": 0, \"host\": \"$MONGO_MASTER:27017\"}]});"

# secondary
hosts=$(echo $MONGO_SECONDARY | tr "," "\n")

for hst in $hosts
do
mongo $MONGO_MASTER:27017/admin --eval "rs.add(\"$hst:27017\")";
done

# arbiter
arbiters=$(echo $MONGO_ARBITER | tr "," "\n")

for arbiter in $arbiters
do
mongo $MONGO_MASTER:27017/admin --eval "rs.addArb(\"$arbiter:27017\")";
done

编写docker-compose.yml

一次性把所有事情做了,让副本集直接可用

这里说明一下net参数的配置, 这是一个自建的网络,docker1.9之后的版本可以手动创建网络,详情参考命令docker network create,我这里使用自定义的网络,主要的目的是让容器可以通过container_name互联,也就是在这种网络模式下,hosts是docker帮忙配置解析的,为什么要这么做呢?因为docker container的ip不是固定的,一旦down掉然后重启,ip有可能会发生变化,但是hostname是不会变的。

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
mongo1:
image: mongo:3.3
container_name: "mongo1"
net: "mongo-cluster"
ports:
- "30001:27017"
command: mongod --replSet mongoreplset
mongo2:
image: mongo:3.3
container_name: "mongo2"
net: "mongo-cluster"
ports:
- "30002:27017"
command: mongod --replSet mongoreplset
mongo3:
image: mongo:3.3
container_name: "mongo3"
net: "mongo-cluster"
ports:
- "30003:27017"
command: mongod --replSet mongoreplset
mongo-repl:
image: mongo-replset-config
container_name: mongo-repl
net: "mongo-cluster"
links:
- mongo1:mongo1
- mongo2:mongo2
- mongo3:mongo3

启动

执行命令docker-compose up -d,复本集群就搭建完成了,连接mongo1,插入数据,在mongo2中查询,就可以看到了,默认SECONDARY不支持查询操作,需要执行命令rs.setSlaveOk(),然后执行查询,就没问题了

  • 查看副本集信息

    1
    2
    docker exec -it mongo1 mongo
    rs.status()
  • 连接副本集,用下面的命令连接副本集,不需要关注到底谁是PRAMARY

    1
    docker run -it --network mongo-cluster --rm mongo sh -c 'exec mongo --host mongoreplset/mongo1,mongo2,mongo3 test'

在Node.js中使用

  • 依赖mongoose
  • 测试脚本index.js如下
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
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const ObjectId = Schema.ObjectId;
// 配置schema
let user = new Schema({
name: {type: String, default: 'kivi'},
sex: {type: String, default: 'male'}
});
// 暴露model
mongoose.model('User', user);
const UserModel = mongoose.model('User');
// 这里的ip和端口号是集群启动的时候配置的
mongoose.connect('mongodb://192.168.99.100:30001,192.168.99.100:30002,192.168.99.100:30003,192.168.99.100:30004/test', {
db: {
readPreference: 'secondary', // primary, primaryPreferred, secondary, secondaryPreferred, nearest
slaveOk: true
}
}, (err) => {
console.log(err);

UserModel.create({name: 'kivi', sex: 'male'}, (err, results) => {
if (err) {
console.log('插入数据失败');
}
else {
console.log(results);
mongoose.disconnect((err) => {
console.log(err);
});
}
});
});

注意

上面有几个点比较关键

  1. 读写分离的关键
    • 连接时候设置slaveOk: true,这样才能从SECONDARY节点取数据
    • 连接时候设置readPreference: secondary,设定只能从SECONDARY节点取数据
  2. 怎么做负载均衡,自己的理解
    连接数据库的时候,配置readPreference: nearest,实现负载均衡
  3. 自动故障转移,mongodb副本集已经帮我们做好了,docker stop mongo1关掉主节点,你会发现mongo2,或者是mongo3就会自动变成主节点,docker start mongo1,重新启动mongo1,发现它会变为SECONDARY节点,并自动同步这down掉期间写入的数据

readPreference参数解释

  • primary 默认参数,只从主节点上进行读取操作;
  • primaryPreferred 大部分从主节点上读取数据,只有主节点不可用时从secondary节点读取数据。
  • secondary 只从secondary节点上进行读取操作,存在的问题是secondary节点的数据会比primary节点数据“旧”。
  • secondaryPreferred 优先从secondary节点进行读取操作,secondary节点不可用时从主节点读取数据;
  • nearest 不管是主节点、secondary节点,从网络延迟最低的节点上读取数据。
# docker # mongodb
git移除之前没有ignore的文件/文件夹
基于docker搭建mongodb分片集群
  • 文章目录
  • 站点概览
kivi

kivi

nodejs | server
58 日志
17 分类
32 标签
RSS
  1. 1. 什么是mognodb副本集
  2. 2. 为什么需要副本集
  3. 3. 副本集复制原理
  4. 4. 基于docker搭建最简单的副本集
    1. 4.1. 基本思路是这个
    2. 4.2. 配置docker环境
    3. 4.3. 编写Dockerfile
    4. 4.4. 编写docker-compose.yml
    5. 4.5. 启动
    6. 4.6. 在Node.js中使用
© 2019 kivi | 173k | 2:37
由 Hexo 强力驱动 v3.9.0
|
主题 – NexT.Pisces v7.3.0
|