Сервер бездисковых станций FreeBSD 5.2.1

Mikhail E. Zakharov zakharov@ipb.redline.ru


Введение

С появлением FreeBSD 5.2.1 отличия в настройках сервера бездисковых клиентов относительно ветки 4.x стали настолько значительны, что даже потребовалось обновлять соответствующую статью handbook. Причем в статье эти изменения были проведены сравнительно недавно, так что на образе инсталляционного диска FreeBSD-5.2.1-Release, записанного 25.02.2004, оказалась уже безнадежно устаревшая, применимая в лучшем случае к FreeBSD 5.1, версия документа. Кроме того, поскольку handbook ограничен размерами, а материал по этой теме весьма объемый, то в соответствующей статье handbook многое описано довольно поверхностно, и сам алгоритм построения сервер-бездисковая система теряется в дебрях вариантов реализаций, технологий и описаний особенностей разных версий FreeBSD.

Поэтому, стараясь не упустить из поля зрения все многообразие вариантов, я сосредоточился на описании только одного из способов настройки машины FreeBSD-5.2.1-Release для работы в качестве сервера бездисковых клиентов. И чтобы сохранить четкость изложения в своей статье, разбираю весь процесс на конкретном примере.

Условия

Поскольку я ограничиваюсь лишь конкретным вариантом, следует выдвинуть некоторые условия. Главное, что способ, разбираемый в моей статье несколько отличается от канонического варианта, который описан в handbook и для которого написаны rc-файлы в дистрибутиве FreeBSD. В разбираемых примерах бездисковые станции не будут располагать свои файловые системы в виртуальной памяти, вместо этого они будут пользоваться NFS. Это относится к файловым системам, которые будут монтироваться как "только для чтения" (например, / и /usr), так и для "чтения-записи" (/etc и /var). Кроме того, для бездисковых станций будут написаны свои rc-скрипты.

Во всех примерах будет использоваться сервер FreeBSD-5.2.1-Release с IP-адресом 192.168.1.2/24, hostname: server. На этой машине будут настроено следующее программное обеспечение:

Если это не оговорено отдельно, все программное обеспечение устанавливается из портов.

Бездисковым станциям выделен диапазон адресов 192.168.1.10-192.168.1.254. Имя хоста - hostname формируется по следующему правилу: diskless-<последний октет IP-адреса станции>. Так, бездисковой станции (Intel Pentium-90, 16Mb RAM, Сетевой адаптер Intel EtherExpress-100 с поддержкой PXE), выбранной для примеров, присвоен IP-адрес 192.168.1.101/24 и hostname: diskless-101.

Загрузка бездискового клиента с высоты птичьего полета.

Суть загрузки бездисковой системы по сети в том, чтобы на этапе загрузки машины передать ей по сети ядро FreeBSD. Handbook в связи с этим упоминает два способа:

Оба варианта: PXE и etherboot пользуются BOOTP или DHCP, посредством которых получают необходимую информацию о сетевых адресах. Разница между этими способами заключается в том, что etherboot, используя TFTP или NFS, целиком выкачивает с сервера и загружает ядро FreeBSD, а PXE вначале по протоколу TFTP получает несравнимо меньшую по размерам программу pxeboot, которая затем уже сама по NFS или TFTP обеспечивает загрузку ядра системы.

Поскольку опыт показывает, что в настоящее время становится значительно проще купить новую сетевую карту с поддержкой спецификации PXE, чем перепрограммировать старую, то способы загрузки бездисковых клиентов с применением etherboot или netboot я оставляю за пределами рассмотрения своей статьи. Скажу только, что etherboot гораздо легче, чем netboot настроить для загрузки ядра FreeBSD, а PXE, в свою очередь, еще проще, поскольку вообще не требуется устанавливать дополнительного программного обеспечения.

Далее ядро ОС бездискового клиента монтирует корень своей файловой системы на NFS-ресурсе сервера и вызывает /sbin/init (или то, что его может заменить init.bak, oinit, sysinstall), монтирует devfs в каталог /dev, выполняет скрипт /etc/rc и для соответствующих терминальных устройств из файла /etc/ttys порождает процессы getty.

Скрипт /etc/rc, который завершает цикл загрузки и запускает основные демоны системы, стоит отметить отдельно. Для данного способа загрузки бездисковых клиентов, скрипт /etc/rc важен еще и тем, что входе его выполнения будет произведена "подмена" одной из файловых систем. Дело в том, что ради экономии места на диске сервера, корневую файловую систему мы сделаем общей для всех бездисковых клиентов, поэтому логичнее всего монтировать ее в режиме "только для чтения". Соответственно, каталог /etc для всех бездисковых станций окажется, к сожалению, одним и тем же и к тому же будет доступен только для чтения. Поэтому, чтобы обеспечить гибкость создания индивидуальных для каждого клиента директорий /etc, заложим в rc-скрипт операцию по монтированию каждой бездисковой станции своего /etc, доступного для чтения и записи. Аналогичных целей можно добиться, применив стандартные rc-скрипты FreeBSD. Однако, в этом случае /etc будет размещаться в виртуальной памяти компьютера, что на мой взгляд не очень хорошо, поскольку для бездисковых станций основным, и часто дефицитным ресурсом является как раз оперативная память. По этому вариант с виртуальной памятью я рассматривать не буду, подробнее о нем можно узнать, изучив скрипты загрузки FreeBSD 5.2.1 /etc/rc.d/initdiskless и /etc/rc.d/diskless.

