Ubuntu 20.04 swapfile 配置与调优实战指南

1. 为什么 Ubuntu 20.04 用户突然开始关心“подкачка”——一个被低估的系统稳定性开关

“подкачка”是俄语中“交换空间”(swap)的直译,这个词本身不重要,但它背后指向的,是 Ubuntu 20.04 系统在内存吃紧时能否不卡死、不崩溃、不丢数据的关键机制。我第一次在客户现场遇到这个问题,是在一台 4GB 内存的旧笔记本上部署 Python 数据分析环境:跑着 Jupyter Notebook、加载 Pandas 处理 50 万行 CSV、后台还开着 VS Code 和 Chrome —— 系统突然完全无响应,鼠标不动、键盘失灵、连 Ctrl+Alt+F2 切终端都失败。强制重启后查日志,dmesg里赫然一行:Out of memory: Kill process 1234 (python3) score 897 or sacrifice child。这不是程序 bug,是内核在绝望中亲手杀掉了最占内存的进程。而这个悲剧,本可以通过正确配置 swap 区域,在内存真正耗尽前就启动缓冲机制,把暂时不用的内存页“挪”到硬盘上腾出空间,让系统有喘息之机。

Ubuntu 20.04 默认安装策略发生了关键变化:它不再为所有硬件配置自动创建传统 swap 分区,而是转向使用 swapfile(交换文件)。这个设计初衷是好的——更灵活、无需重分区、便于调整大小。但问题在于,很多用户(尤其是从 18.04 升级或从 Windows 双系统转来的)根本没意识到 swapfile 是“默认关闭”的。swapon --show命令返回空,free -h显示 swap 行全为 0,系统就像一辆没有备胎的车,高速行驶时爆胎就是瞬间失控。更隐蔽的是,swappiness这个参数,它不是“开/关”开关,而是一个 0–100 的调节阀,控制内核多“积极”地把内存页移到 swap。Ubuntu 20.04 默认值是 60,对桌面用户来说偏高,容易导致 SSD 频繁写入;但设成 0 又可能在内存临界点时失去缓冲,直接触发 OOM Killer。这中间的平衡点,需要你亲手去调校,而不是依赖默认值。

关键词swaponmkswapswappiness,它们不是孤立的命令,而是一套协同工作的“内存压力管理三件套”。mkswap负责格式化一块磁盘空间,赋予它“能当 swap 用”的身份;swapon是启动开关,告诉内核“这块区域现在可以用了”;swappiness则是精细油门,决定内核在多大程度上依赖这个备用空间。忽略其中任何一个,swap 就只是硬盘上一块沉默的“死地”。我见过太多人执行完swapon /swapfile后以为万事大吉,结果几天后发现 swapfile 被写满了,系统又开始卡顿——因为没设置vm.swappiness=10,也没加vm.vfs_cache_pressure=50来优化缓存行为。所以,这篇文章不是教你敲几条命令,而是带你理解:swap 在 Ubuntu 20.04 里,到底是一个被动的“安全气囊”,还是一个主动的“内存协处理器”?答案取决于你怎么配置它。

2. 两种实现路径的硬核对比:swap 分区 vs swapfile,为什么 20.04 选了后者

在深入操作前,必须厘清一个根本性选择:你是要创建一个独立的 swap 分区,还是一个 swapfile?这绝非“哪个更简单”的问题,而是涉及系统架构、可维护性、硬件兼容性和未来升级路径的深层决策。我曾为一家做嵌入式开发的客户同时部署过两种方案,最终 swapfile 成为唯一被保留的选项,原因非常具体。

2.1 swap 分区:传统、稳定,但“动刀”风险高

swap 分区是 Linux 最古老的方式,它要求你在安装系统时就预留一块未格式化的磁盘空间(通常建议大小为物理内存的 1–2 倍),并将其类型标记为82(Linux swap)。它的优势在于极致的性能和稳定性:内核可以直接以块设备方式读写,没有文件系统层的开销,延迟最低。对于运行数据库或实时计算的服务器,这仍是首选。

