3.7GB小服务器跑19个容器,OOM杀掉MySQL致站点全挂:排查与修复实录

简介: 背景:一台贴着天花板跑的小服务器 这台 2 核 / 3.7GB 内存的云服务器上常年跑着一套不小的家当: 19 个 Docker 容器 (nginx 反代、两套应用数据库、缓存、监控面板、文档站……) 宝塔面板 + 主机版 MySQL +

背景:一台贴着天花板跑的小服务器

这台 2 核 / 3.7GB 内存的云服务器上常年跑着一套不小的家当:

  • 19 个 Docker 容器(nginx 反代、两套应用数据库、缓存、监控面板、文档站……)
  • 宝塔面板 + 主机版 MySQL + Redis 各一套
  • 两个 Node 应用 + 一个文档系统 + 一个 AI 编码工具的 web 进程

内存占用常年在 2.6GB / 3.7GB 附近,swap 早已用掉 1.8GB / 1.9GB——也就是说,机器已经贴着天花板硬撑了很长时间,只差一次波动。

故障在凌晨爆发。下面是完整的时间线、排查过程和修复方案,全部是本次真实记录。

一、故障时间线

时间 现象
约 00:10 站点开始无法访问,HTTPS 443 连接建立失败
00:11:02 内核 OOM killer 触发,MySQL 进程被杀
00:12~00:15 mysqld_safe 自动重启 MySQL,磁盘 IO 拉满,load 飙到三位数
00:15 后 负载逐渐回落,站点恢复
00:20 排查时 load average: 0.48, 49.60, 106.52

最刺眼的是 load:1 分钟前 0.48、5 分钟前 49.6、15 分钟前 106——对一台 2 核机器来说,这是彻底的雪崩。

二、排查过程(四步定位)

第 1 步:先判断是「机器挂了」还是「服务挂了」

ping -c 3 服务器IP        # 通,但丢包 33%,rtt ≈ 60ms
# TCP 22 能握手,SSH banner 却超时
# TCP 443 直接连不上

机器活着,但端口层面一通一不通——这个组合通常指向系统级过载(CPU/内存/IO 打满),而不是服务进程崩溃。方向定为:先抢 SSH 进去看系统状态。

第 2 步:SSH 进去,一眼看到元凶

load average: 0.48, 49.60, 106.52
Mem: total 3724  used 2625  free 316  available 1098
Swap: 1987 total / 1876 used / 111 free

swap 只剩 111MB,内存 available 只剩 1GB——内存和 swap 双双见底。磁盘(63%)和负载回落速度都正常,排除磁盘满和持续性攻击。

第 3 步:内核日志给出确凿证据

journalctl --since "00:00" --until "00:20" | grep -i oom
kernel: Out of memory: Killed process 1625616 (mysqld)
        total-vm:2066996kB, anon-rss:763988kB, UID:1003
kernel: oom-kill:constraint=CONSTRAINT_NONE, global_oom,
        task_memcg=/system.slice/mysqld.service, task=mysqld
systemd: mysqld.service: A process of this unit has been killed by the OOM killer.

根因坐实:00:11:02 全局 OOM,RSS 763MB 的 mysqld 被选中杀掉。

第 4 步:排除「被攻击」的可能

# 查看故障时段的 nginx 访问日志
awk '$0 ~ 日期' /www/wwwlogs/站点.log | 统计来源 IP 和状态码

故障 20 分钟内全站只有 4 个请求、全部 200——没有流量攻击,纯粹是自己的内存问题。

三、根因分析:为什么偏偏是 MySQL?

OOM killer 按「谁占内存大杀谁」的原则选择目标。这台机器当时的内存构成是:

进程 内存占用 备注
主机 mysqld 664MB(被杀时 763MB) innodb_buffer_pool_size = 512M
AI 编码工具 web 进程 272MB
Node 应用 ×2 88 + 75MB
nginx worker 73MB
文档系统 73MB
19 个 Docker 容器 数百 MB~1GB+ 各自波动

整条因果链是:

配置总量 > 物理内存上限
  → 系统长期依赖 swap 撑着(已用 1.8G/1.9G)
  → 某次正常业务波动(查询/连接数上升)
  → RAM + swap 双满
  → 内核 OOM → 杀掉最大进程 mysqld
  → mysqld_safe 自动重启(全量初始化、刷脏页、刷缓存)
  → IO/负载风暴 → load 106 → 443 无响应 → 站点全挂

值得注意:站点挂掉不是 OOM 直接造成的,而是 MySQL 重启风暴的次生灾害。这也是为什么「重启恢复了」不等于问题解决——不加缓冲,下一次波动还会重演。

四、修复:先止血,再治本

4.1 止血:追加 2GB swap 作为缓冲垫