Как можно догадаться, настройка самой бездисковой станции требует минимальных усилий и заключается лишь в установке в ее корпус сетевой карты с поддержкой спецификации PXE. Далее, в зависимости от конкретных особенностей оборудования станции, остается указать только правильный порядок загрузки, т.е. выбрать загрузку с LAN. Это обычно делается либо в BIOS компьютера, либо в меню самой сетевой карты. На тестовой станции, BIOS которой, не поддерживал загрузку по сети, мне пришлось проводить настройку через меню сетевой карты. Для того чтобы попасть в меню сетевой карты потребовалось на этапе загрузки компьютера нажать Crtl+S.

Теперь становится очевидным, что поскольку все основные работы по конфигурированию бездисковых станций придется выполнять на сервере, то в первую очередь необходимо настроить сам сервер.

Настройка сервера

Настройка сервера бездисковых станций FreeBSD заключается в установке следующих сервисов: NFS, DHCP и TFTP. Очередность конфигурирования этих служб для конечного результата неважна однако, следуя логике загрузки бездискового клиента, установим следующий порядок: DHCP, TFTP, NFS.

Установка самой ОС сервера производится обычным способом за исключением небольшой детали: в нашем случае, для размещения файловых систем клиента, отводятся две отдельные партиции, одна предназначается "только для чтения", другая для "чтения-записи". В нашем случае мы будем монтировать эти партиции в следующие каталоги корневой файловой системы сервера:

/diskless_ro /diskless_rw

В последствии эти файловые системы будут с соответствующими правами экспортированы бездисковым клиентам по NFS. Если у читателя нет возможности использовать под файловые системы бездисковых клиентов специально выделенные партиции, все же не следует делать ошибки, размещая их на одной физической файоловой системе сервера. Вместо этого лучше выделить подкаталоги в /usr, /home или /var. Например, если /usr и /var это различные файловые системы сервера, то каталоги клиентов могут быть такими: /usr/diskless_ro и /var/diskless_rw. Такое распределение файлов клиента по разным физическим файловым системам обуславливается особенностями работы NFS часть которых, будет затронута, в описании настройки NFS-сервера.

DHCP-сервер

Первое что необходимо для загрузки бездискового клиента, это обеспечить его информацию об IP-адресе, маске подсети, расположению корневой файловой системы и т.д. Для этих целей потребуется установить BOOTP- или DHCP-сервер.

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

Для настройки сервера DHCP под FreeBSD воспользуемся продуктом ISC DHCP (isc-dhcp3), который также поддерживает и протокол BOOTP. ISC DHCP не входит в дистрибутив FreeBSD, поэтому его придется инсталлировать из пакетов, портов или исходных текстов. Установку из портов можно произвести так:

server# cd /usr/ports/net/isc-dhcp3 && make install clean

Поскольку при описании NFS-сервера мы договорились, что каждому бездисковому клиенту будем предоставлять NFS-ресурс для записи, то время от времени менять IP-адреса бездисковым станциям становится просто не корректно, поэтому в конфигурации DHCP-сервера нам придется отказаться от динамического распределения адресов, и использовать ISC DHCP для клиентов BOOTP.

Основные настройки DHCP-сервера хранятся в файле /usr/local/etc/dhcpd.conf. Редактируем его чтобы составить простую конфигурацию. Поскольку в нашем примере всего одна подсеть и один сервер бездисковых клиентов, можно смело выносить почти всю информацию в раздел глобальных параметров:

authoritative; # Сервер авторитетен для своей подсети ddns-update-style none; # Отказываемся от динамического обновления DNS use-host-decl-names on; # выдавать клиентам "hostname", которое указано в поле host option routers 192.168.1.1; # Укажем маршрутизатор по умолчанию next-server 192.168.1.2; # Здесь указывается сервер, с которого берется boot-файл filename "pxeboot"; # Собственно boot-файл, который читается с next-server option root-path "192.168.1.2:/diskless_ro"; # Здесь будет корень файловой системы клиента subnet 192.168.1.0 netmask 255.255.255.0 { } # Описываем подсеть. Динамического распределения адресов # не будет, и здесь тоже не будет никаких опций. Однако, убрать # описание подсети нельзя, иначе DHCP-сервер не загрузится # Описания бездисковых станций: для каждого клиента # указываем имя хоста, его IP и MAC адреса host diskless-10 { # Имя хоста, которое из-за "use-host-decl-names on" будет # будет передано клиенту в качестве его hostname hardware ethernet 00:0D:9D:8B:BB:48; # MAC-адрес сетевой карты бездискового клиента. fixed-address 192.168.1.10; # IP-адрес, назначаемый клиенту. } ... host diskless-101 { hardware ethernet 00:0C:29:74:2B:35; fixed-address 192.168.1.101; } ... host diskless-254 { hardware ethernet 00:02:B3:8A:D6:BF; fixed-address 192.168.1.103; }

Теперь можно стартовать DHCP-сервер:

server# /usr/local/sbin/dhcpd

И если загрузка прошла удачно, получим подобный ответ:

Internet Software Consortium DHCP Server V3.0.1rc12 Copyright 1995-2003 Internet Software Consortium. All rights reserved. For info, please visit http://www.isc.org/products/DHCP Wrote 0 deleted host decls to leases file. Wrote 0 new dynamic host decls to leases file. Wrote 0 leases to leases file. Listening on BPF/fxp0/00:0c:29:f8:78:fc/192.168.1.0/24 Sending on BPF/fxp0/00:0c:29:f8:78:fc/192.168.1.0/24 Sending on Socket/fallback/fallback-net

Остается только добавить вызов DHCP в автоматическую загрузку сервера:

#server cd /usr/local/etc/rc.d #server mv isc-dhcpd.sh.sample isc-dhcpd.sh #server chmod 755 isc-dhcpd.sh

TFTP-сервер

Бездисковый клиент вместе со своим IP-адресом получает от DHCP-сервера еще 3 важных для загрузки параметра:

Параметр root-path потребуется чуть позже при загрузке ядра и монтировании корневой файловой системы. А пока, PXE сетевой карты бездисковой станции, именно два последних, из вышеперечисленных параметров, автоматически попытается использовать для продолжения загрузки. Файл pxeboot (8) является измененной версией загрузчика loader (8), работающего на 3-м этапе загрузки FreeBSD (подробнее о загрузке FreeBSD можно прочитать в handbook). Поскольку бездисковый клиент ожидает получить эту программу по протоколу TFTP, то его следует настроить на сервере 192.168.1.2.

TFTP-сервер во FreeBSD входит в дистрибутив и никакого дополнительного программного обеспечения устанавливать не потребуется. Демон называется tftpd и обычно запускается из inetd. Поэтому для того, чтобы запустить TFTP-сервер, мы просто создадим специальный каталог /tftpboot и запишем туда pxeboot:

server# mkdir /tftpboot server# cp /boot/pxeboot /tftpboot

зададим соответствующую строку в /etc/inetd.conf:

tftp dgram udp wait root /usr/libexec/tftpd tftpd -l -s /tftpboot

Здесь опция -l включает логирование операций, -s устанавливает каталог, который является корневым для tftpd после выполнения им вызова chroot(). Подробнее о работе tftpd и системном вызове chroot() можно прочитать в страницах man tftpd(8) и chroot(2).

Сервер настроен, остается только перезапустить демон inetd:

server# killall -HUP inetd

Если все прошло успешно, команда:

server# sockstat -4l| grep 69

Вернет похожий результат:

root inetd 556 5 udp4 *:69 *:*

NFS-сервер

После того, как загрузчик pxeboot будет успешно скачан по TFTP, он, руководствуясь опцией "root-path" попытается по протоколу NFS обратиться к каталогу /diskless_ro сервера 192.168.1.2, так, как будет считать что, там содержится корневая файловая система с ядром операционной системы бездисковой станции.

Здесь необходимо сделать следующее замечание: что pxeboot может быть также настроен для загрузки ядра по протоколу TFTP, что позволит загружать разным бездисковым станциям разные версии ядер. Правда чтобы это осуществить, необходимо перекомпилировать pxeboot с опцией LOADER_TFTP_SUPPORT= YES, указанной в файле /etc/make.conf сервера. Подробнее об этом см. в handbook и в файле /usr/share/examples/etc/make.conf.

Мы же ограничимся универсальным для всех станций ядром, поэтому возникает необходимость настроить NFS-сервер и экспортировать клиентам соответствующие каталоги сервера. Сделаем общую для всех директорию /diskless_ro доступной режиме "только для чтения", а в /diskless_rw расположим каталоги, индивидуальные для каждого IP-адреса станции, так чтобы каждый бездисковый клиент смог производить туда запись. В таких каталогах для каждой рабочей станции, в свою очередь, необходимо создать поддиректории etc и var. Например, тестовая станция будет иметь собственный каталог: /diskless_rw/192.168.1.101 и два подкаталога /diskless_rw/192.168.1.101/etc и /diskless_rw/192.168.1.101/var. Таким образом, каталог /diskless_ro будет пока пуст а /diskless_rw на этом этапе будет иметь следующую структуру:

/diskless_rw/192.168.1.10 /diskless_rw/192.168.1.10/etc /diskless_rw/192.168.1.10/var ... /diskless_rw/192.168.1.101 /diskless_rw/192.168.1.101/etc /diskless_rw/192.168.1.101/var ... /diskless_rw/192.168.1.254 /diskless_rw/192.168.1.254/etc /diskless_rw/192.168.1.254/var

Кроме того, бездисковые станции, в режиме "только для чтения " будут пользоваться серверной файловой системой /usr.

Для того, чтобы разрешить бездисковым станциям беспрепятственно пользоваться всеми этими каталогами, необходимо соответствующим образом настроить NFS-сервер. Для этого в файл /etc/exports необходимо поместить следующие строки:

# Файловые системы доступные для чтения: /usr -ro -maproot=0 -network 192.168.1.0 -mask 255.255.255.0 /diskless_ro -ro -maproot=0 -network 192.168.1.0 -mask 255.255.255.0 # Файловые системы доступные для записи. Здесь ресурсы # выделяемые каждой бездисковой станции, описываются одной строкой: # # Diskless-10 /diskless_rw/192.168.1.10/etc /diskless_rw/192.168.1.10/var -mapall=root 192.168.1.10 # ... # Diskless-101 /diskless_rw/192.168.1.101/etc /diskless_rw/192.168.1.101/var -mapall=root 192.168.1.101 # ... # Diskless-254 /diskless_rw/192.168.1.254/etc /diskless_rw/192.168.1.254/var -mapall=root 192.168.1.254

Теперь меняем /etc/rc.conf, чтобы стартовать NFS-сервер при загрузке системы:

rpcbind_enable="YES" nfs_server_enable="YES"

Тут следует обратить внимание и при необходимости изменить переменную nfs_server_flags:

nfs_server_flags="-u -t -n 48 -h 192.168.1.2"

Здесь очень важен параметр -n, который определяет количество демонов nfsd, которые обслуживают работу NFS, реально это отражается на числе одновременно подключенных клиентов NFS. Данный параметр следует отрегулировать в зависимости от количества клиентов. Ключи -u и -t включают поддержку соответственно UDP и TCP; -h указывает на привязку к конкретному сетевому интерфейсу.

Теперь, для того, чтобы лишний раз не перезагружать машину стартуем NFS-сервер вручную:

server# rpcbind server# nfsd -u -t -n 48 -h 192.168.1.2 server# mountd -r

После того, как NFS-сервер загрузился без ошибок, проверим, насколько успешно экспортировались файловые системы:

server# showmount -e Exports list on localhost: /usr 192.168.1.0 /diskless_rw/192.168.1.103/var 192.168.1.254 /diskless_rw/192.168.1.103/etc 192.168.1.254 ... /diskless_rw/192.168.1.101/var 192.168.1.101 /diskless_rw/192.168.1.101/var 192.168.1.101 ... /diskless_rw/192.168.1.101/etc 192.168.1.10 /diskless_rw/192.168.1.101/etc 192.168.1.10 /diskless_ro 192.168.1.0

Замечание.

Размещать diskless_rw и diskless_ro на одной физической файловой системе не рекомендуется, поскольку NFS экспортирует клиенту не каталог, а файловую систему целиком. В /etc/exports каждая строка представляет собой описание экспорта одной файловой системы сервера одному или нескольким клиентам. Причем для каждой экспортируемой файловой системы, один и тот же клиент может быть указан только один раз.

Например, если /diskless_rw и /diskless_ro разные файловые системы, то такой /etc/exports будет правильным:

/diskless_rw 192.168.1.101 /diskless_ro -ro 192.168.1.101

Ошибочный вариант /etc/exports:

/usr/diskless_rw 192.168.1.101 /usr/diskless_ro -ro 192.168.1.101

Если diskless_rw и diskless_ro принадлежат одной и той же файловой системе /usr, то при попытке их экспорта одному и тому же клиенту возникнет ошибка, и diskless_ro не будет экспортирован. Правила требуют, чтобы оба ресурса /usr/diskless_rw и /usr/diskless_ro были указаны одной строкой, однако тогда придется выбирать между тем, оставить ли их доступными клиенту "только для чтения" или же разрешить "чтение-запись".

Однако, демон mountd может быть "обманут", если вместо конкретных машин, посредством сетевых масок будут указаны диапазоны адресов. Например, еще один ошибочный вариант /etc/exports, который, тем не менее, будет "успешно" обработан:

/usr/diskless_rw -network 192.168.1.0 -mask 255.255.255.0
/usr/diskless_ro -ro -network 192.168.0.0 -mask 255.255.0.0

В этом случае /usr/diskless_rw и /usr/diskless_ro будут успешно экспортированы. Причем поскольку мы имеем дело не с каталогами, а с файловой системой целиком, то для подсети 192.168.1.0 с маской 255.255.255.0 оба ресурса как /usr/diskless_rw, так и /usr/diskless_ro будут экспортированы с атрибутом rw, т.е. окажутся доступны для чтения и записи, но это не правильно с точки зрения безопасности.

Настройка бездисковой станции

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

Ядро

Как уже было сказано выше, pxeboot будет ожидать, что NFS-ресурс 192.168.1.2:/diskless_ro является корневой файловой системой, и поэтому попытается найти на ней ядро. При загрузке бездискового клиента с использованием pxeboot можно вполне обойтись и ядром GENERIC, однако оно включает в себя много лишних драйверов, но не содержит нескольких удобных BOOTP-опций. Следовательно, нам необходимо скомпилировать свое ядро DISKLESS, чтобы затем вместе с другими файлам разместить его на файловой системе клиента.

Компилируем ядро привычным способом, изменяя GENERIC и убирая из ядра все, что по нашему мнению является лишним. Главное, чтобы там осталось:

options NFS_ROOT options NFSCLIENT

Можно обойтись без, но очень полезно добавить в ядро следующие опции, которые не присутствуют в GENERIC:

options BOOTP options BOOTP_NFSROOT options BOOTP_COMPAT

Включение этих опций вызовет дополнительный диалог с DHCP-сервером по определению IP-адресов бездискового клиента. Однако иначе мы не сможем по BOOTP передавать клиенту такие приятные вещи как hostname. Как отмечено в handbook, можно еще добавить options BOOTP_NFSV3 и BOOTP_WIRED_TO, но это уже по вкусу.

Следующее ядро, было успешно опробовано на нескольких бездисковых станциях:

machine i386 cpu I486_CPU cpu I586_CPU cpu I686_CPU ident DISKLESS options SCHED_4BSD #4BSD scheduler options INET #InterNETworking options FFS #Berkeley Fast Filesystem options SOFTUPDATES #Enable FFS soft updates support options UFS_ACL #Support for access control lists options UFS_DIRHASH #Improve performance on big directories options MD_ROOT #MD is a potential root device options NFSCLIENT #Network Filesystem Client options NFS_ROOT #NFS usable as /, requires NFSCLIENT options CD9660 #ISO 9660 Filesystem options PROCFS #Process filesystem (requires PSEUDOFS) options PSEUDOFS #Pseudo-filesystem framework options COMPAT_43 #Compatible with BSD 4.3 [KEEP THIS!] options KTRACE #ktrace(1) support options SYSVSHM #SYSV-style shared memory options SYSVMSG #SYSV-style message queues options SYSVSEM #SYSV-style semaphores options _KPOSIX_PRIORITY_SCHEDULING #Posix P1003_1B real-time extensions options KBD_INSTALL_CDEV # install a CDEV entry in /dev device isa device pci device fdc # ATA and ATAPI devices device ata device atadisk # ATA disk drives device atapicd # ATAPI CDROM drives options ATA_STATIC_ID #Static device numbering # atkbdc0 controls both the keyboard and the PS/2 mouse device atkbdc # AT keyboard controller device atkbd # AT keyboard device psm # PS/2 mouse device vga # VGA video card driver device splash # Splash screen and screen saver support # syscons is the default console driver, resembling an SCO console device sc device agp # support several AGP chipsets # Floating point support - do not disable. device npx # Add suspend/resume support for the i8254. device pmtimer # Serial (COM) ports device sio # 8250, 16[45]50 based serial ports # Parallel port device ppc device ppbus # Parallel port bus (required) device lpt # Printer # PCI Ethernet NICs. device miibus # MII bus support device fxp # Intel EtherExpress PRO/100B (82557, 82558) # Pseudo devices - the number indicates how many units to allocate. device random # Entropy device device loop # Network loopback device ether # Ethernet support device pty # Pseudo-ttys (telnet etc) device md # Memory "disks" options BOOTP options BOOTP_NFSROOT options BOOTP_NFSV3 options BOOTP_COMPAT

Дерево каталогов

Поскольку бездисковая станция отличается от обычной машины, лишь тем, что имеет файловые системы на NFS-ресурсе, то для ее успешной загрузки необходимо воссоздать, почти без изменений, дерево каталогов FreeBSD. Исходя из того, что на сервере мы для этих целей выбрали каталоги /diskless_ro и /diskless_rw, то все файлы будут располагаться именно там.

/diskless_rw

Сначала подготовим каталог /diskless_rw и отложим его, поскольку он будет использоваться только в конце загрузки бездискового клиента.

Директория /diskless_rw предназначена для хранения индивидуальных файловых систем всех клиентов и, после установки NFS-сервера, организована по следующему правилу: /diskless_rw/IP-адрес-станции/rw-файловая-система. Например, для тестового бездискового клиента были созданы следующие каталоги:

server# mkdir /diskless_rw/192.168.1.101 server# mkdir /diskless_rw/192.168.1.101/etc server# mkdir /diskless_rw/192.168.1.101/var

Это сделано для того, чтобы каталоги /diskless_rw/IP-адрес-станции/etc и /diskless_rw/IP-адрес-станции/var монтировались в качестве /etc и /var бездискового клиента. Такое размещение каталогов позволяет выделить каждой бездисковой станции индивидуальные каталоги, предназначенные для чтения и записи.

Далее, в var и etc необходимо создать еще ряд подкаталогов (etc/pam.d etc/X11 и var/log, var/run, var/tmp, var/home), которые все равно потребуются для работы машины. Поэтому выполняем:

server# mkdir /diskless_rw/192.168.1.101/etc/pam.d server# mkdir /diskless_rw/192.168.1.101/etc/X11 server# mkdir /diskless_rw/192.168.1.101/var/home server# mkdir /diskless_rw/192.168.1.101/var/log server# mkdir /diskless_rw/192.168.1.101/var/run server# mkdir /diskless_rw/192.168.1.101/var/tmp

Таким образом деревья каталогов etc и var на тестовой бездисковой станции, будут выглядеть так:

/diskless_rw/192.168.1.101 /diskless_rw/192.168.1.101/etc /diskless_rw/192.168.1.101/etc/pam.d /diskless_rw/192.168.1.101/etc/X11 /diskless_rw/192.168.1.101/var /diskless_rw/192.168.1.101/var/home /diskless_rw/192.168.1.101/var/log /diskless_rw/192.168.1.101/var/tmp /diskless_rw/192.168.1.101/var/run

Поскольку, во FreeBSD 5.2.1 был убран код, позволяющий во время загрузки передать ядру по BOOTP информацию, о том где располагается swap-файл бездисковой станции, будем размещать swap-файл каждого клиента в его же файловой системе:

dd if=/dev/zero of=diskless_rw/192.168.1.101/swap bs=1k count=32000

Аналогичные действия должны быть произведены для каждой бездисковой станции.

Подробнее, о файловом составе всех этих каталогов, и о том, как обеспечить нормальную работу раздробленных /etc и /var поговорим чуть позже.

/diskless_ro

Директория /diskless_ro предназначена для размещения корневой файловой системы бездисковой станции. Здесь нам потребуются следующие каталоги: bin, boot, dev, etc, lib, libexec, mnt, sbin, usr и var.

Директории bin, lib, libexec и sbin содержат основные библиотеки и программы FreeBSD поэтому, возьмем эти каталоги без изменений из файловой системы сервера:

server# cp -r /bin /lib /libexec /sbin /diskless_ro

Далее, создадим еще директорию usr, куда в дальнейшем по NFS будет монтироваться каталог /usr с сервера:

server# mkdir /diskless_ro/usr

Затем, следует подготовить каталог /boot бездисковой станции и разместить там ядро. Сделаем его из соответствующей директории сервера, и скопируем туда заранее скомпилированное ядро DISKLESS:

server# cp -r /boot /diskless_ro server# cp /sys/i386/compile/DISKLESS/kernel /diskless_ro/boot/kernel

При использовании такой конфигурации у меня возникали проблемы при загрузке некоторых бездисковых станций. Часть этих машин мне удалось победить, убрав из загрузки boot4th и составив свой loader.rc. Ели вы, вместе со мной решились на этот шаг, то:

server# cd /diskless_ro/boot server# rm *.4th

Избавившись от boot4th мы, к сожалению, одновременно лишимся и автоматической подгрузки device.hints. Избежать этой проблемы можно двумя путями:

