到目前为止我做了什么

到目前为止我做了什么

到目前为止我做了什么

我想创建我的服务器 HDD 分区的磁盘映像并将其复制到我的桌面(客户端)。我在服务器上使用了以下命令:

dd if=/dev/md3 status=progress bs=500k | bzip2 --best > /mnt/client/image.bz2

/mnt/client/我的客户端的 sshfs 安装位置在哪里。

不幸的是,我的客户端没有静态 IP 地址,并且磁盘映像花费的时间比预期要长。在 511 GB(1 TB)时,我的 ISP 强制我使用一个新的 IP 地址,两个 ssh 连接都中断了。这是调用的 ssh 连接dd ... | bzip2 > ...和 sshfs 挂载。

请注意,我需要制作磁盘映像。复制文件是不够的。另请注意,我的服务器上没有足够的空间来存储磁盘映像,因此需要将其直接保存到我的客户端。

我的问题

如何恢复磁盘映像?

如何恢复 dd 映像以使之前复制的数据不会再次传输?特别是因为已经传输的数据位于压缩存档内。

保持压缩是更好的选择,因为我的网络连接的带宽似乎是瓶颈。但如果这能让事情变得更容易的话,这并不是绝对必要的。

对于文件,我会想到类似的东西rsync,但这并不适用于这里。

我想解压我已经复制的(部分)存档,测量其大小(以字节为单位),并使用类似的东西

dd if=/dev/md3 skip=[size of uncompressed partial image] iflag=skip_bytes

但我不确定如何将数据流附加到现有存档中。我认为仅使用>>附加到存档可能行不通。并且解压缩存档可能需要一些时间,这可能是必要的,也可能不是必要的。但我不知道如何获得部分图像的未压缩大小。

假设我放弃了整个压缩的事情。至少,会dd if=... skip=... >> /mnt/client/uncompressed_image按预期工作吗?

使用ssh时如何保证“连接安全”?

如何确保我的 ssh 连接不会因非静态 IP 地址而中断?或者至少如何确保 dd 命令中断时可以轻松恢复它?

事后看来,从我的客户那里做如下的事情可能会更明智:

ssh root@server "dd if=/dev/md3 | bzip2 --best -c" | dd of=image.bz2

这样我就不需要客户端的 IP 来进行 sshfs 挂载。但当我的 IP 地址发生变化时,它仍然会破坏 ssh 管道。所以仅此一点解决不了任何问题。

答案1

您已确认正在使用救援映像,因此 1TB 源磁盘当前未使用。这真是个好消息。

现在,要通过不可靠的介质复制磁盘,您需要一个可以自动重新建立自身的传输层(例如,基于 UDP 的 OpenVPN),或者一种发送可重新启动的数据“块”的方法。

我们假设一切都由客户端控制,并且您可以ssh通过证书/密钥进行访问,因此不需要密码。

我建议您使用 1GB 块,这意味着您需要大约 1000 个ssh连接来传输整个磁盘。您可能需要调整脚本顶部附近的块大小和计数。在“有效数据”部分中,您可以选择写入bzip2压缩块或正常的未压缩块。 (bzip2可以处理多个压缩块,所以这不是问题。)

#!/bin/bash
#
dev=/dev/sda1   # Device to read
img=image.dat   # Target image filename
bs=32M          # dd blocksize per read
count=32        # Number of blocks per ssh chunk

for (( chunk=0; ; ))
do
    # Grab chunk from server
    #
    ssh -zn root@remoteServer "
        dev=$dev chunk=$chunk bs=$bs count=$count "'
        echo "chunk $chunk from device $dev"
        {
            dd bs=$bs skip=$((chunk*count)) count=$count if=$dev 2>/tmp/dd.$$
            echo $? >/tmp/ss.$$
            dd bs=$bs iflag=fullblock count=$count if=/dev/zero 2>/dev/null
        } | dd bs=$bs iflag=fullblock count=$count 2>/dev/null
        echo "========"
        echo "status $(cat /tmp/ss.$$)"
        cat /tmp/dd.$$
        rm -f /tmp/dd.$$ /tmp/ss.$$
    ' |
        {
            # Extract data from chunk
            #
            IFS= read -r info
            echo "Received: $info"
            dd bs=$bs iflag=fullblock count=$count of=/var/tmp/data.$$
            cat >/var/tmp/meta.$$
        }

    # Append extracted data
    #
    meta=$(cat /var/tmp/meta.$$)
    echo "Meta:"
    echo "$meta" | sed 's/^/| /'

    if [[ "$meta" =~ ([[:digit:]]+)\+[[:digit:]]+' records in' ]]
    then
        # Valid data
        #
        # bzip2 </var/tmp/data.$$ >> "$img.bz2"
        dd bs=$bs count=$count seek=$((chunk*count)) conv=notrunc if=/var/tmp/data.$$ of="$img"

        # Is this all
        #
        if [[ "${BASH_REMATCH[1]}" -lt $count ]]
        then
            # We are done
            #
            break
        fi

        # Next round the loop
        #
        ((chunk++))
    else
        echo "Invalid data received for chunk $chunk; retrying"
    fi
done

# Tidy up
#
rm -f /var/tmp/data.$$ /var/tmp/meta.$$
exit 0

生成的映像至少具有源磁盘的大小;额外的零字节足以四舍五入到下一个完整的块大小。如果这很重要,您可以用来truncate减小生成的未压缩图像的大小。

答案2

要发送丢失的位,恐怕您仍然需要从一开始就重新压缩,即使您只将丢失的位作为压缩流(如 bzip2 取决于之前看到的内容)传输。

如果$s包含已传输内容的大小,你会这样做

dd if=/dev/md3 status=progress bs=500k | bzip2 --best | {
  head -c "$s" > /dev/null
  ssh host 'cat >> /mnt/client/image.bz2'
}

(假设headGNU 之类的实现head不会读取超出此处要求的内容)。

或者反过来:

ssh other-host "
  dd if=/dev/md3 status=progress bs=500k | bzip2 --best |
    tail -c +$(($s + 1))" >> /mnt/client/image.bz2

假设/dev/md3自上次以来没有被修改(甚至没有安装,例如可以更新一些“上次安装时间”字段)。

使用网络文件系统传输大文件也是一个坏主意。

相关内容