构建 Amazon ElastiCache for Redis 慢日志可视化平台

数据库
存储
Amazon ElastiCache
0
0
Redis 数据库是一个基于内存的 key-value 存储系统,现在 Redis 最常用的使用场景就是存储缓存用的数据,在需要高速读写的场合使用它快速读写,从而缓解应用数据库的压力,进而提升应用处理能力。许多数据库会提供慢查询日志帮助开发和运维人员定位系统存在的慢操作。所谓慢查询日志就是系统在命令执行前后计算每条命令的执行时间,当超过预设阀值,就将这条命令的相关信息(例如:发生时间、耗时、命令的详细信息)记录下来,Redis 也提供了类似的功能。 本文将基于 Amazon 托管的服务来介绍如何展示和分析大规模 Redis 集群慢日志。 ## 架构说明 通过 Amazon Event Bridge 产生定时任务(1分钟)触发 Amazon Lambda 执行,Lambda 通过 Amazon 的 sdk 找到所有 Redis 的节点信息并一个个连接索取慢日志信息。 通过慢日志自带的时间戳过滤出最近1分钟产生的日志并上传到 RDS 数据库保存,最终通过托管的 Amazon Grafana 展示并统计。 ![image.png](https://dev-media.amazoncloud.cn/6f2d16f260b24bd9826913ccbcbbc99d_image.png "image.png") ## 创建 RDS 或者 Amazon Aurora 数据库并初始化表结构 在控制台创建 RDS (过程略),要注意需要开公网访问,因为托管的 Grafana 需要通过公网访问 MySQL 的数据源。这里会有安全的隐患,如果有自建的能力,可以考虑用 VPC 内自建的 Grafana 代替,或者开 case 问 Amazon 后台托管的 Grafana IP 地址范围,通过 RDS 安全组做限制访问并配置复杂的数据库用户名密码。 ![image.png](https://dev-media.amazoncloud.cn/caecafa299bf48e68303fc5e42ca748b_image.png "image.png") 创建好表结构 slowlog.detail: ```js MySQL [slowlog]> show create table detail \\G *************************** 1. row *************************** Table: detail Create Table: CREATE TABLE `detail` ( `id` bigint(40) NOT NULL AUTO_INCREMENT, `nodeid` varchar(100) NOT NULL, `endpoint` varchar(100) NOT NULL, `slowlog_time` int(20) NOT NULL, `today` date NOT NULL, `command` varchar(100) NOT NULL, `Redis_key` varchar(100) NOT NULL, `duration` int(10) NOT NULL, PRIMARY KEY (`id`), KEY `idx_nodeid_time` (`nodeid`,`slowlog_time`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 ``` ## 创建测试用的 Redis 实例 过程略 ![image.png](https://dev-media.amazoncloud.cn/d015e8093a2c44059182dc77a345f5d8_image.png "image.png") ## 创建 Lambda 函数 代码如下: ```js import Redis import boto3 import os,sys import json import time from datetime import datetime,timedelta import PyMySQL import socket import struct # get system environment AWS_REGION=os.environ['AWS_REGION'] DB_HOST=os.environ['db_host'] DB_DataBase=os.environ['db_database'] DB_PW=os.environ['db_pw'] DB_USER=os.environ['db_user'] PORT=int(os.environ['port']) # get current time from internet # time.time() 方式的时间不准 def RequestTimefromNtp(addr='0.de.pool.ntp.org'): REF_TIME_1970 = 2208988800 # Reference time client = socket.socket( socket.AF_INET, socket.SOCK_DGRAM ) data = b'\\x1b' + 47 * b'\\0' client.sendto( data, (addr, 123)) data, address = client.recvfrom( 1024 ) if data: t = struct.unpack( '!12I', data )[10] t -= REF_TIME_1970 return t # get Redis client def aws_client(): client = boto3.client( 'elasticache', region_name=AWS_REGION, ) return client # get all Redis cluster list def get_Redis_cluster_list(client): clusters_metas = client.describe_cache_clusters()['CacheClusters'] clusters = [] for item in clusters_metas: clusters.append(item['ReplicationGroupId']) cluster_list = set(clusters) return cluster_list # get each Redis cluster meta data def get_Redis_meta(client,groupid): info={} response=client.describe_replication_groups( ReplicationGroupId=groupid) NodeGroupMembers=response['ReplicationGroups'][0]['NodeGroups'][0]['NodeGroupMembers'] for item in NodeGroupMembers: k = item['CacheClusterId'] v = item['ReadEndpoint']['Address'] info[k] = v return info # get slow log every 1 min # 本次测试环境密码为空 def get_slow_log_info(endpoint,nodeid,date,port='',pw=''): now=int(RequestTimefromNtp()) last=int(now - 60) r=Redis.Redis(host=endpoint) slow_data = r.slowlog_get() result=[] for item in slow_data: if (item['start_time']>= last and item['start_time']<= now ): try: command_type = str(item['command']).split('\\'')[1].split()[0] except Exception as err: command_type = "null" try: command_key = str(item['command']).split('\\'')[1].split()[1] except Exception as err: command_key = "null" try: duration = int(item['duration']) except Exception as err: duration = 0 data = { 'nodeid' : nodeid, 'endpoint' : endpoint, 'slowlog_time' : int(item['start_time']) , 'today' : str(date), 'command' : command_type, 'key' : command_key, 'duration' : duration, } result.append(data) return result # upload data to RDS def upload_logs(result,DB_HOST,DB_USER,DB_PW,DB_DataBase,PORT): db = PyMySQL.connect(host=DB_HOST, user=DB_USER, password=DB_PW, database=DB_DataBase, port=PORT, cursorclass=PyMySQL.cursors.DictCursor) cur = db.cursor() for item in result: nodeid=item['nodeid'] endpoint=item['endpoint'] slowlog_time=item['slowlog_time'] today=item['today'] command=item['command'] Redis_key=item['key'] duration=item['duration'] SQL = "insert into slowlog.detail (nodeid,endpoint,slowlog_time,today,command,Redis_key,duration) values('%s','%s',%d,'%s','%s','%s',%d)" % \\ (nodeid,endpoint,slowlog_time,today,command,Redis_key,duration) cur.execute(SQL) db.commit() db.close() def lambda_handler(event, context): date = datetime.now().strftime("%Y-%m-%d") client=aws_client() cluster_lists=get_Redis_cluster_list(client) for item in cluster_lists: info = get_Redis_meta(client,item) for k in info.keys(): result=[] result=get_slow_log_info(info[k],k,date) print(result) upload_logs(result,DB_HOST,DB_USER,DB_PW,DB_DataBase,PORT) ``` Python 代码依赖 Redis 和 PyMySQL 包,操作步骤如下: 1) 在本地 Linux 环境(x86)安装 Redis 和 PyMySQL 两个包,建议通过 virtual env 安装, 如果是 通过 pip 安装则包的参考路径为: /usr/local/lib/python3.7/site-packages; 2) 把 Redis 目录 和 PyMySQL 目录拷贝到临时目录/tmp; 3) 下载上面的代码到/tmp; 4) 打包 zip -r lambda.zip PyMySQL Redis lambda_function.py; 5) 在控制台通过 S3 或者本地上传。 ![image.png](https://dev-media.amazoncloud.cn/89a3b172e87b45fb9769bdf741759ad3_image.png "image.png") ## 配置 Lambda 函数 1)给 Lambda 一个执行的 Role,Policy 见下图: ![image.png](https://dev-media.amazoncloud.cn/9ea33b439f8a4f57aeb16e5365dc6499_image.png "image.png") ![image.png](https://dev-media.amazoncloud.cn/fa5f006275114a06bbb496e546850e58_image.png "image.png") 2)配置环境变量 演示为了方便,使用 Lambda 环境变量存数据库访问连接串,建议生产环境中使用 Secret Manager 存取; ![image.png](https://dev-media.amazoncloud.cn/2e84992ba9d64a1982d3fa558a3547f7_image.png "image.png") 3) 配置 Lambda 使用 VPC 下的 Private 子网(确保子网配置了 NAT-gw)。 ![image.png](https://dev-media.amazoncloud.cn/af273c666fc0427d9a7603be003ca917_image.png "image.png") ## 配置 EventBridge 打开控制台,跳转到 EventBridge ,新建一个 Rule; ![image.png](https://dev-media.amazoncloud.cn/eb8e52660c7145f5bfe8a90c4f6fd212_image.png "image.png") Target 选中我们的 Lambda 函数。 ![image.png](https://dev-media.amazoncloud.cn/c16ad52f4edb463aa92d2dea5d794952_image.png "image.png") ![image.png](https://dev-media.amazoncloud.cn/8cbcf14b0dd54d23ac61dd25ee4d5401_image.png "image.png") ## 配置 Grafana 1)首先配置一个新的 workspace; ![image.png](https://dev-media.amazoncloud.cn/d3e97e32cc454c67942b0b8447f5adc9_image.png "image.png") 2)使用托管的 SSO 来配置用户密码; ![image.png](https://dev-media.amazoncloud.cn/cb996d99b7f5473f9c358059bb2dc045_image.png "image.png") ![image.png](https://dev-media.amazoncloud.cn/c20ab21e87dd4778a6a5b08456495a26_image.png "image.png") 记录下 URL: ![image.png](https://dev-media.amazoncloud.cn/9ea25a4be06d4a858ff81e2746015b14_image.png "image.png") 3)到 SSO 创建用户; ![image.png](https://dev-media.amazoncloud.cn/15acee181bbd45cdb87889a09a52660e_image.png "image.png") 4)添加 SSO 用户到 Grafana; ![image.png](https://dev-media.amazoncloud.cn/d52291fa01cd4dcbac6b5be33ff9d9fe_image.png "image.png") 5)配置数据源。 ![image.png](https://dev-media.amazoncloud.cn/032740ae4e7047fdb7863d47407b0dbe_image.png "image.png") ## 测试方案 1)修改 Redis 参数,确保能记录慢日志修改 slowlog-log-slower-than = 1; 2)使用 Redis-benchmark 模拟真实 workload 过程可以观察 lambda 的日志输出或者登陆数据库查看是否有数据插入。 ```js ./Redis-benchmark -h test-cluster-1-001.t5tzux.0001.apse1.cache.amazonaws.com -n 1000000 -c 20 -t hset,lpush,rpush,sadd,zadd,set,get -q > test1.log & ./Redis-benchmark -h test-cluster-2-001.t5tzux.0001.apse1.cache.amazonaws.com -n 1000000 -c 20 -t hset,lpush,rpush,sadd,zadd,set,get -q > test2.log & ./Redis-benchmark -h test-cluster-3-001.t5tzux.0001.apse1.cache.amazonaws.com -n 1000000 -c 20 -t hset,lpush,rpush,sadd,zadd,set,get -q > test3.log & ./Redis-benchmark -h test-cluster-4-001.t5tzux.0001.apse1.cache.amazonaws.com -n 1000000 -c 20 -t hset,lpush,rpush,sadd,zadd,set,get -q > test4.log & ``` 3) 在 Grafana 作图观察分析 第一条 SQL (Slow_log_details) 是看日志明细: ```js SELECT slowlog_time AS "time", nodeid, endpoint, command, Redis_key, duration as "duration(ms)" FROM detail WHERE \$__unixEpochFilter(slowlog_time) ORDER BY slowlog_time ``` 第二条 SQL (slow_log_trend) 查看总体趋势分析: ```js SELECT from_unixtime(slowlog_time) as time, count(*) as cnt, nodeid FROM detail group by time,nodeid ``` 4) 查看效果图 ![image.png](https://dev-media.amazoncloud.cn/f5bb0d7c533749ad9ba695d729628cec_image.png "image.png") ## 参考文档 https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/elasticache.html?trk=cndc-detail
0
目录
关闭