开篇:一场与“死机”的拉锯战
事情的起因很简单:我在谷歌云白嫖了一台 1核1G 的免费 VPS,美滋滋地装上了宝塔面板,又顺手用 Docker 跑了个 OpenList 应用。
结果噩梦开始了——服务器开始频繁死机。
具体表现是:
-
服务器刚重启没多久就“运行堵塞”
-
SSH 连上一小会儿就无响应
-
必须强制重启才能再次连接
-
重启后又迅速进入死循环……
如果你也正在经历类似的痛苦,希望这篇记录能帮你彻底走出来。
第一阶段:那个稍纵即逝的“半分钟窗口”—运行堵塞 — CPU 不高、内存没满,服务器却卡到 SSH 都连不上 但是并不是网络问题
最开始的现象非常诡异。服务器并不是立刻死机,而是 “运行堵塞”——刚重启完,SSH 能连上,宝塔能打开,看起来一切正常。但用不了几十秒,系统就逐渐“僵住”了:
- 宝塔页面开始转圈,再也打不开
- SSH 敲一个命令要等十几秒才有反应
- 再过一会儿,SSH 直接无响应,断开了
内存没满其实是表面现象 实际上服务器的进程很多开机后半分钟后会吃掉大量内存导致死机
而且这台机器没有开启虚拟内存
第二阶段:抓住那“半分钟的黄金窗口”
想开 Swap 就得 SSH 连上去操作,可问题是——刚重启没多久服务器就死,SSH 一直断。
试了好几次后,我发现了一个希望:重启后大约有 30 秒左右,系统还算干净,能连上 SSH。
这 30 秒就是全部的操作窗口。
先给 Docker 踩下急刹车
连上后第一时间:
# 停止所有正在运行的容器
docker stop $(docker ps -q)
# 停止 Docker 服务本身
sudo systemctl stop docker
sudo systemctl stop docker.socket
# 把所有容器的重启策略改成 no(禁止开机自启)
docker update --restart=no $(docker ps -aq)
这一步是为了立刻止血——先让 Docker 别再抢资源。
然后,创建 Swap(最重要的一步)
但直接创建一个 3GB 的 Swap,dd 写到一半 I/O 又炸了。所以后来我学聪明了:重启后趁系统干净,一口气完成。
sudo dd if=/dev/zero of=/swapfile bs=1M count=3072 status=progress && \
sudo chmod 600 /swapfile && \
sudo mkswap /swapfile && \
sudo swapon /swapfile && \
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
如果创建虚拟内存的时候卡死 直接重启机器
重启vps 硬盘会暂时处于闲置状态 性能可以完全发挥 3G的虚拟内存很开就DD好了
刚重启的系统内存充足、I/O 空闲,dd 速度能跑满磁盘性能(几十 MB/s),几分钟就完成了。
参数解释:
-
dd:创建 3GB 的空白文件 -
chmod 600:设置安全权限 -
mkswap:格式化为 Swap 格式 -
swapon:启用 Swap -
最后一行写入
/etc/fstab:保证重启后依然生效
第三阶段:发现真正的“幕后黑手”
Swap 创建好了,但奇怪的是——Swap 一直没人用!
free -h
Swap: 3.0Gi, 0B used, 3.0Gi free
查了一圈才发现问题所在:
cat /proc/sys/vm/swappiness
# 返回:0
vm.swappiness=0 的意思是:哪怕物理内存要爆了,也别把数据往 Swap 里放,优先杀进程。
这就是为什么明明有 Swap,系统还是直接 OOM 杀进程。
立即修正 swappiness-具体操作(选一个):
# 设为 100(最激进保命)
sudo sysctl vm.swappiness=100
echo "vm.swappiness=100" | sudo tee /etc/sysctl.d/99-swap.conf
# 或者设为 80(折中)
sudo sysctl vm.swappiness=80
echo "vm.swappiness=80" | sudo tee /etc/sysctl.d/99-swap.conf
对于 1H1G 的 VPS 来说,swappiness 推荐值:
| 设置值 | 行为 | 适合场景 |
|---|---|---|
| 0 | 几乎不用 Swap,优先杀进程(OOM) | ❌ 极不适合小内存 VPS,会导致服务被杀、系统卡死 |
| 60(默认) | 内存紧张时才开始换出,效率优先 | ✅ 物理内存 ≤2G 的通用场景,较平衡 |
| 80 | 较积极使用 Swap,减轻物理内存压力 | ✅ 1G VPS 运行较多服务(宝塔、Docker、MySQL 等) |
| 100 | 最积极使用 Swap,极力避免 OOM | ✅ 内存极限环境,优先保证进程存活而非性能 |
设成 100 后,Swap 终于开始被使用了,系统也不再动不动就 OOM 了。
为什么建议 80~100?
-
1G 内存太小,宝塔面板、Docker、MySQL 等加起来很容易逼近 800MB,剩余可用内存仅 100MB 左右。
-
此时若
swappiness=0或60,系统会优先通过杀进程(OOM)来释放内存,SSH、宝塔、容器随时可能被随机杀死。 -
设为 80 或 100 后,内核会提前将后台不活跃进程的数据换到 Swap,把宝贵的物理内存留给前台服务,避免 OOM。代价是磁盘 I/O 会稍微增加,但总比服务器“假死”或关键进程被杀要好。
怎么查看cat /proc/sys/vm/swappiness是不是永久有效 重启也有效了?
第一步:写入高优先级配置文件
echo "vm.swappiness=100" | sudo tee /etc/sysctl.d/99-swap.conf
第二步:立刻加载并验证
sudo sysctl --system # 加载所有 sysctl 配置
cat /proc/sys/vm/swappiness # 确认输出为 100
第三步:模拟重启环境,检查配置是否仍会生效
sudo sysctl --dry-run --system 2>&1 | grep swappiness
第四步:检查是否有“幕后黑手”会在开机时改回 0
某些谷歌云、阿里云的优化脚本可能强制将 swappiness 设为 0。你需要检索可能的启动服务:
第五步:终极验证:直接重启一次
如果显示 100,就永久生效了。
如果你的 swappiness 是 0,请立刻执行第一步和第二步,把它改成 100。 这样 Swap 就能开始工作,SSH 掉线、卡死的问题会明显改善。
第四阶段:给 Docker 戴上紧箍咒
虽然 Swap 兜底了,但 Docker 容器仍然会在物理内存里横冲直撞。必须给它们加上硬性资源限制。
方法一:对已存在的容器动态限制
# 批量限制所有容器:最多 0.3 核、150MB 内存
docker update --cpus 0.3 --memory 150m --memory-swap 200m $(docker ps -aq)
验证是否生效:
docker stats --no-stream
看 LIMIT 列,会显示你设定的上限值。
方法二:设置全局默认限制(防止以后忘记)
编辑 /etc/docker/daemon.json:
{
"default-cpus": 0.3,
"default-memory": "150m",
"default-memory-swap": "200m"
}
然后重启 Docker: