Inferno OS Wiki
Advertisement
Оригинал: owen/tut/over.pdf
Автор: Roger Peppe

Owen(TM) - Inferno Compute Grid[]

Robert Owen (1771-1858) был британским предпринимателем и филантропом, чьим достижением было создание беспристрастной системы биржи труда: обмен рабочих часов на рабочее задание между кооперативными обществами. Inferno Compute Grid, названная в его честь, привносит тот же подход в распределенные вычисления.

Базовым понятием является то, что машина выступает как вычислительный узел, сообщающийся с центральным узлом системы, также называемым планировщиком, заявляет о выполнении определенного рода работы и ожидает исходных данных для ее исполнения. Планировщик подбирает рабочих и задания, а так же убеждается что нет простаивающих или ложных (ошибочных) работников, которые не могут нарушить равномерной работы системы.

Система Owen реализован на базе ОС Inferno, которая разрабатывалась для использовании в качестве распределенной системы. Inferno - лекая и переносимая, позволяющая без изменений и перекомпиляции запускать приложения в средах Microsoft Windows, Linux, FreeBSD, MacOS X и других. Вы также можете запустить ее как родную ОС на многих аппаратных платформах. Несмотря на то, что приложения для Inferno написаны на на языке программирования Limbo, взаимодействие с родным приложениями низлежащей ОС остается возможным. В частности, существует способ запускать и управлять уже существующими приложениями ОС из Inferno.

Обзор системы[]

Inferno computational grid состоит из трех компонентов:

  • Планировщик (Scheduler)
Планировщик - это "биржа труда": он привязывает задания к рабочим, посредничает в переносе данных и проверяет, что задания были выполнены на удовлетворительном уровне перед тем как сделать их завершенными.
  • Рабочие (Workers)
Рабочие - это узлы (клиенты), которые фактически выполняют работу для grid. Идея в том, что рабочие идут на биржу труда (сервер), объявляют тип работы, которую они готовы выполнить, и ждут пока что-нибудь не получат.
  • Управляющие (Controllers)
Управляющие также подключаются к бирже труда, но вместо запроса на работу, они объявляют работы, которые нужно сделать. Каждая работа состоит из нескольких заданий, каждое из которых должно быть выполнено рабочим (некоторые или все эти задания могут требовать специального рабочего для выполнения).

Основной компонент труда, используемый в Owen, это работа (job), представляющая труд, который необходимо выполнить. Работа разбивается на множество небольших независимых вычислений, называемых задания (tasks), каждое из которых передается рабочему для выполнения. Так как задания независимы, их произвольное подмножество может быть запущено на нескольких процессорах одновременно. Например, шифровальщик может создать работу по тестированию большого набора ключей против каких-либо зашифрованных данных; пространство ключей может быть разбито на кусочки (chunks), каждый из которых будет тестироваться отдельным заданием, позволяя протестировать множество кусочков за раз, каждую с помощью задания, отданного отдельному рабочему.

При достаточном количестве узлов и размере сети что-нибудь рано или поздно откажет в ходе работы, особенно когда одна задача (не говоря уже о всей работе) занимает значительное время или обрабатывает большое количество данных. Некоторые системы используют сложные схемы автоматического сохранения состояния процессов и их миграции. Вместо этого наша система полагается на избыточность. Разделение работы на задачи не только увеличивает параллелизм, система может положиться на нее для улучшения надежности. Если произойдет сбой в работе узла и сети, планировщик может автоматически перенести задачу на другой узел. Иными словами, система гарантирует, что каждая задача будет выполнена по крайней мере один раз. Из этого следует, что задача должна быть достаточно мала чтобы иметь хорошие шансы на то, что средний узел будет в состоянии завершить ее в течение своего времени работы.

Генератор задач (task generator) отвечает за расщепление работы на отдельные задачи. Запуск работы Owen влечет за собой запуск генератора задач с некоторыми относящимися к работе параметрами, которые позволяют ему создать соответствующие задачи. В примере крекинга кодов параметры могут описывать размер пространства ключей, задавать алгоритм шифрования, а также предоставлять данные для декодирования; затем генератор задач может генерировать задачи на основе сегментов пространства кодов до тех пор, пока одна из задач не объявит о том, что код был взломан, после чего работа будет объявлена выполненной. Планировщик Owen отвечает за устойчивость и восстановление ошибок: он гарантирует, что будут выполнены все задачи, не взирая на неправильно работающих клиентов, отключение сетей, отказы устройств и другие сбои. Генераторы задач не так просто писать, и обсуждение этого вопроса выходит за рамки данного документа, но Owen предлагает несколько генераторов задач общего назначения, каждый из которых готов справиться с некоторым общим классом работы. Один из них, известный как filesplit, берет файл и создает задачи, каждая из которых обрабатывает некоторый указанный участок файла. Другой, известный просто как job (работа), рассчитан на более широкий диапазон работ, которые имеют известное количество задач, каждая из которых требует некоторые входные данные в виде файлов данных и значений параметров, возможно, различные для каждой задачи. Ниже приводятся примеры.

Развертывание Grid[]

Первое, что нужно сделать, это решить где должен располагаться планировщик. Он должен работать на общедоступной машине (он прослушивает сетевой порт для соединения, так что этот порт должен быть доступен для всех машин, которые вовлечены в Owen grid). Процесс установки планировщика сводится к полной установке ОС Inferno и доустановке ПО Owen.

После установки планировщика вам необходимо будет установить программное обеспечение на каждого работника (также известного как "клиент"). Программное обеспечение работника состоит из усеченного дистрибутива Inferno, содержащего только то, что нужно клиенту Owen для запуска, а также для отсылки новых заданий (то есть, любой Owen-рабочий потенциально может выступать в качестве Управляющего).

Для небольшого числа узлов, вероятно, проще всего установить программное обеспечение в индивидуальном порядке на каждый узлел.

При установке на большее количество узлов, существуют различные возможные подходы, в зависимости от локальной установки. Первое, что нужно сделать, это произвести шаблонную установку, содержащую все файлы, которые будут необходимы на каждом клиенте. Обратите внимание, что вам необходимо обдумать, какие задачи будет выполнять рабочий до фактического развертывания установки. Это важно из соображений безопасности.

Затем вам потребуется скопировать инсталляцию на все остальные клиентские узлы. VN Grid Client Replicator может использоваться на Windows-машинах (смотрите далее документацию).

В не-Windows сетях, установка будет зависеть от ваших локальных настроек. Обычно, процедура заключается в копировании инсталяционного дерева в файловую систему каждого клиента, и в настройке каждого клиента так, чтобы клиентская программа запускалась при загрузке. Когда несколько узлов разделяют общую файловую систему, важно чтобы по крайней мере каталог /grid/slave был уникален для каждой запущенной инсталляции, поскольку в нем хранятся случайные данные и возникнет множество проблем в случае использовании одной общего каталога.

В дальнейших примерах, мы предполагаем, что у нас есть локальная сеть из нескольких персоналок под Windows или Unix или их кобианция, защищенная файерволом от Internet. Это возможно, наипростейший вид установки (вы предполагаем что у нас доверенная сеть, и что клиентские машины подготовлены и на них запущено ПО, с которым может взаимодействовать сервер).

Существует множество способов создания вычеслительного grid. Программное обеспечение может быть сконфигурировано так, чтобы удовлетворить запросам каждого приложения.

Однако, в первых двух примерах ручные настройки сведены к минимуму, поскольку они демонстрируют способы создания grid, хорошо срабатывающие в большинстве случаев.

Пример: Установка и тестирование grid[]

Выбор сервера для планировщика[]

Правильным выбором будет машина с высоким up-time, предпочтительно выделенная машина. Обычно, это - сервер, но может быть чьим-то десктопом, если это удобно. В любом случае, если машина оснащена файерволлом, таким как у некоторых Windows-машин, то вам нужно разрешить входящие соединения по крайней мере на один порт. По-умолчанию, используется порт 6678, называемый infsched в Inferno. В Inferno, сетевые адреса задаются текстовой строкой, состоящей из трех частей, разделенных восклицательным знаком (!). Первая часть - тип сетевого протокола (например, "tcp"); вторая - имя или IP-адрес машины в сети, например, gridserver.somewhere.com или 200.1.1.94; третья часть - имя службы или номер ее порта, например, "infsched" или "6678".

Таким образом, полный адрес планировщика Inferno может выглядеть таким образом:

tcp!gridserver.somewhere.com!infsched

Вы можете использовать префикс net вместо tcp для использования любого доступного протокола или сети, средствами которых можно получить доступ к хосту.

Установка программного обеспечения[]

Следуйте приведенным выше инструкциям для установки программного обеспечения планировщика на сервер и клиентского обеспечения на каждую машину-рабочего. Убедитесь, что вы задали адрес сервера в качестве параметра атрибута schedaddr в конфигурационном файле /grid/slave/config.

