Inode 节点占满
查看磁盘占用:
$ df -i
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/vda1 1310720 1310720 0 100% /
tmpfs 983191 1 983190 1% /dev/shm
/dev/vdb 13107200 91566 13015634 1% /data
$ df -h
Filesystem Size Used Avail Use% Mounted on
/dev/vda1 20G 9.0G 9.8G 48% /
tmpfs 3.8G 0 3.8G 0% /dev/shm
/dev/vdb 197G 131G 57G 71% /data
找出文件数量最多的目录:
$ for i in /*; do echo $i; find $i | wc -l; done
删除文件:
$ find dir -type f -name '*' | xargs rm
$ find /var/spool/clientmqueue -type f -mtime +30 -exec rm -f {} \;
进程跑满
错误:
-bash: fork: retry: No child processes
查看进程数限制:
$ cat /proc/sys/kernel/pid_max
49152
调整限制:
$ echo 100000 > /proc/sys/kernel/pid_max
或者查看是否有无用的进程:
$ pstree -up
删除文件未释放磁盘占用
$ lsof | grep delete
$ find /proc/*/fd -ls | grep '(deleted)'
$ : > "/proc/$pid/fd/$fd"
#!/bin/bash
delete_files=$(find /proc/*/fd -ls 2>/dev/null | grep '(deleted)' | awk '{print $11}')
for delete_file in ${delete_files}; do
echo "echom "" > ${delete_file}"
: > ${delete_file}
done
free 解释
$ free -g
total used free shared buffers cached
Mem: 125 122 3 0 0 99
-/+ buffers/cache: 22 102
Swap: 0 0 0
total: 总内存大小used: 已经使用的内存free: 空闲的内存shared: 多个进程共享的内存总额buffers: 块缓存(buffer cache)大小,缓存块设备的块数据(如磁盘)cached: 页缓存(page cache)大小,缓存文件的页数据-buffers/cache: 已用的内存数,不包含磁盘缓存,used - buffers - cached+buffers/cache: 可用的内存数,包含磁盘缓存,free + buffers + cached
这里的 buffers, cached 是磁盘缓存,这部分内存也可以被进程申请到,所以计算可用的内存时,应该用 +buffers/cache,即 free + buffers + cached
实际被进程占用的内存,应该去除磁盘缓存,即 -buffers/cache,即 used - buffers - cached
在 Linux 2.4 版本的内核之前,page cache 与 buffer cache 是完全分离的。但是,块设备大多是磁盘,磁盘上的数据又大多通过文件系统来组织,这种设计导致很多数据被缓存了两次,浪费内存。所以在 2.4 版本内核之后,两块缓存近似融合在了一起:如果一个文件的页加载到了 page cache,那么同时 buffer cache 只需要维护块指向页的指针就可以了。只有那些没有文件表示的块,或者绕过了文件系统直接操作(如dd命令)的块,才会真正放到 buffer cache 里。因此,我们现在提起 page cache,基本上都同时指 page cache 和 buffer cache 两者。
page_cache
linux 使用 page cache 增加对磁盘访问的性能,page cache 在内存中缓存了磁盘上的部分数据。
- 对读请求,系统首先会检查所读页面是否在 cache 中:
- 如果在 cache 中,那么直接从内存中读取,不需要访问磁盘。
- 如果不在 cache 中,那么从磁盘中读取所请求的页面,并 预读 几个相邻的页面(局部性原理),缓存在 cache 中。
- 对写请求,数据直接写入 cache,磁盘内容不会直接更新(断电有丢失数据风险)写入的 page 被标记为 dirty,内核会周期的将 dirty page 写回 磁盘。
相关参数:
# dirty page 大小达到多少字节后开始触发刷磁盘
vm.dirty_background_bytes = 0
# dirty page 内存占比超过 dirty_background_ratio 后开始触发刷磁盘(默认 10%)
vm.dirty_background_ratio = 10
# dirty page 大小达到多少字节后停止接收写请求,开始触发刷磁盘
vm.dirty_bytes = 0
# dirty page 内存占比超过 dirty_ratio 后停止接收写请求,开始触发刷磁盘(默认 30%)
vm.dirty_ratio = 30
# 存在时间超过 dirty_expire_centisecs 的 dirty page 会被周期性刷盘操作写会磁盘(默认 30秒)
vm.dirty_expire_centisecs = 3000
# 多长时间唤醒一次周期性的刷盘操作(默认 5秒)
vm.dirty_writeback_centisecs = 500
清除 cache
仅清除页面缓存(PageCache)
$ sync; echo 1 > /proc/sys/vm/drop_caches
清除目录项和inode
$ sync; echo 2 > /proc/sys/vm/drop_caches
清除页面缓存,目录项和inode
$ sync; echo 3 > /proc/sys/vm/drop_caches
当内存小于 90G 时自动清理:
#!/bin/bash
#
# /etc/cron.hourly/drop_caches.sh
#
free_mem=$(free -g | awk '/^Mem/ {print $4}')
echo "$(date +"%Y-%m-%d-%T") - before free_mem: $free_mem" >> /root/drop_caches.log 2>&1
if [[ free_mem -lt 90 ]]; then
echo "$(date +"%Y-%m-%d-%T") - drop caches" >> /root/drop_caches.log 2>&1
sync; echo 1 > /proc/sys/vm/drop_caches
else
echo "$(date +"%Y-%m-%d-%T") - do nothing" >> /root/drop_caches.log 2>&1
fi
free_mem=$(free -g | awk '/^Mem/ {print $4}')
echo "$(date +"%Y-%m-%d-%T") - after free_mem: $free_mem" >> /root/drop_caches.log 2>&1
messages
$ cat /var/log/messages
...
kernel: SLUB: Unable to allocate memory on node -1 (gfp=0x8020)
...
配置文件:
/etc/rsyslog.conf
ulimit 进程参数
ulimit 控制进程的资源限制
ulimit [-aHS] \
[-c <core文件上限>] \
[-d <数据节区大小>] \
[-f <文件大小>] \
[-m <内存大小>] \
[-n <文件数目>] \
[-p <缓冲区大小>] \
[-s <堆栈大小>] \
[-t <CPU时间>] \
[-u <程序数目>] \
[-v <虚拟内存大小>]
- -H 设置硬件资源限制,是管理员所设下的限制
- -S 设置软件资源限制,是管理员所设下的限制
- -a 显示当前所有的资源限制
- -u num: 用户最多可启动的进程数目
- -c size: 设置core文件的最大值 单位:blocks
- -d size: 设置程序数据段的最大值 单位:kbytes
- -f size: 设置shell创建文件的最大值 单位:blocks
- -l size: 设置在内存中锁定进程的最大值 单位:kbytes
- -m size: 设置可以使用的常驻内存的最大值 单位:kbytes
- -n num: 设置内核可以同时打开的文件描述符的最大数目
- -p size: 设置管道缓冲区的最大值 单位:kbytes
- -s size: 设置堆栈的最大值 单位:kbytes
- -t size: 设置CPU使用时间的最大上限 单位:seconds
- -v size: 设置虚拟内存的最大值 单位:kbytes
内核可以同时打开的文件描述符默认是1024,在高负载下要设置为更高,可以通过以下命令修改
$ ulimit -SHn 65535
ulimit只能做临时修改,重启后失效。
可以加入到 /etc/rc.local 文件,每次启动启用。
可以通过修改 /etc/security/limits.conf 永久调整 Linux 系统的最大进程数和最大文件打开数限制,
#<domain> <type> <item> <value>
* soft nproc 11000
* hard nproc 11000
* soft nofile 655350
* hard nofile 655350
同样需要注意 /etc/security/limits.d 目录下的文件,目录下文件与 /etc/security/limits.conf 有相同 domain 的配置时,会覆盖 /etc/security/limits.conf 中的配置
cat /proc/29097/limits
prlimit -n4096 -p pid_of_process
- https://stackoverflow.com/questions/3734932/max-open-files-for-working-process
sysctl 内核参数
sysctl 修改内核参数,可以通过修改 /etc/sysctl.conf 永久的修改
sysctl -a: 显示所有可用内核参数sysctl <parameter_anme>: 查看指定的参数设置sysctl -p: 重新加载配置文件/etc/sysctl.conf并使其生效
vm.max_map_count
查看 vm.max_map_count 设置:
$ sysctl vm.max_map_count
vm.max_map_count = 65530
修改 vm.max_map_count 设置:
$ sysctl -w vm.max_map_count=262144
编辑 /etc/sysctl.conf 永久修改 vm.max_map_count 参数:
$ echo 'vm.max_map_count = 262144' >> /etc/sysctl.conf
$ sysctl -p
vm.max_map_count = 262144
syn/accept queue
客户 服务器
socket| |socket, bind, listen
| |
| |LISTEN(被动打开)
| |
| |accept(阻塞)
| |
connect(阻塞) |-----------SYN J------------->|SYN_RCVD -> 进入 SYN Queue
(主动打开)SYN_SENT| / |
| / |
ESTABLISHED|<------SYN K, ACK J+1-----+ |
connect(返回)|-----------ACK K+1 ---------->|ESTABLISHED -> 进入 Accept Queue
| |
| |accept(返回) <- 从 Accept Queue 取 socket
| |
客户端连接 tcp 服务端需要 3 次握手,tcp 服务端有 2 个独立的队列:
- SYN Queue 半连接队列: tcp 服务端收到第一个 syn 时,连接处于 SYN_RCVD 状态,并加入到 syn queue 中。随后 tcp 服务端响应 syn,ack,此时 socket 被称为半连接 socket
- Accept Queue 全连接队列: 当服务端收到响应的 ack 后,连接处于 ESTABLISHED 状态,将连接从 syn queue 移除,加入到 accept queue。此时 socket 为已连接 socket,服务端阻塞的
accept()函数会从 accept queue 取 established socket。
内核给监听端口分配 syn queue 和 accept queue,队列的大小由 listen() 系统调用的 backlog 参数决定:
int listen(int sockfd, int backlog)
另外,队列大小还与 net.core.somaxconn, net.ipv4.tcp_max_syn_backlog 系统参数有关:
net.core.somaxconn:监听套接字的最大连接队列长度,max accept queue size = min(backlog, net.core.somaxconn)net.ipv4.tcp_max_syn_backlog:TCP 半连接队列的最大长度,max syn queue size = min(backlog, net.core.somaxconn, net.ipv4.tcp_max_syn_backlog)
对于 syn queue,还与参数 net.ipv4.tcp_syncookies 相关:
net.ipv4.tcp_syncookies=0: 关闭 syncookies。当 syn queue 满了之后,新的连接无法建立。net.ipv4.tcp_syncookies=1: syn queue 满时启用 syncookies。当 syn queue 满了之后,server 仍可以处理新接收到的 syn 包并返回 syn,ack,返回的包中携带服务端生成的隐藏信息,当接收到客户端的 ack 后,验证 ack,通过验证之后 3 次握手完成,连接成功建立。net.ipv4.tcp_syncookies=2: 始终采用 syncookies 连接机制。
对于 accept queue,还与参数 net.ipv4.tcp_abort_on_overflow 相关:
net.ipv4.tcp_abort_on_overflow = 0: accept queue 满后,收到新的 ack 直接被丢弃,客户端返回 read timeout 错误net.ipv4.tcp_abort_on_overflow = 1: accept queue 满后,收到新的 ack 后响应 RST 给客户端,客户端返回 connection reset by peer 错误
通过查看 9094 端口监听 socket 的 accpet queue 大小:
$ ss -ltpn sport = :9094 | cat
Recv-Q Send-Q Local Address:Port Peer Address:Port
0 50 :::9094 :::* users:(("java",34965,4099))
这里 Send-Q 显示队列的最大大小,Recv-Q 显示当前处于 accept queue 队列中 socket 的数量
参考:
- https://zhuanlan.zhihu.com/p/102502913
- https://blog.cloudflare.com/syn-packet-handling-in-the-wild/
keepalive
cat /etc/sysctl.conf
# 空闲时长/发送心跳的周期 7200s
net.ipv4.tcp_keepalive_time=7200
# 探测包发送间隔 75s
net.ipv4.tcp_keepalive_intvl=75
# 达到 tcp_keepalive_time 后,没有接收到对方确认,继续发送探测包次数
net.ipv4.tcp_keepalive_probes=9
overcommit_memory
/proc/sys/vm/overcommit_memory 决定是否可以超发内存:
- vm.overcommit_memory=0:只要有空闲的物理内存,就允许给进程分配,会适度超发内存
- vm.overcommit_memory=1:内核在分配的时候不做检查,进程真正使用的时候,在pagefault里可能会失败
- vm.overcommit_memory=2:允许分配不超过所有物理内存和交换空间总和的内存,上限是 swap + 物理mem * ratio
- vm.overcommit_memory=2,vm.overcommit_ratio=90
查看参数:
$ sysctl vm.overcommit_memory
临时修改参数值:
$ sysctl -w vm.overcommit_ratio=90
编辑文件 /etc/sysctl.conf 修改 vm.overcommit_memory,然后执行 sysctl -p 永久生效配置
连接过多丢包
/var/log/messages
Jan 17 11:12:09 xxg-udw38 kernel: [107394376.115531] nf_conntrack: table full, dropping packet.
/etc/sysctl.conf
If the problem is caused by a large number of connections, try to increase the value of net.ipv4.netfilter.ip_conntrack_max:
sysctl -w net.ip4.netfilter.ip_conntrack_max=655360 //RHEL 5
sysctl -w net.netfilter.nf_conntrack_max=655360 //RHEL 6+
To make the changes permanent, add the following in /etc/sysctl.conf:
net.ip4.netfilter.ip_conntrack_max=655360 //RHEL 5
net.netfilter.nf_conntrack_max=655360 //RHEL 6+
As a general best practice, do not allow too many connections on a server. A conntrack consumes 400 bytes in the kernel (see /proc/slabinfo). This means tracking 655360 connections would consume 262MB of RAM.
for i in `docker ps | awk 'NR>1 {print $NF}'`; do
echo $i;
docker exec $i cat /proc/sys/net/netfilter/nf_conntrack_count;
done
tcp 窗口(缓冲区)
启用 TCP 窗口缩放
sysctl -w net.ipv4.tcp_window_scaling=1
TCP 最大接收窗口
net.core.rmem_max = 212992
sysctl -w net.core.rmem_max=8388608
TCP 最大发送窗口
net.core.wmem_max = 212992
sysctl -w net.core.wmem_max=8388608
TCP 接收内存缓冲区(最小/默认/最大)
net.ipv4.tcp_rmem = 4096 87380 6291456
sysctl -w net.ipv4.tcp_rmem="4096 87380 8388608"
TCP 发送内存缓冲区(最小/默认/最大)
net.ipv4.tcp_wmem = 4096 16384 4194304
sysctl -w net.ipv4.tcp_wmem="4096 87380 8388608"
持久保存
sudo bash -c 'cat << EOF >> /etc/sysctl.conf
net.core.rmem_max=8388608
net.core.wmem_max=8388608
net.ipv4.tcp_rmem=4096 87380 8388608
net.ipv4.tcp_wmem=4096 16384 8388608
EOF'
oom
Linux 系统内存耗尽分配分配失败后,由 /proc/sys/vm/panic_on_oom 决定如何处理:
- /proc/sys/vm/panic_on_oom 设置为 0,系统不会触发 panic,而是 kill 一部分进程
- /proc/sys/vm/panic_on_oom 设置为 1,可能触发 panic,也可能 kill 一部分进程
- /proc/sys/vm/panic_on_oom 设置为 2,系统触发 panic
如果 kill 进程,由 /proc/sys/vm/oom_kill_allocating_task 决定如果处理:
- /proc/sys/vm/oom_kill_allocating_task 设置为 0,内核会检查每个进程的分数,优先 kill 分数高的进程
- /proc/sys/vm/oom_kill_allocating_task 设置为 1,内核会 kill 当前在申请内存的进程
进程分数与以下几个参数有关:
/proc/$PID/oom_adj: 有效值是 -17 到 15,越大越容易被 kill。/proc/$PID/oom_score_adj: 有效值是 -1000 到 1000,Linux 2.6.36 之后用于替换/proc/$PID/oom_adj/proc/$PID/oom_score: 只读配置,查看进程当前的 oom 得分,得分越高越容易被 kill。分数计算会综合以下几项考虑:- 内存消耗量,消耗越多分越高
- CPU时间(utime + stime)
- 存活时间(uptime - start time),存活时间越长分越低
- oom_adj,值越高得分越高
参考:
- https://learning-kernel.readthedocs.io/en/latest/mem-management.html
- https://github.com/Yhzhtk/note/issues/31
- https://dev.to/rrampage/surviving-the-linux-oom-killer-2ki9
磁盘使用率
# df
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/vda1 20504188 4314736 15124852 23% /
tmpfs 16321844 0 16321844 0% /dev/shm
/dev/vdb 1548053708 1129305312 340088812 77% /data
# node
> 1129305312 / 1548053708
0.7295000852774031
> (1548053708 - 340088812) / 1548053708
0.7803120071077018
> 1548053708 - 1129305312 - 340088812
78659584
>
df 显示的 Used + Available != 1K-blocks(total),这是因为 ext2/3/4 文件系统默认保留 5% 的的可用空间给 root 用户,这样即使磁盘使用率达到 100%,仍有系统组件和操作管理所需的工作空间
使用 tune2fs -l /dev/vdb 可用查看 Reserved block count 和其他文件系统的信息。
使用 tune2fs -m <eserved-blocks-percentage> /dev/vdb 调整保留块的百分比。
如果要计算使用率,可以使用 (total - Available) / total