如何找出打满磁盘的进程

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

  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 -P -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

你可能感兴趣的:(如何找出打满磁盘的进程)