Новый подход к переносу процессов

Базовые понятия Развитие современных информационных технологий, широкое распространение распределенных и кластерных систем, а также обилие как вычислительных задач, так и пользовательских сервисов, запускаемых на них, привели к необходимости создания механизмов балансировки загрузки. Балансировку загрузки здесь можно определить как специализированный механизм, собирающий информацию о загруженности узлов распределенной или кластерной компьютерной системы и на основе полученной информации переносящий подзадачи или процессы с одного, более загруженного, узла на другой, менее загруженный. Это позволяет значительно ускорить выполняемые в системе операции, снять чрезмерную нагрузку на сеть в случае задач, основанных на взаимодействии с внешней сетью, а также снять риск отказа системы из-за чрезмерной перегруженности одних узлов и простоя других. Балансировка загрузки имеет три составляющих: механизм сбора информации о загрузке узла, в том числе о степени загрузки, приходящейся на долю каждого процесса, и соединениях каждого процесса; механизм анализа и принятия решений о перераспределении процессов между узлами и механизм переноса процессов. Под переносом процессов здесь подразумевается следующее: прекращение выполнения процесса на одном узле системы; сохранение его состояния, в том числе информации об используемых ресурсах, памяти, регистрах процессора, используемых файлах и связях с другими процессами; перенос этих данных на другой узел и, наконец, на основе этих данных создание нового процесса на новом узле, идентичного ранее выполняемому, будто прежний и не прекращал выполняться. В силу того, что большинство существующих кластерных и распределенных систем построены на основе операционной системы Linux и ее модификаций, а также ввиду открытости ее исходного кода большое количество существующих механизмов переноса процессов работают именно с процессами Linux. И в дальнейшем под переносом процессов будет подразумеваться именно перенос процессов Linux. Помимо использования переноса для балансировки загрузки, системными администраторами Linux-сетей был найден широкий круг задач, в котором этот механизм нашел свое применение. К ним относятся: обеспечение отказоустойчивости системы путем переноса процессов с неисправных узлов на исправные; локализация доступа к данным путем переноса процессов ближе к используемым им данным; ускорение времени отклика системы путем переноса процессов ближе к пользователям; улучшение доступности служб и администрирования путем переноса процессов на другой узел перед обслуживанием узла, на котором они были запущены, так, чтобы приложения могли продолжить свое выполнение с минимальным временем простоя, и другие. Обоснование необходимости нового подхода Среди основных требований, предъявляемых к переносу процессов, можно отметить следующие: прежде всего, перенос образа процесса, состояния регистров, открытых файлов, уникальных идентификационных номеров (UID и GID), текущей рабочей директории, а также состояния и свойств терминала и обработчиков сигналов. Этих требований было достаточно для использования механизма переноса для трудоемких кластерных вычислений, где задача разбивалась на несколько абсолютно независимых и не взаимодействующих частей. Однако не все задачи могут быть разделены таким образом. Как же быть с интерактивными задачами и теми, для выполнения которых требуется взаимодействие с сетью или с другими процессами, например использующими программные каналы (pipes) или сокеты (sockets)? Таким образом, возникли еще два дополнительных требования к механизму переноса: возможность переносить процессы, взаимодействующие через программные каналы и сокеты, не нарушая соединения. Программный канал можно кратко определить как системный интерфейс для передачи данных между процессами в двух направлениях внутри одного узла. И именно то, что все взаимодействие происходит в рамках одного узла, делает перенос процессов, взаимодействующих посредством этого механизма, относительно легким. Это делается посредством переноса обоих взаимодействующих процессов на новый узел и одновременного запуска. Существует несколько реализаций переноса процессов с поддержкой программных каналов. Сокет представляет собой системный интерфейс для передачи данных через протоколы TCP и UDP как внутри одного узла, так и между разными узлами. В реальных Unix-системах сокеты служат также для других целей, но в рамках этой работы будет рассматриваться именно такое определение. Этот интерфейс ссылается на структуру сокетов в ядре операционной системы Linux, хранящую данные об одном TCP-соединении, в том числе информацию о процессах, связанных с ним, его состоянии, о маршрутизации для его пакетов, очередях TCP, а также о номерах пакетов для поддержания связи. Любое открытое TCP-соединение идентифицируется четырьмя параметрами: — IP-адрес и порт одной стороны соединения; — IP-адрес и порт другой стороны соединения. При переносе процесса вместе с сокетом возникает следующая проблема: каким образом переместить процесс на другой узел и при этом не изменить эти четыре параметра или изменить их на обоих концах соединения так, чтобы установленная связь не была нарушена? Существует несколько подходов к решению данной проблемы. Оставлять на месте процесса агент, маршрутизирующий данные соединения. При этом, очевидно, эти четыре параметра никак не затрагиваются. Но для балансировки загрузки такой метод неприменим, в силу того что балансировка предназначена для разгрузки слишком загруженных узлов, а оставление агента не только чрезмерно нагружает сеть в системе, но и создает дополнительную нагрузку в виде самого агента. Менять параметры соединения удаленной стороны. Эти четыре параметра также останутся согласованными. Однако для балансировки загрузки и этот метод неприменим, в силу того что он значительно сужает круг задач, запускаемых на таких системах. Например, это не позволяет запускать такие распространенные задачи, как сетевые сервисы и приложения, использующие соединения со службами и системами, которые мы не в состоянии контролировать. Использовать для всех соединений системы единый proxy-сервер, который будет поддерживать соединение и перенаправлять его пакеты в случае переноса процесса. Четыре вышеуказанных параметра также остаются неизменными. Но этот подход создает такие дополнительные трудности, как единственность точки доступа в систему, а значит, и единственность точки, неисправность которой приведет к невозможности сетевого взаимодействия всей системы; невозможность использования переноса в системах с несколькими точками доступа, в частности в системах, сильно разнесенных географически. Отсюда вытекает необходимость разработки нового подхода к переносу открытых TCP-соединений, не обладающего недостатками вышеуказанных подходов. Некоторые из существующих реализаций неудобны еще и тем, что меняют код ядра, в том числе исходный код реализации IP-стека и TCP-протокола, что добавляет дополнительную трудность для внедрения переноса процессов в стабильно работающие системы. Ведь для установки такого механизма требуется полная перекомпиляция ядра Linux, а это не всегда приемлемо. Таким образом, обозначена потребность в разработке механизма переноса процессов, удовлетворяющего следующим требованиям: — в первую очередь, механизм должен предоставлять возможность переноса образа процесса, состояния регистров, открытых файлов, уникальных идентификационных номеров (UID и GID), текущей рабочей директории, а также состояния и свойств терминала и обработчиков сигналов, а также открытых программных каналов; — возможность переноса открытых TCP-соединений: — абсолютно прозрачно для удаленной стороны соединения; — не оставляя на месте процесса агент, маршрутизирующий данные соединения; — не используя proxy-сервер для маршрутизации соединений или данных соединений системы; — должен быть легко интегрируем в уже работающие системы. Существующие методы решения и их недостатки Как упоминалось выше, в настоящее время существует три подхода к переносу процессов вместе со всеми их открытыми TCP-соединениями: оставлять на месте процесса агент, маршрутизирующий данные соединения; менять параметры соединения удаленной стороны; использовать для всех соединений системы единый proxy-сервер. Для того чтобы понять, чем же предлагаемое нами решение отличается от них и чем оно принципиально лучше, требуется более детальное их рассмотрение. Наиболее известной реализацией первого подхода является система MOSIX , разработанная в Institute of Computer Science of The Hebrew University of Jerusalem. Ее основная идея заключается в следующем: каждый процесс MOSIX имеет свой так называемый «уникальный домашний узел» (Unique Home Node, или UHN), где он был создан. Обычно это узел, на котором авторизовался пользователь. Когда возникает необходимость перенести процесс на другой узел, мигрирующий процесс разделяется на два контекста: контекст пользователя, который мигрирует, и контекст системы, зависящий от UHN и не способный к миграции. Контекст пользователя, называемый также удаленным (remote), содержит программный код, стек, данные, карты памяти и регистры процесса. Удаленный контекст инкапсулирует процесс, когда он запускается на уровне пользователя. Системный контекст, называемый также представителем (deputy), содержит описания ресурсов, которые процесс присоединяет к себе, и стек ядра для запуска системного кода от имени процесса. Представитель инкапсулирует процесс, когда он запускается на уровне ядра. Это сохраняет узло-зависящую часть системного контекста процесса, следовательно, это должно остаться на UHN процесса. В то время как процесс мигрирует в течение времени между различными узлами, представитель никогда не мигрирует. Связь этих двух контекстов реализуется посредством соединительного канала для взаимодействия. На рис. 1 показано взаимодействие двух процессов. На рисунке левый процесс является нормальным процессом ОС Linux, в то время как правый разделен и его удаленная часть мигрировала на другой узел. Системный вызов синхронизируется взаимодействием между двумя контекстами процессов. Все системные вызовы, запущенные процессом, перехватываются на уровне связи (link layer) удаленного узла. Если системный вызов не зависит от узла, то он выполняется локально на удаленном узле. В противном случае системный вызов транслирует представитель, который запускает системный вызов от имени процесса в UHN. Представитель возвращает результат обратно на удаленный узел, который затем продолжает выполнение пользовательского кода. Из вышеописанного понятно, что все сетевые связи (сокеты) создаются на UHN, таким образом, добавляя накладные расходы, связанные с соединением. Говоря в целом, при переносе открытых соединений, впрочем, как и при создании нового соединения, происходит маршрутизация пакетов с UHN на удаленный узел, что создает не только большую нагрузку на сеть между этими двумя узлами, но и дополнительно нагружает систему в целом за счет дополнительных ресурсов на поддержание подобного разделения процессов. К недостаткам MOSIX можно также отнести и необходимость перекомпиляции ядра Linux, что обусловливает относительную сложность ее внедрения в существующие системы. Наиболее успешной и относительно недавней реализацией второго подхода является ZAP — механизм переноса простых и сетевых приложений, разработанный в Department of Computer Science of Columbia University. Для переноса открытого соединения в ZAP необходимо, чтобы на обоих концах соединения была установлена данная система. А сам перенос происходит следующим образом: сначала на удаленную сторону отправляется уведомление о приостановке соединения; затем удаленная сторона приостанавливает процесс; после чего мигрирующий процесс останавливается и переезжает на другой узел; восстановившись, он отправляет уведомление о продолжении соединения и о новом расположении конца соединения; затем параметры соединения удаленной стороны меняются в соответствии с полученным уведомлением, и процесс выполняется, продолжив тем самым взаимодействие через данное соединение. Также механизм ZAP использует третий подход, или подход, основанный на создании единого proxy-сервера, для того чтобы обеспечить возможность взаимодействия с процессами вне системы. Этот факт значительно повышает применимость механизма для решения многих задач. Кроме того, ZAP выполнен в виде модуля ядра, и значит, не требует изменения кода ядра Linux или переустановки системы, что обусловливает легкое внедрение данного механизма в существующие системы. Тем не менее второй подход, используемый в этой системе, не позволяет решать обширный круг задач, а третий недостаточно безопасен. Комбинация этих двух подходов, конечно, позволяет расширить круг задач, но подвергает систему, его использующую, определенным рискам, что не всегда допустимо. Кроме ZAP, proxy подход реализует система MSOCKS (Carnegie Mel- lon University и IBM T. J. Watson Research Center). В основе системы лежит довольно простой алгоритм переноса открытого соединения: в начале proxy-серверу отправляется уведомление о переносе процесса на другой узел, после чего proxy прекращает отправлять пакеты процессу в системе, не разрывая при этом связь с удаленной стороной соединения; после этого процесс останавливается и переносится на другой узел; затем, когда процесс восстановился на новом узле, происходит соединение с proxy-сервером и уведомление его о намерении восстановить соединение и текущем расположении процесса; затем proxy просто продолжает перенаправлять пакеты, приходящие с обеих сторон соединения, изменяя их заголовки. Применяемый подход позволяет использовать перенос процессов и в системах, активно взаимодействующих с внешними ресурсами, контролировать которые мы не в состоянии. Однако и здесь есть свои подводные камни: что произойдет, если proxy-сервер выйдет из строя? Сетевое взаимодействие как внутри системы, так и вне ее будет невозможным, что может привести к неработоспособности всей системы. Это накладывает серьезные ограничения на применимость таких систем. Кроме того, что делать с системами, сильно разнесенными географически, где обращение к proxy-серверу будет создавать огромные задержки? Таким образом, определяется необходимость в разработке нового подхода (или модификации существующих), который позволил бы избежать всех вышеозначенных проблем. Предлагаемое решение и его обоснование Транспортные протоколы TCP и UDP изначально разрабатывались для взаимодействия двух стационарных узлов сети и не предусматривают возможность одного из концов перемещаться с одной машины на другую. В частности, открытое соединение характеризуется, как было сказано выше, четырьмя параметрами, которые не меняются на протяжении всей жизни соединения и идентифицируют соединение среди других. Кратко обозначим их , где IP1, port1 — IP-адрес и порт одной стороны соединения, а IP2, port2 — IP-адрес и порт другой стороны соединения. При переносе одной стороны на другой узел, с другим IP-адресом и, возможно, с занятым портом port1, возникает несогласованность между идентификационной парой соединения и его теку