帮助中心

服务器、网络和账户的分步教程。

Linux 服务器

512MB / 1GB 小内存 Linux VPS 优化:zram、Swap、journald 限制与精简系统服务

512MB / 1GB 小内存 Linux VPS 优化:zram、Swap、journald 限制与精简系统服务

想让 512 MB 或 1 GB 内存的 Linux VPS 跑得顺畅,请先测量内存到底被谁占用,再在小 swap 文件之上加一层内存压缩交换(zram),限制 systemd 日志的大小,并关闭服务器用不到的服务。本文在 Debian 12/13 与 Ubuntu 22.04/24.04 上逐步演示,标注 Rocky Linux 与 AlmaLinux 的差异,并说明内存仍然耗尽时如何解读 OOM killer 的日志。

JUSTG 云服务器 mini 套餐为 512 MB 内存、10 GB SSD,01 套餐为 1 GB 内存、20 GB SSD。硬盘容量不大,swap 和日志也要控制在合理范围。以下命令请以 root 执行。

步骤 1:先测量,再动手

先找出真正占用内存的进程,才能对症下药。

free -h
ps -eo pid,user,rss,comm --sort=-rss | head -n 15   # RSS in KB, biggest first
systemd-cgtop -m -n 1                                # memory per service
cat /proc/pressure/memory                            # memory pressure (PSI)

ps 列出占用最大的进程,systemd-cgtop 则按服务汇总内存,当一个服务有很多工作进程时更直观。/proc/pressure/memory 中的 avg60 如果长期高于 10 左右,说明进程经常在等待内存。(Rocky/AlmaLinux 默认关闭 PSI,需要以 psi=1 内核参数启动才能使用。)

RSS 会把共享库完整地算进每个进程,所以加总后看起来比实际更多。smem 显示的 PSS 会把共享内存合理地分摊到各个进程:

apt install smem            # Rocky / AlmaLinux: from EPEL
smem -tk -s pss -r | head -n 15

步骤 2:正确解读 free -h:看 available,而不是 free

               total        used        free      shared  buff/cache   available
Mem:           961Mi       402Mi        74Mi       5.0Mi       484Mi       410Mi
Swap:          1.0Gi        12Mi       1.0Gi

free 数值很小是正常现象。Linux 会把空闲内存用作页面缓存(buff/cache)来缓存文件,程序需要时会立即释放。真正要看的是 available:估算新任务在不使用 swap 的情况下还能拿到多少内存。上例中“空闲”只有 74 MB,但可用内存有 410 MB,服务器完全正常。只有当 available 长期低于内存的 10% 左右、swap 用量又持续上升时,才需要担心。

步骤 3:启用 zram(内存压缩交换)

zram 会在内存中建立一个交换设备,以压缩形式存放内存页,体积通常只有原来的 1/2 到 1/3。交换到 zram 比交换到硬盘快得多,是给小套餐“扩容”非常有效的方法,代价是压缩需要少量 CPU。

确认内核支持 zram

modprobe zram && echo "zram is available"
swapon --show

如果 modprobe 提示 Module zram not found,说明内核编译时没有包含 zram(部分精简版云内核如此)。请直接跳到步骤 4,只使用 swap 文件。

方式 A:zram-tools(Debian、Ubuntu 22.04 与 24.04)

apt install zram-tools
cp /etc/default/zramswap /etc/default/zramswap.bak
cat > /etc/default/zramswap <<'EOF'
ALGO=zstd
PERCENT=50
PRIORITY=100
EOF
systemctl restart zramswap
zramctl
swapon --show

PERCENT=50 把 zram 设备大小设为内存的一半(指未压缩的容量),实际占用的内存是压缩后的大小。PRIORITY=100 让内核优先使用 zram,再使用硬盘上的 swap。

方式 B:systemd zram-generator(Debian 12/13、Ubuntu 24.04、Rocky/AlmaLinux 9)

apt install systemd-zram-generator      # Rocky / AlmaLinux 9: dnf install zram-generator
cat > /etc/systemd/zram-generator.conf <<'EOF'
[zram0]
zram-size = min(ram / 2, 2048)
compression-algorithm = zstd
swap-priority = 100
EOF
systemctl daemon-reload
systemctl start [email protected]
zramctl

在 Rocky/AlmaLinux 上,如果 dnf 找不到该软件包,请先启用 EPEL(dnf install epel-release)。

两种方式只能选一种:同时使用 zram-tools 和 zram-generator 会创建互相冲突的设备。如果系统不接受 zstd,请改用 lz4,它在负载较高的单核套餐上也更快。

