Сти, гибкости системы администрирования и удобства использования кластера в прикладных расчетах при обеспечении необходимого уровня надёжности и минимуме затрат

Вид материалаДокументы

Содержание


Конфигурация ядра
Initial RAM-диск
Preboot eXecution Environment (PXE)
Файл linuxrc.
Файл mksnapshot.
Подобный материал:
ссылка скрыта

ссылка скрыта

ссылка скрыта


Реализация бездисковых рабочих станций


Введение

В процессе создания кластерных систем необходимо добиться максимума производительности, гибкости системы администрирования и удобства использования кластера в прикладных расчетах при обеспечении необходимого уровня надёжности и минимуме затрат. Одним из вариантов решения поставленной задачи является использование бездисковых рабочих станций (в англоязычной литературе “diskless clients”), т.е. рабочих станций не использующих при работе локальный жёсткий диск. В процессе загрузки ядра системы в оперативной памяти создаётся виртуальный логический диск, на который копируются критически важные для работы операционной системы модули.

Можно добиться бездисковой загрузки используя так называемые live-disk, устройства хранения памяти на сменных носителях (чаще всего компакт-диск). Такой диск содержит минимум стандартных компонентов операционной системы, достаточный для запуска ядра системы и работы определённых приложений, в большинстве случаев специализированных под конкретные задачи. Подход, основанный на использовании сменных носителей, обладает существенными недостатками: во-первых, каждая машина должна обладать устройством чтения и своей копией сменного носителя, что влечёт за собой дополнительные расходы в результате износа носителей и значительных затрат на покупку устройств чтения сменных носителей; во-вторых, такая схема организации работы исключает полноценное администрирование кластера.

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

Список преимуществ загрузки по сети и использования бездисковых рабочих станций приведён ниже.
  1. Снижение себестоимости каждого узла.
  2. Снижение числа механических устройств, а именно - жесткого диска, более всего подверженного износу. Исключение жесткого диска существенно повышает надёжность узла кластера, снижает потребление электроэнергии и шум.
  3. Централизация системы администрирования вычислительного кластера и системы управления задач в прикладных расчетах. Это позволяет упростить действия, необходимые для изменения конфигурации программных средств, используемых всеми узлами, а также в упрощении доступа к данным, необходимых для расчётов. Все данные, используемые пользовательскими программами, в том числе настройки хранятся в одном месте.
  4. Ускорение доступа к данным. Файловая система, расположенная на удалённом узле (сервере), может, и в среднем при правильной организации вычислительного комплекса, когда не допускается частая перегрузка коммуникационной сети, работает быстрее локальной файловой системы. Перегрузка коммуникационной сети может быть вызвана недостаточно продуманной организацией обменов данных в программах пользователей кластера. На сервере установлены высокопроизводительные дисковые массивы (disk array, RAID-массивы, работающие в режиме 10), которые обеспечивают лучшие скорости записи-чтения, чем локальные жесткие диски. Пропускная способность сети Gigabit Ethernet превышает пиковую скорость обмена данными с накопителями на жестких дисках. Не следует упускать из вида и возможность использования нескольких файл-серверов (или специализированных сетевых хранилищ данных), обеспечивающих доступ к данным.
  5. Повышение сохранности информации, записанной на жестких дисках. RAID-массив, работающий в режиме 10 объединяет работу 4 жестких дисков. Это не только обеспечивает более высокую скорость доступа к данным, но и реализует параллельную запись информации на двух дисках ("зеркалирование"), что обеспечивает сохранность записанной информации при выходе из строя одного из дисков. Повышение надежности хранения данных особенно важно для вычислительных кластеров, работающих в режиме коллективного доступа. На каждый узел устанавливать RAID-массивы экономически не выгодно.
  6. Технически просто и быстро осуществляется наращивание вычислительной мощности кластера путем подключения новых дополнительных узлов.
  7. При необходимости технически просто и быстро подключаются для временного использования в качестве дополнительных узлов кластера полноценные рабочие станции или мощные компьютеры, работающие в другое время автономно. При этом не требуется изменять настройки ОС подключаемых рабочих станций, установленных на локальных накопителях данных.