Разрешение произвольного выполнения на каждом клиенте[]

Каждый клиент позволит только серверу запросить выполнение определенного набора заданий, перечисленных в скриптах оболочки Inferno и сохраняемых в каталоге /grid/slave на каждом клиенте. Эта библиотека шелл-скриптов для возможных заданий в /grid/slave/tasks, и данне задание будет доступно клиенту при помощи копирования файла-прототипа задания ".job" в каталог /grid/slave.

В этом примере, мы собираемся разрешить выполнение всего.

Скрипт задания который позволит это сделать будет называться test.job и расположен в /grid/slave/tasks/test.job. В действительности, он очень прост:

load std
fn runtask {
$* > result
}
fn submit {
cat result
}

Для разрешения этого задания скопируем этот .job файл:

cp /grid/slave/tasks/test.job /grid/slave

Запуск grid[]

Запуск серверного программного обеспечения и клиентского программного обеспечения для Windows-клиентов на каждой машине будет детально описан в последующих инструкциях.

Проверка работоспособности grid[]

Теперь мы можем запустить простую работу в grid для того, чтобы проверить что все правильно функционирует. В общем, задание включает в себя чтение неких записей или файлов, их обработки, и записи результатов. Работа включает в себя большой набор записей (или файлов), деление их на меньшие под-наборы, по одному на задание, и передачу их планировщику, который отправляет их подходящему рабочему по его запросу, позднее возвращает результат. Окончательный результат является набором записей (или файлов) каждый из который соответствует результатам одной задачи. На выходе заданий нет повторяющихся результатов, за тем исключением, когда задание было перезапущено или выполнено повторно из-за сбоя.

В первом примере, у нас будет grid обрабатывающая файл, каждая строка которого это является отдельной задачей для выполнения. Задание которое мы запускаем, выдает контрольную сумму MD5 каждой строки текста в файле, очень быстрое задание, однако оно позволит нам легко проверить соответствие между вводом и выводом. (MD5 это математическая функция рассчитывающая "контрольную сумму" - число зависящее от входных данных и которое сильно разниться от одних данных к другим). Для этого начального теста, мы начнем работать внутри окружения Inferno, поскольку оно везде одинаково. Позднее, как запустить собственные приложения хоста (например, бинарные исполняемый файлы) за пределами окружения.


Запуск оконного менеджера Inferno[]

На подходящей клиентской машине, дважды щелкните на иконку Vita Nuova для запуска менеджера Inferno.

Запуск мониторов Grid[]

Щелкните по иконке снизу, в левом углу окна Inferno, для показа меню приложений и щелкните на "Grid" для отображения другого меню с двумя программами-мониторами, одна для "узлов" (так называемых подключенных рабочих машинах), и другая для работ. Сначала выберите "Node Monitor" (монитор узлов). Если все работает нормально, программа соединится с планировщиком Inferno-grid, и отобразит список подключенных в настоящее время клиентов. Если это сработало, вы можете запустить "Job Monitor" (монитор работ) таким же способом.

Создание входных параметров работы[]

Теперь вызовем меню приложений Inferno как раньше, и щелкнем на пункте "Shell" запустив окно командного интерпретатора Inferno. Все что нам надо в этом примере, это маленький текстовый файл который выступив в качестве входных данных. Мы можем создать его сами, но в тестовых целях проще скопировать уже существующий файл, например:

cp /NOTICE /tmp/mytasks

Мы заставим grid обработать каждую строку в этом файле, и выдать результаты в файл /tmp/mytasks.result.

Запуск работы[]

Существует два способа сделать это, использовать для этого приложение Монитор Работ или посредством командной строки. Воспользуемся Монитором работ, щелкнем на кнопку "New" и выберем из появившегося окна "Generic job". В появившейся панеле наберем следующее в текстовой области отмеченной как "Command":

filesplit -l /tmp/mytasks test md5sum

Вы можете ввести необязательное описание, такое как "first test job" в поле "Description".

Затем кликните на "Start job" для отправки очереди работ планировщику.

