Linux性能优化

性能分析常用步骤

平均负载

单位时间内,系统处于可运行状态不可中断状态的平均进程数,平均活跃进程数

当平均负载高于 CPU 数量70%的时候,你就应该分析排查负载高的问题了

相关指令

相关工具

stress --cpu 1 --timeout 600 // 构造 一个 CPU 密集型进程

stress -i 1 --timeout 600 // 构造一个 I/O 密集型进程

stress -c 8 --timeout 600  构造一个 大量进程的场景

CPU上下文切换

前一个任务的CPU上下文(CPU寄存器和程序计数器),保存到系统内核中。加载新任务上下文,跳到程序计数器新位置。

内核空间(Ring 0)具有最高权限,可直接访问所有资源

用户空间(Ring 3)只能访问受限资源,不能直接访问内存等硬件设备

进程既可以在用户,也可以在内核空间运行,分别为用户态,内核态。需要通过系统调用来完成

一次系统调用的过程,一直在同一个进程中运行,其实发生了两次CPU上下文切换(用户态和内核态的切换),通常称为特权模式切换,而不是上下文切换。

进程上下文切换

每次进程上下文切换都需要几十纳秒数微秒CPU时间,大量时间耗费寄存器,内核栈以及虚拟内存资源的保存和恢复,还会导致TLB的刷新高速缓存的刷新

进程上下文切换原因:

线程上下文切换

线程是调度的基本单位,进程是资源拥有的基本单位。

切换两种情况:线程所属不同进程。线程属于同一进程

中断上下文切换

为了快速响应硬件事件,中断能处理会打断进程正常调度和执行

相关指令

vmstat指令:分析系统内存使用情况,CPU上下文切换和中断次数 pstree | grep stress : 用树形形式显示所哟进程间关系

点这里-> linux指令大全

查看具体进程上下文切换情况:pidstat -w 5 // -t 显示线程上下文次数

UID       PID   cswch/s nvcswch/s  Command
0         1      0.20      0.00  systemd
0         8      5.40      0.00  rcu_sched

cswch:每秒自愿上下文切换 常指 进程无法获取所需资源
nvcswch:每秒非自愿上下文切换 常指 时间片等原因

测试工具

$ sysbench --threads=10 --max-time=300 threads run // 10个进程运行5分钟基准测试,模拟多线程切换问题
watch -d cat /proc/interrupts // /proc 实际上是 Linux 的一个虚拟文件系统,用于内核空间与用户空间之间的通信。/proc/interrupts 就是这种通信机制的一部分,提供了一个只读的中断使用情况

该显示信息中会有重调度中断RES---表示唤醒空闲状态CPU来调度新任务运行
多处理器系统SMP调度器用来分散任务到不同 CPU 的机制通常也被称为处理器间中断Inter-Processor InterruptsIPI

CPU使用率

Linux通过事先定义节拍率(内核表示为HZ),触发时间中断,并使用全局变量Jiffies记录开机以来节拍数

grep 'CONFIG_HZ=' /boot/config-$(uname -r) // 查看CPU节拍率

cat /proc/stat | grep ^cpu // 向用户空间提供 CPU相关信息

显示信息 依次 user用户态 CPU 时间nice低优先级用户态 CPU 时间system内核态 CPU 时间
idle空闲时间iowait等待 I/O  CPU 时间irq处理硬中断的 CPU 时间softirq软中断的 CPU 时间
steal当系统运行在虚拟机中的时候被其他虚拟机占用的 CPU 时间guest运行虚拟机的 CPU 时间
guest_nice低优先级运行虚拟机的时间

平均CPU使用率 = 1 - (空闲时间new - 空闲时间old)/(总CPU时间new - 总CPU时间old)

Linux 也给每个进程提供了运行情况的统计信息,也就是 /proc/[pid]/stat

相关指令

top:显示系统总体CPU和内存使用情况,各个进程资源使用情况 // CPU使用率包含了用户态和内核态,具体细分要用pidstat ps:显示每个进程资源使用情况

