# docker_mongodb **Repository Path**: iuhao2016/docker_mongodb ## Basic Information - **Project Name**: docker_mongodb - **Description**: 5分钟装好docker分布式集群 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2022-10-11 - **Last Updated**: 2022-10-11 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 简介 (简书木有目录, 建议去gitee看: https://gitee.com/xiaofeipapa/docker_mongodb) 我司数据说多不多, 说少不少, 也到了搭个大数据系统的时候了. 更何况我们系统最重要的环节-风控系统, 本来就需要OLAP类的数据库对数据进行处理. 于是一把年纪的我也重新捡起这个苦力活, 开始了技术选型和框架搭建. 大数据时代的技术栈让人眼花缭乱. 我先是复习了一下hadoop(若干年前曾经浅尝辙止使用过), 觉得技术栈实在太过繁琐(纯粹个人意见), 出于对mongodb 更熟悉的原因, 我最终选择了mongodb 作为大数据的存储架构, 后续打算用 mongodb + spark 的技术栈. 作为公司产品的基石, 肯定要考虑高可用, 扩展性, 维护性这几个维度的平衡. 在先后搭建了分片副本集和仅副本集的集群模式之后, 我最终决定用副本集的模式. 因为: 1) 我们的数据量没大到需要sharding的程度. 2) 现阶段只要保证高可用, 数据不丢失即可. 3) 未来数据增长了, 还可以再进行数据迁移的嘛. 以下内容总结了搭建副本集和测试的过程, 祝君阅读愉快. (关于更多mongodb的副本集和分片副本集概念, 请自行查询网上资料) # 搭建mongodb副本集 ## 副本集模式的基础知识 副本集集群模式的概念图如下: ![image-20210728163026525](https://upload-images.jianshu.io/upload_images/4074593-97f58c7e983750d1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 每个副本集是由多台机器组成的, 它的特点是: * 主节点(Primary): 所有写入操作都在主节点上进行. * 从节点(Secondary): 作为数据的备份, 和主节点数据完全一致. 默认状态下从节点不可读(可以设置成可读) * 仲裁节点(Arbiter): 当主节点发生故障时, 判断选择哪个从节点成为新的主节点. 如果有多个从节点, 可以设置各个节点的优先级(priority), 仲裁节点会优先选择高优先级的节点. * 整个过程自动故障转移. * 整个过程数据自动恢复. 看上去很美, 要实际试试. ## docker规划和准备 | 名称 | 数据存储位置 | 主从/优先级 | docker映射端口 | | ------- | ------------------- | ----------- | -------------- | | mongo_1 | ~/mongo-data/data01 | 主 | 30001 : 27017 | | mongo_2 | ~/mongo-data/data02 | 从 | 30002 : 27017 | | mongo_3 | ~/mongo-data/data03 | 仲裁节点 | 30003 : 27017 | 准备所需的文件夹和文件: ```bash # 创建文件夹 mkdir -p ~/mongo-data/{data01,data02,data03,key,backup} # 设置key文件. 此文件用于在集群机器间互相访问. cd ~/mongo-data openssl rand -base64 756 > key/mongo-rs.key sudo chown 999 key/mongo-rs.key # 不能是755, 权限太大不行. sudo chmod 600 key/mongo-rs.key ``` ## 创建启动第一个容器 创建并启动第一个mongo容器. 这个容器的几个关键信息是: 节点名称: mongo-1 用户: admin 密码: 123456 副本集名称: MongoSet 数据目录: ~/mongo-data/data01 (在之前创建) 映射端口: 30001 ``` # 第一个 sudo docker run --name mongo-1 -p 30001:27017 -v ~/mongo-data/data01:/data/db -v ~/mongo-data/backup:/data/backup -v ~/mongo-data/key:/data/key -v /etc/localtime:/etc/localtime -e MONGO_INITDB_ROOT_USERNAME=admin -e MONGO_INITDB_ROOT_PASSWORD=123456 -d mongo:5.0.0 --replSet MongoSet --auth --keyFile /data/key/mongo-rs.key --bind_ip_all ``` MONGO_INITDB_ROOT_USERNAME 和 MONGO_INITDB_ROOT_PASSWORD 是 mongo 镜像的方便功能, 关于这个镜像的更多功能, 可以参考官方文档: https://hub.docker.com/_/mongo/ 现在, 用 sudo docker ps 来查看容器状态, 应该能够看到容器已经启动了. 如果不能看到容器启动, 那么可以将上述命令的 -d 参数去掉, 运行的时候在窗口查找原因. ## 启动其他容器 ``` # 第二个 sudo docker run --name mongo-2 -p 30002:27017 -v ~/mongo-data/data02:/data/db -v ~/mongo-data/backup:/data/backup -v ~/mongo-data/key:/data/key -v /etc/localtime:/etc/localtime -e MONGO_INITDB_ROOT_USERNAME=admin -e MONGO_INITDB_ROOT_PASSWORD=123456 -d mongo:5.0.0 --replSet MongoSet --auth --keyFile /data/key/mongo-rs.key --bind_ip_all # 第三个 sudo docker run --name mongo-3 -p 30003:27017 -v ~/mongo-data/data03:/data/db -v ~/mongo-data/backup:/data/backup -v ~/mongo-data/key:/data/key -v /etc/localtime:/etc/localtime -e MONGO_INITDB_ROOT_USERNAME=admin -e MONGO_INITDB_ROOT_PASSWORD=123456 -d mongo:5.0.0 --replSet MongoSet --auth --keyFile /data/key/mongo-rs.key --bind_ip_all ``` 现在你用 sudo docker ps , 应该能够看到这3个容器: ![image-20210728113102579](https://upload-images.jianshu.io/upload_images/4074593-ef3fdf0b6db5a902.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ## 初始化副本集信息 登录第一个容器, 设置副本集的信息 ``` sudo docker exec -it mongo-1 bash mongo use admin # 密码 123456 db.auth("admin","123456") # 设置 # MongoSet 是启动容器时候的副本集名字 var config={ _id:"MongoSet", members:[ {_id:0,host:"172.17.0.1:30001"}, {_id:1,host:"172.17.0.1:30002"}, {_id:2,host:"172.17.0.1:30003",arbiterOnly:true} ]}; rs.initiate(config) # 查看集群状态 rs.status() # 设置完之后, 退出 exit ``` 172.17.0.1 是docker 默认网段的网关信息, 通过它来访问宿主机的端口. 设置完之后退出, 重新登录容器: ``` sudo docker exec -it mongo-1 bash mongo ``` 这个时候可以看到, 这台容器显示的是副本集的主节点: ![image-20210728134215133](https://upload-images.jianshu.io/upload_images/4074593-2f91bef0d72e8a5a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ## 设置从节点可读 默认的情况下, 主节点负责读写, 副节点仅起到备份作用, 不能读也不能写. 所以还要进行如下设置: 登录第2台容器, 依次进行: ``` sudo docker exec -it mongo-2 bash mongo use admin # 密码 123456 db.auth("admin","123456") # 注意:这条命令要在副节点上运行 # mongodb默认是从主节点读写数据,副本节点上不允许读,设置副本节点可读。 # 网上的文章基本都是 setSlaveOk, 这个方法已经是 deprecated 状态. db.getMongo().setSecondaryOk() ``` 设置了之后, 从节点会分担主节点读的压力, 提高了系统的性能. ## 图形化访问工具 mongodb 的图形化工具很多, 官方的mongo compass 就不错, 下载地址: https://www.mongodb.com/try/download/compass 下载安装这个工具之后, 在连接字符串输入: (关于ip: 如果你是用本机测试, 输入 127.0.0.1, 如果是云服务器, 输入云服务器ip. ) ``` mongodb://admin:123456@你的ip:30001,你的ip:30002,你的ip:30003/admin?authSource=admin&replicaSet=MongoSet ``` 点击连接, 就可以看到漂漂亮亮的界面了. ![image-20210729174705009](http://diegooss.oss-cn-shenzhen.aliyuncs.com/24f9de70-0165-5e6b-9441-b7ac1cdf9034.png) 至此, 副本集的搭建就算完成了. # 测试副本集 接下来打算测试一下集群各类性能和可用能力, 先测试主从复制. ## 测试主从复制功能 这时候, 在第一台容器的mongo 控制台输入如下代码: ``` mongo use admin db.auth("admin","123456") # test 使用test 数据库 use test # 插入一条数据 db.users.insert({name:"jack",age:0,addr:"guangzhou",country:"China"}) ``` 登录第二台容器, 看看是否有这条记录: ``` use admin db.auth("admin","123456") use test db.users.find() ``` 此时你应该看到这条记录, 证明我们的主从复制功能已经成功了. ## 测试插入性能 用python写个简单的程序, 往集群插入 10 万条数据 ```python #! /usr/bin/python3 # -*- coding: UTF-8 -*- """ * 作者: 小肥爬爬 * 简书: https://www.jianshu.com/u/db796a501972 * gitee: https://gitee.com/xiaofeipapa * 邮箱: imyunshi@163.com * 您可以自由转载此博客文章, 恳请保留原链接, 谢谢! """ from pymongo import MongoClient import time ip_list = [ '127.0.0.1:30001', '127.0.0.1:30002', '127.0.0.1:30003', ] user = 'admin' pwd = '123456' rs_name = 'MongoSet' conn_str = 'mongodb://%s:%s@%s/%s?replicaSet=%s' \ % (user, pwd, ','.join(ip_list), user, rs_name) conn = MongoClient(conn_str) ExampleDb = conn.ExampleDb # 使用 TestUser 这个Collection TestUser = ExampleDb.TestUser # 测试插入10万数据的时间 def test_1(): time_start = time.clock() # 记录开始时间 count = 100000 # 使用批量插入功能 batch_list = [] for i in range(0, count): data = { 'index': i, 'name': 'test-%d' % i } batch_list.append(data) if len(batch_list) > 1000: TestUser.insert_many(batch_list) batch_list.clear() # 防止还有值遗留 if len(batch_list) > 0: TestUser.insert_many(batch_list) batch_list.clear() # ------------------------- time_end = time.clock() # 记录结束时间 time_sum = time_end - time_start # 计算的时间差为程序的执行时间,单位为秒/s print('程序运行时间: %d 秒' % time_sum) if __name__ == '__main__': test_1() ``` 程序运行几乎是一闪而过, 也可以看到"程序运行时间 0 秒", 证明速度非常快. 用compass 看看, 确实有数据了: ![image-20210728150546547](https://upload-images.jianshu.io/upload_images/4074593-e8f62d50335d3fe3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ## 测试可用性 (为了观察方便, 在compass 里将之前的 TestUser 删掉.) 步骤: 1) 停止主节点(第一个容器) 2) 再次运行python程序 3) compass 检查数据 停止第一个容器: ``` sudo docker stop mongo-1 ``` 然后再次运行之前的python代码. 可以观察到如下现象: 1) 即使主节点关了, 代码还是顺利运行 2) compass 能看到10万条数据. 这说明测试可用性也成功了. 如果此时你登录到第二台容器, 运行mongo 命令. 你会发现这台机器已经成了主节点. 这表示在第一台容器down掉之后, 第二台容器成功地挑起了重担. ## 测试数据丢失 (为了观察方便, 在compass 里将之前的 TestUser 删掉.) 先将第一台容器重新启动: ``` sudo docker restart mongo-1 ``` (此时, 此容器已经成了从节点) 步骤: 1) 用python插入 500 万数据 2) 插入途中停止主节点容器 3) 看看程序的反应如何? 4) 5秒后, 重新启动"主节点"容器(重启之后, 它其实变成了从节点) 5) 程序运行完之后, 检查 compass 到底插入了多少数据 6) 分别进入第一个, 第二个容器, 看看他们的数据是否有差值? 我们期望的结果: 1. 插入过程不能停. (这个已经验证过了) 2. 看看数据是否有丢失? (这些过程你自己可以试试) ### 我的测试结果 查询插入时间: 38 秒 compass 查询结果: 500万数据 (没有丢失!!) 主节点容器数据: 500万. (用 db.TestUser.count() 查总数) 从节点容器运行查询语句的时候报这个错: ![image-20210728160330556](https://upload-images.jianshu.io/upload_images/4074593-d278d6a2c485763b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 这个错误的意思是从节点不可读. 在之前搭建集群的时候, 我们要在从节点这样设置: ```js db.getMongo().setSecondaryOk() ``` 这表明, 当某个节点down掉再重连之后, 它会恢复成mongodb 的默认从节点配置, 没事, 再次运行这句就是了. 比起数据没有丢失, 这不算多大的事吧? 至此, mongodb集群的搭建, 测试就算彻底完成了. # 附录 ## 修改mongodb 密码 为了安装方便, 密码都使用 123456 这样的弱密码. 在安装好集群之后, 我们应该将它改掉. 我向你推荐 pwgen , 用这个小工具来生成密码: ``` sudo apt install pwgen pwgen -s 20 ``` 登录主节点容器, 使用 db.changeUserPassword 来修改密码: ``` sudo docker exec -it mongo-1 bash use admin db.auth('admin', '123456') # 修改密码, 假设你的是 xxxyyy db.changeUserPassword('admin','xxxyyy') ``` ## 防止日志文件过大 mongodb的日志膨胀非常快, 单个文件非常大, 可以通过这个命令来设置: ``` db.adminCommand({logRotate:1}) ``` 之后日志就会按日期分割成独立的文件. 一些过期的日志, 删了即可. ## docker 便捷命令 ### 查看正在运行的容器 ``` sudo docker ps ``` ### 查看创建没有启动的容器 ``` sudo docker ps -a ``` ### 一键停止所有容器 ``` sudo docker ps -q |xargs sudo docker stop ``` ### 删除全部容器 ``` sudo docker ps -aq |xargs sudo docker rm ``` ## springboot 配置 ```yaml spring: data: mongodb: uri: mongodb://test:123456@127.0.0.1:30001,127.0.0.1:30002,127.0.0.1:30003/ExampleDb?authSource=test&replicaSet=MongoSet ``` # 作者简介 艺名小肥爬爬(小肥耙耙/小肥巴巴), 一个喜欢阅读/写字/健身/踢球/吉他的程序员, 他和小田园犬肥花在深圳愉快地生活. 欢迎探讨技术领域的方方面面, 你可以在简书, gitee和知乎找到他: 简书: https://www.jianshu.com/u/db796a501972 gitee: https://gitee.com/xiaofeipapa 知乎: https://www.zhihu.com/people/chen-yun-shi-75 csdn(不常用): https://blog.csdn.net/m0_46322443 ## 参考文章&感谢 https://blog.csdn.net/lzkIT/article/details/8146567 https://blog.csdn.net/zhanngle/article/details/105132527 https://www.cnblogs.com/zqyx/p/10169820.html