![使用 dd 将 1TB 磁盘克隆到 1.2TB 磁盘时出现“设备上没有剩余空间”](https://linux22.com/image/174868/%E4%BD%BF%E7%94%A8%20dd%20%E5%B0%86%201TB%20%E7%A3%81%E7%9B%98%E5%85%8B%E9%9A%86%E5%88%B0%201.2TB%20%E7%A3%81%E7%9B%98%E6%97%B6%E5%87%BA%E7%8E%B0%E2%80%9C%E8%AE%BE%E5%A4%87%E4%B8%8A%E6%B2%A1%E6%9C%89%E5%89%A9%E4%BD%99%E7%A9%BA%E9%97%B4%E2%80%9D.png)
我正在尝试使用以下dd
命令将远程 xfs 磁盘克隆到另一个位置。
ssh root@source_ip "dd if=/dev/vda2 bs=16M conv=noerror,sync status=progress " | sudo dd of=/dev/nvme1n1 bs=16M conv=noerror,sync status=progress
但是,我收到以下错误消息
828895133696 bytes (829 GB) copied, 39610.432258 s, 20.9 MB/s
1288490188800 bytes (1.3 TB) copied, 64529.849170 s, 20.0 MB/s
dd: error writing '/dev/nvme1n1': No space left on device
源磁盘有1TB,目标磁盘有1.2TB。
谁能帮助解释为什么在这种情况下无法执行磁盘到磁盘克隆?谢谢!
我想尝试从源磁盘恢复已删除的文件,并且我不确定DD是否是适合这种情况的唯一正确工具。
答案1
比较这个答案(bs=1K
此处使用注释):
dd
是一个古老的、古怪的程序,最初旨在在磁带设备上运行。当您告诉它读取 1kB 的一个块时,它会尝试读取一个块。如果读取返回的数据少于 1024 个字节,那么你只能得到这些了。
conv=sync
当dd
阅读量确实低于预期时,它就变得很重要。在您的情况下,任何时候管道都ssh
无法将完整的16M
块(因为bs=16M
)传递到本地dd
一口气读完,后者conv=sync
将用零(NUL 字节)填充“丢失”的数据。但真实的数据并不缺失。dd
当本地尝试读取下一个块时,将传递本地认为丢失的内容。
实际上,本地dd
在流的随机(-ish)位置注入零。如果不是很明显,我强调:这会损坏数据,生成的“克隆”实际上将毫无用处。
conv=noerror,sync
即使在远程(读取)端使用也可能是错误的。对比一下这个答案:有什么dd conv=sync,noerror
作用?您需要真正了解它的工作原理才能正确使用它。
对于你使用的本地来说dd
conv=noerror,sync
没有什么意义。删除或添加iflag=fullblock
(如果支持)。坦率地说,我不确定在从管道读取conv=noerror,sync iflag=fullblock
时是否比不使用这些选项有任何优势。dd
答案2
正如其他人已经指出的那样,问题在于您正在使用dd
.使用规则#1dd
是你不使用dd
。使用规则#2dd
是你做的不是使用dd
。使用规则#3 dd
(仅适用于专家)是不要使用该bs
选项。
里面没有魔法dd
。魔法就在/dev/*
.
dd
会很高兴地破坏你的数据和设置块大小可能会也可能不会提高性能。仅dd
在您知道如何安全使用时才使用和您已经在特定用例中完成了基准测试,以确定比让系统执行其操作更快的块大小。
ssh root@source_ip "cat /dev/vda2" | sudo tee /dev/nvme1n1 >/dev/null
答案3
对于任何涉及取证的事情,包括取消删除文件,dd
这是正确的工具(至少对于读取而言)。
正如其他人指出的那样,问题在于,在 ssh 管道的末尾,您没有读取它被阻止的情况。因此,您要么在第二个 dd 上需要 iflag=fullblock,要么完全跳过第二个 dd。
flag=noerror
可能是错误的。你想知道它是否失败了。
flag=sync
常常是一个错误。它告诉 dd 覆盖操作系统的缓冲。你不需要那个。顶多就是跑完sync
之后。而对于读书来说,这是没有意义的。
两个不同的status=progress
条目是一个错误。两者都会报告互相覆盖。你真的只是想知道它是如何完成的。省略状态=...
bs=16M
看起来不错,但理想情况下 bs 应与硬件块大小匹配。对于经典磁盘,该大小为 512 字节。更现代的使用 4k。我推荐 4k、16k 或 64k。
对于未满的磁盘,压缩也很好。解决ssh -C
方案可能是最简单的。
所以你可以写:
ssh -C root@source_ip "dd if=/dev/vda2 bs=64k" | sudo dd iflag=fullblock of=/dev/nvme1n1 bs=64k
或者
sudo sh -c 'ssh -C root@source_ip "dd if=/dev/vda2 bs=64k" >/dev/nvme1n1'
(这个可能有 ssh 问题)
或者
sudo chown `id -u` /dev/nvme1n1
ssh -C root@source_ip "dd if=/dev/vda2 bs=64k" >/dev/nvme1n1
sudo chown root /dev/nvme1n1
或者使用相当聪明的解决方案,涉及使用tee
.
测试
如果您想使用双 dd 形式,最好在进行完整传输之前测试您是否拥有正确的选项。添加count=100
到第一个dd
,以便它占用一点磁盘空间。您应该在几秒钟内看到两个状态行,并且它们应该显示相同的转账金额。如果有效,请重复count=100
删除的操作以完成完整磁盘。
答案4
该命令dd
是根据 20 世纪 60 年代 IBM 大型机的命令DDR
(磁盘转储和恢复)建模的,主要用于将面向块的 I/O 与面向块的磁盘转换和重新块。然而,这不是您打算在这里做的。
您的用例中的重要问题是,中间有一个不可靠的网络管道,它不允许读取特定的块大小。
如果您使用的是真实的dd
程序(不确定您使用什么,因为您的输出与标准不兼容),您应该得到如下输出:
"%u+%u records in\n", <number of whole input blocks>,
<number of partial input blocks>
"%u+%u records out\n", <number of whole output blocks>,
<number of partial output blocks>
您需要dd
以确保仅在加号之前获得数字 > 0 以及在加号之后获得 0 的方式使用。
由于从输出管道读取时没有授权获取特定数量的数据ssh
,因此您需要确保始终尝试读取少于您将获得的数据。您仍然可以使用以下命令从源磁盘读取:
dd if=/dev/xxx bs=16x1024k
没有问题,但是您需要使用此命令从管道读取数据并将其写入目标磁盘:
dd ibs=1b obs=16x1024k of=/dev/yyy
不要使用其他选项(特别是不要使用任何非标准选项)。如果dd
写入目标磁盘的命令最终写入:
nnnn+0 records in
mmmm+0 records out
您已经成功,并且dd
从未从输入中读取少于 512 字节的数据,因此可以正确地重新阻止输出。
如果您看到输入块的部分块(> 0 在加号右侧),则需要重复复制过程。
请注意,由于输出块大小很大,您当然可能会在最后将一个部分块写入目标 dist。因此,dd
写入目标磁盘的摘要消息也可以是:
nnnn+0 records in
mmmm+1 records out