Поясним запущенную нами работу: filesplit берет файл (указанный в качестве первого аргумента), делит его на множество записей - здесь опция "-l" говорит, что записи разделены символом "новая строка" - и компонует задания так, чтобы каждая запись была передана в качестве аргумента. В этом случае, когда запускается тестовое задание, оно вызывает ранее установленный скрипт test.job на каждом клиенте, с аргументом md5sum. Так что, мы договорились запустить команду md5sum на каждом клиенте. По сглашению, filesplit помещает объединенные результаты в файл с названием входного файла (как/tmp/mytasks, указанный выше) и присоединяет к нему суффикс .result.В этом примере, результаты будут сохранены в файле /tmp/mytasks.result.

Поскольку у нас нет много заданий, и они не занимают много времени, эта работа не продлится долго. Монитор Работ должен показать новую работу со статусом "running". Монитор Работ периодически обновляет дисплей сам по себе, поэтому нет необходимости прокручивать задания для обновления состояния. Щелчок на новой работе покажет ее статус и ход.

В качестве альтернативы, вы можете запустить работу из командной строки Inferno. Из окна с запущенным шеллом, сначала сделаем доступным пространство имен планировщика, и затем запустим новую работу, подставив настоящий адрес планировщика в schedaddr:

% run /lib/sh/sched
% mountsched
% start filesplit -l /tmp/mytasks test md5sum
1
%

Первые две команды потребуется ввести только один раз за сессию. Файл /lib/sh/sched содержит несколько функций оболочки Inferno которые упрощают взаимодействие с планировщиком из командной строки. Одна из таких функций mountsched, которая считывает клиентскую конфигурацию, обнаруживает планировщик и делает его доступным в иерархии каталогов в открытом окне интерпретатора Inferno. Вы можете просмотреть пространство имен планировщика при помощи:

lc /n/remote

Другой шелл-скрипт - start, он создает новую работу с заданными параметрами (такими как, "filesplit etc."). Он выводит номер работы (1, как в случае выше).

Проверка результатов вывода работы[]

Теперь мы можете посмотреть результаты работы в файле /tmp/mytasks.result:

% cat /tmp/mytasks.result
data 33 2
68b329da9893e34099c7d8ad5cb9c940
data 33 0
af325e1d9e7df186ec43b7064971e591
data 33 3
b1ab8481a01ba86bc936c679f9d09187
data 33 1
bd04ba4c99cb67c7371b6a2c359d7391
[...]

Обратите внимание, что выходной формат отличается от входного, состоит из записей в простом формате, каждая запись состоит из заголовка и следующих за ним собственно данными, в этом случае это будет вывод команды md5sum для каждой строки входных данных. Поскольку задания обрабатываются параллельно, и завершаются в непредсказуемой последовательности, последовательность записей может быть перемешана - второе число в заголовке каждой записи содержит номер входного задания.

Итак, в выше приведенном случае, мы видим, что задание номер 2 закончилось первым, далее идет задание номер 0 и т.д. Мы можем удостовериться, что результаты соответствуют нашим ожиданиям, запустив команду md5sum локально; например, мы попробуем проверить четвертую входную строку (запись номер 3):

% echo `You may copy and redistribute the package as a whole,` | md5sum
b1ab8481a01ba86bc936c679f9d09187
%

Теперь grid готова к использованию. В этой конфигурации, которая подразумевает однотипный набор программного обеспечения доступный на каждом узле (если один из узлов утрачивает программное обеспечение, он будет быстро занесен в черный список, как не могущий выдавать желаемые результаты). Так же возможно выполнять произвольные шелл-скрипты Inferno, я не только простые команды. Обратите внимание на то, что командный интерпретатор Inferno используется программным обеспечением grid для быстрого портирования рабочего процесса (workflow) между различными оперционными системами и аппаратными платформами. Например, мы можем расширить предыдущий пример, для того чтобы задания выполнялись дольше:

start filesplit -l /tmp/mytasks test {
md5sum
sleep 5
}

Выходные данные работы будут такими же (порядок выполнения может различаться), но теперь каждый узел будет засыпать на 5 секунд, поэтому мы можем (вероятно) более легко увидеть некоторый прирост производительности за счет параллельного выполнения в grid.

Запуск собственной работы[]

Для экономии времени, мы обсудим работы которые могут быть втиснуты в вышеописанную форму. Такие работы состоят из набора независимых заданий, каждое из которых отличается своими входными данными. Если задача просто читает стандартный ввод и пишет на стандартный вывод, все просто - все что вам потребуется это заменить md5sum на команду для запуска. Если команда не запускается напрямую из Inferno (обычно так и бывает), вы можете использовать команду Inferno os(1) для запуска программы хост-ОС (ниже приведен пример).