Из-за того, что старое оборудование капризно, то иногда требуется довольно много времени на его конфигурацию, и поэтому чтобы обеспечить более гибкие настройки параметров ядра стоит пользоваться именно вторым вариантом.

Поскольку те значения, которые записаны в device.hints являются обыкновенными переменными ядра, нужные из их мы можем включить в виде set variable="value" в файл loader.rc. Тогда loader.rc будет выглядеть так:

set hint.fdc.0.at="isa" ... boot /boot/kernel/kernel

Здесь последняя строка loader.rc, указывает, какое ядро следует загружать.

Проще всего компиляцию loader.rc проводить так, а затем просто убрать лишние переменные ядра:

server# cd /diskless_ro/boot server# awk '{print "set "$1}' device.hints > loader.rc server# echo "boot /boot/kernel/kernel" >> loader.rc

Например, меня выручал такой loader.rc:

set hint.fdc.0.at="isa" set hint.fdc.0.port="0x3F0" set hint.fdc.0.irq="6" set hint.fdc.0.drq="2" set hint.fd.0.at="fdc0" set hint.fd.0.drive="0" set hint.fd.1.at="fdc0" set hint.fd.1.drive="1" set hint.ata.0.at="isa" set hint.ata.0.port="0x1F0" set hint.ata.0.irq="14" set hint.ata.1.at="isa" set hint.ata.1.port="0x170" set hint.ata.1.irq="15" set hint.atkbdc.0.at="isa" set hint.atkbdc.0.port="0x060" set hint.atkbd.0.at="atkbdc" set hint.atkbd.0.irq="1" set hint.atkbd.0.flags="0x1" set hint.psm.0.at="atkbdc" set hint.psm.0.irq="12" set hint.vga.0.at="isa" set hint.sc.0.at="isa" set hint.sc.0.flags="0x100" set hint.vt.0.at="isa" set hint.vt.0.disabled="1" set hint.apm.0.disabled="1" set hint.apm.0.flags="0x20" set hint.sio.0.at="isa" set hint.sio.0.port="0x3F8" set hint.sio.0.flags="0x10" set hint.sio.0.irq="4" set hint.sio.1.at="isa" set hint.sio.1.port="0x2F8" set hint.sio.1.irq="3" set hint.ppc.0.at="isa" set hint.ppc.0.irq="7" boot /boot/kernel/kernel

Теперь device.hints на файловой системе клиента больше не нужен, и его можно удалить. Кроме того, каталог defaults, содержащий loader.conf теперь тоже не имеет смысла, поэтому смело выполняем:

server# cd /diskless_ro/boot server# rm -r defaults device.hints

Стоит отметить, что иногда очень полезно выключить загрузку модуля ACPI. Бывают случаи, когда бездисковые станции просто отказываются загружаться с включенным ACPI, однако, возможна и обратная ситуация, когда бездисковые клиенты не смогут загрузиться без модуля ACPI, см. acpi (4). Отключить ACPI можно выставив переменную hint.acpi.0.disabled="1".

Вариант для device.hints:

hint.acpi.0.disabled="1"

Аналогичная запись для loader.rc выглядит так:

set hint.acpi.0.disabled="1"

На этом с директорией /boot наконец покончено.

Далее, каталог dev необходим для монтирования файловой системы devfs, на которой располагаются все файлы устройств FreeBSD. Если случайно не создать эту директорию, бездисковая станция после загрузки ядра, без всяких ошибок просто остановится во время запуска процесса init. Следовательно:

server# mkdir /diskless_ro/dev

Позаботимся также заранее о пустом каталоге var, куда в дальнейшем по NFS будет монтироваться доступная для чтения и записи директория var из соответствующего подкаталога /diskless_rw с сервера 192.168.1.2:

server# mkdir /diskless_ro/var

Кроме того, чтобы не создавать отдельные файловые системы для home и tmp бездискового клиента, сделаем символьные линки /home -> /var/home и /tmp -> /var/tmp, которые будут обеспечивать доступ в каталоги, доступные для чтения и записи:

server# cd /diskless_ro server# ln -s /var/tmp . server# ln -s /var/home .

Далее, необходимо создать и заполнить каталог etc. Поскольку он будет располагаться на /diskless_ro, то есть, будет общим для всех и доступен только "для чтения", сделаем его содержимое универсальным и компактным. Он будет состоять всего из нескольких обязательных файлов. Часть из них (services, netconfig и login.conf) возьмем из файловой системы сервера:

server# mkdir /diskless_ro/etc server# cp /etc/services /etc/netconfig /etc/login.conf /diskless_ro/etc

Затем, чтобы иметь возможность индивидуально настроить каждую бездисковую станцию, применим один нехитрый трюк: воспользуемся тем, что процесс init во время загрузки машины запускает /etc/rc и во время работы этого скрипта, смонтируем поверх diskless_ro/etc файловую систему из diskless_rw/etc. Для этого напишем собственный etc/rc, который тоже следует разместить в /diskless_ro/etc:

#!/bin/sh PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin; export PATH boot_ip=`kenv boot.netif.ip` mount -t nfs 192.168.1.2:/diskless_rw/${boot_ip}/etc /etc mount -t nfs 192.168.1.2:/diskless_rw/${boot_ip}/var /var swapon /var/swap rm -rf /var/tmp/*; rm -rf /var/tmp/.*; . /etc/rc2 exit 0