点这里-> linux指令大全

perf:可以指定进程或时间进行采样,调用栈形式,输出整个调用链汇总信息

perf top:实时显示占用CPU时钟最多函数或指令 perf record:保存数据 perf report:解析数据

$ perf top

// 采样数(Samples)、事件类型(event)和事件总数量(Event count)
Samples: 833  of event 'cpu-clock', Event count (approx.): 97742399 
Overhead  Shared Object       Symbol
   7.28%  perf                [.] 0x00000000001f78a4
   4.72%  [kernel]            [k] vsnprintf
   4.32%  [kernel]            [k] module_get_kallsym
   3.65%  [kernel]            [k] _raw_spin_unlock_irqrestore
...

Shared 是该函数或指令所在的动态共享对象Dynamic Shared Object),如内核进程名动态链接库名内核模块名等
Object 是动态共享对象的类型比如 [.] 表示用户空间的可执行程序或者动态链接库 [k] 则表示内核空间
Symbol 是符号名也就是函数名

相关工具

execsnoop 就是一个专为短时进程设计的工具。它通过 ftrace 实时监控进程的 exec() 行为,并输出短时进程的基本信息,包括进程 PID、父进程 PID、命令行参数以及执行的结果

碰到常规问题无法解释的 CPU 使用率情况时,首先要想到有可能是短时应用导致的问题

线程状态

相关工具

dstat:同时观察系统CPU,磁盘I/O,网络以及内存使用情况

进程组和会话

iowait 高不一定代表I/O 有性能瓶颈。当系统中只有 I/O 类型的进程在运行时,iowait也会很高,但实际上,磁盘的读写远没有达到性能瓶颈的程度。

中断

软中断

为了解决中断处理程序执行过长和中断丢失,中断处理过程分成两个阶段

/proc/softirqs 提供了软中断的运行情况
/proc/interrupts 提供了硬中断的运行情况

Linux 中,每个CPU都对应一个软中断内核线程,名字是ksoftirqd/CPU编号.软中断事件的频率过高时,内核线程也会因为CPU使用率过高而导致软中断处理不及时,进而引发网络收发延迟、调度缓慢等性能问题。

相关工具

性能优化方法论

优化前思考

  1. 既然要做性能优化,那要判断优化是否有效?能提升多少性能?
  2. 多个性能问题,先优化哪一个
  3. 提升性能方法很多,使用哪一个呢,性能优化成本

怎么评估优化效果

  1. 确定性能量化指标
  2. 测试优化前性能指标
  3. 测试优化后性能指标

应用程序的维度(吞吐量和请求延迟),系统资源的维度(CPU使用率)

注意: 1避免性能测试工具干扰程序性能 2避免外部环境变化
3要忍住“把 CPU 性能优化到极致”的冲动

Linux内存管理

Linux内核给每个进程提供一个连续的独立的虚拟地址空间。虚拟地址分为内核空间用户空间实际使用的虚拟内存才分配物理内存,分配后,通过内存映射来管理。内存映射使用页表来记录。

页表

页表实际上存储在CPU内存管理单元MMU,当进程访问虚拟地址在页表中找不到,系统产生一个缺页异常, 进入内核空间分配物理内存,更新页表,再返回用户空间,恢复进程运行。

TLB是MMU中页表高速缓存,减少进程上下文切换,减少TLB刷新次数,提高TLB缓存使用率,提高CPU内存访问性能

MMU通过页而不是字节管理内存,页的大小4KB,为了解决页表项过多问题,Linux提供两种机制,多级页表和大页

多级页表:把内存分成区块来管理,将原来映射关系改为区块索引和区块偏移

虚拟内存空间分布

使用C标准malloc函数或使用mmap函数进行动态分配,小块内存(小于128K),C 标准库使用 brk() 来分配,大块内存(大于 128K),则直接使用内存映射 mmap() 来分配

