美文网首页
如何找出打满磁盘的进程

如何找出打满磁盘的进程

作者: chenhong_f1e2 | 来源:发表于2019-11-05 20:19 被阅读0次

    集群机器的磁盘经常会被某些进程打满

    1. 磁盘空间被打满,其他进程无法继续写入
    2. 磁盘的读写通道被打满,其他进程因为读写缓慢而影响性能

    问题1: 磁盘空间被打满

    1.1. 检查机器磁盘使用

    主要是使用df命令来查看磁盘空间的使用,如下图所示:


    image.png

    Linux中df命令的输出清单的第1列是代表文件系统对应的设备文件的路径名(一般是硬盘上的分区);第2列是文件系统的类型;第3,4,5列给出分区总大小、已使用、剩余。用户也许会感到奇怪的是,第4,5列之和不等于第3列。这是因为缺省的每个分区都留了少量空间供系统管理员使用。即使遇到普通用户空间已满的情况,管理员仍能登录和留有解决问题所需的工作空间。清单中Use% 列表示普通用户空间使用的百分比,即使这一数字达到100%,分区仍然留有系统管理员使用的空间。最后,Mounted on列表示文件系统的挂载点。

    上图中文件系统的类型有两种,ext4和tmpfs,可能会有疑问tmpfs是什么。ext4是基于硬盘的文件系统,tmpfs是基于内存的文件系统,具体可看:https://blog.csdn.net/cherisegege/article/details/79311621

    由上图可知,通过df命令,上诉的%user列就是我们要找的磁盘使用情况,因此我们只要编写一个命令来获取磁盘使用即可

    ##df 获取磁盘使用情况
    ##grep 过滤出以dev开头的设备(比如基于内存的文件系统不以/deve开头)
    ##awk 输出df命令的第1,6,7列,分别是设备路径、磁盘使用率、挂载点
    ##sed 将一行中的%去除
    df -lhT |grep '^/dev/'|awk '{print $1,$6,$7}' |sed 's/%//g'
    
    

    1.2 找出占用空间最大的目录

    ## du -a 显示指定目录下文件的大小
    ## sort -n -r 按数值大小降序
    ## head -n 10 保留top 10
    du -a /dump/15 | sort -n -r | head -n 10
    
    ## find指令为找出500M以上的文件,print0和xargs -0配合使用,用来解决文件名中有空格或特殊字符问题
    ## du -m是查看这些文件的大小,并以m为单位显示。
    ## sort -nr是按照数字反向排序(大的文件在前)
    find /dump/15 -size +500M -print0|xargs -0 du -m|sort -nr
    

    问题2: 磁盘读写通道被打满

    2.1 使用iotop找到读写最大的进程

    若是io util很高,但是无法快速的定位到IO负载的来源进程和来源文件,可以使用iotop来定位。iotop是一个用来监视磁盘I/O使用状况的 top 类工具,可监测到哪一个程序使用的磁盘IO的信息(https://www.cnblogs.com/yinzhengjie/p/9934260.html)

    ## -o:只显示正在产生I/O的进程或线程
    ## -d 10: 输出间隔为10s 
    sudo iotop -o -d 10
    

    使用iotop找出大量读写的进程


    image.png

    然后使用以下命令查看该进程的io详情信息

    ## -b : 非交互模式,一般用来记录日志
    ## -o : 只显示正在产生I/O的进程或线程
    ## -q :禁止头几行,非交互模式
    ## -t : 加上时间戳,非交互非模式
    sudo iotop -boqt -p 1377
    

    2.2 使用lsof找出进程读写的文件

    在上述2.1 找到读写最大的进程号后,可以使用lsof来看该进程读写的文件(https://blog.csdn.net/qq_27870421/article/details/92803453)

    $sudo lsof -p 1377
    COMMAND    PID USER   FD      TYPE DEVICE SIZE/OFF NODE NAME
    jbd2/sdj1 1377 root  cwd       DIR    8,3     4096    2 /
    jbd2/sdj1 1377 root  rtd       DIR    8,3     4096    2 /
    jbd2/sdj1 1377 root  txt   unknown                      /proc/1377/exe
    

    2.3 利用 block_dump 找出进程的写入量

    除了上述方案,还可以使用block_dump来找出进程的读写量。当block_dump为非零值时(echo 1 > /proc/sys/vm/block_dump ),block_dump启用块I / O调试。 设置此标志后,Linux将报告发生的所有磁盘读写操作,block_dump的输出将写入内核输出,并且可以使用“ dmesg”进行检索。 当您使用block_dump调试消息时,您可能希望关闭系统日志(/etc/init.d/klogd stop),否则系统日志的太多可能会掩盖掉真正想要找到的问题

    ## 切换到root
    sudo bash -c "su root"
    ## 打印dmesg当前信息后清除缓存中留存的当前信息
    dmesg -c 
    #开启block_dump
    echo 1 > /proc/sys/vm/block_dump 
    ##删除上次的临时文件
    rm /tmp/disklog 
    ## 收集一段时间的日志 到disklog,一段时间后 crtl+c ,中断收集
    watch "dmesg -c >> /tmp/disklog" 
    ## 关闭block_dump
    echo 0 > /proc/sys/vm/block_dump
    ## 分析收集的日志
    cat /tmp/disklog | awk '/WRITE block/{a[$2]+=$5}END{for(i in a) print i,a[i]}'|column -t
    

    输出结果为:


    image.png

    列分别是: 进程名(pid): 写入量

    3: 使用 iodump

    3.1 iodump介绍

    iodump就利用内核tracepoint静态探针点技术实现的一个io问题排查工具。通过iodump工具,我们可以获取每一个IOPS(w/s和r/s)的详细信息,不仅包括IO请求的size大小,还包括IO请求的扇区地址,同时还包含IO请求的发生时间、读写的文件全路径、产生IO请求的进程信息等。这其中最具有特色的就是读写的文件全路径功能。

    sudo yum install -b current iodump -y
    sudo iodump -p sda 
    

    iodump的输出如下所示:

       Timestamp(us) P_name             Pid RW RqSize      Sector       Ino Partition FilePath
    1572573081340474 syslog-ng       193050  R  32768    84803792   2622641 sda3      /usr/lib64/libsyslog-ng-3.6.so.0.0.0
    1572573082629797 jbd2/sda5-8       1033  W   8192  2716230616  87295156 sda5      /home/admin/cloud/log/mi_config.INFO.log.190
    

    3.2 利用iodump找出pid/FilePath的读写量

    我们将iodump的结果,按照pid/FilePath聚合RqSize,即可获得每一个pid/FIlePath的读写量

    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    
    import commands
    import logging
    import sys
    
    logging.getLogger().setLevel(logging.INFO)
    
    TIMESTAMP = 0
    PNAME = 1
    PID = 2
    RW = 3
    RQSIZE = 4
    SECTOR = 5
    INFO = 6
    PARTITION = 7
    FILEPATH = 8
    ## 获取IODump的输出
    def get_iodump_output(disk='sda',last_time=5):
        """
        return:[ ['Timestamp','P_name','Pid','RW','RqSize','Sector','Ino','Partition','FilePath'] ]
        """
        iodump_output = []
        ## 获取sda盘的ipos信息
        cmd = "sudo iodump -p %s -t %s" % (disk,last_time)
        logging.info(cmd)
        status,output = commands.getstatusoutput(cmd)
        ## 第1行和最后两行是冗余的无效数据,需去除
        output_lines = output.split("\n")[1:-2]
        for line in output_lines:
            ### 按一个或多个空格分隔
            iodump_output.append(line.split())
        return iodump_output
    
    def group_data_by_pid(data):
        """
        data: [ ['Timestamp','P_name','Pid','RW','RqSize','Sector','Ino','Partition','FilePath'] ]
        return : ['Pid':"11",'R':0,"W":1,FilePath:{"path1":{"R":0,":W":1,"FilePath":"xxx"}}]
        """
        pid_ipos_dict = {}
        for item in data:
            try:
                pid = item[PID]
                rw = item[RW]
                filepath = item[FILEPATH]
                ### 按照pid聚合
                if not pid_ipos_dict.has_key(pid):
                    pid_ipos_dict[pid] = {
                        "Pid": pid,
                        "R": 0,
                        "W": 0,
                        "FilePath": {}
                    }
                pid_ipos_dict[pid][rw] = pid_ipos_dict[pid][rw] + int(item[RQSIZE])
                ### 同一pid下,再按照FilePath聚合RW
                if not pid_ipos_dict[pid]['FilePath'].has_key(filepath):
                    pid_ipos_dict[pid]['FilePath'][filepath] = {
                        "FilePath": filepath,
                        "R":0,
                        "W":0
                    }
                pid_ipos_dict[pid]['FilePath'][filepath][rw] = pid_ipos_dict[pid]['FilePath'][filepath][rw] + int(item[RQSIZE])
            except Exception as e:
                logging.error(e)
        return pid_ipos_dict.values()
    
    def print_format(arr):
        """
        arr: ['Pid':"11",'R':0,"W":1,FilePath:{"path1":{"R":0,":W":1,"FilePath":"xxx"}}]
        """
        logging.info("----------iodump group by pid----------")
        for item in arr:
            logging.info('PID:{Pid}, R:{R} ,W:{W}'.format(Pid=item['Pid'],R=item['R'],W=item['W']) )
            filePaths = item['FilePath'].values()
            for path in filePaths:
                logging.info('\t R:{R},W:{W},FilePath:{FilePath}'.format(FilePath=path['FilePath'],R=path['R'],W=path['W']))
    
    
    if __name__ == '__main__':
        disk =  sys.argv[1]
        last_time = sys.argv[2]
        ## 获取iodump的输出
        iodump_data = get_iodump_output(disk,last_time)
        ## 将iodump的输出按pid聚合
        pid_ipos_dict = group_data_by_pid(iodump_data)
        ## 格式化输出结果
        print_format(pid_ipos_dict)
    

    运行脚本,输出命令如下:


    image.png

    借此,可以找出IO最大的PID和以及写的FilePath。就可以找出一段时间(工具里 -t 10 表示10秒)内IO最大的进程和读写量最大的目录

    问题

    1. 如何获取iodump工具?

    刚了解到大神的iodump还只在内部使用,尚未开源(T.T等着大神完工吧)。其本质是获取磁盘每一个进程的iops详情信息。

    参考自:

    https://blog.csdn.net/saga_gallon/article/details/82891709
    https://blog.csdn.net/Aaron528928/article/details/80548139
    https://www.cnblogs.com/peida/archive/2012/12/07/2806483.html
    https://blog.csdn.net/qq_27870421/article/details/92803453
    https://www.cnblogs.com/yinzhengjie/p/9934260.html
    https://stackoverflow.com/questions/249570/how-can-i-record-what-process-or-kernel-activity-is-using-the-disk-in-gnu-linux
    https://blog.csdn.net/zwjzqqb/article/details/78959952

    相关文章

      网友评论

          本文标题:如何找出打满磁盘的进程

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