Redis状态监控

作者: 不正经运维 | 来源:发表于2018-05-28 16:56 被阅读70次

    2018年5月31日 星期四

    09:52

    背景

    这个更简单,管理员不会做,所以得我们做。

    需求

    需求就对Redis进行监控,当然,这个需求说了和没说一样。

    分析

    既然要监控,那么就先理清监控流程,然后结合流程编写脚本就好。

    监控流程

    因为Redis可以多实例部署,所以要自动发现(主要是太懒所致)。

    1. 运行脚本发现Redis实例监听端口;
    2. 测试端口Redis是否正常响应,如正常则返回实例信息。
    3. 根据实例信息,添加监控项目。
    4. 监控项目通过脚本获取监控数据。

    监控参数

    从网上了解到的信息,Redis的监控可以分为以下2类:

    1. 状态,是否在正常运行;
    2. 性能,是否存在瓶颈;

    系统资源我就不说了,默认监控着的。

    Redis自带的redis-cli名有一个monitor的选项,用于实时查看系统的操作,可以Debug用。

    状态指标

    指标名称 说明 阈值
    redis_ping Redis存活 !=1
    uptime_in_days 已启动天数 <1
    rdb_last_save_time 持久化时的上次保存时间 >1800
    rdb_changes_since_last_save 未保存的操作数量 >1000
    cluster_enabled 集群模式 !=1
    cluster_info 集群状态 !=ok
    cluster_slots 集群slot数量 !=16384
    cluster_slots_fail 集群故障slots数量 >0
    cluster_known_nodes 集群节点数量 <N(最小提供服务或者已知节点数量)。
    connected_slaves 已连接的Slave数量 !=N(实际的Slave数量)
    last_interaction_seconds Master和Slave上次交互后的时间 >30
    link_down_since_seconds Master和Salve连接断开后的时间 >60

    性能指标

    指标名称 说明 阈值
    connected_clients 当前连接的客户端数量 -
    blocked_clients 正在等待的客户端数量 >100
    rejected_connections 达到maxclient限制后拒绝的连接数量 >200
    key 数据库中的键数量 -
    keys_hit_rate 键空间的命中率,命中/(命中+未命中) <0.8
    key_misses 查询失败的键数量 -
    instantaneous_ops_per_sec 每秒执行命令数 >100000
    latest_fork_usec fork阻塞时间
    latency 延迟时间
    used_memory 已分配的内存总量 >最大可用内存
    mem_fragmentation_ratio 内存碎片百分比 >1.5
    evicted_keys 被驱逐的键数量 >100

    解决方案

    使用说明

    本地Redis,自动发现

    zabbix_agentd的配置中增加如下参数:

    UserParameter=redis_discovery[*], /usr/local/zabbixagent/scripts/redis_status.sh $1
    UserParameter=redis_info[*], /usr/local/zabbixagent/scripts/redis_status.sh -p $1 -m info -f $2 $3
    

    本地Redis,指定端口

    直接使用redis_info即可。

    设置密码

    使用-a参数指定密码。

    支持参数

    redis_info支持参数如下:

    • ping;
    • mem_utilization
    • hit_rate
    • 其他info模式支持的参数

    脚本

    具体脚本内容如下,请保存为redis_status.sh

    #!/bin/bash
    #
    # -------------------- Copyright --------------------
    # FileName: redis_status.sh
    # Description: Get status of Redis.
    # Version: 1.1
    # Date: 2018/05/21
    # Author: Rex Kang
    # Email: jl_kang@haihangyun.com
    # -------------------- History --------------------
    # 2018/05/21: First version
    # 2018/05/31: fix bug of full path of redis-cli 
    # -------------------- End --------------------
    
    
    CMD="/usr/local/bin/redis-cli"
    
    fdiscover() {
        json="{'data': ["
        json_body=''
    
        instances=`netstat -ntpl | grep "redis" | awk -F'[ :]+' '{print $5}'`
        count=${#instances[@]}
        $DEBUG && echo -e "REDIS_COUNT:\t$count"
    
        for ((i=0; i<${count}; i++))
        do
            result="0"
            result=`/usr/bin/redis-cli -h ${SERVER} -p ${instances[$i]} ping 2>/dev/null | grep -c PONG`
            $DEBUG && echo -e "RESULT of ${instances[$i]}:\t$result"
            if [ $result = "1" ]; then
                json_body=${json_body}"{'{#${MACRO_VARNAME}}': '${instances[$i]}'},"
            fi
        done
     
        [ -n "${json_body}" ] && json_body=${json_body%?}
        $DEBUG && echo -e "\n"
        echo $json${json_body}"]}"
        return 0
    }
    
    
    finfo() {
        value=0
        $CLUSTER && CMD="$CMD -c"
        CMD="$CMD -h $SERVER -p $PORT"
        [ -n "$AUTH" ] && CMD="$CMD -a $AUTH"
    
        if [ $FIELD = 'ping' ]; then
            result=`$CMD ping 2>/dev/null 2>/dev/null | grep -c PONG`
            [ -n "$result" ] && echo $result || echo 0
        elif [ $FIELD = 'latency' ]; then
            resutl=`$CMD --latency 2>/dev/null | awk {'print $6'} | tr -d '[:cntrl:]'`
            [ -n "$result" ] && echo $result || echo 0
        elif [ $FIELD = 'intrinsic-latency' ]; then
            result=`$CMD --intrinsic-latency 5 2>/dev/null | grep 'avg latency' | awk {'print $6'} | tr -d '[:cntrl:]'`
            [ -n "$result" ] && echo $result || echo 0
        elif [ $FIELD = 'mem_utilization' ]; then
            mem_total=`$CMD info | grep "^total_system_memory:" | awk -F':' '{print $2}' | tr -d '[:cntrl:]'`
            mem_used=`$CMD info | grep "^used_memory:" | awk -F':' '{print $2}' | tr -d '[:cntrl:]'`
            $DEBUG && echo -e "RESULT:\t\t$mem_used, $mem_total"
            utilization=`echo "scale=4;$mem_used*100/$mem_total" | bc | awk '{printf("%0.2f", $0)}'`
            echo $utilization
        elif [ $FIELD = 'hit_rate' ]; then
            hit_rate=0
            hits=`$CMD info | grep "^keyspace_hits:" | awk -F':' '{print $2}' | tr -d '[:cntrl:]'`
            misses=`$CMD info | grep "^keyspace_misses:" | awk -F':' '{print $2}' | tr -d '[:cntrl:]'`
            $DEBUG && echo -e "RESULT:\t\t$hits + $misses"
            total=`expr $hits + $misses`
            if [ $total -ne 0 ]; then
                hit_rate=`echo "scale=4;$hits*100/$total" | bc | awk '{printf("%0.2f", $0)}'`
            fi
            echo $hit_rate
        else
            result=`$CMD info | grep "^${FIELD}:"`
            $DEBUG && echo -e "RESULT:\t$result"
            $DEBUG && echo -e "CMD:\t $CMD info | grep '${FIELD}:'"
            if [ -n "$result" ]; then
                echo $result | awk -F':' '{print $2}'
            else
                echo 0
            fi
        fi
    
        return 0
    }
    
    
    usage() {
        echo -e "usage:\t$1 [-dh ][-s server] [-p port] [-a auth]"
        echo -e "\t [-m discover|info] [-f field]"
        echo -e "\nDiscover mode parameters:"
        echo -e "-s server\t\tDefault is 127.0.0.1"
        echo -e "-p port\t\tDefault is '6379"
        echo -e "-a pass\t\tThe pass if configured"
        echo -e "[-m discover]\t\tDefault mode is discover"
    
        echo -e "\nInfo mode parameters:"
        echo -e "-s server\t\tDefault is 127.0.0.1"
        echo -e "-p port\t\tDefault is '6379"
        echo -e "-a pass\t\tThe pass if configured"
        echo -e "-m info\t\t\tGet info of redis"
        echo -e "-f field\t\tGet info of specified field"
        echo -e "\nPlease modified var CMD in this file.
        return 0
    }
    
    main () {
        DISCOVER=true
        DEBUG=false
    
        MACRO_VARNAME="REDIS_PORT"
    
        SERVER="127.0.0.1"
        PORT="6379"
        AUTH=""
    
        CLUSTER=false
    
        # parameters
    
        while getopts "s:p:a:m:f:cdh" OPT; do
            case $OPT in
                f)
                    FIELD="$OPTARG"
                    ;;
                m)
                    [ $OPTARG = "info" ] && DISCOVER=false
                    ;;
                s)
                    SERVER="$OPTARG"
                    ;;
                p)
                    PORT="$OPTARG"
                    ;;
                a)
                    AUTH="$OPTARG"
                    ;;
                c)
                    CLUSTER=true
                    ;;
                d)
                    DEBUG=true
                    ;;
                h)
                    usage $0
                    exit 0
                    ;;
                ?)
                    usage $0
                    exit 1
                    ;;
            esac
        done
    
    
        VAR_OK=false
    
        if $DEBUG; then
            echo -e "SERVER: \t$SERVER"
            echo -e "PORT: \t\t$PORT"
            [ -n "$AUTH" ] && echo -e "AUTH: \t\t$AUTH" || echo -e "AUTH: \t\tFalse"
            echo -e "DISCOVER: \t$DISCOVER"
            echo -e "CLUSTER: \t$CLUSTER"
            echo -e "FIELD: \t\t$FIELD"
            echo -e "VAR_OK: \t$VAR_OK"
        fi
        # basic info check
        if [ -n "$SERVER" ] && [ -n "$PORT" ]; then
            if $DISCOVER; then
                fdiscover
            else
                if [ -n "$FIELD" ]; then
                    finfo
                else
                    echo "$0 missing parameters!"
                    usage
                    exit 2
                fi
            fi
        else
            echo "$0 missing parameters!"
            usage
            exit 1
        fi 
    }
    
    
    main $@
    
    

    遗留问题

    1. latency的监控暂未实现,因为没有发现只让它运行5秒就结束的方式;

    其他

    1. 在Redis官网上,看到一个config resetstat用于重置统计数据,可能系统需要在进行这种操作时取消告警;
    2. 根据优化依据来进行告警或者运维自动化工作是一个非常靠谱的做法;
    3. 在添加模板时,注意将结果类型调整为float
    4. 如果不使用redis-cli的全路径,会找不到路径。

    参考

    1. How to Monitor Redis
    2. Monitor
    3. redis监控指标
    4. 关于redis性能问题分析和优化

    相关文章

      网友评论

        本文标题:Redis状态监控

        本文链接:https://www.haomeiwen.com/subject/atigjftx.html