步骤 4:保留小 swap 文件作后备,并调整 swappiness

zram 位于内存中,所以硬盘上的小 swap 文件仍有价值,可作为最后的后备:这几档套餐用 512 MB–1 GB 就够了。创建方法、写入 fstab 和调整大小,请按Linux VPS 添加 Swap 交换空间教程逐步操作。swap 文件的默认优先级低于 zram 设备,只有 zram 用满后才会用到它。

启用 zram 后,请调高 vm.swappiness:把内存页压缩换进 zram,比丢弃文件缓存再从硬盘重新读取更划算。vm.page-cluster = 0 让内核不再一次预读多个 swap 页,这种预读只对硬盘有帮助。

cat > /etc/sysctl.d/99-zram.conf <<'EOF'
vm.swappiness = 100
vm.page-cluster = 0
EOF
sysctl --system
sysctl vm.swappiness vm.vfs_cache_pressure

/etc/sysctl.d/ 下的文件按字母顺序读取,因此 99-zram.conf 会覆盖 swap 教程中 99-swap.conf 设置的 vm.swappiness=10。没有 zram 时请保持 10。该教程中的 vm.vfs_cache_pressure=50 可以保留;但千万不要设为 0,否则内核将永远不回收文件系统元数据。

步骤 5:限制 journald 日志

systemd 日志存放在硬盘的 /var/log/journal;如果日志不是持久化存储,则存放在 /run/log/journal。/run 是 tmpfs,也就是说这份日志占用的是内存。请同时限制两者:

journalctl --disk-usage
mkdir -p /etc/systemd/journald.conf.d
cat > /etc/systemd/journald.conf.d/size.conf <<'EOF'
[Journal]
SystemMaxUse=100M
RuntimeMaxUse=20M
MaxRetentionSec=1month
EOF
systemctl restart systemd-journald
journalctl --vacuum-size=100M
journalctl --disk-usage

SystemMaxUse 限制硬盘上的日志,RuntimeMaxUse 限制内存中的日志,MaxRetentionSec 会删除超过一个月的记录。Ubuntu 上的 rsyslog 还会把同样的消息写入 /var/log/syslog;该文件由 logrotate 轮转,一般保留即可。

步骤 6:关闭服务器用不到的服务

停用任何服务之前,先看清楚正在运行什么,并且只关闭你能确认用途的服务:

systemctl list-units --type=service --state=running
systemctl status multipathd --no-pager                  # what is it and what started it?
systemctl list-dependencies --reverse multipathd.service  # does anything need it?
服务常见于可以关闭的情况
snapdUbuntusnap list 中没有你在用的 snap(见下文)
multipathdUbuntu Servermultipath -ll 没有输出,VPS 上通常如此
ModemManager部分 Ubuntu 镜像VPS 上一律可以关闭:根本没有调制解调器
tunedRocky / AlmaLinux可选,能省下少量内存;拿不准就保留
# Ubuntu: multipath storage daemon (not needed without SAN/multipath disks)
multipath -ll                       # no output = not in use
systemctl disable --now multipathd.service multipathd.socket

# Modem manager - a VPS has no modem
systemctl disable --now ModemManager

# Undo at any time:
systemctl enable --now multipathd.service multipathd.socket

snapd 会占用几十 MB 内存,并为每个 snap 挂载一个 loop 设备。卸载它会同时删除所有已安装的 snap 及其数据,所以请先检查 snap list。例如 certbot 是用 snap 安装的,请先改用 apt 安装 certbot,再卸载 snapd。pin 文件可以防止 apt 以后又把 snapd 当作依赖装回来。

snap list                           # what is installed as a snap?
apt purge snapd
cat > /etc/apt/preferences.d/no-snapd.pref <<'EOF'
Package: snapd
Pin: release a=*
Pin-Priority: -10
EOF
以下服务不要关闭:ssh/sshd、systemd 自身的服务(systemd-journald、systemd-logind、systemd-networkd、systemd-resolved)、networking 或 NetworkManager、cron、时间同步(chrony 或 systemd-timesyncd)以及防火墙(ufw、firewalld、nftables)。如果装有 qemu-guest-agent 和 cloud-init,也请保留:平台可能依赖它们,而且服务器启动后它们几乎不占内存。请用 disable 而不是 mask,方便日后重新启用。

步骤 7:把自动更新安排在低峰时段