При выборе метода сетевой загрузки кластера следует учитывать следующие достоинства и недостатки такого подхода:

  1. Более сложная настройка и отладка. Долгое время бездисковая загрузка не поддерживалась напрямую операционными системами, и до сих пор нет ни одного общего стандарта, хотя де-факто уже выделились основные программные продукты, получившие поддержку в компьютерной индустрии.
  2. Если будет нарушено функционирование сети и сервер не будет доступен узлам, то будет нарушена и работа всего кластера как целого, и каждого отдельного узла как бездисковой рабочей станции.
  3. Возникают ограничения на объём доступной памяти. Использование выгружаемой на удалённый компьютер оперативной памяти возможно, однако с практической точки зрения, для достижения максимального быстродействия узла не целесообразно использовать механизм выгружаемой памяти.
  4. При использовании единственного централизованного хранилища данных и интенсивной одновременной работы узлов с ними возникнет падение скорости доступа к данным. Такие ситуации редки, но могут иметь место в некоторых задачах, где поток данных велик.


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


Процесс загрузки бездисковой рабочей станции включает следующие этапы:
  • BIOS пытается поместить в память загрузчик, полученный с указанного ему устройства. Для простоты будем считать, что в состав системного блока не входят никакие дополнительные внешние устройства, кроме сетевой карты.
  • Загрузчик получает характеристики сети, свой ip-адрес, сервер на котором находится ядро ОС и т.д. Например, устройство может использовать протокол DHCP, с его помощью найти в сети сервер, который выделит ip-адрес этому устройству. Отметим, что в данной работе мы будем рассматривать только сетевые адаптеры, поддерживающие стандарт PXE, который не является единственным возможным. Существуют сетевые адаптеры, например, Etherboot, Netboot, поддерживающие другие стандарты.
  • С сервера передается загрузчик ядра, который осуществляет загрузку ядра и сжатого RAM-диска (более точно initial RAM-disk, «диск начальной загрузки»). На диске будет расположена на начальных стадиях загрузки корневая файловая система (root file system), и содержаться компоненты, необходимые для дальнейшей загрузки ОС (в том числе утилиты типа grep, cat, простейший командный интерпретатор, DHCP-клиент и т.д.).
  • С RAM-диска запускаются скрипты и программы, организующие следующий этап загрузки, второй сеанс DHCP-запросов, монтирование основной файловой системы (например, удалённой посредством NFS). Затем возможна смена корневой файловой системы (pivot_root) на удалённую, что впрочем, не является обязательным.
  • На этом загрузка самой системы закончена, начинается загрузка прикладных программ.



Конфигурация ядра


Первое, с чего надо начать развёртывание бездисковых рабочих станций – это конфигурирование ядра с заданием следующих необходимых параметров.
  1. Желательно включить поддержку используемого сетевого устройства в ядро, а не использовать загружаемый модуль, хотя можно положить необходимый модуль на initial RAM-диск. В этом случае необходимо организовать загрузку модуля из стартового скрипта ОС.
  2. Во вкладке «Networking options»: «IP: kernel-level configuration support» можно указать необходимый протокол для авто конфигурирования сетевого интерфейса. Этот протокол надо передать ядру в параметре ip или же указать в том же параметре ip-адрес самостоятельно. Если не планируется проводить конфигурирование на уровне ядра, и тем самым уменьшить объём ядра, необходимо отключить все предложенные варианты, а DHCP клиент запустить со смонтированного RAM-диска.
  3. Использование RAM-диска требует установки опции «Block devices»: «RAM-disk support», «Initial RAM disk (initrd) support» и указания необходимого размера диска по умолчанию. 10 мегабайт достаточно в большинстве случаев, но если планируется оставить initial RAM-disk основным, то следует подобрать необходимый минимум.
  4. Использование в качестве удалённой корневой файловой системы NFS требует установки «File Systems»: «Network File Systems»: «Root file system on NFS». Следует заметить, что поддержка такой возможности появилась в ядрах сравнительно недавно и долгое время не рекомендовалась к использованию. Поэтому возможно придётся получить последние стабильные ядра для используемой ОС.


Initial RAM-диск


