Skip to content

基于 linux 上的网络监控工具的使用

· 10 min
TL;DR

涉及到 iotop, iostat, netstat, sar 命令的使用。

本文全面介绍 Linux 系统下四种强大的网络与 IO 监控工具及其实际应用。详细讲解了 iotop 进程级 IO 监控、iostat 设备 IO 统计、netstat 网络连接分析和 sar 系统活动报告工具的安装配置、命令选项和常见使用场景。文章最后提供了一个实用脚本,用于自动收集系统网络数据并实现日志轮转,帮助系统管理员高效监测和排查网络性能问题。

1. iotop#

类似于 top 命令,用于监控和显示进程级别的磁盘 I/O 使用情况,可以像 top 一样实时刷新显示。

如果系统默认没有安装,需要先安装:

Terminal window
sudo yum install iotop

使用格式:iotop [options]

常用的选项 options 有:

Terminal window
-o 仅显示当前正在进行 I/O 操作的进程
-b 批处理模式,不刷新屏幕,直接输出结果,适用于脚本
-n NUM 指定输出的刷新次数(默认无限刷新)
-d SEC 指定刷新间隔,单位为秒(默认 1 秒)
-p PID 只显示特定进程的 I/O 活动
-P 只显示进程,不显示线程
-u USER 只显示属于特定用户的进程的 I/O 使用情况
-q 使 iotop 运行时安静,只输出一次统计信息而不刷新
-k 输出的单位,kbytes/s
-t 显示时间戳

使用场景:

2. iostat#

系统的 I/O 数据统计,可以动态的监视磁盘设备的 IO 负载情况

如果系统默认没有安装,需要先安装:

Terminal window
sudo yum install sysstat

使用格式:iostat [ options ] [ interval [ count ] ]

常用的选项 options 有:

Terminal window
-d 输出设备 (磁盘) 的 IO 统计信息
-c 输出 CPU 的统计信息,与 -d 选项相互排斥
-h 输出 human readable 格式信息
-k/-m 输出的单位,kbytes/s, mbytes/s,默认是块(每块 512 字节)
-x 输出拓展信息,包含更详细的 I/O 指标
-t 显示采集数据时的时间
-p [device] 显示所有设备或指定设备的**分区**统计
-y 忽略自机器启动以来的累计统计数据,而从命名执行开始统计

使用场景:

3. netstat#

netstat 查看系统中网络相关活动的重要工具,常用于查看 TCP/UDP 连接,socket 活动等等。

如果系统默认没有安装,则需要进行安装:

Terminal window
sudo yum install net-tools

常用的选项有:

Terminal window
-r, --route 显示路由表
-i, --interfaces 显示所有网卡表,如 `netstat -i`
-I, --interfaces=<Iface> 显示指定的网卡表,如 `netstat -I=eth0`
-s, --statistics 输出网络的统计信息 (like SNMP)
-v, --verbose verbose 输出
-c, --continuous 连续输出模式
-n,--numeric 不解析主机名,直接显示对应的 ip
-e, --extend 显示更多拓展 or 隐藏的信息,如 `netstat -i -e` 的输出相当于 `ifconfig`
-p, --programs 显示 PID/Program 占用的 socket
-l, --listening 显示正在监听的 socket 活动
-a, --all 显示所有的 socket 活动 (默认:connected)
-t, --tcp 显示 TCP 连接活动
-u, --udp 显示 UDP 连接活动

实际场景:

4. sar#

sar 表示 system activity reporter, 也是 Linux 上有用的性能分析工具。

使用格式是:sar [ options ] [ t [ n ] ] [-o file]

常用的选项 options 有:

Terminal window
-b I/O 和传送速率的统计 [A_IO]
-B 内存分页统计 [A_PAGE]
-d 块设备统计 [A_DISK]
-n { <关键词> [,...] | ALL }
网络统计 [A_NET_...]
关键词有:
DEV Network interfaces
EDEV Network interfaces (errors)
IP IP traffic (v4)
EIP IP traffic (v4) (errors)
TCP TCP traffic (v4)
ETCP TCP traffic (v4) (errors)
UDP UDP traffic (v4)
UDP6 UDP traffic (v6)
SOCK Sockets (v4)
SOCK6 Sockets (v6)
IP6 IP traffic (v6)
EIP6 IP traffic (v6) (errors)
-q [ <关键词> [,...] | PSI | ALL ]
System load and pressure-stall statistics
关键词有:
LOAD Queue length and load average statistics [A_QUEUE]
CPU Pressure-stall CPU statistics [A_PSI_CPU]
IO Pressure-stall I/O statistics [A_PSI_IO]
MEM Pressure-stall memory statistics [A_PSI_MEM]
-r [ ALL ]
内存使用统计 [A_MEMORY]
-S 交换内存使用统计 [A_MEMORY]
-u [ ALL ]
CPU 利用率 [A_CPU]
-v Kernel tables statistics [A_KTABLES]
-W Swapping statistics [A_SWAP]
-w Task creation and system switching statistics [A_PCSW]
-y TTY devices statistics [A_SERIAL]

t 表示采样间隔,n 表示采样次数

-o 表示输出到指定文件,之后还可以通过 -f 指定读取该文件,查看之前记录的统计数据。

实际场景:

