Параллельное выполнение задач


В связи с тем, что на современных машинах стоят многоядерные камни
и в linux есть прекрасная возможность использовать контейнеры lxc или systemd-nspawn,
- можно значительно ускорить решение трудоёмких задач. Например - стендовая модель ниже!

Есть задача - найти какие-то текстовые данные в другом большом массиве текстовых данных,
т.е. найти позиции из файла search.txt в файле catalogue.txt

По простому это аналогично:

grep -f search.txt catalogue.txt

Но долго. Мы будем делать быстро. Для этого сделаем такой алгоритм с помощью "gnu parallel":

1. (Уже) Будем иметь настроенными несколько нод - узлов: физических или виртуальных.
2. Разобъем файл search.txt на столько частей, сколько у нас нод (или больше)
3. На каждую ноду автоматически положим каталог catalogue.txt и ту часть файла search.txt, которую требуется "найти"
4. Запустим параллельный поиск и после его окончания - автоматически соберём результаты в один файл на "центральной" ноде.

Итого у нас будет 5 файлов bash:

s0-tash.sh     - главный скрипт, который мы запускаем на "главной" машине.
s1-split.sh    - скрипт, который делит файл задачу search.txt на части
s2-send.sh     - скрипт, который параллельно отправляет части search.txt на ноды
s3-job.sh      - скрипт, который запускается параллельно на каждой ноде и выполняет поиск
s4-receive.sh  - скрипт, который параллельно забирает результаты с нод и собирает в один файл

Дальше - коды.

-------------------------------------------------
# s0-task.sh
# Центральный блок управления всем
# У нас 2 контейнера: .0.2 и .0.3
-------------------------------------------------

#!/bin/bash

srv_cnt=2

srv="-S test@192.168.0.2 -S test@192.168.0.3"

echo SPLIT
./s1-split.sh

echo SEND TASK 
parallel -j2 ./s2-send.sh '{%}' '{}' ::: prt-*

echo RUN NODE TASKS
seq $srv_cnt | parallel -n0 -j2 $srv ./s3-job.sh '{%}'

echo RECEIVE RESULT
seq $srv_cnt | parallel -j2 ./s4-receive.sh '{%}'

echo CLEAN
seq $srv_cnt | parallel -n0 -j2 $srv 'rm res-*'
seq $srv_cnt | parallel -n0 -j2 $srv 'rm prt-*'

cat res-* > result.txt
rm res-*
rm prt-*

-------------------------------------------------
# s1-split.sh
# Разбиваем файл на части по 1000 строк
-------------------------------------------------

#!/bin/bash

split -l 1000 search.txt prt-

-------------------------------------------------
# s2-send.sh
# Отправляем каталог, скрипт-искатель и какую-то часть search.txt на ноды
# Номер ноды и часть для отправки - приходят на входе
-------------------------------------------------

#!/bin/bash

n="$1"
f="$2"

s1="192.168.0.2"
s2="192.168.0.3"

if [ "$n" != "" ]
then
  echo "send: $n"
  if [ $n -ge 1 ] && [ $n -le 2 ]
  then
    eval srv=\${s$n}
    scp catalogue.txt test@$srv:~
    scp s3-job.sh test@$srv:~
    scp "$f" test@$srv:~ 
  fi
fi


-------------------------------------------------
# s3-job.sh
# Скрипт - выполнитель кусочка задачи на ноде 
# - копируется на ноду вместе с заданием
-------------------------------------------------


#!/bin/bash

ls prt-* > list

while read wc
do
  echo "NODE: $HOSTNAME $wc"
  while read -r a
  do
    printf -v x "%q" "$a"
    agrep -n -B "$x" catalogue.txt | head -n 1 >> res-$wc
  done < $wc
done < list

rm list

-------------------------------------------------
# s4-receive.sh
# Скрипт, собирающий результаты с нод
# Номер ноды приходит на вход
-------------------------------------------------

#!/bin/bash

n="$1"

s1="192.168.0.2"
s2="192.168.0.3"

if [ "$n" != "" ]
then
  echo "receive: $n"
  if [ $n -ge 1 ] && [ $n -le 2 ]
  then
    eval srv=\${s$n}
    scp test@$srv:~/res-* .
  fi
fi

-------------------------------------------------

Для того, чтобы эта задача работала - конечно нужно настроить ноды (LXC)
и разрешить к ним доступ по ключам (RSA) с помощью "ssh-copy-id"

Главный фокус состоит в том, что с помощью lxc-copy мы в один момент можем
наклонировать столько нод, сколько нам нужно, раскидать их по всем доступным
серверам и по всем доступным ядрам - и за 10 минут сделать хоть 100 нод,
решающих большие задачи параллельно - а значит быстро, очень быстро.

#eof