目录
1.缓存云
1.1 简介
本人毕业后在北京一家互联网金融公司工作,一个偶然的机会接手了公司的缓存云系统;通过一段时间的运维开发,对缓存云管理系统有了更深的理解,然后分享给大家。
缓存云:Redis作为缓存中间件在互联网公司得到了广泛的应用,拿我们公司来说,生产环境有40多台缓存云机器, 60多个集群,后端、大数据、前端都使用到缓存云集群,所以就需要一个平台来统一管理并维护Redis集群,我们称之为"缓存云"。
搜狐缓存云:https://github.com/sohutv/cachecloud
1.2 功能设计
- 集群申请:完善的申请、审批流程
- 集群部署:集群的机器分配、可视化部署
- 机器维护:缓存云机器信息维护以及图表展示(连接数、CPU负载、内存负载、网卡负载、内存负载)
- 集群管理:命令执行窗口、集群信息(集群内存使用情况、QPS、带过期时间Key数量、连接数、内存碎片率、实时输入输出流量、慢查询日志、客户端列表)
- 监控报警:监控集群信息,钉钉、邮件告警
- 集群切换:双机房集群切换
- 数据同步:集群变动时,将集群信息通知给客户端
- 用户管理:缓存云用户管理
- 权限管理:普通用户和管理员权限区分
- 智能客户端:封装Jedis,限制部分命令的执行
1.3 架构设计
1.3.1 Zookeeper
我们将集群的元数据(机器IP和端口列表)存储在ZK中,这样客户端就能无感知于集群节点的变化(诸如:扩容操作);同时将客户端列表维护到ZK中;
关于大数据端,由于大数据缺乏对应的Redis客户端(具体的细节我也不太清楚),所以我们公司大数据端是采用直连机器的方式,这样给集群的扩容和迁移带来了很多麻烦;(java端采用Jedis实现智能客户端,移动端node.js采用的是ioredis实现智能客户端)
我们在部署、扩容、迁移集群时,需要同时将集群元数据(机器端口信息,后文不再重复赘述)维护到ZK和DB中;
1.3.2 InfluxDB
InfluxDB是一个时序数据库,不清楚的朋友可以先去了解下。我们使用InfluxDB主要是为了存储机器、集群的一些统计信息(包括:内存使用情况、内存碎片率、CPU负载、硬盘负载等),然后以图表的形式直观的展示出来。我们生产环境是数据保存1个月,测试环境的数据保存一周。
1.3.3 Mysql
Mysql大家都很熟悉了,我们用Mysql存储一些业务信息;在这里我需要特殊强调的是集群相关的数据,这个数据是需要我们跑定时任务,实时去查询集群info信息,再根据info信息更新表中数据;比如,水平扩容会增加集群的节点数,垂直扩容会增大集群节点的最大内存,主从切换会导致机器角色改变等。
1.3.4 Client
我们在使用Jedis时需要传入集群的机器信息和端口信息,这样用户就需要知道我们将集群部署在哪台机器哪个端口上;如果我们扩容集群,难道我们还要一一通知我们的客户端?当时不是,在这里我们通过ZK使用户和缓存云机器之间解耦,这样我们就需要封装下Jedis。我们要根据用户传入的集群名(为了安全,一般还要传入一个集群token),去ZK中获取集群的元数据,然后给用户返回一个可用的客户端。其次我们要对命令过滤,过滤一些不安全的命令(比如:keys)。当然,还有很多其他细节需要注意,这里我就不一一描述了。
1.3.5 Ruby脚本
很遗憾的是,我不会Ruby,所以对Ruby脚本的实现原理也不是很清楚。但是Ruby脚本在这个系统主要作用就是:执行接收到的命令,然后返回执行结果。
2. 监控告警
有时我们需要监控集群,获取集群的运行信息,以便在第一时间发现解决问题.
2.1 监控项
我列一些监控指标:
- 内存使用率: 当内存使用率高于配置的阈值时,通知管理员及时扩容
- 请求命中率: 当请求命中率低于配置的阈值时,通知管理及使用者。过低的请求命中率,可能是因为使用不规范,也有可能是恶意请求;在一定场景下,还有可能发生缓存击穿,打爆数据库
- 内存碎片率: 内存碎片率的正常范围是1-1.5,如果内存碎片率<1,则表示系统分配给程序的内存小于程序使用的内存,也就是说当前程序正在使用虚拟内存(硬盘),这样会大幅降低Redis的速率;当内存碎片率大于1.5时,则表示当前内存中有很多碎片,高版本的Redis(4.0)提供了整理内存碎片的功能。很不幸的是,我们使用的3.0版本的Redis不支持这样的功能,最好的办法是重启服务,当然线上不支持这样;再一个办法就是修改Redis的内存分配器(Redis支持glibc’s malloc、jemalloc11、tcmalloc几种不同的内存分配器),但是这样也要重启服务。所以如果你的机器充足,也只好睁一只眼闭一只眼了。
- 主从挂载: 缓存的主机和从机有时会因为网络抖动的原因断开连接,所以我们需要监控主从挂载的情况,必要的情况下我们指定主从连接(cluster replicate master_node_id)。
- AOF文件大小:AOF文件过大,可能会导致Redis重启失败。但是监控的意义不是很大
- 机器信息: 机器的剩余未使用内存,我们需要监控机器的剩余内存信息,以便在内存不足时增加机器内存或者迁移集群。原先在分配集群机器时,是通过free -g 命令,选出内存最大的机器,但是这样会导致分配溢出。举个例子,我们有台16G内存的机器,我们分配了4个Redis节点,每个节点的最大内存为8G,但是现在每个节点只使用1G,很显然这样是存在风险的(我们原先的分配方式确实存在这样的情况)
- 客户端连接数:当连接的客户端高于最大连接数时(默认是1万),Redis会拒绝客户端的连接。
- 集群状态:Redis有个配置项cluster-require-full-coverage(只有当16384个槽全部分配完毕时,集群才能上线),所以这样会因为一个槽不可用导致整个集群不可用,个人建议关闭这个配置(当然需要根据不同的场景配置)
2.2 告警信息
告警信息过多也是程序员比较头疼的事情,所以我给每个告警加了一个告警级别配置(紧急、严重、一般),这样就可以避免错过重要的信息。其次,我在配置页提供了监控频率(在?分钟发送?次告警),控制告警信息发生的频率,不过后台在生产上,没采用该配置,直接修改TbSchedule,一小时跑一次;
告警方式:钉钉、邮箱
2.3 告警实现
通过TbSchedule定时跑监控任务,通过info命令获取集群的,对于当前值与配置的阈值,再根据当前告警频率来决定要不要告警。因为不同监控项的逻辑大致相同(校验是否开启监控项、获取当前数值、获取数据库配置的阈值、对比阈值、判断告警频率、获取告警方式、发送告警信息),其中有些步骤是通用的(比如:校验是否开启对应的监控项,获取数据库配置的阈值、发送告警信息等),所以我采用模板模式,将这些通用的步骤定义在模板中,其余步骤通过子类来具体实现。