Этот файл представляет собой сжатую файловую систему (ФС) поддерживаемую ядром (ext2), которую загружает с сервера загрузчик ядра и передаёт ядру. Ядро распаковывает файл в оперативную память (RAM-disk) и монтирует его как корневую ФС. Далее ищется скрипт загрузки модулей ядра. Им чаще всего выступает /linuxrc, более точно это зависит от параметров, переданных ядру (например, утилита redhat-config-netboot прилагающаяся к дистрибутивам от Red Hat конфигурирует систему таким образом, что ядру передаётся параметр disklessrc и соответственно ядро загружает скрипт /disklessrc). Этот скрипт запускается с uid 0.

В последних строках скрипта происходит смена корневой системы (c ФС в памяти на ФС по NFS) и продолжается загрузка операционной системы вызовом скрипта /etc/init. Однако, если указанный скрипт отсутствует или не вызывает pivot_root, смены корневой системы не происходит. В этом случае корневая система остаётся на RAM-диске.

Существует несколько способов получить файл сжатого RAM-диска. Например, можно используя loopback block device. Более подробную информацию можно получить, например, по адресу1.

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

Эта файловая система будет доступна только для чтения по NFS. Далее необходимо создать так называемые слепки (snapshot), т.е. создать файлы, уникальные для каждой машины (например, файлы fstab, ключи для авторизации, используемые ssh и т.д.), которые доступны клиенту как на чтение, так возможно и на запись. Изменение этих файлов клиентом никак не повлияет на остальные узлы. При загрузке ядру необходимо передать соответствующие параметры для запуска системой стартового скрипта, который выполнит инициализирующие действия. Последняя команда этого скрипта инициализирует «классический» процесс загрузки, однако корневая файловая система узла будет удалённой.

# полагаем, что исполняемый скрипт linuxrc

root=/dev/ram0 init=/linuxrc rw


Preboot eXecution Environment (PXE)


PXE – это результат разработок компании Intel, вылившийся в стандарт для загрузочных ПЗУ (boot ROM). В последние годы этот стандарт находит всё больше и больше поклонников и поддерживается многими производителями комплектующих (включая, конечно же саму Intel, а также 3COM, d-Link и т.д.). Существует также проект NILO (Network Interface Loader, www.nilo.org) для разработки свободно используемой реализации PXE.

Устройство, поддерживающее этот стандарт, будучи выбранным в качестве загрузочного посылает широковещательный DHCP-пакет в сеть и ждёт ответа от сервера, поддерживающего PXE-расширения. В этом ответе содержится имя файла загрузчика ядра (boot loader), который предположительно может быть выполнен на узле, посылавшем запрос. Загрузчик скачивается и запускается на выполнение, используя параметры сети, полученные в DHCP-пакете и сохранённые в памяти. Цель загрузчика, как было уже сказано выше – загрузить ядро операционной системы.

Итак, остался вопрос: а что за загрузчик?

Заслуженной популярностью пользуется загрузчик pxelinux, являющийся частью проекта Syslinux (zytor.com/). Этот загрузчик использует конфигурационные файлы, находящиеся а каталоге /tftpboot/pxelinux.cfg (предполагаем, что TFTP демон запущен с установками по умолчанию, и путь к доступным по TFTP протоколу файлам /tftpboot). Pxelinux запрашивает файл из вышеуказанного каталога по следующему принципу:

Адрес клиента, например 192.168.0.1, записывается в шестнадцатеричной системе счисления: C0A80001. Загрузчик пытается получить файл pxelinux.cfg/C0A80001. Если это невозможно, крайние цифры отбрасываются, и загрузчик пытается получить файл C0A800. Так повторяется, пока подходящий файл не будет найден, либо таких файлов в итоге не окажется. Последние версии pxelinux также пытаются загрузить файл соответствующий MAC-адресу сетевой платы (записанный в виде: 00-07-E9-4A-99-0F)


Файл C0A80001 имеет примерно следующий вид:


DEFAULT linux

APPEND initrd=initrd.gz root=/dev/ram rw

nfsroot=192.168.1.1:/export/root/node1,rw

ip=192.168.1.2:192.168.1.1:192.168.1.1:255.255.255.0:node1:eth0:off


nfsroot – если мы хотим использовать удалённую корневую файловую систему.

ip – параметры в следующем порядке: адрес клиента, адрес сервера, шлюз по умолчанию, маска сети, имя клиента, интерфейс и протокол автоконфигурирования. Также может быть ip=dhcp, если ядро скомпилировано с поддержкой конфигурирования на уровне ядра. Можно пропустить параметр ip если предпочтительнее иное конфигурирование сетевых интерфейсов.


DHCP

Протокол DHCP был создан для динамического получения ip-адреса компьютера, подключающегося к сети. Он пришёл на смену протоколу BOOTP, и не является единственно возможным способом получения ip-адреса: например, всегда остаётся возможность использования RARP протокола (используется, в частности, некоторыми маршрутизаторами для загрузки по сети), однако в наши задачи не входит рассмотрение этих протоколов, поскольку на мой (и не только мой) взгляд DHCP обладает наибольшей гибкостью.

Вкратце этот протокол работает следующим образом:
  1. Клиент посылает широковещательный пакет DHCPDISCOVER по адресу 255.255.255.255 с маской 0.0.0.0. Этот пакет может содержать некоторую информацию о клиенте, например какова его аппаратная архитектура или какой адрес ему требуется.
  2. Один или несколько серверов в сети отсылают клиенту пакет DHCPOFFER, содержащий предлагаемый сервером ip-адрес и другие параметры.
  3. Клиент формирует пакет DHCPREQUEST и отсылает ответ одному из серверов.
  4. Если сервер согласен с предлагаемыми условиями он возвращает пакет DHCPACK (содержащий назначенный ip-адрес и прочие параметры), иначе он отправляет пакет DHCPNAK и всё повторяется с шага 1.
  5. После получения ip-адреса клиент использует ARP, чтобы проверить уникальность адреса. Если этот адрес уже занят, клиент отсылает серверу DHCPDECLINE и все начинается сызнова.


Последнюю версию самой распространённой свободной реализации DHCP всегда можно получить на сайте Internet Software Consortium: ссылка скрыта. Большинство дистрибутивов содержат именно эту реализацию.


Достаточно стандартная установка (связка configure – make – make install) пропишет файл /etc/dhcpd.conf который используется демоном dhcpd. Как несложно догадаться, самое важное правильно настроить конфигурационный файл. Пример настройки приводится ниже:


# простейший конфигурационный файл DHCP


ddns-update-style none;

max-lease-time 86400;

default-lease-time 86400;

range 192.168.0.2 192.168.0.254;


### option space PXE;

### option PXE.mtftp-ip code 1 = ip-address;


# далее следует настройки для конкретной подсети

subnet 192.168.0.0 netmask 255.255.255.0 {


class "pxeclients" {

match if substring (option vendor-class-identifier, 0, 9) = "PXEClient";

option vendor-class-identifier "PXEClient";


### vendor-option-space PXE;

### option PXE.mtftp-ip 0.0.0.0;


# имя файла с загрузчиком ядра

filename "pxelinux.0";

# и адрес сервера, на котором этот загрузчик лежит

next-server 192.168.0.1;

}


# «жёсткая» привязка ip по MAC адресу

host node {

hardware ethernet 00:0C:6E:71:B6:55;

fixed-address 192.168.0.2;

}

}


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

Жёсткая привязка по MAC-адресу в общем-то не является обязательной и в настроенном кластере не нужна, может применяться при отладке системы.

Заметим, что при загрузке в среде PXE инициируется два сеанса DHCP-запросов: первый проводит PXE-клиент, а второй уже загруженное ядро для получения адреса. При этом в логах можно увидеть нечто вроде:


node33 dhcpd: DHCPDISCOVER from 00:0e:0c:a0:7e:e5 via eth0

node33 dhcpd: DHCPOFFER on 192.168.9.8 to 00:0e:0c:a0:7e:e5 via eth0

node33 dhcpd: DHCPREQUEST for 192.168.9.8 (192.168.9.33) from 00:0e:0c:a0:7e:e5 via eth0

node33 dhcpd: DHCPACK on 192.168.9.8 to 00:0e:0c:a0:7e:e5 via eth0

node33 dhcpd: DHCPDISCOVER from 00:0e:0c:a0:7e:e5 via eth0

node33 dhcpd: DHCPOFFER on 192.168.9.8 to 00:0e:0c:a0:7e:e5 via eth0

node33 dhcpd: DHCPREQUEST for 192.168.9.8 (192.168.9.33) from 00:0e:0c:a0:7e:e5 via eth0