但问题在于“不可变性”。在 Ubuntu 20.04 上,一旦系统已安装完毕,想新增 swap 分区,你必须:

  1. 使用gpartedfdisk缩小现有根分区(如/),这一步本身就有数据丢失风险;
  2. 在腾出的空间上创建新分区,并用mkswap格式化;
  3. 修改/etc/fstab,添加新分区的挂载项;
  4. 执行swapon激活。

整个过程需要重启或至少卸载根分区,对生产环境是不可接受的。更麻烦的是,如果客户用的是 NVMe SSD + LVM 加密卷,分区操作会变得异常复杂,稍有不慎就导致系统无法启动。我亲眼见过一位同事在为客户扩容 swap 时,误操作将 LVM 的物理卷元数据覆盖,最终花了 8 小时才从备份恢复。

2.2 swapfile:Ubuntu 20.04 的默认答案,灵活但需精细呵护

swapfile 的核心思想是“用文件模拟分区”。它本质上就是一个普通文件(如/swapfile),通过fallocatedd创建,再用mkswap标记其 swap 属性。它的革命性优势在于零侵入性:不需要动任何分区表,不依赖特定磁盘布局,甚至可以在不同文件系统(ext4、xfs、btrfs)上创建。Ubuntu 20.04 安装器默认创建的/swapfile就是这种思路的体现。

然而,“灵活”背后是“脆弱”。swapfile 的性能受文件系统影响极大。例如,在 btrfs 文件系统上,如果启用了压缩(compress=zstd),swapon会直接失败,报错swapon: /swapfile: swapon failed: Invalid argument,因为内核不允许在压缩文件上启用 swap。另一个致命陷阱是文件碎片:如果/swapfile在磁盘上是高度碎片化的,swap I/O 性能会断崖式下跌。我测试过,在一块 7200RPM 机械硬盘上,一个碎片率 40% 的 4GB swapfile,其随机读写延迟比连续分配的高出 3 倍以上。

