Linux调优

Linux作为服务器针对使用场景, 有许多需要调优的地方, 本文记录常用优化项.

核心参数调优

通常涉及到/etc/sysctl.conf和/etc/security/limits.conf配置文件的修改.

也可使用命令修改,使用sysctl -p 以立即生效(不必重启).

内核参数设置

sysctl命令可以用来实时的读取/修改内核参数

# 显示所有可用内核参数
sysctl -a

# 加载/etc/sysctl.conf的参数
sysctl -p

提高文件描述符限制

默认的1024偏小.

如果使用默认值,压测时在Nginx错误日志/var/log/nginx/error.log通常可以发现大量以下类似错误:

socket() failed (24: Too many open files) while connecting to upstream,

常用的命令:

# 查看系统当前设置
ulimit -n

# 查看当前进程的限制
cat /proc/$(pgrep java)/limits

# 查看当前进程句柄数
lsof -p $(pgrep java)|wc -l
## 或者使用
ls /proc/$(pgrep java)/fd|wc -l

soft limit类似于warning, hard limit是真实的最大值限制.

有两种方式修改:

临时设置

# 临时增加,重启后失效
ulimit -n 655350
# 单个进程的限制为soft limit
# hard limit应小于当前系统打开的文件描述符

CentOS7/Ubuntu设置

在CentOS7使用SystemD启动的服务不同于CentOS6, 对/etc/security/limits.conf的设置不会生效.

需要同时修改/etc/systemd/system.conf 和 /etc/systemd/user.conf。 或具体的服务 /etc/systemd/system.conf.d/tomcat.conf,之后重启机器生效.

# 对应下面几项
DefaultLimitCORE=infinity
DefaultLimitNOFILE=102400
DefaultLimitNPROC=102400
# Ubuntu用户 需要同时修改
sudo vi /etc/pam.d/common-session
# 并增加
session required pam_limits.so

CentOS6设置

修改 /etc/security/limits.conf,重启以生效.

*               soft    nofile           655350
*               hard    nofile           655350
root            soft    nofile           655350
root            hard    nofile           655350
# 系统级内核句柄限制
fs.file-max = 1000000

其他设置及命令

# 默认通常够用

# 相应增加nr_open
echo 2000000 > /proc/sys/fs/nr_open
# 系统级限制, 上限为nr_open
echo 1000000 > /proc/sys/fs/file-max


# 列出打开/占用的文件描述符
cat /proc/sys/fs/file-nr
# 三个值分别代表 占用/未使用/最大可用值
# 注: lsof只会列出进程占用
lsof | wc -l
# 要得到线程占用,需要使用
ps -eLf

增加可用端口数

如果是压测工具端,报错:connect: cannot assign requested address.

IPv4端口可用数:端口号是16位无符号整数,即65535

# 查看当前端口范围,默认28000
cat /proc/sys/net/ipv4/ip_local_port_range
# 实时生效
echo 1024 65000 > /proc/sys/net/ipv4/ip_local_port_range

# 永久生效
vi /etc/sysctl.conf 
net.ipv4.ip_local_port_range = 1024 65000
# 重新加载
sysctl -p /etc/sysctl.conf

可选:最大线程数

一般不需要设置

# 默认31299
echo 100000 > /proc/sys/kernel/threads-max

查看当前线程数:

  • top, then hit H to view threads
  • top -H
  • htop

内存调优

实际上不需要调优.

free -m
# cache和buffers被包含在used,但是当新进程需要而没有free内存时,kernel会回收(写到硬盘)以释放

# 每隔1分钟记录一次,共记录100次
vmstat -SM 60 100 > memoryusage.out &

fdisk -l

swap检查/配置

# 启用 swap
swapon -a

# 检查
swapon -s   # swap未开启则返回空
free -h        # swap未开启则total为0

# 创建
fallocate -l 4G /swapfile  # 等价于  dd if=/dev/zero of=/swapfile count=4096 bs=1MiB

ls -lh /swapfile
chmod 600 /swapfile  # 安全加固
mkswap /swapfile
swapon /swapfile


# 永久生效
vi /etc/fstab  # 添加
/swapfile   swap    swap    defaults  0   0

# 禁用
swapoff -v /swapfile
/etc/fstab  # 去掉 /swapfile swap swap defaults 0 0
rm /swapfile

OOM Killer

Linux允许程序申请内存的overcommitment(Linux的malloc分配内存,先承诺,实际用到时再去系统分配),实际使用时可能内存不足,从而触发OOM-Killer的自我保护机制。 即当系统分配不出内存时触发,由操作系统在已有进程中挑选一个占用内存最大的进程kill掉来释放内存.

  • 日志通常位于: /var/log/kern.log 或 /var/log/dmesg 或 /var/log/messages
  • /proc//oom_score 可查看进程得分(0~1000)
  • choom -p 用命令查看/调整得分

调整参数(-1000~1000),实际得分会累加:

choom -p 123
# 调整
choom -p 123 -n -30

# 或直接修改
sudo echo -30 > /proc/<pid>/oom_score_adj