node33 dhcpd: DHCPACK on 192.168.9.8 to 00:0e:0c:a0:7e:e5 via eth0

node33 rpc.mountd: authenticated mount request from node8:677 for /home/netboot/cent/root (/home)

node33 rpc.mountd: authenticated mount request from node8:697 for /home/netboot/cent/snapshot (/home)


TFTP

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

Установка его также тривиальна и особых пояснений не требует. TFTP запускается как сервис в xinetd и единственное, что следует настроить это путь к файлам, доступным по этому протоколу, а также права доступа к этим файлам. Для запуска демона tftp необходимо добавить в файл /etc/xinetd.d/tftp соответствующие параметры для демона. Если он отсутствует – следует создать его скопировав любой другой и сделав соответствующие изменения.

В последних дистрибутивах протокол классифицируется как legacy software. Но достойной альтернативы ему, поддерживаемому большинством оборудования, пока нет.


NFS

Network File System (NFS), - «сетевая файловая система», была внедрена компанией Sun Microsystems в далёком 1985 году как подобие файловой системы для бездисковых клиентов. Однако предложенный протокол оказался настолько удачным, что подобие со временем стало полноценным и универсальным решением для совместного использования файлов. Мало того, протокол оказался на удивление стабильным: первый выпуск имел версию 2, затем в начале 90-х годов была выпущена версия 3, которая поддерживает и старый протокол. Больше коренных изменений не было.

В данной работе не рассматриваются проблемы блокировки файлов, вопросы безопасности и конфиденциальности. Далее кратко описаны используемые нами настройки NFS.

В Linux имена экспортируемых каталогов хранятся в файле /etc/exports в текстовом виде. Нам необходимо подправить его, добавив следующие строки:


/home 192.168.9.0/24(rw,sync,no_root_squash)


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

ro экспорт только для чтения

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

root_squah закрепляет UID 0 и GID 0 за идентификаторами, указываемыми в опциях anonuid и anongid. Опуская подробности, это сделано в целях безопасности. Полностью противоположна no_root_squash, которая разрешает доступ пользователю root.


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


Приложение 1


Процедура создания образа системы, инициализации RAM-диска


Файл mk_net_rd.sh:


#!/bin/bash


PATH=/sbin:$PATH


# путь к корню файловой системы

ROOT="/home/netboot/cent/root"

# версия ядра

VERSION="2.6.12.6-sc-xen0"

#размер образа RAM-диска (если скрипт будет создавать образ и файловую систему сам)

IMAGESIZE=10000

#имя файла образа

IMAGE="initrd-xen.img"

#точка монтирования, для работы с образом

MNTPOINT="/mnt/initrd_image"

#путь к ядру

KERNEL=$ROOT/boot/vmlinuz-$VERSION

#путь к модулям для данного ядра

MODULES=$ROOT/lib/modules/$VERSION

TOP_DIR=`pwd`

# каталог в который произвести копирование готового образа. Должен существовать.

INSTALL_DIR="sc"

# версия CRT библиотеки

CVERS=2.3.4


# выполнение некоторых операций требует привилегий суперпользователя

echo -n "Checking uid.."

if [ $(id -u) != 0 ]; then

echo "mk_net_rd must be run as root"

exit 1

fi

echo "OK"


# далее производится создание файла необходимого размера и создание файловой

#системы в нём

echo -n "Creating image file.."

dd if=/dev/zero of=$IMAGE bs=1k count=$IMAGESIZE || {

echo "FAILED"

exit 1;

}

echo "OK"


echo -n "Creating ext2 filesystem.."

mke2fs -F -m0 -b 1024 $IMAGE || {

echo "FAILED ($?)"

exit 1;

}

echo "OK"


# монтируем полученный образ, затем создаём в нём необходимое дерево каталогов

echo -n "Mounting image.."

mount -t ext2 $IMAGE -o loop $MNTPOINT || {

echo "FAILED"

exit 1

}

echo "OK"

echo "Creating directory structure"

mkdir -p $MNTPOINT

mkdir -p $MNTPOINT/lib

mkdir -p $MNTPOINT/bin

mkdir -p $MNTPOINT/etc

mkdir -p $MNTPOINT/etc/rc.d/init.d/