malloc() 申请虚拟内存后,系统并不会立即为其分配物理内存,而是在首次访问时,才通过缺页异常陷入内核中分配内存

buff/cache

我们使用free指令时在输出信息中有 buff/cache 列:

Buffer既可以用作“将要写入磁盘数据的缓存”,也可以用作“从磁盘读取数据的缓存”。 Cache既可以用作“从文件读取数据的页缓存”,也可以用作“写文件的页缓存”。

Buffers 和 Cache 都是操作系统来管理的,应用程序并不能直接控制这些缓存的内容和生命周期

同一个指标具体含义,因为内核版本,性能工具版本不同有挺大差别。

相关工具 安装 bcc-tools工具

## 缓存命中的次数  缓存未命中的次数  新增到缓存中的脏页数  命中率 Buffers大小  Cache大小
HITS   MISSES  DIRTIES HITRATIO   BUFFERS_MB  CACHED_MB
   0        0        0    0.00%          207       9810
   3        0        4  100.00%          207       9810
   0        0        0    0.00%          207       9810
   0        0        4    0.00%          207       9810
 
10:57:40 Buffers MB: 207 / Cached MB: 9811 / Sort: HITS / Order: ascending
PID      UID      CMD              HITS     MISSES   DIRTIES  READ_HIT%  WRITE_HIT%
    2013 root     wrapper                 2        0        0     100.0%       0.0%
    2425 root     Wrapper-Connect         2        0        0     100.0%       0.0%
   25326 root     java                    2        1        1	   33.3%       0.0%

根据Linux内核eBPF机制,跟踪内核中管理缓存,并输出缓存使用和命中。

内存泄漏会导致两种可能结果:

Swap原理

Swap把不常访问内存先写到磁盘中,然后释放内存给其他进程使用。

把一块磁盘或一个本地文件当成内存来使用,包括换入和换出两个过程

什么时候回收

页低阈值通过内核选项 /proc/sys/vm/min_free_kbytes 的 min_free_kbytes参数 来间接设置,其他两个阈值,根据以下计算生成的

pages_low = pages_min*5/4
pages_high = pages_min*3/2

三个内存阈值通过proc 文件系统中的接口 /proc/zoneinfo 来查看

/proc/zoneinfo

NUMA与Swap

NUMA 架构下,多个处理器被划分到不同 Node 上,且每个 Node 都拥有自己的本地内存空间

一个 Node 内部的内存空间,实际上又可以进一步分为不同的内存域(Zone),比如直接内存访问区(DMA)、普通内存区(NORMAL)、伪内存区(MOVABLE)等

numactl --hardware  // 通过 numactl 命令,来查看处理器在 Node 的分布情况,以及每个 Node 的内存使用情况

通过 /proc/sys/vm/zone_reclaim_mode 来调整回收模式

通过 /proc/sys/vm/swappiness 选项,用来调整使用Swap的积极程度,范围是0-100,数值越大,越积极使用Swap。 但是即使你把它设置成0,当剩余内存+文件页小于页高阈值时,还是会发生Swap

通常,降低Swap的使用,可以提高系统的整体性能

Linux文件系统

Linux中一切皆文件。不仅普通的文件和目录,就连块设备、套接字、管道等,也都要通过统一的文件系统来管理

目录项和索引节点

每个文件都分配两个数据结构:

目录项和索引节点的关系是多对一

虚拟文件系统

为了支持各种不同的文件系统,Linux内核在用户进程和文件系统的中间,引入虚拟文件系统VFS

文件系统可以分为三类:

文件系统,要先挂载到 VFS 目录树中的某个子目录(称为挂载点),然后才能访问其中的文件

文件读写方式分类

是否利用标准库缓存

是否利用操作系统的页缓存

应用程序是否阻塞自身运行

是否等待响应结果

查看磁盘容量

df -i /dev/sda1 

当你发现索引节点空间不足,但磁盘空间充足时,很可能就是过多小文件导致(删除小文件或移到索引节点充足磁盘)