调整Service:

[Service]
OOMScoreAdjust=-30

查询所有进程得分并排序:

#!/bin/bash
while read -r pid comm
do
    printf '%d\t%d\t%s\n' "$pid" "$(cat /proc/$pid/oom_score)" "$comm"
done < <(ps -e -o pid= -o comm=) | sort -k2 -n

相关日志演示:

# oom-killer
May 13 09:46:36 moon kernel: dockerd invoked oom-killer: gfp_mask=0x201da, order=0, oom_score_adj=-500
May 13 09:46:36 moon kernel: dockerd cpuset=/ mems_allowed=0

# 如果kernel的 free 内存小于 min,会触发杀掉进程
May 13 09:46:36 moon kernel: Node 0 Normal free:42856kB min:42980kB 
# java分数及当时内存占用
May 13 09:46:36 moon kernel: Out of memory: Kill process 17965 (java) score 424 or sacrifice child
May 13 09:46:36 moon kernel: Killed process 17965 (java) total-vm:10461800kB, anon-rss:3390232kB, file-rss:0kB, shmem-rss:0kB
# total-vm 虚拟内存(可忽略该字段,实际意义不大),包含申请的 + 真实使用的
# rss是真实使用的
# 注: 如果page size是4k的话,计算大小要 x4
# anon-rss 虚拟内存页, anonymous memory,RSS映射到真实内存
# file-rss 打开大文件时占用的内存页, RSS映射到文件或设备

一些其他参数:

# 0意味着启发式策略,允许OverCommit,默认设置
cat /proc/sys/vm/overcommit_memory

# 不要在生产环境完全关闭OOM Killer,即
# echo "vm.overcommit_memory=2" >> /etc/sysctl.conf

cat /proc/pagetypeinfo

网络调优

通用网络参数

/etc/sysctl.conf
# 系统网络设置
# 生效值取系统和下面TCP设置值的最大值
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216

TCP/IP调优

Backlog Queue

最大连接数队列. 可选, 查看kernel日志决定是否需要调整

net.core.somaxconn
net.core.netdev_max_backlog = 300000

网络缓冲区大小

# TCP读取缓冲区
# 格式: 最小值/默认值/最大值 字节数
# cat /proc/sys/net/ipv4/tcp_rmem
net.ipv4.tcp_rmem = 4096 87380 16777216
# 发送缓冲区
net.ipv4.tcp_wmem = 4096 65536 16777216
# TCP内存, 对应 low/pressure/high 页大小(4K)
net.ipv4.tcp_mem = 786432 2097152 3145728

UDP调优

默认比较受限

#改成8M
sysctl -w net.core.rmem_max=8388608

主要参数

Receive-Side Scaling (RSS)

即multi-queue receive, 在基于硬件的多个接受队列间分发网络接受的数据,可以被多核CPU处理以提高性能.

cat /sys/class/net/eth1/queues/<rx-0>/
ethtool --show-rxfh-indir eth1

CLOSE_WAIT 和 TIME_WAIT 解释

  • CLOSE_WAIT:被动关闭,表示远程端 (连接的另一端) 已经关闭连接。
  • TIME_WAIT:主动关闭, 表示本地端 (这边) 已经关闭连接,即主动关闭连接的一方保持的状态。 连接会被保持一会儿,以保证该连接上延迟的数据包可以被正确匹配处理。当超时4分钟后连接会被移除。

netstat -nalp | grep -E ‘:80 |:443 ’ | awk ‘{print $6}’ | sort | uniq -c

TCP是全双工的,任何一端可以是source或destination. 在TCP/IP工作时,连接并不会被立即关闭. 在连接被关闭后,数据包也可能乱序或被重传。

  • 注1: TIME_WAIT太多的问题可以通过优化服务器参数解决(开启keepalive)
  • 注2: 但CLOSE_WAIT太多通常是程序的问题,需要代码判断(释放连接)或优化Nginx参数。
  • 注3: MSL一般设置为30秒、1分钟,2分钟(RFC标准),主动关闭方会保持状态2MSL后彻底回收资源。

tcp_tw_reuse和tcp_tw_recycle

tcp_tw_reuse仅对outgoing有效(连接有incoming和outgoing之分).

设计协议时,尽量不要让客户端先关闭连接,应该让服务端控制.

不用开启 net.ipv4.tcp_tw_recycle, 最新内核4.12已结去掉该参数.

TCP/UDP参数

  • Socket receive buffer size: Socket send and receive sizes are dynamically adjusted, so they rarely need to be manually edited.
  • rmem_default : A kernel parameter that controls the default size of receive buffers used by sockets.

调优常用指标

  • Ping 100以下
  • 网络延迟50ms以下
  • Dns解析尽量快
  • 尽量少丢包
  • 反向代理优化

调优辅助工具

perf-tools

开源的性能分析工具,基于perf和ftrace.

Linux Performance Observability Tools

sysdig

sysdig: Troubleshooting 工具,支持容器

SystemTap

  • SystemTap[官方网站][1]
  • CentOS[有用的脚本][2]

扩展阅读