mkdir -p $MNTPOINT/etc/sysconfig/network-scripts

mkdir -p $MNTPOINT/dev

mkdir -p $MNTPOINT/proc

mkdir -p $MNTPOINT/tmp

mkdir -p $MNTPOINT/var/lib/nfs

mkdir -p $MNTPOINT/var/lib/dhcp

mkdir -p $MNTPOINT/usr/share/hwdata/

mkdir -p $MNTPOINT/mnt


# удаляем созданный mk2fs каталог, который нам абсолютно не нужен

rm -rf $MNTPOINT/lost+found


# создаём специальные файлы, которые должны существовать

# к ним относятся базовые устройства: консольный ввод-вывод и т.д.

echo "Perform mknode..."

mknod $MNTPOINT/dev/console c 5 1

mknod $MNTPOINT/dev/null c 1 3

mknod $MNTPOINT/dev/ram b 1 1

mknod $MNTPOINT/dev/systty c 4 0

for i in 1 2 3 4; do

mknod $MNTPOINT/dev/tty$i c 4 $i

done


# прописываем конфигурационные файла для ethernet интерфейсов.

for ((i=0;i<2;i=i+1)); do

cat << __EOF > $MNTPOINT/etc/sysconfig/network-scripts/ifcfg-eth$i

DEVICE=eth$i

BOOTPROTO=dhcp

ONBOOT=yes

TYPE=Ethernet

__EOF

done


# Копируем стартовую процедуру (скрипт) и прочие файлы,

# в том числе динамические библиотеки, простейшие утилиты и т.п.

echo "Copying rc-scripts"

cp linuxrc2 $MNTPOINT/disklessrc

cp $ROOT/etc/rc.d/init.d/functions $MNTPOINT/etc/rc.d/init.d/

cp $ROOT/etc/sysconfig/network-scripts/network-functions \

$MNTPOINT/etc/sysconfig/network-scripts/


cp $ROOT/bin/bash $MNTPOINT/bin

cp $ROOT/bin/mount $MNTPOINT/bin

cp $ROOT/bin/cat $MNTPOINT/bin

cp $ROOT/bin/grep $MNTPOINT/bin

cp $ROOT/bin/sed $MNTPOINT/bin

cp $ROOT/bin/sleep $MNTPOINT/bin

cp $ROOT/bin/sort $MNTPOINT/bin

cp $ROOT/bin/umount $MNTPOINT/bin


cp $ROOT/sbin/insmod $MNTPOINT/bin/

cp $ROOT/sbin/dhclient $MNTPOINT/bin/

cp $ROOT/sbin/dhclient-script $MNTPOINT/bin/

cp $ROOT/sbin/lspci $MNTPOINT/bin

cp $ROOT/sbin/ifconfig $MNTPOINT/bin

cp $ROOT/sbin/consoletype $MNTPOINT/bin

cp $ROOT/sbin/pivot_root $MNTPOINT/bin

cp $ROOT/sbin/route $MNTPOINT/bin


cp $ROOT/usr/bin/expr $MNTPOINT/bin

cp $ROOT/usr/share/hwdata/pcitable $MNTPOINT/usr/share/hwdata/

cp ./busybox $MNTPOINT/bin/busybox


# делаем ссылки на busybox

cd $MNTPOINT/bin/

ln -sf bin ../sbin

ln -sf insmod modprobe

ln -sf insmod lsmod

ln -sf insmod rmmod

ln -sf busybox awk

ln -sf busybox basename

ln -sf busybox cut

ln -sf busybox df

ln -sf busybox ln

ln -sf busybox ls

ln -sf busybox more

ln -sf busybox rm

ln -sf busybox uname

ln -sf bash ash


# Создаём файл fstab


cd ../etc

touch fstab

# mtab подменяем ссылкой на /proc/mounts

ln -sf /proc/mounts mtab


# Собираем файлы библиотек

cd ../lib

cp $ROOT/lib/ld-${CVERS}.so .

ln -sf ld-${CVERS}.so ld-linux.so.2

cp $ROOT/lib/libc-${CVERS}.so .

ln -sf libc-${CVERS}.so libc.so.6

cp $ROOT/lib/libdl-${CVERS}.so .

ln -sf libdl-${CVERS}.so libdl.so.2