Этот простейший скрипт последовательно выполняет следующие действия:

  1. устанавливает переменную окружения PATH, которая необходима для указания путей к исполняемым файлам.
  2. определяет IP-адрес, который был получен при загрузке и помещает его в переменную boot_ip
  3. исходя из значения boot_ip, т.е. IP-адреса клиента, по NFS монтирует свои файловые системы /etc и /var с сервера 192.168.1.2. Именно в этом пункте доступные только для чтения директории /etc и /var превращаются в каталоги, достуные как для чтения, так и для записи, но уже с разными файлами для разных станций.
  4. устанавливается swap в файл /var/swap
  5. Очищается каталог /var/tmp и /tmp, поскольку выше мы сделали соответствующий софт-линк.
  6. и наконец запускает второй rc-скрпипт (rc2), который и продолжит загрузку системы. Здесь следует отметить тот факт, что поскольку монтируемая в каталог /etc файловая система для каждого бездискового клиента индивидуальна, то скрипт rc2, как и все остальные файлы каталога /etc могут быть для каждой станции своими. Именно с этого момента, помещая в файл rc2 команды, можно проводить индивидуальную настройку каждого бездискового клиента.

Таким образом, записывая в каталог /diskless_rw/IP-адрес-станции/etc нужные файлы и редактируя файл rc2 в этой же директории, осуществляется конфигурация каждой станции с конкретным IP-адресом. Чтобы такая станция продолжила загрузку, ей потребуются, как минимум, следующие файлы в этой директории:

auth.conf disktab fstab gettytab group hosts login.access login.conf login.conf.db master.passwd netconfig protocols pwd.db rc rc2 services spwd.db syslog.conf termcap -> /usr/share/misc/termcap ttys

Почти все эти файлы можно скопировать из каталога /etc сервера в /diskless_rw/192.168.1.101/etc без изменений. Исключения составят только несколько файлов:

Начнем с fstab, который для станции с IP-адресом 192.168.1.101 будет выглядеть так:

# Device Mountpoint FStype Options Dump Pass 192.168.1.2:/diskless_ro / nfs ro 0 0 192.168.1.2:/diskless_rw/192.168.1.101/etc /etc nfs rw 0 0 192.168.1.2:/diskless_rw/192.168.1.101/var /var nfs rw 0 0 192.168.1.2:/usr /usr nfs ro 0 0

Скрипт /etc/rc уже есть в корневой системе бездискового клиента, поэтому:

server# cp /diskless_ro/etc/rc /diskless_rw/192.168.1.101/etc

Далее, редактируем rc2:

#!/bin/sh mount -a /sbin/ldconfig -elf /usr/lib/compat /usr/X11R6/lib /usr/local/lib syslogd exit 0

Скрипт rc2, на который возложено продолжение загрузки бездискового клиента, последовательно выполняет следующие действия:

Зададим конфигурацию syslogd. Для этого редактируем /etc/syslog.conf бездискового клиента, т.е. для тестовой станции этот файл будет /diskless_rw/192.168.1.101/etc/syslog.conf. Здесь может быть множество вариантов, рассмотрим два из них. Все системные отчеты syslogd отправляет на сервер 192.168.1.2:

*.* @server

или укажем демону syslogd направлять все отчеты в /var/log/all.log файловой системы клиента:

*.* /var/log/all.log

Если был выбран второй вариант, то предварительно необходимо создать файл var/log/all.log с соответствующими правами:

server# touch /diskless_rw/192.168.1.101/var/log/all.log server# chmod 600 /diskless_rw/192.168.1.101/var/log/all.log

Если же был выбран первый, необходимо убедиться, что на сервере 192.168.1.2 демон syslogd запущен с ключом -a. Например:

syslogd -a 192.168.1.0/24

Далее если требуется локализация системы необходимо добавить несколько строк в rc2 и отредактировать ttys. Так, для того, чтобы включить поддержку русского языка (koi8-r) добавляем в rc2 следующие команды:

kbdcontrol < /dev/ttyv0 -l "ru.koi8-r" vidcontrol < /dev/ttyv0 -l "koi8-r2cp866" vidcontrol < /dev/ttyv0 -f 8x16 "cp866-8x16" vidcontrol < /dev/ttyv0 -f 8x14 "cp866-8x14" vidcontrol < /dev/ttyv0 -f 8x8 "cp866-8x8"

и приводим соответствующую модификацию ttys, т.е. меняем для всех виртуальных терминалов тип консоли с cons25 на cons25r, например для ttyv0 строка будет выглядеть так:

ttyv0 "/usr/libexec/getty Pc" cons25r on secure

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

Файлы аутентификации были взяты с сервера, и теперь все имена пользователей и их пароли на сервере и всех клиентах совпадают. Скорее всего, такое положение вещей вас не устраивает, поэтому для каждого клиента редактируем master.passwd с тем, чтобы убрать от туда всех лишних пользователей и оставить root с home-каталогом "/ " и пустым паролем. Например, так:

root::0:0::0:0:Charlie &:/root:/bin/csh toor:*:0:0::0:0:Bourne-again Superuser:/root: daemon:*:1:1::0:0:Owner of many system processes:/root:/sbin/nologin operator:*:2:5::0:0:System &:/:/sbin/nologin bin:*:3:7::0:0:Binaries Commands and Source:/:/sbin/nologin tty:*:4:65533::0:0:Tty Sandbox:/:/sbin/nologin kmem:*:5:65533::0:0:KMem Sandbox:/:/sbin/nologin games:*:7:13::0:0:Games pseudo-user:/usr/games:/sbin/nologin news:*:8:8::0:0:News Subsystem:/:/sbin/nologin man:*:9:9::0:0:Mister Man Pages:/usr/share/man:/sbin/nologin sshd:*:22:22::0:0:Secure Shell Daemon:/var/empty:/sbin/nologin smmsp:*:25:25::0:0:Sendmail Submission User:/var/spool/clientmqueue:/sbin/nologin mailnull:*:26:26::0:0:Sendmail Default User:/var/spool/mqueue:/sbin/nologin bind:*:53:53::0:0:Bind Sandbox:/:/sbin/nologin uucp:*:66:66::0:0:UUCP pseudo-user:/var/spool/uucppublic:/usr/libexec/uucp/uucico pop:*:68:6::0:0:Post Office Owner:/nonexistent:/sbin/nologin www:*:80:80::0:0:World Wide Web Owner:/nonexistent:/sbin/nologin nobody:*:65534:65534::0:0:Unprivileged user:/nonexistent:/sbin/nologin test:$1$nkgb9jxT$a5ZgR4DUgOIUJGBg3.gJr.:1001:1001::0:0:test:/home/test:/bin/sh

