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)
...
ulimit
ulimit
控制 shell 程序资源
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
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
系统参数有关:
max accept queue size = min(backlog, net.core.somaxconn)
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
/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
- 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
oom
/proc/$PID/oom_adj
: 有效值是 -16 到 15,越大越容易被 kill。/proc/$PID/oom_score
: 综合进程的内存消耗量、CPU时间(utime + stime)、存活时间(uptime - start time)和oom_adj计算出的,消耗内存越多分越高,存活时间越长分越低/proc/$PID/oom_score_adj
: 有效值是 -1000 到 1000,Linux 2.6.36 之后用于替换/proc/$PID/oom_adj
磁盘使用率
# 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
- older
- Newer