不要关闭 unattended-upgrades:安全更新比几 MB 内存重要得多。内存高峰来自 apt 运行本身,所以把它安排到空闲时段即可。默认情况下,apt 会在 12 小时窗口内的随机时间下载更新,并在约 06:00–07:00 之间安装;以下 drop-in 配置把下载改到服务器时间 04:00、安装改到 04:30(可用 timedatectl 查看服务器时区):

mkdir -p /etc/systemd/system/apt-daily.timer.d /etc/systemd/system/apt-daily-upgrade.timer.d
cat > /etc/systemd/system/apt-daily.timer.d/override.conf <<'EOF'
[Timer]
OnCalendar=
OnCalendar=*-*-* 04:00
RandomizedDelaySec=15m
EOF
cat > /etc/systemd/system/apt-daily-upgrade.timer.d/override.conf <<'EOF'
[Timer]
OnCalendar=
OnCalendar=*-*-* 04:30
RandomizedDelaySec=15m
EOF
systemctl daemon-reload
systemctl list-timers 'apt-*'

Rocky/AlmaLinux 如果使用 dnf-automatic,请用同样的方法修改 dnf-automatic.timer。

步骤 8:选择更轻量的软件

  • 保留 OpenSSH。它空闲时只占几 MB。Dropbear 等替代品省不了多少内存,缺少部分安全功能,发行版的更新也更少,不值得冒这个险。
  • 优先使用 nginx,而不是 Apache + mod_php。在 Apache 的 prefork 模式下,每个子进程都带着完整的 PHP 解释器。nginx + PHP-FPM,或至少 Apache 的 mpm_event + PHP-FPM,内存占用要少得多。
  • PHP-FPM 使用 pm = ondemand。有请求时才启动 worker,空闲一段时间后自动退出,而不是一直驻留在内存里。
  • 按套餐调整数据库。MySQL 与 MariaDB 的默认值是按更大内存设计的:请调小缓冲池和 max_connections,并关闭 performance_schema。
; /etc/php/8.2/fpm/pool.d/www.conf  (Rocky / AlmaLinux: /etc/php-fpm.d/www.conf)
pm = ondemand
pm.max_children = 5
pm.process_idle_timeout = 10s
pm.max_requests = 500

路径中的 PHP 版本因系统而异:Debian 12 为 8.2,Debian 13 为 8.4,Ubuntu 22.04 为 8.1,Ubuntu 24.04 为 8.3。pm.max_children 的取值是能分给 PHP 的内存除以单个 worker 的平均大小,例如 200 MB / 40 MB = 5。先测量大小,再检查配置并重新加载:

# Average size of a PHP-FPM process
ps -eo rss,comm | awk '/php-fpm/ {s+=$1; n++} END {if (n) printf "%d processes, average %.0f MB\n", n, s/n/1024}'
php-fpm8.2 -t && systemctl reload php8.2-fpm    # Rocky / AlmaLinux: php-fpm -t && systemctl reload php-fpm

步骤 9:了解 OOM killer 并找出 OOM 事件

内存和 swap 都用尽时,内核的 OOM killer 会终止一个进程来保住整个系统,通常是 oom_score 最高、一般也就是占用最大的那个。服务“莫名其妙”地重启、数据库突然消失,往往都是被 OOM 杀掉了。请在内核日志中搜索:

journalctl -k | grep -i -E 'out of memory|oom-kill|killed process'
dmesg -T | grep -i -E 'out of memory|killed process'
journalctl -k -b -1 | grep -i 'killed process'     # previous boot (persistent journal only)

典型的日志如下:

Out of memory: Killed process 812 (mariadbd) total-vm:1325400kB, anon-rss:301220kB, file-rss:0kB, shmem-rss:0kB, UID:106

其中写明了进程名称和它当时占用的内存(anon-rss,单位 kB)。如果同一个服务反复被杀,请调小它的配置、添加 swap 或 zram,或按步骤 10 为它设置上限。在 Ubuntu 上还请检查 systemctl status systemd-oomd:该服务处于运行状态时,可能会在内核出手之前,因内存压力直接停止整个服务。

步骤 10:用 systemd MemoryMax 限制吃内存的服务

给单个服务设置内存上限,可以保护服务器上的其他服务:如果 PHP-FPM 发生内存泄漏,被限制的只有 PHP-FPM,SSH 和数据库照常运行。MemoryHigh 是软限制,超过后内核会让该服务变慢并回收其内存;MemoryMax 是硬限制,达到后 OOM killer 只在该服务内部动手。