cp $ROOT/lib/libm-${CVERS}.so .

ln -sf libm-${CVERS}.so libm.so.6

cp $ROOT/lib/libtermcap.so.2 .

cp $ROOT/lib/librt-${CVERS}.so .

ln -sf librt.so.1 librt-${CVERS}.so

cp $ROOT/lib/libpcre.so.0.0.1 .

ln -sf libpcre.so.0.0.1 libpcre.so.0

cp $ROOT/lib/libuuid.so.1.2 .

ln -sf libuuid.so.1.2 libuuid.so.1

cd $TOP_DIR


# Создаём каталог для модулей и копируем в него все модули,

# в основном для файловых систем и сетевых адаптеров.


NEWMODDIR=$MNTPOINT/lib/modules/$VERSION

mkdir -p $NEWMODDIR

cp $MODULES/modules.* $NEWMODDIR


mkdir -p $NEWMODDIR/kernel/drivers/

#cp -r $MODULES/kernel/drivers/net $NEWMODDIR/kernel/drivers

#cp -r $MODULES/kernel/drivers/net $NEWMODDIR/kernel/drivers

mkdir -p $NEWMODDIR/kernel/fs/

#cp -r $MODULES/kernel/fs/nfs $NEWMODDIR/kernel/fs/

#cp -r $MODULES/kernel/fs/lockd $NEWMODDIR/kernel/fs/

mkdir -p $NEWMODDIR/kernel/net/

# dummy.ko чаще всего отсутствует

cp -r $MODULES/kernel/drivers/net/dummy.ko $NEWMODDIR/kernel/net/


umount $MNTPOINT

# сжимаем образ и копируем по указанному пути

echo "Compressing initial ram disk image"

gzip -9 $IMAGE


echo "installing kernel image and initrd"

cp -v $KERNEL /tftpboot/linux-install/$INSTALL_DIR/vmlinuz

mv -v $IMAGE.gz /tftpboot/linux-install/$INSTALL_DIR/initrd0


exit $rc


Файл linuxrc.

Загрузочный скрипт, запускаемый ядром.


#!/bin/ash


# Процедура монтирует файлы и каталоги поверх тех, что в root (которая

# должна быть примонтирована как read-only), с целью допустить изменение

# указанных файлов узлами

mountfile () {

snapshotfile=/.snapshot/${2}${1}

dir=`dirname $snapshotfile`

# Проверяем наличие файла в снэпшоте, если да – пропускаем,

# иначе копируем в снэпшот

if [ ! -e $snapshotfile ]; then

mkdir -p $dir

echo "${1} missing from client specific area."

if [ -e ${1} ] ; then

echo "Copying ${1}"

rsync -a ${1} $dir

else

echo "Creating ${1}"

touch $snapshotfile

fi

else

# Если dev в снэпшоте старее аналогичного в корневой папке,

# то использовать последний

if [ ${1} == "/dev" -a ${1} -nt ${dir}/dev ]; then

rsync -a ${1} $dir

fi

fi

mount -n -o bind /.snapshot/${2}${1} ${1}

}


echo "Running /disklessrc"


echo "Mounting /proc"

/bin/mount -n -t proc /proc /proc

if [ -z "${ETHERNET}" ]; then export ETHERNET="eth0"; fi


echo "Mounting /"

/bin/mount -n -o remount,rw /

/bin/mount -n -t tmpfs /dev/shm /tmp


# Конфигурируемый интерфейс для запроса по DHCP необходимых параметров


ifconfig


cat </tmp/dhclient.conf

interface "$ETHERNET" {

request subnet-mask,

broadcast-address,

routers,

domain-name,

domain-name-servers,

host-name,

root-path;

}

EOF


# Запуск клиента получения параметров по DHCP

echo "Running dhclient"

/bin/dhclient -cf /tmp/dhclient.conf -pf /tmp/dhclient.pid \

$ETHERNET > /tmp/dhclient.out 2>&1

if [ $? -ne 0 ]; then

exec /bin/ash

cat /tmp/dhclient.out

echo "ERROR! dhclient failed!"

exit 1

fi


# Проверяем получение пути к корню с сервера

if [ -z "${NFSROOT}" ]; then

echo "ERROR! No root-path. "

exit 1

fi


# Разделяем полученную строку на ip и путь

