<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Lvm · XB 现场笔记</title><link>https://www.xbcatm.com/zh/tags/lvm/</link><description>基于 HTTP、DNS、TLS、Caddy、SSH、发布与恢复的可复现运维现场笔记。</description><generator>Hugo</generator><language>zh-CN</language><atom:link href="https://www.xbcatm.com/zh/tags/lvm/index.xml" rel="self" type="application/rss+xml"/><item><title>LVM 快照在时间上定格一块块设备</title><link>https://www.xbcatm.com/zh/posts/lvm-logical-volume-snapshot/</link><pubDate>Mon, 10 Apr 2028 00:00:00 +0000</pubDate><guid>https://www.xbcatm.com/zh/posts/lvm-logical-volume-snapshot/</guid><description>逻辑卷快照保留原始数据块，让你能在不卸载分区的前提下备份或回滚。</description><content:encoded>&lt;p&gt;LVM 快照既不是拷贝，也不是 reflink：它是一个挂在源逻辑卷上的、大小固定的写时复制异常存储。&lt;code&gt;lvcreate -s -L 5G -n snap vg/data&lt;/code&gt; 从卷组里划走 5 GiB，之后 device-mapper 的 &lt;code&gt;snapshot&lt;/code&gt; 目标会在某个源 chunk 第一次被写时，把它的原始内容记录下来。无论源卷多大，创建都是瞬时的，因为创建时什么都没复制。&lt;/p&gt;
&lt;p&gt;这个大小就是你全部的安全余量，也正是最常被写错的参数。按“一周内没什么写入”配的存储，会被一次数据库 checkpoint 或一次 &lt;code&gt;rsync&lt;/code&gt; 在一小时内吃光；而它填满时快照不会降级，而是直接失效并开始返回 I/O 错误。恢复点恰好在你最需要它的那一刻消失，所以存储的剩余空间该进监控面板，而不是躺在 runbook 里。&lt;/p&gt;
&lt;h2 id="异常存储不是副本"&gt;异常存储，不是副本&lt;/h2&gt;
&lt;p&gt;一个经典的 LVM 快照用到两个 device-mapper 目标：源卷被重新映射为 &lt;code&gt;snapshot-origin&lt;/code&gt;，另有一个新的 &lt;code&gt;snapshot&lt;/code&gt; 设备把它与 COW 设备组合起来。读未改动的 chunk 直接落到源卷；某个 chunk 第一次被写时，旧 chunk 被复制进 COW 设备，然后新数据才写下去。所以消耗量跟“发生过改动的不同 chunk 数”成正比，而不是跟写入次数成正比：fixture 往 16 个热 chunk 写了一百万次，只消耗 15 个异常，约 60 KiB。&lt;/p&gt;
&lt;p&gt;用 &lt;code&gt;lvs -a -o lv_name,lv_attr,origin,data_percent,lv_size&lt;/code&gt; 观察它。经典快照的 &lt;code&gt;lv_attr&lt;/code&gt; 是 &lt;code&gt;swi-a-s---&lt;/code&gt;：&lt;code&gt;s&lt;/code&gt; 表示快照、&lt;code&gt;w&lt;/code&gt; 表示可写，而状态位在存储耗尽时会变成 &lt;code&gt;I&lt;/code&gt;。&lt;code&gt;data_percent&lt;/code&gt; 是存储的填充度，这才是该告警的数字，阈值取 70% 或 80%；100% 不是警告，是讣告。&lt;code&gt;--chunksize&lt;/code&gt; 决定异常存储的粒度：粒度越大，块映射表的元数据越省，但每一次改动要拷贝的旧数据也越多。快照还有效时可以用 &lt;code&gt;lvextend&lt;/code&gt; 扩容 COW 设备；&lt;code&gt;lvchange --monitor y&lt;/code&gt; 则让 dmeventd 自动做这件事。&lt;/p&gt;
&lt;h2 id="耗尽即失效"&gt;耗尽即失效&lt;/h2&gt;
&lt;p&gt;内核文档说得很直白：COW 设备往往比源卷小，一旦填满，快照就变得无用并被禁用，开始返回错误。LVM 给出的容量经验是按预期写入量规划——常常取源卷的 20%——并在填满之前扩容。另外，存储里有一小块要用来存放 COW 块映射表，所以可用于数据的容量略小于 &lt;code&gt;-L&lt;/code&gt; 的数值。&lt;/p&gt;
&lt;p&gt;影响范围很窄，但时机很残忍：源卷照常运行，所以在读取该快照的备份任务失败之前，或者在你要用的那次回滚已经不可用之前，看不出任何异常。它没有中间状态：到了 100%，正确的心理模型是这份快照从未存在过。&lt;/p&gt;
&lt;h2 id="合并回滚以及-thin-如何改变失败形态"&gt;合并回滚，以及 thin 如何改变失败形态&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;lvconvert --merge vg/snap&lt;/code&gt; 把快照中改动过的 chunk 合并回源卷，合并在后台进行，期间源卷仍然可用——对尚未合并的 chunk 的写入会被推迟。约束在源卷一侧：建立合并时它必须处于非活动状态，所以要么先卸载，要么先安排合并、之后再激活源卷。合并只对持久化快照有效，临时快照走不了这条路；合并会消耗掉这份快照，所以需要的东西先拷出来。&lt;/p&gt;
&lt;p&gt;thin 快照改变的是失败形态，而不是消除失败。当源是 thin LV 且不给大小时，创建出来的就是 thin 快照；它共享 thin 池里的块，而不拥有自己的存储，所以它自身的分岔无法让它失效。此时共享资源变成了池：池一旦耗尽，坏掉的不是一份快照，而是池里所有 thin 卷，而 LVM 明确警告池可能因此损坏、且难以甚至无法修复。&lt;code&gt;thin_pool_autoextend_threshold&lt;/code&gt; 与 dmeventd 的存在，正是因为池耗尽是一次池级事故。&lt;/p&gt;
&lt;h2 id="坏掉的样子"&gt;坏掉的样子&lt;/h2&gt;
&lt;p&gt;最经典的是“备份悄悄不再是备份”。任务挂载快照开始拷贝，拷贝耗时超过了存储能覆盖的时长，存储在某刻到达 100%；快照随即失效，拷贝失败——更糟的情况是，监控只看任务的退出码，而任务读的早就是一个过期的挂载点。所以要盯每一个快照 LV 的 &lt;code&gt;data_percent&lt;/code&gt;，70% 就告警，并且按保留窗口内最忙的那一小时来定容量，而不是按平均值。&lt;/p&gt;
&lt;p&gt;第二种是“要用的时候合并不了”。&lt;code&gt;lvconvert --merge&lt;/code&gt; 要求源卷处于关闭状态，而团队往往是在事故当中才发现这一点。合并开始后由 &lt;code&gt;snapshot-merge&lt;/code&gt; 目标接管源卷，&lt;code&gt;dmsetup status&lt;/code&gt; 里的已分配扇区数会持续下降，降到只剩元数据就表示合并完成。先在一个一次性卷上演练合并，另留一份不依赖快照的真实备份，并记住：存储已满的快照根本无法合并。&lt;/p&gt;
&lt;h2 id="快速验证"&gt;快速验证&lt;/h2&gt;
&lt;p&gt;在信任这份快照之前，先看存储的填充度：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;lvs -a -o lv_name,vg_name,lv_attr,origin,data_percent,lv_size
lvs -a -o lv_name,data_percent,metadata_percent vg/thinpool
dmsetup status vg-snap
lvextend -L +2G vg/snap
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;data_percent&lt;/code&gt; 就是 COW 存储的填充度，健康的快照应当远低于 100；&lt;code&gt;lv_attr&lt;/code&gt; 里出现状态位 &lt;code&gt;I&lt;/code&gt; 说明快照已经失效。对 thin 快照而言，该看的是池上的 &lt;code&gt;data_percent&lt;/code&gt; 与 &lt;code&gt;metadata_percent&lt;/code&gt;；&lt;code&gt;dmsetup status&lt;/code&gt; 会给出已分配扇区与总扇区之比，可以据此实时观察一次合并把扇区数排空的过程。&lt;/p&gt;
&lt;h2 id="来源"&gt;来源&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://man7.org/linux/man-pages/man8/lvcreate.8.html"&gt;lvcreate(8)&lt;/a&gt;，说明 &lt;code&gt;-s&lt;/code&gt;/&lt;code&gt;--snapshot&lt;/code&gt;、COW 与 thin 快照的区别以及容量建议，已核验 2026-09-16。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://man7.org/linux/man-pages/man8/lvs.8.html"&gt;lvs(8)&lt;/a&gt;，定义 &lt;code&gt;lv_attr&lt;/code&gt; 状态位，含无效快照，已核验 2026-09-16。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.kernel.org/admin-guide/device-mapper/snapshot.html"&gt;Device-mapper snapshot 支持&lt;/a&gt;，异常存储、失效语义与 &lt;code&gt;lvconvert --merge&lt;/code&gt; 行为，已核验 2026-09-16。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://man7.org/linux/man-pages/man7/lvmthin.7.html"&gt;lvmthin(7)&lt;/a&gt;，thin 快照、池耗尽与自动扩容，已核验 2026-09-16。&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item></channel></rss>