如何找出打滿磁盤的進程

集羣機器的磁盤經常會被某些進程打滿

  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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章