将数据从一台机器快速复制到多台机器的方法

将数据从一台机器快速复制到多台机器的方法

我发现我的工作流程存在一些瓶颈,如下所示。我确实有一台主计算机需要将数据发送到其他节点计算机。这是在 for 循环中完成的,例如:

for all nodes: rsync <Options> <Master> <Node>

如果节点数量不是很大,例如 4 或 8 个(复制时间大约 2 分钟),这会非常有效。然而,它是一条线性曲线。对于 16 个节点,大约需要 3.5 分钟,对于 128 个节点,已经需要 20 分钟(然后这个东西几乎成为我工作流程的瓶颈)。

我的目的是摆脱愚蠢的for all循环并做更多类似的事情:

1. copy master to node1
wait & check if successfull
2. copy master to node2 && copy node1 to node3
wait & check if successfull
3. copy master to node4 && copy node1 to node5 && copy node3 to node6
wait & check if successfull
...

我的问题: bash 中是否有任何可用的内容可以帮助我做类似的事情,或者是否有更可靠的解决方案?基于IT,我在使用工具方面受到了一些限制。任何建议都受到热烈欢迎。

我想到的另一件好事可能会更好:

 1. copy master data to memory
 2. send the memory stuff to all nodes simultaneously (if that is possible) and all nodes write simultaneously 

也许rsync也能处理类似的事情?任何想法都受到热烈欢迎。

出于信息目的,我已经创建了一些测试脚本,如下所示,也可以进行优化。例如,这些master东西并不是我们可以处理的必需品,node0master节点。因此,代码可以进一步减少。但是,仍然存在检查,复制是否成功丢失等 - 只是作为我的想法的一个例子?你可以复制它,创建一个主文件夹,放入一些文件,然后创建128个节点。然后运行脚本并与序列副本进行比较(脚本中给出的标准方法)。即使我们只在一台机器/硬盘上工作,它的速度也更快(因此,并不真正具有代表性)。

#!/bin/bash
#
# Tobias Holzman
# 25.03.2023
#
# Description
#   Script that speeds-up the function serial copying from master to nodes
#   by using other nodes at which the data are already copied. 
#
#   Standard Approach:
#   forAll(nodes, nodei)
#   {
#       rsync <masterData> <nodei_path>
#   }   
#
#------------------------------------------------------------------------------

function copyToNode ()
{
    # Simple check if folder exist
    if [ ! -d $1/triSurface ]
    then
        echo "Error"
        return 0
    fi

    rsync -av --progress $1/triSurface $2/ > /dev/null &

    return 0
}


#------------------------------------------------------------------------------

t1=$(date +%s)

# How many nodes we have (for test purpose)
nCopy=$(ls -d node* | wc -l)

nodesEmpty=()
nodesCopied=()
copyNodes=()

# Create a string array that includes all node names
for i in $(seq 0 $nCopy)
do
    nodesEmpty+=("node$i")
done


echo "We need to copy the data from master to $nCopy nodes"
echo "----------------------------------------------------"

i=0
done=0
dataAtHowManyNodes=0
while true
do
    i=$((i+1))

    # Copy array nodesCopied which we work with in one loop 
    # as we dont want to change the original array
    copyNodes=("${nodesCopied[@]}")

    echo ""
    echo " ++ Copy run #$i"
    echo " ++ Remaining nodes to which we need to copy: ${nodesEmpty[@]}"
    echo " ++ Available nodes used for copy           : ${nodesCopied[@]}"

    echo "  |-> parallel copy sequences: $dataAtHowManyNodes"

    # Only master copy
    if [ $dataAtHowManyNodes -eq 0 ]
    then

        nodeToCopy=${nodesEmpty[0]}

        echo "  |    |-> master to $nodeToCopy"

        copyToNode "master" "$nodeToCopy"

        # Add node to nodesCopied
        nodesCopied+=("$nodeToCopy")

        # Remove node from nodesEmpty
        unset nodesEmpty[0]

        # Update the index
        nodesEmpty=(${nodesEmpty[*]})

    else

        for ((j=0; j<$dataAtHowManyNodes; j++))
        do

            echo "  |-> copy sequenz $j"

            nodeToCopy=${nodesEmpty[0]}

            # Master to node copy
            if [ $j -eq 0 ]
            then

                echo "  |    |-> master to $nodeToCopy"

                copyToNode "master" "$nodeToCopy"

                # Add node to nodesCopied
                nodesCopied+=("$nodeToCopy")

                # Remove node from nodesEmpty
                unset nodesEmpty[0]

                # Update the index
                nodesEmpty=(${nodesEmpty[*]})

            # Node to node copy
            else

                nodeMaster=${copyNodes[0]}

                # Remove copyNode to ensure its not used again
                unset copyNodes[0]
                # Update the index
                copyNodes=(${copyNodes[*]})

                echo "  |    |-> $nodeMaster to $nodeToCopy"

                copyToNode "$nodeMaster" "$nodeToCopy"

                # Add node to nodesCopied
                nodesCopied+=("$nodeToCopy")

                # Update the index
                nodesCopied=(${nodesCopied[*]})

                # Remove node from nodesEmpty
                unset nodesEmpty[0]

                # Update the index
                nodesEmpty=(${nodesEmpty[*]})

            fi

            # Check if still remaining emptyNodes
            if [ ${#nodesEmpty[@]} -eq 0 ]
            then
                echo " ++ Done ..."
                done=1
                break
            fi
        done

    fi

    wait

    if [ $done -eq 1 ]
    then
        echo ""
        break
    fi

    dataAtHowManyNodes=$(echo "scale=0; 2^$i" | bc)
    echo "  |-> Data are now on $dataAtHowManyNodes nodes"


done
t2=$(date +%s)
dt=$(echo "scale=0; ($t2 - $t1)" | bc)
t=$(echo "scale=2; $dt/60" | bc)

echo "Time = $dt s ($t min)"


#------------------------------------------------------------------------------

答案1

您应该查看使用多播或广播数据帧的程序。这样主站就不会受到网络带宽的太大限制,因为所有文件都会被传输一次。

mrsync这里可能很有趣。还有uftp

https://serverfault.com/questions/173358/multicast-file-transfers

答案2

如果无法使用网络文件系统,则安装并使用GNU并行同时运行多个rsync命令。

调整执行程序的数量,以免带宽完全饱和,因为其他人和进程需要访问系统。

相关内容