“土豆服务器”这个词这几年经常出现,最早是游戏圈拿来调侃官方服务器性能差、一开活动就排队掉线。放到现实运维里,意思其实差不多:一台配置并不富裕的服务器,平时用着还行,一旦业务量上来、有人调任务、或者后台在跑什么没控制好资源的脚本,整台机器就直接卡死。SSH 连不上、页面超时、重启后还不知道问题出在哪,只能再赌一次。
这次就围绕“服务器卡死”这个场景,把一套真正能落地的现场排查流程写出来。文章不会只讲某个发行版或某个云厂商的控制台技巧,而是解决一个更普遍的问题:不管你是 Linux 服务器、Windows 服务器、GPU 推理服务器,还是群晖 NAS、录播服务器上跑的 Linux 虚拟机,当负载上来、整机“假死”的时候,应该按什么顺序取证、怎么定位根因、用什么命令临时恢复业务,以及做完之后怎么防止下次再卡成土豆。
内容适合运维刚入门、兼职管服务器的开发者、还有搭了自己服务但没上监控的人。建议先收藏,真遇到的时候照着查。
1. 服务器卡死现场排查核心能力速览
| 能力项 | 说明 |
|---|---|
| 适用范围 | Linux 服务器(Ubuntu/CentOS/Debian 等)、Windows Server、虚拟机和云服务器 |
| 核心思路 | 先分清卡死层级,再采集现场信息,最后定位根因,能临时恢复就不再轻易重启 |
| 常用工具 | top/vmstat/free/df/iostat/dmesg/journalctl/ss/lsof/pidstat |
| 典型根因 | CPU 打满、内存耗尽或 swap 颠簸、磁盘 IO 拥塞、文件句柄耗尽、网络与 DNS 异常、进程死锁 |
| 现场采集耗时 | 5 到 15 分钟,视系统响应情况而定 |
| 操作风险 | 中。误杀进程、强制重启生产库、批量清理日志都有风险,需要先确认再操作 |
| 是否支持远程排查 | 支持。SSH 能通就能排查;SSH 不通时需借助云厂商 VNC 或带外管理口 |
| 最适合人群 | 自建服务器的个人开发者、中小团队运维、需要临时顶上的业务研发 |
| 不适用场景 | 云厂商机房断电、物理硬盘物理损坏、交换机故障、突发高可用架构整体挂掉 |
2. 适用场景与排查边界
“土豆服务器卡死”通常发生在下面几类场景里:
- 低配置云服务器。2 核 4G 的小机器被开发环境、数据库、反向代理、定时任务全塞在一起,CPU 或内存先扛不住。
- 被批量任务打满的机器。例如 rsync 同步大量小文件、视频转码、模型推理、爬虫任务同时跑,磁盘 IO 排队严重,系统像被冻住一样。
- 图形界面服务器。服务器装了桌面环境,某次更新显卡驱动或桌面组件后,图形界面卡死,停在黑底命令行或者直接无响应。
- 长时间不重启的录播、监控、NAS 等业务服务器。内存碎片、文件句柄泄漏、日志膨胀,运行几个月之后突然在某次高峰期卡死。
- 容器宿主机。多个 Docker 容器共享内核资源,某个容器没有资源限制直接耗尽宿主机的 CPU 或内存,导致其他容器一起卡。
这套排查流程更适合硬件本身没有物理故障、系统还能部分响应或日志还能保留的情况。如果主板报警、硬盘 SMART 状态异常、服务器完全不通电或者云厂商底层故障,排查重点就变成硬件检测和业务迁移,而不是在系统里敲命令。
另外,排查生产环境之前必须明确边界:不确定某个进程是什么业务的时候,不要随手 kill。可以先采集现场信息,找日志确认后再处理。强制重启应该是最后手段,因为重启会清空内存态信息、中断日志写入顺序、还可能导致文件系统检查或数据库恢复时间更长。
3. 到现场先分清三种“卡死”
服务器“卡死”并不是同一种问题。处理方式完全不一样,第一步一定先判断是哪一种。
3.1 SSH 连接不上
现象是控制台能登录,但 SSH 客户端一直卡在连接阶段;或者已经登录的会话光标不动,敲命令没有任何回显。
可能的原因很分散:CPU 100% 导致 sshd 进程长时间抢不到 CPU、系统内存耗尽触发内核 OOM、磁盘 IO 拥塞导致用户态进程全部阻塞、防火墙或网络问题、sshd 本身异常、DNS 反向解析卡住等。
此时优先通过云厂商 VNC 或者服务器带外管理口进入系统,先看负载和 CPU 状态,再按下面的命令采集现场。
3.2 SSH 能连但系统响应极慢
现象是 SSH 能连上,按住回车能看到新行输出,但执行 top 要等十几秒甚至更久。
这种状态说明内核还在响应,用户态进程调度已经严重受阻。常见原因是 CPU 资源被线程占满、D 状态进程太多、swap 疯狂换页,或者文件系统等待 IO 完成。
3.3 业务服务假死,但系统本身没死
网页访问超时、API 接口不返回、数据库连接数飙升,但通过 SSH 到服务器执行 top 是正常的,CPU、内存、磁盘都不高。
这时候问题多数出在应用层:线程池耗尽、连接数超限、死锁、消息堆积、单点进程状态异常。排查重点从操作系统转移到进程日志、中间件日志以及服务调用链上。
三种情况有交叉,也可能同时发生。比如业务假死导致请求堆积,最终把 CPU 和内存打满,对外表现就是 SSH 也连不上了。所以先区分层级,再逐层筛查。
4. 现场信息采集:重启前必做
很多人一看到卡死,直接按电源键重启,这是最亏的做法。服务器卡死时,内存里的进程表、内核日志、IO 队列信息都还残留着。一旦重启,几乎只能靠主观猜测找原因。
下面这一套命令可以在系统还能响应时,把现场信息保存到文件里。
# 创建现场目录 mkdir -p /tmp/server_fault_report # 1. 系统基本信息和运行时长 uname -a > /tmp/server_fault_report/uname.txt uptime >> /tmp/server_fault_report/uptime.txt cat /etc/os-release >> /tmp/server_fault_report/os-release.txt # 2. CPU 和内存快照,保存两到三次用于对比 top -bn1 > /tmp/server_fault_report/top_1.txt sleep 3 top -bn1 > /tmp/server_fault_report/top_2.txt free -h > /tmp/server_fault_report/free.txt cat /proc/meminfo > /tmp/server_fault_report/meminfo.txt # 3. 磁盘容量和 IO 状态 df -h > /tmp/server_fault_report/df.txt df -i > /tmp/server_fault_report/inode.txt # 4. 网络连接状态 ss -s > /tmp/server_fault_report/ss_summary.txt ss -tulnp > /tmp/server_fault_report/ss_port.txt # 5. 内核日志,重点看 OOM、IO error、soft lockup、hung_task dmesg -T > /tmp/server_fault_report/dmesg.txt # 6. systemd 日志最后 200 行 journalctl -n 200 --no-pager > /tmp/server_fault_report/journalctl.txt # 7. 最近登录记录,确认是不是有人在跑大任务 last -n 20 > /tmp/server_fault_report/last.txt # 8. 当前正在运行的进程 ps auxww > /tmp/server_fault_report/ps_aux.txt采集完现场信息后,再把整个目录打包下载到本地留档:
tar czvf /tmp/server_fault_report_$(date +%Y%m%d_%H%M%S).tar.gz /tmp/server_fault_report这些文件是排查的第一手证据。后续无论是自己分析、还是找别人协助,都直接看这些文件,省掉反复复现问题的过程。
5. 五个高频卡死原因定位
现场信息采集完成后,重点从五个方面判断根因。
5.1 CPU 被打满
特征:uptime 里的 load average 很高,top 里某个进程或者内核线程把 CPU 消耗到接近 100%。
常见来源是业务代码死循环、并发线程数失控、数据库慢查询、后台脚本触发了全表扫描、挖矿程序被入侵植入。有时候 CPU 打满不是单一进程,而是多个进程累计叠加。
查看该进程的线程栈和高占用线程:
top -Hp 12345如果是 Java 进程,可以配合 jstack 抓取线程栈,看到底卡在哪个方法上。如果是 Python 或 Node 进程,优先确认是否有无依赖死锁的任务在跑。
5.2 内存耗尽与 swap 颠簸
特征:free -h 中 available 接近为 0,swap 的 used 在涨,si/so 持续非零,系统体感很慢。
当物理内存不够时,Linux 内核会频繁换页,把不常用的内存页写到 swap。如果 swap 本身就很小或者没有配置,内核会触发 OOM Killer 杀掉进程,数据库、主业务进程可能会瞬间消失。
使用 dmesg 查看是否发生了 OOM:
dmesg -T | grep -i -E "out of memory|oom-kill|killed process"更稳妥的判断是结合重启前的 meminfo 文件和日志,看是哪个进程触发了内存膨胀。
5.3 磁盘 IO 拥塞
特征:top 里 %wa 或 %iowait 很高,很多进程处于 D 状态,但 CPU 使用率看起来不高。
例如 rsync 大量小文件同步、磁盘阵列重建、数据库全量导出、日志文件疯狂写入,都会让 IO 设备队列堆积。此时即使 CPU 有空格,业务也像是被冻住。
iowait 高的机器,要先确认是哪块磁盘、哪个进程在产生 IO:
iostat -x 1 5也可以列出当前处于 D 状态且与内核 IO 等待相关的进程:
ps auxww | awk '$8 ~ /^D/'5.4 文件句柄或进程数耗尽
特征:服务启动失败,日志提示 too many open files 或 cannot fork new threads。
一个进程通过 ulimit -n 控制打开文件数,整个系统通过 fs.file-max 控制总文件数。如果程序有句柄泄漏,长时间运行后就会把资源耗尽,进而拖垮依赖该机器的所有服务。
查看当前句柄使用量:
cat /proc/sys/fs/file-nr5.5 网络与 DNS/端口异常
特征:SSH 连不上或连上后卡顿,业务接口提示连接超时、域名解析失败。
常见情况包括网卡丢包、DNS 反向解析导致 SSH 连接缓慢、NTP 时间跳变引起服务握手失败、端口被防火墙封掉或已被占满、云服务器安全组误配置。
对 SSH 连接慢的问题,很多 Ubuntu 服务器卡在客户端连接阶段时,可以先检查 sshd 是否在做反向解析:
grep -E "UseDNS|GSSAPIAuthentication" /etc/ssh/sshd_config如果参数没有被正确配置,可以改为:
UseDNS no GSSAPIAuthentication no修改后重启 sshd:
systemctl restart sshd这能去掉很多 SSH 等待 DNS 或 GSSAPI 认证导致的连接卡顿问题。
6. 用命令逐项验证
现场信息采集结束后,如果你还能执行交互命令,再按下面顺序逐项验证,基本能定位 90% 以上问题。
6.1 先看负载和 CPU 态
执行 top,第一行 load average 有三个数字,分别代表 1 分钟、5 分钟、15 分钟平均负载。如果单个数字长期超过 CPU 核心数,说明系统一直处于排队状态。
看 CPU 状态行时,重点关注:
- us:用户态进程占用
- sy:内核态占用
- wa:IO 等待
- st:虚拟化环境被宿主机抢占的时间
- id:空闲
如果 wa 很高,去查磁盘;如果 sy 很高,排查异常系统调用、驱动或内核线程;如果是云服务器整机卡死,st 长期偏高说明宿主机资源超售严重,只能迁移或换规格。
6.2 看内存和 swap
执行 vmstat 1 5 观察内存和 IO 变化:
vmstat 1 5重点看 si 和 so 两列。持续大量换入换出,说明物理内存不足,程序运行数据一直在 swap 里来回搬运。此时再查看占用内存最大的进程:
ps auxww --sort=-%mem | head -20如果发现单个进程内存占用异常,正常情况下 Ctrl+C 杀掉测试进程即可,生产环境则需要检查该进程对应业务能否重启。
6.3 看 D 状态进程和 IO
执行 iostat -x 1 5 看 await、util,util 接近 100% 说明磁盘基本满载。再配合 D 状态进程列表:
for i in $(ls /proc | grep -E '^[0-9]+$'); do if [ -r /proc/$i/stat ]; then state=$(awk '{print $3}' /proc/$i/stat) if [ "$state" = "D" ]; then echo -n "PID: $i " cat /proc/$i/cmdline 2>/dev/null | tr '\0' ' ' echo "" fi fi doneD 状态通常是不可中断睡眠,一般伴随磁盘或 NFS 等待。这类进程用 kill -9 也无法结束,只能等 IO 恢复或重启服务所在挂载点。
6.4 看系统日志和内核日志
这是定位根因的最后一步,也最直观:
# 内核信息 dmesg -T | tail -100 # systemd 核心服务状态,判断关键服务是否还活着 systemctl --failed --no-pager # 数据库或应用服务自己的日志 journalctl -u mysql --since "30 minutes ago" --no-pager内核日志里经常能看到这几类关键线索:
- **Out of memory**:内存不足,OOM Killer 在工作。
- **hung_task_timeout_secs**:内核认为某个任务卡死。
- **soft lockup** / **hard lockup**:CPU 无法正常调度,常见于驱动异常或 CPU 过载。
- **blocked for more than 120 seconds**:进程阻塞时间过长。
7. 临时缓解:先恢复业务再深究
定位到大致原因后,不要马上做复杂优化。先让业务恢复,再研究长期方案。
7.1 杀掉明显异常进程
如果你已经确认某个进程就是导致 CPU 或内存飙高的元凶,并且在业务上允许退出,可以按进程号结束:
kill -9 12345如果是配置了 systemd 管理的服务,用 systemctl restart 替代直接 kill,这样进程生命周期会被正确跟踪,避免重启后无人拉起。
7.2 限制单任务资源
临时要跑批量任务时,可以用 systemd-run 限制任务的 CPU 和内存配额,避免一锅粥全搅在里面。
下面这个示例把 CPU 限制在 2 核、内存限制在 2G 内执行数据同步脚本:
systemd-run --unit=batch-sync --scope -p CPUQuota=200% -p MemoryMax=2G /opt/scripts/sync_data.sh如果任务已经以普通命令方式启动,但造成了资源失控,可以先暂停、再慢慢调整:
# 暂停进程组 kill -STOP 12345 # 调整资源策略后继续执行 kill -CONT 123457.3 只重启服务,不重启整机
系统盘空间不足、服务假死、端口被占用这类问题,优先重启服务本身。服务器频繁整机重启,会让日志连续性变差,也不利于观察内存和句柄增长趋势。
需要重启某个服务时,先看服务名:
systemctl list-units --type=service --state=running确认后:
systemctl restart nginx systemctl restart mysql不必动不动就 reboot。除非系统完全无响应、通过任何方式都无法进入环境,再考虑强制重启,且重启后优先检查文件系统完整性。
7.4 临时清理空间,但要克制
磁盘空间满导致的卡死很常见,清理时优先清理确定不用的临时文件、旧日志和回收站文件。不要在不确定的情况下直接删除数据库 binlog 或应用核心目录。
查看占用较大的目录:
du -h --max-depth=2 /var/log 2>/dev/null | sort -hr | head -20对于 active 中的日志文件,可以使用 truncate 而不是删除文件,避免持有文件句柄的进程继续向已删除文件写入,造成磁盘空间仍然不释放的问题:
truncate -s 0 /var/log/nginx/access.log8. 根治与预防:避免再次卡成土豆
临时恢复只是第一步。如果根本问题不解决,同样的卡死会在几天后再次出现。预防工作比排查更重要。
8.1 监控与告警前置
没有监控的服务器就像没有仪表盘的车。至少要监控这几个指标,并配置告警阈值:
- CPU 使用率,建议 80% 持续 5 分钟告警
- 内存使用率,建议 85% 持续 5 分钟告警
- 磁盘使用率,建议 85% 以上告警
- inode 使用率,建议 85% 以上告警
- 系统 load average 与 CPU 核数对比
- 关键服务存活状态,如 nginx、mysql、容器实例等
常用的开源监控方案有 Prometheus + Grafana + node_exporter,或者轻量一点的 Netdata、Glances。至少给 node_exporter 配一个好一点的存储和告警,方便趋势分析。
8.2 日志轮转和磁盘容量管理
很多服务器卡死不是因为性能差,而是日志文件没轮转,磁盘空间或 inode 被吃完。
配置 logrotate 可以让日志定期切割:
/var/log/nginx/*.log { daily rotate 7 compress delaycompress missingok notifempty create 0640 www-data adm sharedscripts postrotate [ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid` endscript }具体路径和用户需要根据实际部署调整。日志量大的服务,最好把日志目录单独挂一块数据盘,避免根分区被撑满之后系统直接卡死。
8.3 系统参数调优
一些常见的系统参数可以根据业务场景进行调整,但要结合真实负载,不能盲目加。例如文件句柄限制:
# 临时调整 sysctl -w fs.file-max=1000000 ulimit -n 1024000持久化可以写入 /etc/sysctl.conf 或 /etc/security/limits.conf。再比如 vm.swappiness 默认一般是 60,对于需要低延迟的数据库服务器,可以降为 10 左右:
sysctl -w vm.swappiness=10调整后,观察 swap 使用和业务响应变化。如果业务本来就吃内存,单纯调 swappiness 并不能替代加内存。
8.4 升级配置或拆分集群
当一台服务器长时间 CPU 或内存使用率偏高,并且业务量还在增长,与其继续优化代码,不如直接换规格或者做多节点。诸如 2 核 4G 的小机器就不要把数据库、缓存、Web 服务、定时任务全塞在里面。
有条件的话采用动静分离、数据库独立、缓存独立的结构,再往上是服务多副本加负载均衡。服务器集群能显著降低单点卡死造成的业务影响,但也会引入配置同步、网络分区、链路追踪等新复杂度。小团队要权衡成本和收益。
8.5 备份与冗余
卡死可能伴随文件系统异常、数据库崩溃。运维上需要定期备份应用配置、数据库数据和关键文件目录。如果是虚拟机,可以结合快照进行恢复演练;如果是物理服务器,磁盘阵列要做好状态监控,看到 raid 降级要及时处理。
备份不是“存起来”就完事,要定期执行恢复验证。否则真到服务器卡死、磁盘无法读取时才发现备份不可用,问题会彻底扩大。
9. 服务器卡死常见问题与排查表
下面的表格汇总了很多服务器卡死场景中的典型现象、原因和应对方式,可以直接对照排查。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| Ubuntu 系统运行一段时间后完全卡死,鼠标键盘都没响应 | 图形驱动异常、内存耗尽、桌面组件崩溃 | 切换到 tty,执行 dmesg -T 查看驱动报错 | 重启桌面服务或更新显卡驱动;生产服务器尽量最小化安装 |
| Linux 图形化界面卡死,只剩黑底白字命令行 | X Server 或 Wayland 崩溃、显卡驱动不兼容 | 按 Ctrl+Alt+F2 进入 tty,查看日志 | 修复图形组件或禁用桌面环境,改用 SSH 管理 |
| 执行 rsync 复制大量小文件时服务器卡顿 | 磁盘 IO 队列堆积、单目录文件数过大 | 执行 iostat -x 1 5 观察 await | 拆分批次同步,限制 IO 速度,使用 rsync --bwlimit 方式 |
| SSH 连接远程服务器要等很久才能登录 | sshd 反向解析 DNS、GSSAPI 认证卡住 | 检查 ss -t 和 sshd 日志 | 配置 UseDNS no、GSSAPIAuthentication no |
| VSCode Remote SSH 连接远程服务器失败 | 服务器端远端服务未启动、网络不稳定、权限问题 | 查看 VSCode 输出日志和服务器端 ~/.vscode-server 日志 | 重装远端服务端,清理 ~/.vscode-server 后重连 |
| Windows 服务器系统卡死,鼠标右键桌面文件夹就卡住 | 资源管理器异常、磁盘掉盘、挂载的网络路径不可达 | 打开任务管理器,查看资源占用 | 结束 explorer.exe 并重启,移除不可达网络映射盘 |
| 服务器负载不高但网站接口全部超时 | 应用进程死锁、线程池耗尽、连接数打满 | 查看应用日志、线程 dump、数据库连接池 | 重启应用服务,修复死锁或连接泄漏问题 |
| 数据库进程突然消失,服务器仍在运行 | 内存耗尽触发 OOM Killer | 执行 dmesg -T 搜索 out of memory | 缩小数据库缓存,或给实例增加内存 |
| 启动每个新进程都提示 cannot fork | 线程数或进程数达到上限 | 查看 ulimit -u、sysctl kernel.pid_max | 调整进程数上限,排查进程泄漏并清理僵尸进程 |
| 服务器在 GPU 训练任务高峰期卡死 | 设备温度过高、GPU 显存不足、驱动崩溃 | 查看 nvidia-smi 和 dmesg | 限制并行任务数量,检查散热和电源冗余 |
| 服务器时间跳变导致业务异常 | NTP 服务未配置或时钟源不可达 | 执行 timedatectl status、chronyc sources | 配置国内可用的 NTP 时间服务器并开启自动同步 |
| DNS 解析超时,业务请求排队 | resolv.conf 配置错误、DNS 服务器不响应 | 执行 dig、nslookup、cat /etc/resolv.conf | 切换可靠 DNS 服务器,做好本机 fallback |
10. 最佳实践:从运维角度总结
经过一次服务器卡死处理后,建议形成一套可复用的运维习惯。
10.1 维护最小可行的排障清单
把本次卡死涉及的服务器 IP、登录方式、部署路径、日志路径、关键服务和历史故障记录整理成一个文档。下次再遇到类似现象,直接按文档顺序体检,先查监控,再查日志,再查硬件状态。
10.2 生产环境谨慎操作
不要在生产环境上直接执行不熟悉的调优命令。修改任何内核参数、重启任何服务之前,确认这个服务的从属关系。数据库、配置中心、认证服务这类高依赖组件,更应该提前做变更窗口和回滚方案。
10.3 日志分层管理
应用日志、访问日志、系统日志、内核日志分开存储,并设置合理的保留周期。日志是排查服务器卡死的第一依据,日志丢失等于现场被破坏。
推荐至少保留 7 到 30 天关键日志,核心数据库和交易类服务建议异地归档,避免磁盘故障后彻底失去线索。
10.4 定期处理定时任务和批处理脚本
定时任务最容易间接导致服务器卡死。排查运维计划中是否有某个 crontab 脚本会在整点或夜里触发全量数据同步、压缩备份等重负载操作。尤其是多个任务时间重叠时,IO 和 CPU 瞬间并发,小规格服务器很快就会被拖垮。
建议把所有批处理任务错峰执行,重任务并行数限制为 1 到 2 个,并且加上日志和告警。
10.5 做好资源配额
不管是本机服务还是容器服务,都建议配置 CPU、内存、文件句柄配额。Docker 部署时,用 --memory 和 --cpus 限制容器资源:
docker run -d \ --name my-service \ --memory="2g" \ --cpus="2" \ --restart=unless-stopped \ my-image:latest这样即使某个容器内部发生内存泄漏或死循环,也只会影响该容器本身,不会拖垮整台物理机或宿主机上的所有服务。
10.6 规范化处理接管现场
每次故障处理完成后,保留一份当时的现场信息目录,并写一段简短的故障复盘记录。内容包含硬件配置、故障时间、现象、根因、临时措施、长期优化项。长期积累之后,这些记录会成为团队最实用的运维手册。
11. 总结与下一步
服务器卡死不是某一种固定原因,而是一连串资源瓶颈叠加后的表现。排查时最忌讳上来就重启,最有效的动作是分清卡死层级,采集现场信息,按 CPU、内存、磁盘 IO、文件句柄、网络五个方向依次排除。
建议下次遇到“土豆服务器”卡死,你先做三件事:第一,用 uptime 和 top 确认负载来源;第二,用 dmesg 找内核关键报错;第三,把现场信息归档后再决定是否重启。只要这三步做到位,绝大多数卡死问题都能在 15 分钟内找到方向。
后续值得继续扩展的方向包括:部署一套轻量监控、给批处理任务加资源限制、把关键服务拆到独立机器或容器里。服务器不用追求每次都换新硬件,但一定要让它“卡得明白、挂得可控、恢复得快”。