查询目录项和各种文件系统索引节点的缓存情况

cat /proc/slabinfo | grep -E '^#|dentry|inode' 

slabtop 

在读写普通文件时,I/O请求先经过文件系统,再与磁盘交互。而读写块设备时,跳过文件系统,直接与磁盘交互,也所谓“裸I/O”

通用块层

处在文件系统和磁盘驱动中间的一个块设备抽象层。

Linux 内核支持四种I/O调度算法

磁盘性能指标

不同应用场景,要考虑不同的I/O指标,结合读写比例,I/O类型(read,write),I/O大小,综合分析

用性能工具得到的这些指标,可以作为后续分析应用程序性能的依据。一旦发生性能问题,你就可以把它们作为磁盘性能的极限值,进而评估磁盘 I/O 的使用情况

相关工具

iostat:提供了每个磁盘的使用率、IOPS、吞吐量等各种常见的性能指标,指标来自 /proc/diskstats。

iostat指令

%util:磁盘I/O使用率; r/s+ w/s:IOPS; rkB/s+wkB/s:吞吐量; r_await+w_await:响应时间。

pidstat -d 1 // 观察进程的I/O情况
iotop // 观察进程的I/
strace -p 18940 // 分析系统调用  
lsof -p 18940  // 查看进程打开文件列表 包括了目录、块设备、动态库、网络套接字等
 ./filetop -C  // 跟踪内核中文件的读写情况,并输出线程ID(TID)、读写大小、读写类型以及文件名称
opensnoop  // 动态跟踪内核中的 open 系统调用

磁盘I/O性能优化

I/O基准测试 fio是常用的文件系统和磁盘I/O性能基准测试工具


// direct 是否跳过系统缓存  1 表示跳过系统缓存
// iodepth  使用异步I/O  发出的 I/O 请求上限
// rw,  I/O 模式  顺序/随机/读/写
// ioengine I/O 引擎,支持同步(sync)、异步(libaio)、内存映射(mmap)、网络(net)等各种 I/O 引擎
// bs   表示I/O 的大小
// filename  文件路径   磁盘路径(用磁盘路径测试写,会破坏这个磁盘中的文件系统)/文件路径

# 随机读
fio -name=randread -direct=1 -iodepth=64 -rw=randread -ioengine=libaio -bs=4k -size=1G -numjobs=1 -runtime=1000 -group_reporting -filename=/dev/sdb

# 随机写
fio -name=randwrite -direct=1 -iodepth=64 -rw=randwrite -ioengine=libaio -bs=4k -size=1G -numjobs=1 -runtime=1000 -group_reporting -filename=/dev/sdb

# 顺序读
fio -name=read -direct=1 -iodepth=64 -rw=read -ioengine=libaio -bs=4k -size=1G -numjobs=1 -runtime=1000 -group_reporting -filename=/dev/sdb

# 顺序写
fio -name=write -direct=1 -iodepth=64 -rw=write -ioengine=libaio -bs=4k -size=1G -numjobs=1 -runtime=1000 -group_reporting -filename=/dev/sdb 

测试结果例:

read: (g=0): rw=read, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=libaio, iodepth=64
fio-3.1
Starting 1 process
Jobs: 1 (f=1): [R(1)][100.0%][r=16.7MiB/s,w=0KiB/s][r=4280,w=0 IOPS][eta 00m:00s]
read: (groupid=0, jobs=1): err= 0: pid=17966: Sun Dec 30 08:31:48 2018
   read: IOPS=4257, BW=16.6MiB/s (17.4MB/s)(1024MiB/61568msec)
    slat (usec): min=2, max=2566, avg= 4.29, stdev=21.76 // 指从 I/O 提交到实际执行 I/O 的时长(Submission latency)
    clat (usec): min=228, max=407360, avg=15024.30, stdev=20524.39 // 指从 I/O 提交到 I/O 完成的时长(Completion latency)
     lat (usec): min=243, max=407363, avg=15029.12, stdev=20524.26 // 指的是从fio 创建 I/O 到 I/O 完成的总时长
    clat percentiles (usec):
     |  1.00th=[   498],  5.00th=[  1020], 10.00th=[  1319], 20.00th=[  1713],
     | 30.00th=[  1991], 40.00th=[  2212], 50.00th=[  2540], 60.00th=[  2933],
     | 70.00th=[  5407], 80.00th=[ 44303], 90.00th=[ 45351], 95.00th=[ 45876],
     | 99.00th=[ 46924], 99.50th=[ 46924], 99.90th=[ 48497], 99.95th=[ 49021],
     | 99.99th=[404751]
   bw (  KiB/s): min= 8208, max=18832, per=99.85%, avg=17005.35, stdev=998.94, samples=123 // 代表吞吐量
   iops        : min= 2052, max= 4708, avg=4251.30, stdev=249.74, samples=123
  lat (usec)   : 250=0.01%, 500=1.03%, 750=1.69%, 1000=2.07% // 每秒 I/O 的次数
  lat (msec)   : 2=25.64%, 4=37.58%, 10=2.08%, 20=0.02%, 50=29.86%
  lat (msec)   : 100=0.01%, 500=0.02%
  cpu          : usr=1.02%, sys=2.97%, ctx=33312, majf=0, minf=75
  IO depths    : 1=0.1%, 2=0.1%, 4=0.1%, 8=0.1%, 16=0.1%, 32=0.1%, >=64=100.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.1%, >=64=0.0%
     issued rwt: total=262144,0,0, short=0,0,0, dropped=0,0,0
     latency   : target=0, window=0, percentile=100.00%, depth=64

Run status group 0 (all jobs):
   READ: bw=16.6MiB/s (17.4MB/s), 16.6MiB/s-16.6MiB/s (17.4MB/s-17.4MB/s), io=1024MiB (1074MB), run=61568-61568msec

Disk stats (read/write):
  sdb: ios=261897/0, merge=0/0, ticks=3912108/0, in_queue=3474336, util=90.09% 

借助 blktrace,记录磁盘设备的I/O访问情况;然后使用 fio ,重放 blktrace 的记录,实现对应用程序 I/O 模式的基准测试

I/O 性能优化

应用程序优化

文件系统优化

磁盘优化

Linux 网络

Linux网络栈

根据TCP/IP模型,数据包按照协议栈,对上层数据进行逐层处理,封装,发送到下一层。网络接口配置最大传输单元(MTU)规定了最大数据包大小

Linux网络收发流程

接收

  1. 网络帧到达网卡后,网卡通过DMA方式,把网络包放入收包队列(环形缓冲区)。然后硬中断,告诉中断处理程序已收到网络包
  2. 中断处理程序为网络帧分配内核数据结构(sk_buff),将其拷贝到sk_buff缓冲区,然后通过软中断,通知内核。
  3. 内核协议栈从缓冲区取出网络帧,通过网络栈逐层处理帧包
    • 链路层检查报文合法性,去掉帧头,帧尾,交给网络层
    • 网络层取出IP头,判断网络包下一步走向(转发,交由上层),若是后者,去掉IP头,交由传输层
    • 传输层根据端口IP四元组,找到对应socket,并把数据拷贝到Socket接收缓存中

发送

