Search     or:     and:
 LINUX 
 Language 
 Kernel 
 Package 
 Book 
 Test 
 OS 
 Forum 
iakovlev.org

Распределенные вычисления в питоне

Кластерная архитектура обеспечивает высокую масштабируемость за счет распределенных ресурсов, разбросанных по сети. На питоне написано большое количество библиотек, выполняющих эту задачу. Я выбрал три библиотеки, которые наиболее просты, не требуют много времени для освоения и дают толчок для дальнейшей работы в этом направлении. Более подробно и детально вы можете почитать тут.

Тестировать вы сможете на локальной машине, но лучше иметь хотя бы одну удаленную машину, доступ к которой возможен по протоколу ssh. Если у вас есть такая удаленная машина, для удобства и автоматизации процесса кластеризации вам понадобится доступ на эту машину по протоколу ssh без пароля. Для этого вам нужно сгенерировать публичные ключи, которые обычно лежат в домашней папке пользователя в подкаталоге .ssh, после чего скопировать эти ключи на удаленную машину в аналогичный каталог. Для генерации ключей выполните на своей локальной машине команду:

ssh-keygen -trsa

После чего зайдите в свой подкаталог .ssh и скопируйте два файла - id_rsa.pub и authorized_keys - на удаленную машину.

Начнем мы с библиотеки:

DistributedPython

Скачать можно тут.
Распределенный софт зачастую использует на самом нижнем уровне команду "ssh где-то сделать-что-то". Эта библиотека использует питоновские системные модули multiprocessing и subprocess для формирования очереди задач и управления этими задачами на удаленных хостах. Вы просто формируете обычный список команд и запускаете их в параллельном режиме. Под командами здесь понимаются обычные юниксовые команды, выполняемые из командной строки. Например, если нам нужно узнать системную дату на каком-то удаленном компьютере, вы просто в терминале набираете команду "ssh computer.at.your.domain date". Для нашего случая это будет означать, что в списке будет 'date'.

В этой библиотеке всего несколько файлов:

testSubmitMaster.py - исполняемый скрипт верхнего уровня, включающий в себя тот самый список команд. Он при своем выполнении будет генерировать логи в виде outputX.dat.

testScript - это shell-скрипт, который используется testSubmitMaster.py. Он содержит несколько переменных - id-шник процесса, имя хоста, время.

submitMaster.py - включает две функции - processCommandsInParallel и submitMaster.
Функция processCommandsInParallel вызывается из testSubmitMaster.py и делает следующее:
1. Создает процесс submitMaster
2. Назначает команды для submitMaster
3. Ожидает окончания работы submitMaster.
Коннект между submitMaster и функцией осуществляется с помощью канала Pipe, создается JobDistributor и очередь queue. В цикле:
прослушиваются каналы
проверяется очередь, если она освобождается, назначаются новые задачи

jobDistributor.py - класс содержит список доступных машин и назначает им задачи. Создается один инстанс JobDistributor. Он использует subprocess, с помощью которого создает новый процесс (Popen) для выполнения команды ssh. Информация о процессе хранится в обьекте Job, JobDistributor формирует словарь, в котором ключ - это имя хоста, а значение обьект Job. Этот скрипт нужно запустить отдельно.

listQueue.py - класс, отвечающий за работу очереди задач.






Batchlib

Эта питоновская библиотека предоставляет интерфейс для выделения достурных машин в сети. назначает им вычислительные задачи и собирает результаты. Его можно загрузить по адресу Batchlib. Его также можно загрузить у меня. Для выполнения скрипта на удаленной машине Batchlib в свою очередь использует другой питоновский пакет - exec_proxy, который можно также скачать тут. exec_proxy позволяет работать по протоколу ftp, дает возможность использовать команды системного питоновского модуля os. Этот модуль можно использовать не только в параллельных вычислениях, но и вообще в обыденной жизни, например для администрирования удаленных машин.

Модуль exec_proxy использует протокол ssh для коннекта с удаленной машиной, после чего позволяет выполнить на ней питоновскую программу. У этого модуля есть следующие две глобальные переменные:

 
 connection_program -  "ssh -x" (по умолчанию)
 remote_program - по умолчанию это полный путь к скрипту remote.py на удаленной машине
 
 
Класс Exec_proxy этого модуля имеет параметр - хост. У этого класса есть много доступных методов, которые выполняют различные действия на удаленной машине либо обрабатываюn ошибки, например:
 
 newdir() - создает временную директорию для хранения файлов
 mkdir() - создает новый каталог на удаленной машине
 chdir()
 rmdir()
 getcwd()
 isdir()
 isfile()
 exists()
 listdir()
 chmod()
 stat()
 mkfifo() - создает именованный канал на удаленной машине
 symlink()
 unlink() - удаляет файл
 upload() - загружает файл с локальной на удаленную машину
 download() - загружает файл судаленной на локальную машину
 write() - создает файл
 read()
 run() - выполняет команду, которая выступает в качестве параметра 
 
 