对比维度swap 分区swapfile(Ubuntu 20.04 推荐)
创建难度高(需分区操作,风险大)低(纯命令行,无损)
调整大小极难(需重新分区)极易(sudo swapoff /swapfilesudo fallocatesudo mkswapsudo swapon
性能上限最高(直接块设备访问)高(但受文件系统和碎片影响)
SSD 友好度中等(固定位置,磨损集中)高(可配合fstrim定期优化)
LVM/加密支持复杂(需在 LVM 层创建逻辑卷)完美(swapfile 位于加密卷内,透明)
故障排查简单(blkid查分区,swapon -s复杂(需检查文件权限、SELinux、碎片、压缩)

提示:Ubuntu 20.04 的官方文档明确指出,对于桌面用户和大多数云实例,swapfile 是首选方案。它与 systemd 的集成也更自然——systemd-swap服务可以自动管理多个 swapfile,而 swap 分区只能由/etc/fstab静态定义。

2.3 一个被忽视的第三种方案:zram,内存中的“伪 swap”

在讨论完主流方案后,必须提一个 Ubuntu 20.04 原生支持但常被忽略的黑科技:zram。它不使用硬盘,而是将一部分 RAM 划出来,用 LZO 或 LZ4 算法进行实时压缩,然后作为 swap 使用。这意味着,一个 4GB 的 zram 设备,实际能提供远超 4GB 的“逻辑 swap 空间”,因为数据是压缩存储的。

我曾在一台只有 2GB 内存的老旧 Atom 笔记本上部署 zram,效果惊人:free -h显示 swap 为 1.5G,但zramctl显示其disksize为 1.5G,而data(实际压缩后数据量)仅为 320MB。这意味着,内核把 1.5G 的内存页压缩后,只占用了 320MB 的真实 RAM。这完美规避了 SSD 写入磨损,且速度比任何硬盘 swap 快 10 倍以上。Ubuntu 20.04 的systemd-zram-generator默认已启用,只需确保/etc/systemd/zram-generator.conf存在并配置正确。不过,zram 不是万能药:它会增加 CPU 开销(压缩/解压),在 CPU 已经满载时可能适得其反。因此,我的建议是:优先配置 swapfile 作为基础保障,再根据 CPU 负载情况,用 zram 作为性能加速层。两者可以共存,swapon --show会同时列出它们,内核会按优先级(priority)自动调度。

3. 从零构建一个健壮的 swapfile:每一步背后的“为什么”和实操细节

现在,我们进入最核心的实操环节。目标是创建一个 4GB 的 swapfile,它能稳定工作、避免常见陷阱,并为后续调优打下基础。注意,这不是一个“复制粘贴就能用”的脚本,而是每一步都解释其原理和风险的深度指南。我将用一台全新的 Ubuntu 20.04 虚拟机演示,全程记录所有关键检查点。

3.1 第一步:确认磁盘空间与文件系统兼容性——别在错误的地基上盖楼

在创建任何文件前,必须先确认/分区是否有足够空间,以及其文件系统是否支持 swapfile。这是最容易被跳过的步骤,却会导致后续所有操作失败。

首先,检查可用空间:

df -h /

输出类似:

Filesystem Size Used Avail Use% Mounted on /dev/sda1 50G 12G 36G 25% /

Avail(可用)一栏必须大于你计划创建的 swapfile 大小(例如 4G)。如果只剩 2G,强行创建会导致根分区写满,系统彻底瘫痪。

其次,确认文件系统类型及特性:

lsblk -f # 或 findmnt -T /

重点关注/挂载点的 FSTYPE(如ext4)和 OPTIONS。对于 ext4,你需要确保它没有启用dax(Direct Access)模式,因为 dax 允许文件绕过 page cache 直接映射到内存,这与 swap 的工作原理冲突。检查方法:

tune2fs -l /dev/sda1 | grep "Filesystem features"

如果输出中包含dax,则不能在此分区上创建 swapfile。幸运的是,Ubuntu 20.04 默认安装不会启用 dax。

注意:如果你的系统使用的是 btrfs,必须禁用压缩。检查方法:

mount | grep " / " # 如果输出包含 "compress=zstd" 或类似,需临时 remount: sudo mount -o remount,compress=none /

3.2 第二步:创建 swapfile——fallocate为何比dd更优?

创建一个 4GB 的文件,有两种主流方法:ddfallocate。很多人习惯用dd if=/dev/zero of=/swapfile bs=1G count=4,但这存在严重缺陷。

dd的工作原理是:从/dev/zero读取 4GB 的零字节流,并逐块写入/swapfile。这个过程会:

  • 触发真实的磁盘 I/O,耗时长(在 HDD 上可能需数分钟);
  • 在文件系统层面,它会为每个块分配实际的磁盘扇区,即使内容全是零;
  • 如果写入中途被中断(如Ctrl+C),会留下一个不完整的、损坏的文件。

fallocate是 Linux 3.15+ 引入的系统调用,它直接在文件系统元数据层“预留”空间,不进行任何实际数据写入。命令极其简洁:

sudo fallocate -l 4G /swapfile

它在毫秒级内完成,且生成的文件是“稀疏文件”(sparse file),即文件系统知道这块空间已被占用,但物理磁盘上并未写入任何数据。这对 SSD 尤其友好,避免了不必要的写入放大。

验证创建是否成功:

ls -lh /swapfile # 应输出:-rw------- 1 root root 4.0G ... /swapfile

如果显示大小为 0,说明fallocate失败,此时应改用dd作为备选:

sudo dd if=/dev/zero of=/swapfile bs=1G count=4 status=progress sudo chmod 600 /swapfile

3.3 第三步:格式化与激活——mkswap的隐藏参数和swapon的权限陷阱

mkswap不仅仅是“格式化”,它还在文件头部写入一个魔数(magic number)和校验信息,让内核能识别这是一个合法的 swap 区域。标准命令是:

sudo mkswap /swapfile

但这里有一个关键细节:mkswap默认会为 swapfile 设置一个“优先级”(priority),值为-1。优先级决定了当系统有多个 swap 区域(如一个 swap 分区 + 一个 swapfile)时,内核先用哪个。数值越大,优先级越高。对于单一 swapfile,-1没问题;但如果你想让它比其他 swap 区域更高,可以显式指定:

sudo mkswap -p 10 /swapfile

接下来是swapon。这是最常出错的一步。执行:

sudo swapon /swapfile

如果报错swapon: /swapfile: swapon failed: Operation not permitted,不要慌。这几乎 100% 是因为/swapfile的权限设置错误。swapon要求该文件必须是 root 所有,且权限严格为 600(即-rw-------。任何其他权限(如 644、755)都会被拒绝,这是内核的安全策略,防止非特权用户篡改 swap 内容。

修复方法:

sudo chown root:root /swapfile sudo chmod 600 /swapfile sudo swapon /swapfile

最后,验证是否生效:

swapon --show # 输出应包含:/swapfile file 4194300 0 -2 free -h # 输出中 Swap 行应显示 total: ~4.0G, used: 0, avail: ~4.0G

3.4 第四步:永久化与开机自启——/etc/fstab的魔鬼细节

swapon命令的效果是临时的,重启后就会失效。要让它永久生效,必须写入/etc/fstab。标准写法是:

/swapfile none swap sw 0 0

但这行配置里藏着两个极易被忽略的“魔鬼”:

  1. sw参数的含义:它等价于defaults,但defaults在某些文件系统(如 xfs)上可能包含noatime,这与 swap 无关。更精确、更安全的写法是显式指定sw,它告诉swapon“这是一个 swap 类型的条目”。

  2. 最后两个0的作用:第一个0表示不进行dump备份(swap 不需要备份);第二个0表示fs_passno,即fsck检查顺序。对于 swap,它必须是0,否则系统启动时会尝试对 swapfile 进行文件系统检查,这必然失败并导致启动卡住。

我曾因一个手误,把0 0写成了0 1,结果客户服务器重启后停在fsck阶段,黑屏无响应。修复方法只能进 recovery mode,手动编辑 fstab。

提示:在修改/etc/fstab前,务必先备份:

sudo cp /etc/fstab /etc/fstab.backup-$(date +%Y%m%d)

并使用sudo mount -a测试 fstab 语法是否正确(此命令会尝试挂载所有未挂载的条目,对 swapfile 无效,但能捕获语法错误)。

4. 调优与监控:让 swap 从“能用”变成“好用”的关键参数

创建并激活 swapfile 只是万里长征第一步。真正的挑战在于让它智能、高效、不拖慢系统。Ubuntu 20.04 的默认swappiness=60对桌面用户而言,往往是个“甜蜜的陷阱”——它让内核过于积极地把内存页换出,导致 SSD 频繁写入,而这些被换出的页可能下一秒就被再次读入,形成无谓的 I/O 循环。下面,我将分享一套经过上百台机器验证的调优组合拳。

4.1vm.swappiness:不是越低越好,而是找到你的“甜点值”

swappiness的取值范围是 0–100,它并不直接控制“多少内存被换出”,而是影响内核的页面回收(page reclaim)策略。具体来说,它决定了内核在面临内存压力时,是优先回收“文件页”(如缓存的磁盘文件),还是优先回收“匿名页”(如程序堆栈、malloc 分配的内存)。匿名页无法直接写回磁盘,只能写入 swap。

  • swappiness=0:内核会极度抗拒换出匿名页,只在绝对内存耗尽(OOM)前最后一刻才行动。这听起来很安全,但代价是:文件缓存会被大量回收,导致频繁的磁盘读取(如打开文件、加载库),CPU 等待 I/O,系统整体变慢。
  • swappiness=100:内核会非常激进地换出匿名页,哪怕还有大量空闲内存。这会导致 swap 空间被快速填满,SSD 寿命缩短,且换入换出开销巨大。

那么,最佳值是多少?我的经验是:桌面用户设为 10,服务器用户设为 1–5。为什么是 10?

因为swappiness=10意味着,当系统有 90% 的内存是“干净”的(可回收的文件缓存)时,内核才会开始考虑换出匿名页。这给了文件缓存足够的空间来加速日常操作(如浏览器缓存、IDE 代码索引),同时又保留了在内存真正紧张时(如开 20 个 Chrome 标签页)的缓冲能力。调整方法:

# 临时生效(重启失效) sudo sysctl vm.swappiness=10 # 永久生效 echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.conf

4.2vm.vfs_cache_pressure:拯救你的文件系统缓存

这个参数常被遗忘,但它对桌面体验的影响甚至超过swappinessvfs_cache_pressure控制内核回收“目录项(dentry)”和“inode”缓存的“积极性”。dentry 缓存保存了路径名到 inode 的映射(如/home/user/Documents/file.txt→ inode 12345),inode 缓存则保存了文件元数据(大小、权限、时间戳)。

默认值是100,意味着内核会像回收普通内存页一样,积极地清理这些缓存。后果是:当你在文件管理器中频繁切换目录、在终端中ls不同路径时,会明显感觉到卡顿,因为每次都要重新解析路径、读取磁盘。

将它调低到50,能显著提升文件操作的响应速度:

sudo sysctl vm.vfs_cache_pressure=50 echo 'vm.vfs_cache_pressure=50' | sudo tee -a /etc/sysctl.conf

50的含义是:内核回收 dentry/inode 缓存的积极性,只有回收普通内存页的一半。这会让常用路径的解析快如闪电,而对内存占用的影响微乎其微(这些缓存本身就很轻量)。

4.3 实时监控与故障诊断:读懂sarvmstat的语言

配置完参数,你不能就撒手不管。必须建立一套监控习惯,才能在问题发生前就发现苗头。我每天必看的两个命令是sarvmstat

vmstat是最轻量的实时观察者:

vmstat 1 5 # 输出示例: # procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- # r b swpd free buff cache si so bi bo in cs us sy id wa st # 1 0 0 2456784 123456 3456789 0 0 12 34 123 456 12 3 84 1 0

关键列解读:

  • swpd:当前使用的 swap 量(KB)。持续增长是危险信号。
  • si(swap-in)和so(swap-out):每秒从 swap 读入/写入的 KB 数。如果so长期 > 1000,说明内存严重不足,swap 正在成为瓶颈。
  • wa(I/O wait):CPU 等待 I/O 的时间百分比。如果wa长期 > 20%,且so也高,基本可以断定是 swap I/O 拖垮了系统。

sar(来自sysstat包)则提供历史视角:

# 安装 sudo apt install sysstat # 查看过去一小时的 swap 使用率 sar -W 1 60 # 查看过去一天的内存统计(需 sysstat 服务开启) sar -r

sar -W的输出是pgpgin/spgpgout/s,即每秒换入/换出的页数。一个健康的桌面系统,pgpgout/s日均值应低于 50;如果超过 200,就需要检查是否有内存泄漏的程序。

经验技巧:我写了一个简单的监控脚本,放在/usr/local/bin/swap-watch.sh

#!/bin/bash while true; do SWAP_USED=$(free | awk '/Swap:/ {print $3}') if [ "$SWAP_USED" -gt 1048576 ]; then # > 1GB echo "$(date): High swap usage: ${SWAP_USED}KB" | logger -t swapwatch # 可选:发送邮件或桌面通知 fi sleep 30 done

配合systemd服务,让它在后台默默守护。

5. 常见故障排查链路:从“swap 不工作”到“swap 毁掉 SSD”的完整复现

理论和配置讲得再好,不如一次真实的排错过程来得深刻。下面,我将还原一个典型的、让客户焦头烂额的 swap 故障案例,展示我是如何一步步抽丝剥茧,最终定位到那个被所有人忽略的“元凶”的。这个过程,就是你未来遇到类似问题时的完整排查手册。

5.1 故障现象:客户抱怨“Ubuntu 20.04 越用越卡,重启后变快,几小时后又卡死”

客户是一所大学的实验室管理员,管理着 20 台 Ubuntu 20.04 桌面工作站。所有机器配置相同:Intel i5, 8GB RAM, 256GB SSD。他们反馈,新装系统时一切正常,但使用 3–4 天后,系统会变得极其缓慢,鼠标移动有明显延迟,打开终端要等 5 秒以上。重启后立刻恢复正常,但问题会在几小时内重现。

5.2 第一步:基础检查——确认 swap 是否真的在工作

我远程登录一台问题机器,第一反应是检查 swap 状态:

swapon --show # 输出:空!swapfile 未激活。 free -h # 输出:Swap: 0B Total, 0B Used

这很奇怪,因为客户确认他们按教程创建了 swapfile。我检查/etc/fstab

/swapfile none swap sw 0 0

语法正确。再检查文件是否存在且权限正确:

ls -lh /swapfile # -rw------- 1 root root 4.0G ...

一切看起来都没问题。那为什么swapon --show是空的?我尝试手动激活:

sudo swapon /swapfile # 报错:swapon: /swapfile: swapon failed: Text file busy

Text file busy!这个错误信息是关键线索。它意味着/swapfile正被某个进程以“写入”方式打开,而swapon要求该文件必须是“未被占用”的。这通常发生在:

  • 有人用文本编辑器(如nanovim)打开了/swapfile(虽然不太可能);
  • 或者,更常见的是,rsynccpmv等命令正在操作该文件。

我用lsof查找谁在占用它:

sudo lsof /swapfile # 输出:COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME # rsync 1234 root 3w REG 253,1 4294967296 123456 /swapfile

果然!一个rsync进程正在向/swapfile写入数据。原来,客户为了“备份系统”,编写了一个脚本,每天凌晨 2 点执行rsync -av / /backup/,而这个脚本错误地将/swapfile也包含在了同步源中!rsync在同步过程中会打开并写入/swapfile,导致它被锁定。而客户的定时任务又设置了@reboot,每次重启后自动执行swapon /swapfile,但由于文件被rsync锁定,swapon失败,且错误被静默忽略。于是,系统在无 swap 的状态下运行,内存压力全部落在 RAM 和文件缓存上,vfs_cache_pressure默认的 100 让缓存被疯狂回收,最终导致 I/O 瓶颈。

5.3 第二步:根因修复与防御性配置

修复很简单:修改rsync脚本,排除 swapfile:

rsync -av --exclude='/swapfile' / /backup/

但更重要的是,如何防止此类问题再次发生?我为客户增加了两层防御:

  1. chattr不可变属性:给/swapfile加上i(immutable)属性,使其无法被任何进程(包括 root)修改、删除或重命名:

    sudo chattr +i /swapfile

    这样,即使rsync脚本试图写入,也会立即报错Operation not permitted,并在日志中留下清晰痕迹。

  2. systemd服务依赖:创建一个systemd服务,确保swaponrsync任务之后才执行,并加入错误重试逻辑:

    # /etc/systemd/system/swap-activate.service [Unit] Description=Activate swapfile After=rsync-backup.service Wants=rsync-backup.service [Service] Type=oneshot ExecStart=/bin/sh -c 'swapon /swapfile || (sleep 5 && swapon /swapfile)' RemainAfterExit=yes [Install] WantedBy=multi-user.target

    这样,即使rsync偶尔晚结束几秒,服务也能自动重试。

5.4 第三步:SSD 寿命保护——fstrim与 swapfile 的协同

客户还有一个隐忧:“swapfile 频繁写入,会不会把我们的 SSD 写坏?” 这是个好问题。现代 SSD 的 TBW(总写入字节数)很高,但无谓的写入确实会缩短其寿命。关键在于,swapfile 的写入是“随机小块写入”,这对 SSD 友好度极低。

解决方案是fstrim。它告诉 SSD 哪些逻辑块(LBA)已经“无效”,可以被 SSD 的垃圾回收(GC)机制安全擦除。对于 swapfile,我们需要确保fstrim能正确识别其占用的空间。

Ubuntu 20.04 的fstrim.timer默认是启用的,它每天运行一次。但 swapfile 的特殊性在于:fstrim默认只对挂载点(如/)生效,而 swapfile 是一个文件,不是挂载点。因此,我们必须手动为 swapfile 添加discard选项。

修改/etc/fstab中的 swapfile 条目:

/swapfile none swap sw,discard 0 0

discard选项的作用是:每当 swapfile 中的页被“释放”(即从 swap 中移出,回到内存或被丢弃)时,内核会立即向底层文件系统发送TRIM命令,通知 SSD 这些块可以回收。这比每天一次的fstrim更及时、更精准。

注意:discard选项会带来轻微的性能开销(每次释放页都要发 TRIM 命令),但对于桌面用户,这点开销远小于 SSD 寿命损耗的风险。你可以用sudo hdparm -I /dev/sda | grep TRIM确认 SSD 支持 TRIM。

6. 进阶场景与边界思考:当 swap 遇上容器、加密和内核更新

到此为止,你已经掌握了在 Ubuntu 20.04 上配置、调优和排错 swap 的全套技能。但现实世界永远比教程复杂。最后,我想分享几个在真实项目中遇到的、超越基础配置的进阶场景,它们代表了 swap 技术的边界和未来演进方向。

6.1 Docker 容器环境下的 swap 管控:隔离还是共享?

在一台运行 Docker 的 Ubuntu 20.04 服务器上,swap 的角色变得微妙。Docker 默认会为每个容器设置内存限制(--memory),但这个限制只针对物理内存(RSS),不包括 swap。这意味着,一个被限制为 1GB 内存的容器,如果swappiness全局设为 60,它依然可以把大量数据换出到系统的 swapfile 中,从而“偷用”其他容器的 swap 带宽,造成 I/O 争抢。

我的解决方案是:在容器级别禁用 swap。Docker 19.03+ 支持--memory-swap参数:

docker run --memory=1g --memory-swap=1g nginx

--memory-swap=1g表示该容器的“内存 + swap”总和不能超过 1GB。由于--memory已设为 1GB,这就等效于禁用了 swap。如果容器尝试分配超过 1GB 的内存(含 swap),它会被 OOM Killer 杀死。这比让整个系统陷入 swap 泥潭要可控得多。

6.2 全盘加密(LUKS)下的 swapfile:安全与性能的权衡

许多企业客户要求 Ubuntu 20.04 启用 LUKS 全盘加密。这时,swapfile 的安全性就至关重要。如果 swapfile 位于加密卷内(这是 Ubuntu 安装器默认做法),那么其中的数据自然是加密的,无需额外操作。

但有一个性能陷阱:LUKS 加密层会增加 I/O 延迟。为了缓解,我建议在创建 swapfile 时,使用更大的块大小(block size),减少加密/解密的调用次数。fallocate本身不指定块大小,但我们可以用dd配合oflag=direct绕过 page cache,直接与块设备交互:

sudo dd if=/dev/zero of=/swapfile bs=1M count=4096 oflag=direct

bs=1M意味着每次 I/O 操作都是 1MB,这比默认的 512 字节块效率高得多,尤其在加密环境下。

6.3 内核更新后的 swap 兼容性:一个被忽视的“版本锁”

Ubuntu 20.04 的内核版本从 5.4.x 升级到 5.15.x 后,我遇到了一个诡异问题:一台机器的 swapfile 在新内核下swapon失败,报错swapon: /swapfile: swapon failed: Invalid argument。排查数小时后发现,罪魁祸首是mkswap的版本。

Ubuntu 20.04.1 的util-linux包(包含mkswap)版本是 2.34,它创建的 swapfile 格式与内核 5.15 的解析器不完全兼容。解决方案是