跟接收流程相反

  1. 调用Socket API发送网络包(切换到内核态,套接字层把数据包放到Sokcet发送到缓存区
  2. 网络协议栈从缓冲区取出数据包,逐层封装,对网络包按照MTU大小分片
  3. 网络包送到网络接口层,进行物理地址寻址,找到下一跳MAC地址添加帧头和帧尾,放到发包队列。然后软中断通知驱动程序
  4. 驱动程序通过DMA,从发包队列读出网络帧,通过物理网卡发出

这些缓冲区都处于内核管理的内存中

sk_buff、套接字缓冲、连接跟踪等,都通过slab分配器来管理。你可以直接通过/proc/slabinfo,来查看它们占用的内存大小

性能指标

查看网络配置

ifconfig eth0

eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500 // RUNNING 表示物理网络连通
        inet 10.20.20.170  netmask 255.255.255.0  broadcast 10.20.20.255
        inet6 fe80::f816:3eff:fe4e:8817  prefixlen 64  scopeid 0x20<link>
        ether fa:16:3e:4e:88:17  txqueuelen 1000  (Ethernet)
        RX packets 34282995  bytes 33190146394 (30.9 GiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 24099082  bytes 7036560497 (6.5 GiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
// RX,TX:

errors--表示发生错误数据包数校验错误帧同步);
dropped--丢弃的数据包数内存不足
overruns--超限数据包数网络I/O速度过快队列满导致的丢包
carrier--发生carrirer错误的数据包数双工模式不匹配物理电缆出现问题
collisions--碰撞数据包数

查看套接字,网络栈,网络接口,路由表信息

# -l 表示只显示监听套接字
# -t 表示只显示 TCP 套接字
# -n 表示显示数字地址和端口(而不是名字)
# -p 表示显示进程信息
$ ss -ltnp | head -n 3

查看协议栈统计信息

 netstat -s

查看网络吞吐和PPS

sar -n DEV 1
 
Linux 4.15.0-1035-azure (ubuntu) 	01/06/19 	_x86_64_	(2 CPU)

接收和发送的 PPS接收和发送的吞吐量接收和发送的压缩数据包数网络接口的使用率
13:21:40        IFACE   rxpck/s   txpck/s    rxkB/s    txkB/s   rxcmp/s   txcmp/s  rxmcst/s   %ifutil
13:21:41         eth0     18.00     20.00      5.79      4.25      0.00      0.00      0.00      0.00
13:21:41      docker0      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00


Bandwidth 可以用 ethtool

??? ethtool eth0 | grep Speed

连通性和延时

ping -c3 114.114.114.114

Nginx采用工作模型

采用主进程+多个worker子进程的工作模型

accept,epoll_wait引发惊群问题,Nginx通过全局锁来解决惊群问题

采用监听到相同端口的多进程模型

内核保证了只有进程被唤醒,不会出现惊群问题

C10K问题 依靠I/O多路复用,请求处理优化可以解决

C100K C1000K问题 从应用程序到Linux内核,CPU,内存,网络各个层次深度优化,特别是增加硬件资源,提高硬件性能

C10M问题 跳过内核协议栈的冗长路径,把网络包直接送到应用程序

网络基准测试

评估网络性能,应该清楚应用程序基于协议栈哪一层?需要测试什么层的网络性能?

转发性能

相关工具

hping3:测试网络包处理能力 pktgen:根据实际需要构造所需网络包,准确地测试出目标服务器的性能。(可能需编译内核,劝退)

TCP/UDP 性能

相关工具

iperf

# -s表示启动服务端-i表示汇报间隔-p表示监听端口
$ iperf3 -s -i 1 -p 10000

# -c表示启动客户端192.168.0.30为目标服务器的IP
# -b表示目标带宽(单位是bits/s)
# -t表示测试时间
# -P表示并发数-p表示目标服务器监听端口
$ iperf3 -c 192.168.0.30 -b 1G -t 15 -P 2 -p 10000

result


测试时间数据传输量以及带宽 

[ ID] Interval           Transfer     Bandwidth
...
[SUM]   0.00-15.04  sec  0.00 Bytes  0.00 bits/sec                  sender
[SUM]   0.00-15.04  sec  1.51 GBytes   860 Mbits/sec                  receiver

HTTP 性能

相关工具

ab

 ./ab -n 10000 -c 2000 -p post/valid.txt -T application/json  http://localhost:8090/spring-test/valid

wrk 一个HTTP性能测试工具,内置了LuaJIT,根据实际需求,生成所需的请求负载,自定义响应的处理方法

DNS

域名以分层的结构进行管理,域名解析也是递归方式(从顶级开始,以此类推)。发送给每个层级域名服务器。

DNS 服务通过资源记录的方式,来管理所有数据支持 A、CNAME、MX、NS、PTR 等多种类型的记录

相关工具

需按照 bind-utils 软件包

nslookup 查DNS信息

[root@ecs-pre-business-01 ~]# nslookup www.car-house.cn
Server:		100.125.1.250 // 并不是直接管理的域名服务器,只是返回结果
Address:	100.125.1.250#53

Non-authoritative answer:
Name:	www.car-house.cn
Address: 114.116.239.62

加 -debug参数开启nslookup调试输出

有时候我们发现DNS解析返回太慢,我们可以配置域名服务器

echo nameserver 114.114.114.114 > /etc/resolv.conf

DNS解析不稳定,我可以使用 DNS 缓存

/etc/init.d/dnsmasq start

// 修改 /etc/resolv.conf,将 DNS 服务器改为 dnsmasq 的监听地址
echo nameserver 127.0.0.1 > /etc/resolv.conf

dig 提供了 trace 功能,可以展示递归查询的整个过程

dig +trace +nodnssec www.car-house.cn

从上往下,四部分依次为根域名NS记录,顶级域名记录,二级域名记录,最终注解A记录

tcpdump

tcpdump基于libpcap,利用内核中的 AF_PACKET 套接字,抓取网络接口中传输的网络包

我们使用ping指令的时候,有时候发现 ping指令返回很慢,但是延迟却不高

可以通过tcpdump 在服务器中抓取和分析网络包

$ tcpdump -nn udp port 53 or host 35.190.27.188

Wireshark

在Tcpdump得到的数据上进行分析的一个工具

缓解DDOS攻击

DDoS类型

使用功能hpin3 作为攻击攻击

// 客户端1发起攻击
hping3 -S -p 80 -i u10 192.168.0.30 // 效果不明显调小 u10,反之调大

// 服务器查看网络收包情况
sar -n DEV 1

// 服务器抓取具体网络包数据
tcpdump -i eth0 -n tcp port 80

09:15:48.287047 IP 192.168.0.2.27095 > 192.168.0.30: Flags [S], seq 1288268370, win 512, length 0
09:15:48.287050 IP 192.168.0.2.27131 > 192.168.0.30: Flags [S], seq 2084255254, win 512, length 0

通过观察 我们发现,大量的SYN包,极有可能是 SYN Flood 攻击。

半开连接

这种等待状态的TCP连接,称为半开连接,大量的半开连接导致连接表迅速占满。从而无法建立新的连接

netstat -n -p | grep SYN_REC // 查看半开连接

netstat -n -p | grep SYN_REC | wc -l // 统计半开连接数

$ sysctl -w net.ipv4.tcp_max_syn_backlog=1024
net.ipv4.tcp_max_syn_backlog = 1024 // 修改半开连接容量

DDoS 的分布式、大流量、难追踪等特点,目前还没有方法可以完全防御 DDoS 带来的问题,只能设法缓解这个影响

DDOS攻击 应对方案

# 拒绝指定IP的包接收
iptables -I INPUT -s 192.168.0.2 -p tcp -j REJECT 

# 限制syn并发数为每秒1次
$ iptables -A INPUT -p tcp --syn -m limit --limit 1/s -j ACCEPT

# 限制单个IP在60秒新建立的连接数为10
$ iptables -I INPUT -p tcp --dport 80 --syn -m recent --name SYN_FLOOD --update --seconds 60 --hitcount 10 -j REJECT

# 限制连接每个SYN_RECV失败后内核重试次数
$ sysctl -w net.ipv4.tcp_synack_retries=1
net.ipv4.tcp_synack_retries = 1

TCP SYN Cookies:SYN Cookies,防御SYN Flood攻击方法。基于连接信息(包括源地址、源端口、目的地址、目的端口等)以及一个加密种子(如系统启动时间),计算出一个哈希值(SHA1),这个哈希值称为 cookie。cookie 就被用作序列号,来应答 SYN+ACK 包,并释放连接状态。

这样不需要维护半开连接状态

## 开启 SYN Cookies
sysctl -w net.ipv4.tcp_syncookies=1 // 配置都是临时的,重启后这些配置就会丢失.写入 /etc/sysctl.conf 文件才能持久化
net.ipv4.tcp_syncookies = 1 

DDoS 并不一定是因为大流量或者大 PPS,有时候,慢速的请求也会带来巨大的性能下降(这种情况称为慢速 DDoS)。专业的流量清洗设备和网络防火墙,在网络入口处阻断恶意流量,只保留正常流量进入数据中心的服务器

网络延迟

ping基于ICMP协议,计算ICMP回显响应报文与ICMP 回显请求报文的时间差,来获得往返延时

获取网络延迟

hping3 -c 3 -S -p 80 baidu.com
traceroute --tcp -p 80 -n baidu.com

TCP延迟确认和Nagle算法会导致延迟增长问题

NAT

NAT技术通过重写IP数据包的源IP或目的IP,用来解决公网IP地址短缺的问题。

NAT 分类

NAPT 分类

容器启动失败,慢

dmesg命令查看系统日志

在docker等容器的使用中,没有资源限制,会导致容器占用整个资源,一旦程序发生异常,有可能拖垮整台机器

丢包

网络数据的收发过程,数据包还没传输到应用程序中,就被丢弃,被丢弃包的数量/总的传输包数,也就是我们常说的丢包率

丢包通常意味着网络拥塞和重传,导致网络延迟增大、吞吐降低

丢包发生地

链路层

## 显示网卡统计信息 可以查看丢包记录
netstat -i 

如果用tc等工具配置QoS,那么tc规则导致丢包,不会包含在网卡统计信息中

## 检查eth0是否配置tc规则查看有没有丢包 
tc -s qdisc show dev eth0

网络层和传输层

## 查看各协议收发汇总以及错误信息
netstat -s

iptables

# 主机终端中查询内核配置  连接跟踪数
$ sysctl net.netfilter.nf_conntrack_max
net.netfilter.nf_conntrack_max = 262144
$ sysctl net.netfilter.nf_conntrack_count
net.netfilter.nf_conntrack_count = 182
## 查看各条规则的统计信息
iptables -t filter -nvL

网卡的MTU设置过小也会导致丢包问题

CPU使用率过高

内核线程

## 查找kthreadd进程的子进程即内核线程
ps -f --ppid 2 -p 2
 ## 对指定进程进行调用栈查询并保存
 perf record -a -g -p 9 -- sleep 30

火焰图

## 下载火焰图生成源码
git clone https://github.com/brendangregg/FlameGraph

cd FlameGraph  

1. 执行perf script将perf record记录转换为可读采样记录
2. 执行stackcollapse-perf.pl脚本合并调用栈信息
3. 执行 flamegraph.pl 脚本生成火焰图

动态追踪

通过探针机制,采集内核或应用程序运行信息,获取丰富信息。

DTrace

运行常驻在内核中,用户可以通过dtrace命令,把D语 言编写的追踪脚本,提交到内核中的运行时来执行。

SystemTap

没有常驻内核的运行时,它需要先把脚本编译为内核模块,然后再插入到内核中执行

Dtrace 和 SystemTap 都会把用户传入的追踪处理 函数(一般称为Action),关联到被称为探针的检测点上。这些探针,实际上也就是各种动态追踪技术所依赖的事件源

动态追踪的事件源

动态追踪机制

添加探针

perf probe –add do_sys_open

对 10s内do_sys_open函数采样

perf record -e probe:do_sys_open -aR sleep 10

追踪显示函数的参数

perf probe -V do_sys_open ```

USE法

USE法把系统资源的性能指标,三个类别,使用率、饱和度以及错误数