机器原有 2G swap 文件(几乎用尽),磁盘还有 20G+ 空闲,追加 swap 是零风险、立即生效的手段:

fallocate -l 2G /swapfile2
chmod 600 /swapfile2
mkswap /swapfile2
swapon /swapfile2

# 写入 fstab,重启后自动挂载
echo "/swapfile2 none swap sw 0 0" >> /etc/fstab

# 控制换出倾向(有 swap 后可以稍调低)
sysctl vm.swappiness=10

验证:

$ swapon --show
NAME         TYPE  SIZE USED PRIO
/swap.img    file  1.9G 1.8G   -2
/swapfile2   file  2.0G   0B   -3

$ free -m
Swap: 4035 total / 1875 used / 2160 free

swap 余量从 111MB 变成 2.1GB——下次内存波动有足够的腾挪空间,不会再直接触发 OOM kill。

补充一点:fallocate 创建的 swap 文件在多数主流文件系统(ext4/xfs)上可用;如果内核报 swapon: swapfile has holes,改用 dd if=/dev/zero of=/swapfile2 bs=1M count=2048 重新生成即可。

4.2 治本方向(三项,按性价比排序)

  1. 调小主机 MySQL 内存参数:innodb_buffer_pool_size 512M → 256M、max_connections 500 → 150(500 对小机器明显过大),可回收约 400MB。动手前先 SHOW DATABASES 确认主机 MySQL 真有业务在用——如果没有,甚至可以停掉它,让业务全走容器。
  2. 清理闲置容器:19 个容器逐一确认 docker ps 里谁还在被使用,停掉不用的,收益立竿见影。
  3. 限制单个进程的膨胀:给 MySQL 配 OOMScoreAdjust,或用 cgroup 给非核心服务设 MemoryMax,避免「谁大谁被杀」杀到业务核心。

4.3 防复发:一个 30 分钟一次的轻量看门狗

把「站点可用性 + 内存 + swap + 负载 + 关键容器」打包成一个纯脚本巡检(无外部依赖、正常时静默零输出):

#!/bin/bash
# vps_watchdog.sh — 正常时无输出;异常时输出一行告警
SSH="ssh -o ConnectTimeout=20 root@服务器IP"
ALERT=""

# ① 关键站点 HTTP
for u in https://站点A/ https://站点B/; do
  code=$(curl -s -o /dev/null -w '%{http_code}' -m 15 "$u")
  [ "$code" != "200" ] && ALERT="$ALERT 站点异常:$u→HTTP${code:-超时}"
done

# ② 内存 / swap / load
MEM=$($SSH 'free -m | awk "/^Mem:/{print \$7}"; free -m | awk "/^Swap:/{print \$4}"; uptime | grep -oE "[0-9.]+" | head -1')
AVAIL=$(echo "$MEM" | sed -n 1p)
SWAPFREE=$(echo "$MEM" | sed -n 2p)
LOAD=$(echo "$MEM" | sed -n 3p)
[ "${AVAIL:-9999}" -lt 250 ] && ALERT="$ALERT 内存告急:available=${AVAIL}MB"
[ "${SWAPFREE:-9999}" -lt 150 ] && ALERT="$ALERT swap将尽:剩${SWAPFREE}MB"
awk "BEGIN{exit !($LOAD > 8)}" && ALERT="$ALERT 负载过高:1m_load=${LOAD}"

# ③ 关键容器是否退出
DOWN=$($SSH 'docker ps -a --filter "status=exited" --format "{
   {.Names}}" | grep -E "mysql|nginx|app" | tr "\n" " "')
[ -n "$DOWN" ] && ALERT="$ALERT 容器退出:$DOWN"

[ -n "$ALERT" ] && echo "⚠️ 服务器告警:${ALERT# }"
exit 0

接到任意调度器上(每 30 分钟一次):正常时零输出、零打扰;异常时立刻收到一行可执行的告警,不用等用户来报「网站打不开了」。

五、经验总结

教训 对策
swap 用掉 80%+ 仍是「健康」状态,监控不会报警 把 swap 剩余量纳入监控阈值,它比内存更早示警
「机器 load 高」和「网站挂了」可能是同一件事的两个阶段 先查 journalctl -k 找 OOM/硬件事件,再查应用日志
OOM 杀掉的是最大进程,不一定是肇事者 主动限制核心服务的内存上限,别让「谁大谁死」
MySQL 自动重启本身会引发次生雪崩 看门狗同时盯 load 和站点,两者一起异常时优先怀疑重启风暴
一切「自愈」都可能复发 加 swap 缓冲 + 持久化 fstab + 周期巡检,三件套缺一不可

结语