NFS_IP=` echo ${NFSROOT} | cut -d : -f 1`

NFS_DIR=`echo ${NFSROOT} | cut -d : -f 2`


echo "Mounting root filesystem: ${NFS_DIR} from: ${NFS_IP}"

mount -n -o nolock,ro ${NFS_IP}:${NFS_DIR}/root /mnt


if [ $? -ne 0 ]; then

echo "ERROR! Failed to mount the root directory."

exit 1

fi


# Меняем корень с помощью системного вызова

umount /proc

echo "Doing the pivot_root"

cd /mnt

/sbin/pivot_root . .oldroot


if [ $? -ne 0 ]; then

echo "ERROR! Failed to do pivot_root."

exit 1

fi

cd /

mount -n -t proc /proc /proc


# Получаем ip

IP=`/sbin/ifconfig $ETHERNET|grep inet|cut -f 2 -d ':'|cut -f 1 -d ' '`


# Задаём имя узла по ip

hostname `host $IP | sed 's/.* \(.*\)./\1/'` 2> /dev/null


if [ -z "${SNAPSHOT}" ]; then

SNAPSHOT=`hostname`

fi


# монтируем каталог со снэпшотами

echo Mounting Snapshot directories

mount $NFS_IP:${NFS_DIR}/snapshot /.snapshot -o nolock &&

{


# Просматриваем два файла и монтируем указанные в них файлы и папки.

# Принципиально можно обойтись и одним

for i in `grep -v "#" /.snapshot/files`; do

mountfile $i ${SNAPSHOT}

done

if [ -e /.snapshot/files.custom ]; then

for i in `grep -v "#" /.snapshot/files.custom`; do

mountfile $i ${SNAPSHOT}

done

fi

RELEASE=`uname -r`

for i in `ls /lib/modules/$RELEASE/modules.*`; do

mountfile $i ${SNAPSHOT}

done

/sbin/ifup lo

}


# Уничтожаем демон клиента DHCP

/bin/kill -TERM `cat /.oldroot/tmp/dhclient.pid`

umount /proc

umount /.oldroot/tmp


# Выводим баннер

echo "--------======== cluster node info ========--------"

echo " Cluster: X"

echo " boot system: diskless, NFS"

echo " NFS server ip : ${NFS_IP}"

echo " NFS root directory : ${NFS_DIR}"

echo " local info:"

echo " local ip (${ETHERNET}) : ${IP}"

echo " using snapshot : ${SNAPSHOT}"

echo " just to know : ${RELEASE}"

echo "--------======== OK ========--------"

/bin/sleep 4s


# Инициируем стандартную загрузку (скрипт init)

echo "Running /sbin/init"

/sbin/init –init


Файл mksnapshot.

Создание слепка для узла.


#!/bin/bash


# Имя снэпшота

snap="cent05"

# файлы которые необходимо скопировать в слепок храним в ./files

files=`cat ./files`

# Путь к корню

files_root="/home/netboot/cent/root"


mkdir $snap


for i in $files ; do


if [ -d $i ]; then

s=${i:0:${#i}-1}

mkdir -pv ./$snap$s

cp -rv $files_root$i ./$snap$i/..;

else

pw=`stat --format=%a $files_root$i`

us=`stat --format=%u $files_root$i`

gr=`stat --format=%g $files_root$i`


install -D -v -m $pw -o $us -g $gr $files_root$i $snap$i;

fi


done


# Формируем файл network

cat < ./$snap/etc/sysconfig/network

NETWORKNIG=yes

HOSTNAME=$snap

EOF

# Формируем fstab

cat < ./$snap/etc/fstab

none /dev/pts devpts gid=5,mode=620 0 0

none /dev/shm tmpfs defaults 0 0

none /proc proc defaults 0 0

none /sys sysfs defaults 0 0

EOF


mkdir ./$snap/tmp


Пример файла files, используемого при монтировании слепков


/dev

/etc/resolv.conf

/etc/adjtime

/etc/sysconfig/hwconf

/root

/var/empty

/var/lib/nfs

/var/lib/random-seed

/var/tmp

/var/lock

/var/spool

/var/run

/var/log

/var/lib/logrotate.status


1 io.org/navigator-bin/navigator.cgi?Documentation/initrd.txt