Linux 系统管理与内核调优实战

Linux 系统管理与内核调优实战

引言

Linux 服务器的稳定运行离不开扎实的系统管理功底。无论是生产环境中的进程守护、内核升级,还是内存泄漏排查与内核参数调优,都是运维工程师日常工作中绕不开的核心技能。本文将从实战角度出发,系统性地梳理 Supervisor 进程管理、CentOS 内核升级、Core Dump 调试、内核参数(sysctl)调优以及内存分析五大主题,帮助读者构建完整的 Linux 系统管理知识体系。


一、Supervisor 进程守护

在生产环境中,应用程序需要具备自动重启、异常恢复的能力。Supervisor 是一个成熟的进程管理工具,能够监控和控制 Unix 系统中的进程,当进程异常退出时可以自动将其拉起来。

1.1 安装与启动

在 CentOS 7 上通过 yum 安装:

yum install supervisor

安装完成后,启动 Supervisor 服务:

supervisord -c /etc/supervisord.conf

进入 /etc 目录可以看到主配置文件 supervisord.confsupervisord.d 目录。编辑 supervisord.conf,在文件末尾可以看到如下配置:

[include]
files = supervisord.d/*.ini

这行配置说明 Supervisor 会自动加载 supervisord.d 目录下所有 .ini 配置文件,因此我们可以在该目录中按应用创建独立的配置文件。

1.2 编写应用配置

supervisord.d 目录下创建应用的配置文件,例如 deploy.ini

[program:DeployLinux]                      ; 程序名称
command=dotnet DeployLinux.dll              ; 需要执行的命令
directory=/home/publish                     ; 执行目录
environment=ASPNETCORE__ENVIRONMENT=Production  ; 环境变量
user=root                                   ; 执行用户
stopsignal=INT
autostart=true                              ; 是否自启动
autorestart=true                            ; 是否自动重启
startsecs=3                                 ; 自动重启时间间隔(秒)
stderr_logfile=/var/log/ossoffical.err.log  ; 错误日志文件
stdout_logfile=/var/log/ossoffical.out.log  ; 输出日志文件

重载配置文件使其生效:

supervisorctl reload

1.3 启用 Web 管理界面

编辑 supervisord.conf,取消 [inet_http_server] 段的注释并配置如下:

[inet_http_server]
port=0.0.0.0:9001
username=admin
password=admin

重载配置并开放防火墙端口:

supervisorctl reload

# 检查端口是否开启
firewall-cmd --query-port=9001/tcp

# 开放 9001 端口
firewall-cmd --add-port=9001/tcp --permanent
firewall-cmd --reload

配置完成后,通过浏览器访问 http://服务器IP:9001 即可进入 Web 管理界面。

1.4 故障排查

如果执行 supervisorctl 命令时出现 "unix:///var/run/supervisor/supervisor.sock no such file" 错误,说明 Supervisor 服务未启动,执行以下命令即可:

supervisord -c /etc/supervisord.conf

二、CentOS 7 内核升级

CentOS 7 默认搭载的 3.10 内核版本较老,某些新硬件或容器特性(如 Cilium、eBPF)需要更高版本的内核支持。通过 ELRepo 仓库可以方便地安装主线或长期支持版本内核。

2.1 升级步骤

# 1. 更新系统
yum update -y

# 2. 导入 ELRepo 仓库公钥
rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org

# 3. 安装 ELRepo 仓库
rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-3.el7.elrepo.noarch.rpm

# 4. 查看可用的内核包
yum --disablerepo="*" --enablerepo="elrepo-kernel" list available

ELRepo 提供两种内核:

  • kernel-ml(主线内核):最新稳定版本
  • kernel-lt(长期支持内核):更保守的稳定版本
# 5. 安装最新主线内核
yum --enablerepo=elrepo-kernel install kernel-ml -y

# 6. 重新生成 GRUB 配置
grub2-mkconfig --output /boot/grub2/grub.cfg

# 7. 查看所有可用内核
awk -F\' '$1=="menuentry " {print i++ " : " $2}' /etc/grub2.cfg

# 8. 设置新内核为默认启动项(0 对应第一个内核条目)
grub2-set-default 0

# 9. 重新生成 GRUB 配置
grub2-mkconfig -o /boot/grub2/grub.cfg

2.2 验证与回滚

重启系统后通过 uname -r 确认内核版本已更新。如果新内核存在兼容性问题,可在 GRUB 启动菜单中选择旧内核进入系统后,执行:

# 查看内核列表
awk -F\' '$1=="menuentry " {print i++ " : " $2}' /etc/grub2.cfg

# 设置旧内核为默认(假设旧内核序号为 1)
grub2-set-default 1
grub2-mkconfig -o /boot/grub2/grub.cfg

三、Core Dump 文件配置与调试

当程序发生段错误(Segmentation Fault)或其他致命信号时,内核会产生 Core Dump 文件。对于 C/C++ 开发者来说,Core 文件结合 GDB 是定位内存错误最有效的手段。

3.1 确认程序是否发生了 Core Dump

首先通过 dmesg 或系统日志确认程序是否真的发生了 Core Dump:

dmesg -H | grep -i "segfault\|core"
# 或
tail -f /var/log/messages

如果程序确实 Core Dump 了,日志中会出现类似如下的记录:

segfault at 0 ip 00007f8b2c3a1b2f sp 00007ffc8a6b3e20 error 6 in a.out

3.2 排查 Core 文件丢失的原因

确认了 Core Dump 却找不到 Core 文件,通常有以下几种可能:

(1)core 文件大小限制

使用 ulimit -c 检查:

ulimit -c

如果输出为 0,说明禁止生成 Core 文件。在用户的 .bash_profile 中添加:

ulimit -c unlimited

(2)core_pattern 配置

查看当前 core 文件的保存规则:

cat /proc/sys/kernel/core_pattern

CentOS 7/8 默认可能输出:

|/usr/lib/systemd/systemd-coredump %P %u %g %s %t %c %h %e

以管道符 | 开头的配置表示 core 内容被传递给用户空间程序处理(此处为 systemd-coredump),而不是直接写入文件。这种情况下 Core 文件默认以 LZ4 压缩格式保存在 /var/lib/systemd/coredump/ 目录中。

3.3 自定义 Core 文件保存位置

临时修改:

# 在当前目录生成 core 文件
echo "core-%e-%p-%t" > /proc/sys/kernel/core_pattern

# 指定固定保存路径
echo "/tmp/core-%e-%p-%t" > /proc/sys/kernel/core_pattern

格式说明:

占位符含义
%e程序文件名
%p进程 PID
%u进程用户 ID
%g进程用户组 ID
%s触发 core dump 的信号编号
%tcore dump 的时间戳(Unix 时间)
%h主机名
%%输出 % 字符

永久修改:

# 编辑 /etc/sysctl.conf,添加以下行
echo "kernel.core_pattern=core-%e-%p-%t" >> /etc/sysctl.conf

# 使配置立即生效
sysctl -p

3.4 使用 systemd-coredump 工具

CentOS 7/8 下也可以借助 coredumpctl 管理 Core 文件:

# 查看最近 5 个 core dump 记录
coredumpctl list | tail -5

# 将指定 PID 的 core dump 导出到当前目录
coredumpctl dump 92362 -o core

如果想全面恢复经典的 core 文件生成方式:

echo "kernel.core_pattern=core.%p" > /etc/sysctl.d/50-coredump.conf
/lib/systemd/systemd-sysctl

3.5 使用 GDB 调试 Core 文件

gdb 应用程序名 core.xxx

进入 GDB 后执行:

(gdb) where     # 查看调用栈
(gdb) bt full   # 查看完整调用栈及局部变量
(gdb) info registers  # 查看寄存器状态

注意: 编译程序时需要加上 -g 选项以包含调试符号信息,否则 GDB 无法显示源码级别的调用信息。


四、Linux 内核参数(sysctl)调优

Linux 内核提供了数百个可通过 sysctl 接口动态调整的运行时参数,涵盖内核行为、内存管理、网络协议栈、文件系统等方方面面。下面按类别介绍生产环境中最常用且影响最大的参数。

4.1 内核核心参数

# 系统发生 panic 后自动重启前的等待时间(秒),0 表示不自动重启
kernel.panic = 10

# 发生 Oops 时触发 panic(生产环境建议开启,便于快速发现问题)
kernel.panic_on_oops = 1

# Ctrl+Alt+Delete 行为:0 表示捕获信号传递给 init,1 表示立即重启
kernel.ctrl-alt-del = 1

# 进程 PID 的最大值
kernel.pid_max = 65536

# 系统最大线程数
kernel.threads-max = 256000

# Core 文件命名规则
kernel.core_pattern = core-%e-%p-%t

# Core 文件名是否添加 PID 后缀
kernel.core_uses_pid = 1

4.2 内存管理参数

这是生产调优中最关键的部分。

# 每个内存区保留的最小空闲内存(KB),防止内存完全耗尽
# 推荐值:物理内存较大时设为总内存的 0.5%-1%
vm.min_free_kbytes = 65536

# swappiness:值越大越倾向于使用 swap(0-100)
# 对于数据库服务器建议设为 1-10,尽可能使用物理内存
vm.swappiness = 1

# 脏页比例达到此值时,write() 调用会阻塞并开始回写
vm.dirty_ratio = 10

# 脏页比例达到此值时,后台 flusher 线程开始回写(不阻塞 write)
vm.dirty_background_ratio = 5

# 脏数据过期时间(百分之一秒),超过后 flusher 线程会将其回写磁盘
vm.dirty_expire_centisecs = 3000

# flusher 内核线程唤醒间隔(百分之一秒)
vm.dirty_writeback_centisecs = 500

# 内存过量分配策略
# 0:启发式检查(默认) 1:始终允许过量分配  2:禁止过量分配
vm.overcommit_memory = 0

# 过量分配百分比(仅 overcommit_memory=2 时生效)
vm.overcommit_ratio = 50

# OOM 时是否打印系统进程信息(0:不打印 1:打印)
vm.oom_dump_tasks = 1

# OOM 时的行为:0=OOM Killer, 1=有时恢复, 2=直接 panic
vm.panic_on_oom = 0

# OOM Killer 策略:0=杀触发 OOM 的进程, 非0=杀占用内存最多的进程
vm.oom_kill_allocating_task = 0

# 单个进程能拥有的最大内存映射区域数(Elasticsearch 等服务需调大)
vm.max_map_count = 262144

# 释放缓存(手动执行,非持久配置)
# echo 1 > /proc/sys/vm/drop_caches   # 释放 page cache
# echo 2 > /proc/sys/vm/drop_caches   # 释放 dentries 和 inodes
# echo 3 > /proc/sys/vm/drop_caches   # 释放全部

4.3 网络参数

# SYN 队列最大长度(应对高并发连接)
net.core.somaxconn = 65535

# 接收/发送缓冲区默认值和最大值(字节)
net.core.rmem_default = 262144
net.core.rmem_max = 16777216
net.core.wmem_default = 262144
net.core.wmem_max = 16777216

# 每个网络接口接收队列长度
net.core.netdev_max_backlog = 10000

# === IPv4 核心配置 ===

# 开启 IP 转发(网关/路由器必需)
net.ipv4.ip_forward = 1

# 本地端口范围
net.ipv4.ip_local_port_range = 1024 65535

# TCP keepalive 探测间隔(秒)
net.ipv4.tcp_keepalive_time = 1200
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 3

# TCP FIN-WAIT-2 状态超时时间
net.ipv4.tcp_fin_timeout = 30

# 加速 TIME-WAIT 回收(高并发 Web 服务器建议开启)
net.ipv4.tcp_tw_reuse = 1

# SYN 队列及重试
net.ipv4.tcp_max_syn_backlog = 8192
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syn_retries = 2

# 开启 SYN Cookie 防护
net.ipv4.tcp_syncookies = 1

# 开启 TCP 时间戳和窗口缩放
net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_window_scaling = 1

# TCP 缓冲区自动调整(1=开启)
net.ipv4.tcp_moderate_rcvbuf = 1

# TCP 内存限制(最小值 压力值 最大值,以页为单位)
# net.ipv4.tcp_mem = 786432 1048576 1572864

# TCP 读写缓冲区(最小值 默认值 最大值,字节)
# net.ipv4.tcp_rmem = 4096 87380 16777216
# net.ipv4.tcp_wmem = 4096 65536 16777216

# 反向路径过滤(防 IP 欺骗,建议开启)
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1

# 不响应 ICMP echo 请求(禁止 ping)
# net.ipv4.icmp_echo_ignore_all = 1

# 不接收 ICMP 重定向
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0

4.4 文件系统参数

# 系统最大文件句柄数
fs.file-max = 6553560

# 单个进程最大文件句柄数
fs.nr_open = 1048576

# inotify 监听数量上限
fs.inotify.max_user_watches = 8192000

# 最大 AIO 请求数(数据库相关)
fs.aio-max-nr = 1048576

# 限制普通用户创建硬链接/软链接(安全加固)
fs.protected_hardlinks = 1
fs.protected_symlinks = 1

# 是否允许 SUID 程序产生 core dump
fs.suid_dumpable = 0

4.5 使配置永久生效

将参数写入 /etc/sysctl.conf/etc/sysctl.d/ 下的配置文件(推荐使用后者以便于管理):

# 创建独立调优配置文件
cat > /etc/sysctl.d/99-tune.conf << 'EOF'
# 内存调优
vm.swappiness = 1
vm.dirty_ratio = 10
vm.dirty_background_ratio = 5
vm.min_free_kbytes = 65536
vm.max_map_count = 262144

# 网络调优
net.core.somaxconn = 65535
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_syncookies = 1
net.ipv4.ip_local_port_range = 1024 65535

# 文件系统调优
fs.file-max = 6553560
fs.inotify.max_user_watches = 8192000
EOF

# 使配置立即生效
sysctl -p /etc/sysctl.d/99-tune.conf

五、内存分析与 OOM 故障排查

内存问题是线上故障的高频来源。掌握快速定位内存占用大户、分析 OOM Killer 行为的方法,是运维工程师的必备技能。

5.1 快速定位高内存占用进程

使用 free 查看系统整体内存状态:

free -m

关注 available 列,它反映了在不进行 swap 的情况下系统还能分配给新进程的内存量。

使用 top 按内存排序:

top -d 1 -n 1 -o %MEM | grep PID -A 10

使用 ps 查看内存占用 Top 10:

ps auxw --sort=-rss | head -n 10

5.2 分析 /proc/meminfo

/proc/meminfo 提供了详细的内存分配视图。以下脚本可以计算各项内存指标的占比:

#!/bin/bash
# 分析内存分配详情

total=$(grep MemTotal /proc/meminfo | awk '{print $2}')
while read line; do
    flag=$(echo $line | egrep -vi "mem*|Vmallo*" | awk '$2!=0{print}' | awk '{print $1}')
    value=$(echo "$line" | egrep -vi "mem*|Vmallo*" | awk '$2!=0{print}' | awk '{print $2}')
    if [[ -n "$value" ]]; then
        precent=$(echo "scale=2; $value / $total * 100" | bc)
        echo -e "$flag \t内存占比 $precent %"
    fi
done < /proc/meminfo

5.3 OOM Killer 原理与排查

当系统内存耗尽时,内核的 OOM Killer 会根据进程的 oom_score 选择并杀死进程。oom_score 越高,被选中的概率越大。

查看进程的 OOM 分数(Top 10):

#!/bin/bash
for proc in $(find /proc -maxdepth 1 -regex '/proc/[0-9]+'); do
    printf "%2d %5d %s\n" \
        "$(cat $proc/oom_score)" \
        "$(basename $proc)" \
        "$(cat $proc/cmdline | tr '\0' ' ' | head -c 50)"
done 2>/dev/null | sort -nr | head -n 10

手动调整进程的 OOM 优先级:

# 将 PID 为 1234 的进程排除在 OOM Killer 之外(调整为 -1000)
echo -1000 > /proc/1234/oom_score_adj

# 让 PID 为 5678 的进程更容易被 OOM Killer 选中(调整为 1000)
echo 1000 > /proc/5678/oom_score_adj

5.4 查看 OOM Killer 历史记录

# 查看系统日志中的 OOM 记录
dmesg | grep -i "oom\|out of memory\|killed"

# 使用 journalctl 查看
journalctl -k | grep -i oom

OOM Killer 日志中会包含被杀进程的 PID、名称、内存占用(RSS)以及当时的系统内存状态快照,这些信息对于回溯问题根因非常关键。


总结

本文从实际运维场景出发,系统梳理了 Linux 系统管理的五个核心方向:

  1. Supervisor 进程守护:确保应用进程的高可用性,通过简单的 INI 配置即可实现自动重启和日志管理。
  2. CentOS 7 内核升级:通过 ELRepo 仓库安全升级到新版本内核,满足新特性需求。
  3. Core Dump 调试:排查 Core 文件丢失的常见原因,掌握 systemd-coredump 与传统方式的切换以及 GDB 基本调试方法。
  4. sysctl 内核参数调优:覆盖内存管理、网络协议栈、文件系统三大类关键参数,附生产环境推荐配置。
  5. 内存分析与 OOM 排查:通过 free/top/ps 快速定位问题进程,结合 /proc/meminfo 和 OOM Score 机制深入分析内存瓶颈。

掌握这些技能后,运维工程师能够在大多数生产场景下快速定位问题、合理配置系统参数,保障服务的稳定性和性能。建议将常用的 sysctl 调优配置文件纳入配置管理(如 Ansible 或 SaltStack),实现标准化的系统初始化流程。

暂无评论