小内存服务器跑全家桶,本质上是在做「配置总量」和「物理上限」的长期博弈。这次故障没有数据损失(业务数据库在独立容器里,毫发无伤),但它暴露的信号很明确:swap 见底就是 OOM 的倒计时。把 swap 余量放进监控、给核心进程上内存约束,比事后追查 OOM killer 日志要省心得多。


本文记录的是一次真实故障的排查与处置全过程,机器配置、日志时间线、内存数据均来自现场。

相关文章
|
24天前
|
人工智能 JSON API
全网刷屏的 Jev 模型正式开放!一手实战测评 + 保姆级教程
全网爆火的 Jev 模型是什么?有什么用?怎么使用?怎么接入 AI 编程工具?效果真的好么?傻子可懂的 Jev 保姆级实战教程 + 项目实战测评来啦
9478 31
|
1天前
|
云安全 人工智能 运维
当攻击全面Agent化,你的安全防御能否跟上AI Speed?
多起客户侧AK泄露事件应急的真实观察
1155 0
|
10天前
|
存储 人工智能 并行计算
大模型本地部署终端选型方法论:以 Qwen3.8-27B 为例的四档分层完整流程
本文提出一套大模型本地部署终端选型方法论:定约束、定档位、定框架、定参数四步决策法,配合入门、主力、质量、无损四档分层模型。以 Qwen3.8-27B 实测数据为例,逐环节解读显存、带宽、存储、散热、系统、预算等要素,给出面向不同预算的优选方案、决策自查清单与市场观察框架。文末前瞻 AI 笔记本的 CPU+GPU 与统一内存两条路线,论证四步决策法在新品类上的延续性。
|
9天前
|
人工智能 JSON 自然语言处理
2026 年 Jev 决策模型深度拆解:原理解读、实战测评与保姆级落地教程
有一款特殊AI模型在开发者圈子刷屏,它摒弃传统大模型擅长的对话聊天能力,专注做高速结构化决策,它就是TypeSafe AI推出的Jev模型。该模型由ChatGPT共同发明人Diogo Almeida主导研发,定位为**System One Model(系统一模型)**,对标人类大脑快速直觉判断的思维模式,在响应延迟、调用成本、结构化输出稳定性上相比传统生成式大模型有着巨大差异。本文会完整拆解Jev底层原理、三大核心原语能力、适用业务场景,同时提供可直接运行的curl、Python代码示例,并且结合多组实测数据,客观分析模型优势与能力边界,帮助普通开发者和AI应用从业者快速上手落地。
716 1
|
5天前
|
人工智能 JSON 自然语言处理
2026 年 Jev 决策模型全解析:核心原理拆解,性能实战测评与落地部署教程
有一款特殊AI模型在开发者圈子刷屏,它摒弃传统大模型擅长的对话聊天能力,专注做高速结构化决策,它就是TypeSafe AI推出的Jev模型。该模型由ChatGPT共同发明人Diogo Almeida主导研发,定位为System One Model(系统一模型),对标人类大脑快速直觉判断的思维模式,在响应延迟、调用成本、结构化输出稳定性上相比传统生成式大模型有着巨大差异。本文会完整拆解Jev底层原理、三大核心原语能力、适用业务场景,同时提供可直接运行的curl、Python代码示例,并且结合多组实测数据,客观分析模型优势与能力边界,帮助普通开发者和AI应用从业者快速上手落地。
467 0
|
22天前
|
人工智能 测试技术 API
最近全网爆火的 Jev 到底是什么?适合干什么、怎么用,一篇讲透!
Jev是TypeSafe AI推出的“系统一模型”,不生成文本,专做毫秒级结构化决策:Choice(多选)、Score(打分)、Noul(是非概率)。响应快193倍、成本低444倍,适合工单路由、内容审核、测试定级等高频判断场景。
2546 4
最近全网爆火的 Jev 到底是什么?适合干什么、怎么用,一篇讲透!
|
4天前
|
存储 缓存 测试技术
QWT 3D 模块重构记:旧渲染管线改进介绍及实时瀑布图示例
QWT 3D模块完成重大重构:解耦窗口与图形(Plot+Item),摒弃OpenGL 1.x固定管线,迁至Core Profile着色器渲染;支持多图元同场景叠加,并以实时3D瀑布图验证——滑动窗口、固定色域、64帧×256点达61fps。兼容Qt 5.12+/6,LGPL协议。
177 120
|
16天前
|
Windows
【保姆级】.NET Framework 3.5离线安装教程(2026最新实测)
.NET Framework 3.5是微软经典运行库,专为兼容老软件、老游戏设计。Win7自带,Win10/11需手动启用(在线或离线安装)。它与4.x系列并存不冲突,缺它会导致“找不到组件”报错。本文详解两种安装方式及常见问题解决,小白也能轻松上手。(239字)
2080 0