Ошибки обрабатываются с помощью следующих методов:
 
 is_ok() - возвращает 1 в случае ошибки
 get_error() - возвращает саму ошибку
 reset_error() - 
 
 
Следующий простой пример показывает, как можно вызвать скрипт на удаленной машине. Предварительно нужно скопировать remote.py, который вы сможете найти в пакете, на удаленную машину и проверить, что на ней доступен питон, а также 22-й порт по протоколу ssh.
 import exec_proxy
 import os,stat
 
 exec_proxy.remote_program=os.path.join(os.getcwd(),'.','remote.py')
 ep=exec_proxy.Exec_proxy('user@remote_host')
 print ep.listdir('./');
 
Скрипт должен вывести содержимое корневого каталога текущего пользователя на удаленной машине.






dispy

Следующая библиотека, которую мы рассмотрим - dispy. Официальный сайт - http://dispy.sourceforge.net/. Его также можно скачать у меня.

Этот модуль можно использовать на локальной много-процессорной машине(SMP), а также в кластере. Хорошо подходит для распараллеленных вычислений, где вычислительные задачи не связаны между собой. Dispy использует asyncoro - фреймворк, построенный на асинхронных сообщениях, использующий неблокирующие сокеты, epoll, Поддерживается передача вычислений, файлов, сообщений. В качестве вычислений могут выступать как функции, так и отдельные программы. Ноды могут располагаться как в локальной сети, так и в удаленной, в последнем случае можно использовать ssh. Ноды можно открывать динамически: в модуле есть шедулер, который контролирует работу нод в кластере и может перераспределять задания. Доступен механизм callback. Установить dispy можно, скачав пакет исходников, либо с помощью команды pip3 install dispy.

Dispy состоит из 4 основных компонентов или собственно модулей :
1. dispy.py - базовая библиотека классов для cоздания кластеров. Есть 2 механизма создания кластеров - JobCluster либо SharedJobCluster. В первом случае используется встроенный шедулер, во втором случае должен быть создан отдельный шедулер с помощью другого модуля - dispyscheduler.py

2. dispynode.py - устанавливается на каждой кластерной ноде и выполняет там всю работу

3. dispyscheduler.py - шедулер, который может управлять работой нод самостоятельно.

4. dispynetrelay.py - нужен в случае использования сложной сетевой конфигурации. Если все ноды кластера перчислены в качестве параметра на этапе его создания, этот модуль не нужен. Он может получать информацию о дополнительных нодах в процессе работы.

Теперь рассмотрим простой пример. Создадим на локальной машине кластер из 5 нод, каждая из которых будет выполнять одну и ту же функцию compute. Под локальной нодой мы понимаем отдельный питоновский процесс. Каждой ноде будет передаваться случайное число в диапазоне от 1 до 10 секунд, в течение которых каждая нода будет находиться в паузе, после чего каждая нода будет возвращать собственный id-шник. В конце основной сценарий будет распечатывать протокол ответов. Основной сценарий.

 def compute(n):
     import time, socket
     time.sleep(n)
     host = socket.gethostname()
     return (host, n)
 
 if __name__ == '__main__':
     import dispy, random
     cluster = dispy.JobCluster(compute, nodes=['localhost'])
     jobs = []
     for n in range(5):
         job = cluster.submit(random.randint(1,10))
         job.id = n
         jobs.append(job)
     # cluster.wait()
     for job in jobs:
         host, n = job()
         print '%s executed job %s at %s with %s' % (host, job.id, job.start_time, n)
     cluster.stats()
 
Перед тем, как запустить основной сценарий, нужно сначала локально запустить следующую команду:

dispynode.py -d -i localhost

После этого запускаем основной сценарий, который выведет что-то типа:

 localhost executed job 0 at 1414068881.77 with 19
 localhost executed job 1 at 1414068881.77 with 12
 localhost executed job 2 at 1414068893.8 with 20
 localhost executed job 3 at 1414068900.81 with 11
 localhost executed job 4 at 1414068911.84 with 18
 ()
                            Node |  CPUs |    Jobs |    Sec/Job | Node Time Sec
 ------------------------------------------------------------------------------
  127.0.0.1 (localhost)          |     2 |       5 |     16.020 |        80.100
 
 






Оставьте свой комментарий !

Ваше имя:
Комментарий:
Оба поля являются обязательными

 Автор  Комментарий к данной статье