Теперь перестроим базу паролей:

pwd_mkdb -d /diskless_rw/192.168.1.101/etc /diskless_rw/192.168.1.101/etc/master.passwd

После этих операций надо не забыть отредактировать файл etc/group (например, /diskless_rw/192.168.1.101/etc/group) и создать с необходимыми правами в директории var/home (/diskless_rw/192.168.1.101/var/home) домашние каталоги оставшимся пользователям.

Далее, поскольку мы настроили систему таким образом, что пользователи хранят свои домашние каталоги в файловых системах бездисковых станций (директория /var/home и символьная ссылка /home -> /var/home), то при такой ситуации пользователь, пересевший за другую станцию, не сможет получить доступ к своим файлам, оставленным на предыдущей машине. Решением этой проблемы может стать экспорт каталога /home с сервера на все бездисковые станции. Например, соответствующая запись /etc/exports сервера может быть такой:

/home -network 192.168.1.0 -mask 255.255.255.0

Следовательно, в fstab каждой станции надо будет добавить:

192.168.1.2:/home /home nfs rw 0 0

И наконец, не забыть заменить ссылку /home -> /var/home настоящим каталогом /home:

server# cd /diskless_ro server# rm /home server# mkdir /home

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

X-window

Если возникла необходимость в системе X-window, то можно пойти как минимум двумя путями. Первый - использовать скрипт startx, и запускать X-window целиком на бездисковой станции, или второй - настроить xdm и пользоваться ресурсами сервера. Для обоих вариантов потребуется загрузка демона мыши. Для тестовой станции, у которой мышь была подключена к порту com2, демон moused вызывается так:

/usr/sbin/moused -p /dev/cuaa1 -t auto

Поскольку на разных станциях мышь может быть подключена в разные порты, эту команду для каждого бездискового клиента надо модифицировать и поместить в скрипт /etc/rc2, для автоматического выполнения при загрузки системы.

Рассмотрим сначала первый способ. При этом варианте X-window на сервере устанавливается и настраиваются обычным образом. Каждому бездисковому клиенту в каталоге /etc/X11 помещается индивидуальный XF86Config. Соответственно для тестовой станции с IP-адресом 192.168.1.101 этот файл на сервере располагается в каталоге /diskless_rw/192.168.1.101/etc/X11.

На бездисковом клиенте в этом случае X-window стартует командой startx. Кроме того, если необходимо обеспечить доступ к Microsoft Windows Terminal Server, то на сервер FreeBSD устанавливается программа rdesktop, которая поддерживает протокол RDP:

server# cd /usr/ports/net/rdesktop server# make install clean

После этого, чтобы автоматически запускать rdesktop при загрузке системы X-window, каждому пользователю бездисковой системы в их домашних каталогах можно создать файлы .xsession или .xinitrc, содержащие вызов:

rdesktop -f mswinserver

Здесь, опция -f указывает, что нужен полноэкранный режим работы, а mswinserver имя или IP-адрес сервера Microsoft Windows Terminal Server.

Второй способ, основанный на протоколе XDMCP, удобнее в том случае, если множеству пользователей необходимо настроить доступ к UNIX-серверу, для того чтобы дать им возможность работать с графическими приложениями. В этом случае, на бездисковой станции создается такой же файл XF86Config, как и при первом варианте, однако требуется дополнительная конфигурация сервера, для работы xdm.

На сервере редактируется файл /usr/X11R6/lib/X11/xdm/xdm-config, который может быть например таким:

DisplayManager.errorLogFile: /var/log/xdm.log DisplayManager.pidFile: /var/run/xdm.pid DisplayManager.keyFile: /usr/X11R6/lib/X11/xdm/xdm-keys DisplayManager.servers: /usr/X11R6/lib/X11/xdm/Xservers DisplayManager.accessFile: /usr/X11R6/lib/X11/xdm/Xaccess DisplayManager.willing: su -m nobody -c /usr/X11R6/lib/X11/xdm/Xwilling DisplayManager*authorize: true DisplayManager*resources: /usr/X11R6/lib/X11/xdm/Xresources DisplayManager*session: /usr/X11R6/lib/X11/xdm/Xsession DisplayManager*authComplain: true

Далее файл Xservers будет пустым, Xaccess будет содержать только *, позволяющий на любой машине получить приглашение login. Затем на сервере xdm запускается командой:

server# xdm

Вызов xdm полезно оформить в виде xdm.sh и положить в /usr/local/etc/rc.d.

После того, как на сервере xdm успешно стартовал, на бездисковых клиентах X-сервер можно запускать так:

diskless-101# X -query 192.168.1.2

Если при старте бездисковой станции необходимо обеспечить автоматическую загрузку X-window, эту команду следует поместить в скрипт rc2 каждого бездискового клиента.