使用 dd 将 1TB 磁盘克隆到 1.2TB 磁盘时出现“设备上没有剩余空间”

Tim*_*Tim 20 linux filesystems dd command

我正在尝试使用以下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
Run Code Online (Sandbox Code Playgroud)

但是,我收到以下错误消息

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
Run Code Online (Sandbox Code Playgroud)

源盘有 1TB,目标盘有 1.2TB。

谁能帮助解释为什么在这种情况下不能执行磁盘到磁盘克隆?谢谢!

我想尝试从源磁盘恢复已删除的文件,但我不确定 DD 是否是这种情况下唯一正确的工具。

Kam*_*ski 31

比较这个答案(注释bs=1K在那里使用):

dd是一个旧的、古怪的程序,最初打算在磁带设备上运行。当您告诉它读取一个 1kB 的块时,它会尝试读取一个块。如果读取返回少于 1024 个字节,很难,这就是你所得到的。

conv=syncdd确实读取少于预期时变得重要。在你的情况下,任何时候从管道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从管道读取的情况下,我不确定是否比完全不使用这些选项有任何优势。

  • 更好的是,根本不要使用 `dd` (14认同)
  • 是的,在这个例子中OK。但是对于更复杂的用例,`dd` 有一些选项(seek、skip 等)你不能只用 `cat` 来复制 (13认同)
  • @a3nm `cat` 应该可以,或者 `pv` 是一个不错的进度条。`pv /dev/sda > /dev/sdb` (9认同)
  • @roaima:那么应该使用什么? (3认同)
  • @a3nm 在这些情况下考虑使用 `dd`。但是通常对于磁盘或磁盘分区的直接复制,`dd` 绝对不是要走的路。(这就是这个问题的内容。) (3认同)
  • @Tim 取证,将原始磁盘 DD 到新磁盘上的文件中。您仍然可以将该文件用作磁盘映像。 (2认同)
  • --retry-partial 发生了什么?它基本上修复了 `dd` 几乎不会做任何愚蠢的事情,因为它会使用部分读取来修复部分读取,直到读取整个块或达到 EOF。 (2认同)

Gil*_*il' 21

正如其他人已经确定的那样,问题在于您正在使用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
Run Code Online (Sandbox Code Playgroud)

  • @roaima 是的,很好。或者更简单:`ssh -C`。 (5认同)
  • 可能值得`ssh root@source_ip "gzip </dev/vda2" | zcat | sudo tee /dev/nvme1n1 >/dev/null` 对于所有可能为空的块 (2认同)

Dav*_* G. 8

对于任何涉及取证的事情,包括取消删除文件,dd都是正确的工具(至少对于读取而言)。

正如其他人指出的那样,问题在于,在 ssh 管道的末尾,您并没有阻止读取它。所以你要么需要 iflag=fullblock 在第二个 dd 上,要么完全跳过第二个 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
Run Code Online (Sandbox Code Playgroud)

或者

sudo sh -c 'ssh -C root@source_ip  "dd if=/dev/vda2 bs=64k" >/dev/nvme1n1'
Run Code Online (Sandbox Code Playgroud)

(这个可能有 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
Run Code Online (Sandbox Code Playgroud)

或使用相当聪明的解决方案,涉及使用tee.

测试

如果您想使用双 dd 形式,最好在进行完整传输之前测试您是否有正确的选项。添加count=100到第一个,dd以便它做一点磁盘。您应该会在几秒钟内看到两个状态行,并且它们应该显示相同的转移金额。如果有效,请重复count=100删除以完成整个磁盘。