Для большинства программ будет необходимо выстроить файл со входными данными содержащими записи как было описано ранее в обсуждении выходного формата. Такой формат должен быть простым, чтобы его было просто сгенерировать самостоятельно скриптовым языком по вашему выбору. В качестве альтернативы, вы можете воспользоваться двумя командами доступными из командной строки Inferno, owen/file2rec и owen/rec2file; производящие чтение данных их аргументов и выдающих файлы с записями, каждая запись которого содержит данные из соответствующего файла; последняя записывает файл для каждой записи которую найдет в своем стандартном входе.

Довольно обычно для команд, принимать файл в качестве ввода в дополнение (или взамен) стандартного ввода. Это можно уладить путем построения входных данных задания как файлового архива содержащего все необходимые файлы. В этом примере мы распределим двоичный файл Windows со всей информацией которую он использует для расчетов. Помните, что в этом примере мы действуем в доверительном окружении; если серверу нельзя доверять, тогда клиенты ни в коем случе не должны иметь установленный скрипт test.job! Также обратите внимание на то, что следующая техника не будет работать (без изменения) если клиенты запущены на других операционных системах или используют разные двоичные архитектуры. Мы предполагаем в этом примере, что бинарные файлы для выполнения будут скопированы в /tmp/myprog.exe, и что входные файлы для каждой задачи будут помещены в каталоги /tmp/task/0, /tmp/task/1, и т.д.

Для простоты, предположим, что все результаты выполнения команды записываются на стандартный вывод.

Во-первых, мы подготовим специальный файл который будет содержать входные данные для всех заданий в работе, включая копии исполняемого файла для каждого задания. (На практике, особенно когда исполняемый файл большого размера, мы могли разместить его в разделяемое хранилище и получить к нему доступ или скопировать его на каждый клиент.) Из командного интерпретатора Inferno наберите следующие команды:

% cd /tmp/task
% mkdir record
records
% for(i in [0-9]*){
mkdir record/data
cp -r $i/* record/data
cp /tmp/myprog.exe record
cd record
puttar . > /tmp/task/records/$i
cd ..
rm -r record/*
}
% cd /tmp/task/records
% owen/file2rec * > /tmp/mytasks
%

Теперь у нас есть один файл содержащий все входные данные, каждая запись в котором это tar-архив содержащий исполняемый файл и входные файлы к нему (расположены в каталоге data). Мы можем запустить работу из командной строки Inferno, в окне где запущен планировщик. В новом окне наберите следующее:

run /lib/sh/sched
mountsched

это сделает планировщик видимым как описывалось ранее. Теперь запустим работу:

% start filesplit /tmp/mytasks test {
gettar
hostwork = $emuroot/$work
os -n -d $hostwork/data $hostwork/myprog.exe < /dev/null
}
%

Перед выполнением каждой задачи (команды между скобками), переменная окружения emuroot устанавливается в имя каталога Inferno на этом хосте, и переменная work - временный каталог созданный для запуска задания. Соединив их вместе в hostwork создаем имя команды хост-системы (такое как myprog.exe см. выше) для обращения к каталогам и файлам содержащим задания внутри дерева Inferno. Команда os запускает команду основной ОС при помощи интерпретатора команд основной ОС. Ей передаются две опции: -n, которая запускает команду с низким приоритетом, и -d, которая содержит имя каталога основной ОСкоторая команда должна использовать в качестве своей рабочей (текущей) директории, $hostwork/data в нашем случае, помещает в этот каталог входные файлы задания. Обратите внимание на то, что команда os читает ввод и из устройства /dev/null, когда команда myprog.exe не читает свой стандарный ввод. Если бы ей был необходим ввод, скажем из файла myinput в рабочем каталоге, мы могли бы написать:

os -n -d $hostwork/data $osroot/myprog.exe <myinput

Когда все задания в этой работе бужут завершены, результаты будут сохранены в /tmp/mytasks.result как и раньше.

The job task type[]

There is no particular association of task generator with the actual task performed. For instance, we saw that the task generator filesplit reads input records from a file and allows them to be processed by an arbitrary task before writing the results record to another file. In this section, we look at the job task type, which uses a compact high-level job description to describe the input data for a specified task, and tells how to collect the resulting output. This is probably the easiest way to use Owen.


TO DO[]

  • complete example using "job"
  • where do the results go, and how to collect them?
  • job submission from command line? perhaps non-Windows only
Advertisement