stat -fc %T /sys/fs/cgroup          # cgroup2fs = cgroup v2 (required)
mkdir -p /etc/systemd/system/php8.2-fpm.service.d
cat > /etc/systemd/system/php8.2-fpm.service.d/memory.conf <<'EOF'
[Service]
MemoryHigh=250M
MemoryMax=300M
# A killed worker is replaced by the master instead of stopping the service
OOMPolicy=continue
Restart=on-failure
EOF
systemctl daemon-reload
systemctl restart php8.2-fpm
systemctl show php8.2-fpm -p MemoryHigh -p MemoryMax -p MemoryCurrent

Debian 12/13、Ubuntu 22.04/24.04 与 Rocky/AlmaLinux 9 默认使用 cgroup v2。在 Rocky/AlmaLinux 8 等使用 cgroup v1 的旧系统上,请改用 MemoryLimit=;MemoryHigh 只在 v2 下有效。不要给数据库本身设置硬上限:突然被杀会导致崩溃恢复,应该调整它自身的参数。

步骤 11:判断何时该升级套餐

优化适用于偶发的内存高峰。如果 available 一直很低、swap 始终处于繁忙状态,vmstat 1 10 显示持续的换入换出(si/so),说明业务本身就需要更多内存。JUSTG 云服务器可以在服务页面升级套餐,详见VPS 升级内存、SSD 硬盘或 IP 教程。

常见问题

zram 比 swap 文件更好吗?

速度上是的:它以内存速度工作,不产生硬盘 I/O。但它受内存大小限制,所以小套餐的最佳方案是两者并用:高优先级的 zram 负责日常交换,小 swap 文件作为最后的后备。

zram 会占用很多 CPU 吗?

压缩需要一些 CPU,通常很少。如果单核套餐上的服务器一直在往 zram 交换,请改用 lz4 并减轻负载,因为持续交换说明内存确实不够。

512 MB 的 mini 套餐能跑 Docker 吗?

跑一两个小应用的容器可以,但 Docker 守护进程和容器运行时本身也要占用内存。请为每个容器设置内存上限,同时启用 zram 和 swap;容器较多时,建议选择 1 GB 及以上的套餐。

如果服务仍不断被杀、又找不到原因,请至 https://www.justg.com/submitticket.php 提交工单,并附上服务器 IP 以及 free -h 和 journalctl -k | grep -i oom 的输出。JUSTG 提供 7×24 小时 AI 客服(工单),需要人工处理的问题会由工程师跟进。

这篇文章有帮助吗?
帮助中心
Linux 服务器 Linux SSH 密钥登录教程:设置 ed25519 密钥并禁用密码登录 只要服务器有公网 IPv4,上线几分钟内就会遇到 SSH 暴力破解机器人,而密码登录正是它们的首要目标。本文说明如何用较新的 ed25519 密钥完成 Linux SSH 密钥登录,再禁用 SSH 密码登录,全程避免把自己锁在门外。步骤适用于 Debian… Linux 服务器 Linux VPS 修改 SSH 端口与 root 密码教程(安全不断线) 把 SSH 从 22 端口移走虽然挡不住有心的攻击者,却能让日志里大部分自动扫描消失;而任何新服务器的第一件事,就是换一组新的 root 密码。本文说明如何在 Linux VPS 修改 SSH 端口与 root 密码且不断线:修改 sshd_config、在 Rocky… Linux 服务器 Linux 防火墙设置教程:VPS 使用 UFW 与 firewalld(含 IPv6) 新安装的 Linux 服务器,任何服务打开的端口都会直接对外响应,因此主机防火墙是最先要设置的项目之一。本文说明如何在 Debian / Ubuntu 用 UFW 设置 Linux 防火墙,以及在 CentOS、Rocky Linux、AlmaLinux 使用… Linux 服务器 Linux VPS 添加 Swap 交换空间教程(适合 512MB / 1GB 小内存套餐) 在 512 MB 或 1 GB 的云服务器上,数据库、PHP-FPM 加上一次软件包更新,很容易在同一时间用光内存。这时内核的 OOM killer 会强制终止进程,最常被牺牲的就是 MySQL 或 MariaDB。在 Linux VPS 添加 Swap… Linux 服务器 Linux 硬盘空间已满怎么办?VPS 找出并释放磁盘空间教程 “No space left on device”几乎会让服务器上所有东西出问题:数据库无法写入、网站返回错误、软件包更新失败,有时甚至无法正常登录。本文一步步说明如何排除 Linux 硬盘空间已满:先确认是数据块还是 inode 用完,再用 du 与 ncdu…
AlipayUnionPayVISAMastercardPayPalUSDTstripe