注意到这个命令在不同的操作系统或发行版本中可能在选项上有些差异,需要在具体系统中使用 sar --help 来确认。

参考:https://linuxtools-rst.readthedocs.io/zh-cn/latest/tool/sar.html

一个小脚本#

之前项目中需要对系统网络情况进行监控,所以写了这个脚本。 它实现了将网络统计数据写入日志文件,然后定期对日志文件进行 rotate,丢掉过期的文件 (logroate 服务有系统的 crontab job 定期执行)。

#! /bin/bash
set -e
LOGROTATE_IOSTAT=/etc/logrotate.d/iostat
LOGROTATE_IOTOP=/etc/logrotate.d/iotop
LOGROTATE_SAR=/etc/logrotate.d/sar
LOGROTATE_NETSTAT=/etc/logrotate.d/netstat
# todo
# SYSTEMD_PATH=/usr/lib/systemd/system/watcher.service
new_date() {
date '+%Y-%m-%d-%H-%M-%S'
}
function run_iotop() {
# time-stamp, batch, only process
ret=`iotop -tboP -d $interval | tee -a $logger`
if [[ $? -eq 0 ]]; then
echo "done ..." | tee -a $logger
exit
fi
}
run_iostat() {
while true; do
ret=`iostat -t -y -d $interval | tee -a $logger`
if [[ $? -eq 0 ]]; then
echo "done ..." | tee -a $logger
exit
fi
done
}
run_netstat() {
ret=`netstat -s $interval | tee -a $logger`
if [[ $? -eq 0 ]]; then
echo "done ..." | tee -a $logger
echo >&3
exit
fi
}
run_sar() {
while true; do
ret=`sar -n ETCP $interval | tee -a $logger`
if [[ $? -eq 0 ]]; then
echo "done ..." | tee -a $logger
exit
fi
done
}
setup_logroate() {
LOG=$(readlink -f ${1})
LOG_CONF=$2
if [[ ! -f $LOG_CONF ]]; then
cat <<-EOF > $LOG_CONF
$LOG {
daily
create
rotate 31
missingok
copytruncate
dateext
compress
minsize 1M
create 666 root root
}
EOF
fi
}
stop_iostat() {
ps -ef | grep iostat | grep -v grep | grep -v $1 | grep -v tee | awk '{print $2}' | xargs -I{} kill -9 {}
}
stop_iotop() {
ps -ef | grep iotop | grep -v grep | grep -v $1 | grep -v tee | awk '{print $2}' | xargs -I{} kill -9 {}
}
stop_netstat() {
ps -ef | grep netstat | grep -v grep | grep -v $1 | grep -v tee | awk '{print $2}' | xargs -I{} kill -9 {}
}
stop_sar() {
ps -ef | grep sar | grep -v grep | grep -v $1 | grep -v tee | awk '{print $2}' | xargs -I{} kill -9 {}
}
show_usage() {
echo "Usage: $0 [-c | --cmd] [-o | --operate] [-i | --interval] [-f | --log-file] [-h | --help]"
echo "Example: $0 -c iostat -o start -i 3 -f /home/logs/iostat.log "
echo "Example: $0 -c iostat -o stop"
echo "COMMONDS: iotop, iostat, netstat, sar"
}
main() {
if [[ $# -lt 1 ]]; then
show_usage
exit 1
fi
script_name=$0
ARGS=$(getopt -o i:,f:,o:,c:,h --long cmd:,operate:,interval:,log-file:,help, -- "$@")
if [[ $? -ne 0 ]]; then
echo "parse params error."
show_usage
exit 1
fi
eval set -- "${ARGS}"
while true; do
case $1 in
-c | --cmd)
cmd=$2
shift 2
;;
-o | --operate)
operate=$2
shift 2
;;
-i | --interval)
interval=$2
shift 2
;;
-f | --log-file)
logger=$2
shift 2
;;
-h | --help)
show_usage
exit
;;
--)
shift
break
;;
*)
show_usage
exit 2
;;
esac
done
if [[ -z $logger ]]; then
logger=$(new_date)-${cmd}.log
fi
if [[ -z $interval ]]; then
interval=1
fi
# iostat
if [[ $cmd == "iostat" && $operate == "start" ]]; then
setup_logroate $logger $LOGROTATE_IOSTAT
run_iostat &
fi
if [[ $cmd == "iostat" && $operate == "stop" ]]; then
stop_iostat $script_name
fi
# iotop
if [[ $cmd == "iotop" && $operate == "start" ]]; then
setup_logroate $logger $LOGROTATE_IOTOP
run_iotop &
fi
if [[ $cmd == "iotop" && $operate == "stop" ]]; then
stop_iotop $script_name
fi
# netstat
if [[ $cmd == "netstat" && $operate == "start" ]]; then
setup_logroate $logger $LOGROTATE_NETSTAT
run_netstat &
fi
if [[ $cmd == "netstat" && $operate == "stop" ]]; then
stop_netstat $script_name
fi
# sar
if [[ $cmd == "sar" && $operate == "start" ]]; then
setup_logroate $logger $LOGROTATE_SAR
run_sar &
fi
if [[ $cmd == "sar" && $operate == "stop" ]]; then
stop_sar $script_name
fi
}
main "$@"