• RAID的介绍、使用以及监控

    RAID详解

    简介

    背景:因为当时CPU的速度增长很快,而磁盘驱动器的数据传输速率无法大幅提高,所以需要有一种方案解决二者之间的矛盾。

    所以raid的方案就诞生,raid是由很多廉价的磁盘,组合成一个容量巨大的磁盘组,在RAID中,可以让很多磁盘驱动器同时传输数据,而这些磁盘驱动器在逻辑上又是一个磁盘驱动器,所以使用RAID可以达到单个的磁盘驱动器几倍、几十倍甚至上百倍的速率,RAID最后成功解决了磁盘驱动器的数据传输速率的问题。

    分类

    一是外接式磁盘阵列柜、二是内接式磁盘阵列卡,三是利用软件实现

    外接式磁盘阵列柜是一种独立的存储设备,可以通过外部接口(如 USB、eSATA、Thunderbolt 等)连接到计算机或其他服务器,适合个人用户、小型办公室或作为备份解决方案,在此不展开讨论。

    内接式磁盘阵列卡是安装在计算机内部的硬件设备,直接连接到主板的 PCIe 插槽,并管理连接到它的硬盘。raid卡可以提供更多的 RAID 配置选项和高级功能,如缓存、电池后备写入缓存(BBU)等

    软件 RAID 是通过操作系统或独立的软件来管理硬盘的 RAID 配置,如mdadm,LVM2,成本较低,配置灵活。

    级别

    常见的有:RAID 0,RAID 1,RAID 5,RAID 6,RAID 10

    RAID 0(至少需要两块盘)

    RAID 0是以条带化的方式,分别向两块盘,并行地写入数据,读取时也是并行的。一方面,并行操作可以充分利用I/O总线的**带宽**,提高磁盘整体存取性能;另一方面,它有RAID级别中最高的空间利用率,但是没有数据冗余,存在单点故障。

    RAID 1(至少需要2块硬盘,一般为双数)

    RAID 0是以镜像的方式,分别向两块盘,并行写入相同的数据。有数据冗余,可靠性强,一半写数据,一半用来做备份,但是硬盘容量会减少一半。读取时,从两块硬盘上并行读取,写入慢,读取快。

    RAID 5(需要3块以上硬盘)

    RAID 5采用奇偶校验,可靠性强,磁盘校验和被散列到不同的磁盘里面,增加了读写速率。只有当两块磁盘同时丢失时,数据才无法恢复,至少三块硬盘并且硬盘大小应该相等才能组成Raid 5阵列

    RAID使用

    软raid

    mdadm是multiple devices admin的简称,它是Linux下的一款标准的软件 RAID 管理工具

    安装:

    #直接用yum\apt安装即可
    yum install mdadm

    常用参数:

    mdadm工具指令基本格式:

    mdadm -C -v 目录 -l 级别 -n 磁盘数量 设备路径
    举例:
    mdadm -C -v /dev/md0 -l 0 -n 2 /dev/sda1 /dev/sdb1
     //在/dev/md0目录下将sda1与sdb1两块磁盘创建为RAID级别为0,磁盘数为2的RAID0阵列

    创建流程

    1.使用 lsblk 命令查看可用的磁盘:

    lsblk

    2、使用 mdadm 命令创建 RAID 0 阵列:

    mdadm -C -v /dev/md0 -l 0 -n 2 /dev/sda /dev/sdb

    需要将 /dev/sda 和 /dev/sdb 替换为您的磁盘名称。

    #检查/dev/md0设备是否已经正确创建并且处于“活动”状态
    mdadm --detail /dev/md0

    3.格式化 RAID 0 阵列:

     mkfs.ext4 /dev/md0

    4.挂载 RAID 0 阵列:

    mkdir /mnt/raid0
    mount /dev/md0 /mnt/raid0(临时挂载)

    硬raid

    Raid卡市场主要是LSI、Adaptec、Highpoint、Promise等厂商提供。Adaptac被PMC收购后,提供的Raid卡即为PMC,简称为P卡。LSI公司提供的Raid卡,即为L卡。

    查看raid卡的类型可以使用 lspci | grep -i raid命令。

    PMC配置操作工具为:arcconf,LSI配置操作工具为:MegaCli、storcli。本文仅讨论MegaCli工具的使用

    安装

    MegaCli 安装脚本

    #!/bin/bash
    #下载安装包
    wget --user=hetzner --password=download <http://download.hetzner.de/tools/LSI/tools/MegaCLI/8.07.14_MegaCLI.zip>
    
    # 获取操作系统 ID
    os_id=$(grep -oP '^ID=\\K.*' /etc/os-release)
    # 判断操作系统并执行相应的命令
    if [ "$os_id" = "debian" ]; then
        apt install unzip alien libncurses5 -y
        unzip -d ./megacli 8.07.14_MegaCLI.zip
        cd ./megacli/Linux/
        alien MegaCli-8.07.14-1.noarch.rpm
        dpkg -i megacli_8.07.14-2_all.deb  
        echo "MegaCLI安装成功"
    elif [ "$os_id" = '"centos"' ] || [ "$os_id" = '"rocky"' ]; then
        yum install unzip  -y
        unzip -d ./megacli 8.07.14_MegaCLI.zip
        cd ./megacli/Linux/
        rpm -ivh MegaCli-8.07.14-1.noarch.rpm 
        echo "MegaCLI安装成功"
    else
        echo "操作系统不符,已退出"
        exit 1
    fi

    MegaCli常用命令

    # 查看raid卡信息
    /opt/MegaRAID/MegaCli/MegaCli64 -AdpAllInfo -aALL
    # 查看所有 raid 级别
    /opt/MegaRAID/MegaCli/MegaCli64 -LDInfo -Lall -aALL 
    # 查看所有硬盘状态
    /opt/MegaRAID/MegaCli/MegaCli64  -PDList -aALL 
    /opt/MegaRAID/MegaCli/MegaCli64  -PDList -aALL | grep 'Firmware state'
    #删除raid (注意这里L1的数字为Target Id的数字)
    /opt/MegaRAID/MegaCli/MegaCli64 -CfgLdDel -L1 -a0 

    创建流程

    1.查看所有硬盘的信息

    /opt/MegaRAID/MegaCli/MegaCli64 -PDList -aALL

    常见参数含义

    Medai Error Count 不为0,表示磁盘可能错误,可能是磁盘有坏道,数值越大,危险系数越高

    Other Error Count 不为0,表示磁盘可能存在松动,可能需要重新再插入

    Firmware state: 磁盘的状态,Online是最好的状态,除此之外还有 Unconfigured、Offline、Failed

    2.根据磁盘数量创建raid

    /opt/MegaRAID/MegaCli/MegaCli64 -CfgLdAdd -rX [Enclosure:Slot,...] [WT|WB] [NORA|RA] [Direct|Cached]
    -r:指定raid级别
    WB:写缓存策略
    WT:直接写入磁盘,不适用RAID卡缓存                       
    Direct:读操作不缓存到RAID卡缓存
    Cached:读操作缓存到RAID卡缓存
    举例:创建一个包括4块盘的raid0,写缓存策略,自适应预读,不缓存到RAID卡缓存
    /opt/MegaRAID/MegaCli/MegaCli64 -CfgLdAdd -r0 [0:0,0:1,0:2,0:3] WB Direct -a0

    3.查看raid和磁盘的状态

    #查看raid的状态
    /opt/MegaRAID/MegaCli/MegaCli64 -LDInfo -Lall -aALL
    #查看磁盘的状态
    /opt/MegaRAID/MegaCli/MegaCli64  -PDList -aALL | grep 'Firmware state'

    RAID监控

    通过脚本监控硬raid状态

    #!/bin/bash  
    #配置告警信息
    TIME=$(date +'%Y-%m-%d %H:%M:%S')
    HOST=$(hostname)
    IP="`hostname -I | awk 'BEGIN{FS=" "}{print $1}'`"
    logfile=/logs/check_megacli.log
    
    # Telegram信息
    t_token='xxxxx'
    t_chat_ID="xxxxx"
    t_url='https://api.telegram.org/bot'xxxx'/sendMessage' 
    
    # Telegram通知函数
    function send_telegram() {
        local msg="$1"
        local formatted_msg=$(echo -e "$msg")
        curl -s -X POST "$t_url" -d chat_id="$t_chat_ID" -d text="$formatted_msg" -d parse_mode="$t_mode"
    }
    
    # 定义 MegaCli64 的路径  
    MEGACLI_PATH="/opt/MegaRAID/MegaCli/MegaCli64"  
    
    # 获取 RAID 状态  
    raid_status=$($MEGACLI_PATH -LDInfo -Lall -aALL | grep -i "State" | awk '{print $NF}')
    echo "$raid_status" > /tmp/raidstate.log 
    # 获取硬盘状态  
    disk_status=$($MEGACLI_PATH -pdlist -aALL  | grep "Firmware state" | awk -F : '{print $2}' | awk -F , '{print $1}') 
    echo "$disk_status" > /tmp/fireware.log
    
    # 检查 RAID 状态
    for i in `cat < /tmp/raidstate.log`  
    do
    if [[ "$i" != "Optimal" ]]; then  
        echo "$current_time - RAID 状态异常: $raid_status" >> $logfile
        # 发送Telegram告警
        send_telegram "时间: $(date +'%Y-%m-%d %H:%M:%S')\n服务器: $(hostname)\nIP: $(hostname -I | awk '{print $1}')\n告警信息: RAID状态:$i,请立即检查!\n报错详细信息: /logs/check_megacli.log" 
    fi  
    done
    
    # 检查硬盘状态  
    for i in `cat < /tmp/fireware.log`
    do
    if [[ "$i" != "Online" ]]; then  
        echo "$current_time - 硬盘状态异常: $i" >> $logfile  
        # 发送Telegram告警
        send_telegram "时间: $(date +'%Y-%m-%d %H:%M:%S')\n服务器: $(hostname)\nIP: $(hostname -I | awk '{print $1}')\n告警信息: RAID状态:$i,请立即检查!\n报错详细信息: /logs/check_megacli.log"
    fi  
    done 

    通过脚本监控软raid状态

    #!/bin/bash
    
    TIME=$(date +'%Y-%m-%d %H:%M:%S')
    HOST=$(hostname)
    IP="`hostname -I | awk 'BEGIN{FS=" "}{print $1}'`"
    logfile=/logs/check_mdadm.log
    
    # 钉钉
    token="xxxx"
    dingtalk_url="https://oapi.dingtalk.com/robot/send?access_token=$token"
    
    # Telegram
    t_chat_ID="xxxx"
    t_mode='HTML'
    t_url='https://api.telegram.org/bot"xxxx"/sendMessage'  
    
    # 钉钉通知函数
    function send_dingtalk() {
    local msg="$1"
    local json_payload=$(cat <<EOF
    {
        "msgtype": "text", 
        "text": {
            "content": "时间: $TIME\n服务器: $HOST\nIP: $IP\n告警信息: $msg\n报错日志路径: /logs/check_mdadm.log"
        },
        "at": {
            "isAtAll": false
        }
    }
    EOF
    )
        curl -X POST "$dingtalk_url" \
             -H 'Content-Type: application/json' \
             -d "$json_payload"
    
    }
    
    # Telegram通知函数
    function send_telegram() {
        local msg="$1"
        local formatted_msg=$(echo -e "$msg")
        curl -s -X POST "$t_url" -d chat_id="$t_chat_ID" -d text="$formatted_msg" -d parse_mode="$t_mode"
    }
    
    hostname=`hostname`
    cd /dev
    list=`ls md?`
    
    if [ ! -n "$list" ] ; then
        exit
    fi
    #主循环
    for name in $list ; do
        subject=`/sbin/mdadm -D /dev/$name |grep "State :"`
        num1=`echo $subject |grep degraded |wc -l`
        num2=`echo $subject |grep FAILED |wc -l`
        num3=`echo $subject |grep clean |wc -l`
        status="`sudo mdadm --detail /dev/md? | grep -e "State : " | awk 'BEGIN{FS=":"}{print $2}' | uniq`"
        remark=$(GetRemark /tmp/$name-lastsend)
    
      if [ $num1  -ne 0 ] || [ $num2 -ne 0 ] ; then
            echo -e " \e[31m Hello World \e[0m"
            echo -e "$hostname $name \e[31m Error \e[0m  $subject"
            if [ "$remark" =  "" ] ; then
                /sbin/mdadm -D $name  > /tmp/disk_result.txt
                disklist=`ls /dev/sd?`
    
                for disk in $disklist ; do
                    echo $disk >> /tmp/disk_result.txt
                    smartctl -a $disk |grep Serial >> /tmp/disk_result.txt
                done
    
            # 发送钉钉告警
            send_dingtalk "主机RAID状态:$status,请立即检查!"
    
            # 发送Telegram告警
            #send_telegram "时间: $(date +'%Y-%m-%d %H:%M:%S')\n服务器: $(hostname)\nIP: $(hostname -I | awk '{print $1}')\n告警信息: 主机RAID状态:$status,请立即检查!\n报错详细信息: /logs/check_mdadm.log"
            fi      
    
        elif [ $num3 -eq 1 ] ; then
            echo -e "$(date +'%Y-%m-%d %H:%M:%S')  $hostname $name \e[34m OK \e[0m   $subject" >>$logfile
      fi  
    done

    附录

    遇到的问题

    1、在对RAID组扩容时,热添加的扩容盘意外下线,导致文件系统损坏,不能读写,raid状态变成了Offline

    热添加的盘在重构期间应保持在线,以确保数据完整性和系统稳定性。如果意外将热添加的盘下线,会导致整个raid中的所有硬盘下线,无法读写

    如果意外下线了,如何进行恢复文件系统

    首先将原有的硬盘重新上线,此时raid的状态恢复为Optimal,但是文件系统仍然有问题,不能读写

    /opt/MegaRAID/MegaCli/MegaCli64 -PDOnline -PhysDrv [0:0,0:1,0:2,0:3] -a0

    接着将挂载的文件目录卸载掉

     umount /data

    然后利用fsck修复损坏的文件系统(通常用于检查 ext2、ext3 或 ext4 文件系统)

    fsck -y /dev/sda

    如果是xfs的文件系统使用xfs_repair

    xfs_repair /dev/sda

    修复完成后重新挂载,检查状态

    如果你运行fsck或xfs_repair命令(文件系统检查和修复命令),它也许会找到一些数据碎片,这些文件碎片在硬盘中并没有引用。特别的,fsck也许能找到看起来是完整的文件,但是在系统中没有名字-一个inode但是不对应文件名。这个数据仍然占用硬盘空间,但是并不能通过正常方式访问。
    lost+found目录的文件通常是未链接的文件(名字已经被删除),但是这些文件还被一些进程使用(数据没有删除),在突然关机时(内核panic或者突然断电)出现。

    如何恢复lost+found目录中的数据?

    如果是xfs可以使用xfs_undelete,ext4的可以使用extundelete工具,testdisk适用于多种文件系统,。这里就不多讨论了,可以参考下面的文章。

    如有相关问题,请在文章后面给小编留言,小编安排作者第一时间和您联系,为您答疑解惑。

    extundelete参考文章:

    blog.51cto.com/u_151275

    testdisk参考文章:

    cnblogs.com/zafu/p/1140

    xfs_undelete参考文章:

    blog.yufei.im/posts/%E6

    参考文章链接:

    blog.csdn.net/ChenVast/

    cnblogs.com/benjamin77/

    blog.csdn.net/qq_448956

    blog.csdn.net/weixin_43

    «
    »
以专业成就每一位客户,让企业IT只为效果和安全买单

以专业成就每一位客户,让企业IT只为效果和安全买单