АНДРЕЙ ЕЛСУКОВ
Создание релиза FreeBSD
В этой статье я хочу обобщить свой опыт по сборке релиза операционной системы FreeBSD. Создание релиза – не такая уж сложная задача, но достаточно длительная и при некоторых обстоятельствах может продолжаться дольше, чем ей необходимо. Да и к тому же описание этого процесса на русском языке мне найти не удалось. Надеюсь, что статья поможет желающим собрать свой релиз и обойти те проблемы, с которыми столкнулся когда-то я.
Для чего это нужно?
Я вижу несколько причин, для чего может понадобиться создать релиз ОС FreeBSD в «домашних» условиях:
- не всегда есть время, средства, возможность покупать свежий релиз системы, чтобы иметь его на CD для более быстрой установки «свежей» системы;
- хочется иметь под рукой загрузочный инсталляционный диск системы со специфическими настройками: установочный диск STABLE-ветки; диск с обновленными драйверами, например для RAID-контроллеров; с определенным набором скомпилированных пакетов; с архивами исходных файлов некоторых программ для компиляции из «портов»;
- хочется всё знать и уметь.
Кто-то, возможно, предложит свои причины, для меня достаточно и этих.
Что нам необходимо для начала?
Как это ни банально, нужна машина под управлением ОС FreeBSD. Причём желательно, чтобы версия системы была из той же ветки, что и версия будущего релиза (почему «желательно» – расскажу позднее). Если ваша машина подключена к Интернету и проблемы с ценой трафика для вас не существует, то следующие компоненты для вас не важны, если же нет, то идём по пунктам.
CVS-репозитарий
Эта «свалка файлов» в архиве на данный момент у меня занимает порядка 500 Мб. Чтобы получить репозитарий – проще всего воспользоваться CVSup. У меня по крону раз в сутки запускается следующая команда для обновления репозитария:
# cvsup -g -L 2 /mnt/cvs/cvs-supfile
где конфигурационный файл CVSup выглядит так:
*default host=cvsup2.FreeBSD.org
*default base=/var/db
*default prefix=/mnt/cvs/ncvs
*default release=cvs
*default delete use-rel-suffix
*default compress
src-all
ports-all
doc-all
cvsroot-all
Лучше всего, если вы достанете архив репозитария, пусть даже старый у своего знакомого или ещё где-то – сэкономите кучу времени и трафика. Поддержание репозитария в «свежем» состоянии уже не так накладно, так что, сделав один раз, использую и сейчас.
Архивы исходных файлов для сборки портов
Не пугайтесь, дистрибутивы для всех портов нам не понадобятся. Но и перечислять каждый порт довольно утомительное занятие, да и от релиза к релизу этот список может различаться, поэтому я немного обобщу:
- для сборки проекта документации понадобятся исходные файлы для портов, список которых можно посмотреть в файле src/release/Makefile.inc.docports;
- для создания ISO-образов дисков нужен порт ports/sys utils/cdrtools;
- для создания полноценного miniinst-диска нужны исходные файлы Perl и X.org, либо XFree (в зависимости от версии системы).
В общем-то, вот и всё. Недостающие компоненты можно будет скачать во время сборки (если есть доступ к Интеренету, то они скачаются автоматически).
Как это происходит?
Весь процесс сборки я условно разделил на несколько этапов. Отчасти моя классификация перекрывает ту, что описана в мануале release(7). Итак, по порядку.
Подготовка к сборке
Кроме вышеперечисленного я отнёс сюда следующие действия:
- получение исходных файлов системы для будущего релиза;
- выбор раздела, на котором достаточно свободного места.
К слову сказать, для сборки релиза может понадобиться от двух до четырёх с небольшим гигабайт свободного места.
Сборка мира
Собственно, сборка мира точно такая же, как и при обновлении системы, подробности – в handbook.
Создание chroot-окружения
Весь процесс сборки релиза, сразу после компиляции мира, происходит в chroot-окружении. Сначала туда инсталлируется собранный мир, затем там происходит дальнейшая компиляция системных библиотек, ядер, портов, проекта документации и т. п.
Создание дистрибутива
Это заключительная стадия, включает в себя компиляцию указанных ядер, создание структуры каталогов дистрибутива, размещение архивов бинарных файлов и установочных скриптов, создание каталога для инсталляции с FTP, создание ISO-образов дисков.
На что следует обратить внимание?
Рекомендую прочитать Makefile в каталоге src/release. Из него можно почерпнуть довольно много полезной информации. Так же обязательно прочитать мануал release(7) и желательно просмотреть все файлы, на которые он ссылается.
make.conf
При сборке мира используются установки из /etc/make.conf текущей системы. Рекомендую просмотреть их и подумать – нужны ли они вам. К примеру, некоторые настройки могут запрещать сборку отдельных частей системы, отсутствие которых может фатально сказаться на дальнейшей сборке релиза, например Perl. Так что, если не хотите терять время, лучше на первый раз закомментировать все опции в make.conf или переименовать этот файл. Возможно, наоборот, вы хотите использовать какие-то специфические опции, например дополнительную оптимизацию, – дело ваше.
Переменные окружения
Часть переменных окружения описана в мануале release(7). Часть можно найти в make-скриптах, используемых при сборке (в папке src/release). Хочу обратить внимание на некоторые из них:
- DOC_LANG – в ней можно перечислить через пробел языки, для которых вы хотите собрать документацию, например, DOC_LANG=”en_US.ISO8859-1 ru_RU.KOI8-R”. Если вы не укажете языки, то будут собираться все возможные, а это довольно долго.
- NOPORTREADMES – при включении в дистрибутив дерева портов не будут генерироваться README.html с описанием портов, что существенно ускорит процесс сборки релиза.
- RELEASENOUPDATE – если при сборке у вас возникнет ошибка, эта переменная совместно с целью make rerelease освободит вас от повторного обновления исходных файлов системы из CVS-репозитария.
- BUILDNAME – имя релиза. Если не зададите, то получите в результате что-то подобное FreeBSD-4.11-20050131-STABLE. Ещё рекомендую задавать имя в стандартном виде, как это делается в оригинальных дистрибутивах, например 5.3-STABLE. Не нужно называть релиз своим именем, если не хотите в дальнейшем иметь проблемы, например, при установке или обновлении портов.
- MAKE_ISOS – если задать, то при завершении всех этапов сборки автоматически будут созданы ISO-образы дисков дистрибутива. Советую не спешить с указанием этой переменной, создать образы никогда не поздно.
- hostname – это не переменная окружения, но я отнёс её сюда. Когда вы соберёте релиз, при его установке, в выводе команды uname, будет фигурировать имя хоста, на котором происходила сборка. Из эстетических соображений на время сборки можно сменить имя хоста командой hostname.
«distfiles» для портов
Как я уже говорил, для сборки релиза понадобятся дистрибутивы некоторых портов. Если у вас уже есть скачанные дистрибутивы, вы можете указать скриптам их местоположение, и они не будут скачиваться снова. Для этого используется переменная окружения RELEASEDISTFILES. Если вы пользуетесь системой портов и в вашем дереве есть каталог ports/distfiles, то переменную можно не определять, этот каталог будет скопирован в chroot-окружение (наверное, стоит задуматься о том, сколько места он занимает).
Когда что-то пошло не так
Если с машины, где происходит сборка релиза, нет прямого доступа к Интернету, то есть большая вероятность того, что на каком-нибудь этапе сборка прервётся с ошибкой. Хотя практика показывает, что они могут возникать и при наличии доступа в Интернет. Ошибки, препятствующие сборке релиза, могут быть разными. Начиная от простых – нехватки каких-нибудь дистрибутивов для сборки из портов и заканчивая различными ошибками при компиляции.
Не нужно начинать всё сначала
Чтобы не начинать всё заново, расскажу, как с «минимальными потерями» продолжить сборку релиза. Итак, предположим, что процесс сборки прервался по какой-то причине. Решив проблему, вы хотите продолжить сборку. Для этого нужно указать утилите make цель «rerelease». И если нет необходимости в обновлении исходных файлов системы из репозитария, желательно определить переменную RELEASE NOUPDATE.
Теперь, предположим, что вы хотите повторить какой-то этап сборки, например пересобрать ISO-образы системы. Предыдущая схема здесь не сработает. Если ISO-образы уже были созданы, то повторного создания таким методом не добиться, даже если предварительно их удалить. По той простой причине, что при завершении этапов сборки (они описаны в мануале release(7)) скриптами сохраняются «пометки» об их завершении. Пометки – это обычные файлы, созданные командой touch. Имена файлов соответствуют названию завершившегося этапа. Файлы находятся в каталоге ${CHROOTDIR}/usr/obj/usr/src/release. Для того чтобы вновь пересобрать ISO-образы, нужно удалить файл «iso.1» и уже тогда запускать make rerelease.
Не хватает дистрибутивов для установки порта
Если есть доступ в Интернет, то возникновение такой ошибки маловероятно. Если же доступа нет, то можно вручную скачать дистрибутив для установки порта с машины, где есть доступ, и поместить его в папку ${CHROOTDIR}/usr/ports/distfiles, конечно, нужно не забывать, что некоторые дистрибутивы могут располагаться в отдельных каталогах. Чтобы узнать, должен ли дистрибутив порта располагаться в отдельном каталоге, нужно выполнить в каталоге порта команду grep DIST_SUBDIR Makefile. После этого можно продолжить сборку, как это описано выше.
Ошибки компиляции
Однозначный ответ на вопрос: что делать при ошибках компиляции? – пожалуй, дать довольно сложно. Опять же, могу направить к чтению handbook, в котором есть несколько советов на эту тему. Приведу несколько примеров ошибок и возможных путей их решения:
- При сборке порта выводится ошибка о нехватке какой-либо библиотеки, вместо установки её как зависимости. Возможно, у вас отсутствует индексный файл портов – ports/INDEX или ports/INDEX-5 (для 5-й ветки FreeBSD), ports/INDEX-6 (для 6-й ветки FreeBSD). Можно скачать индексный файл с сайта – http://www.freebsd.org/ports/INDEX.bz2, INDEX-5.bz2 или INDEX-6.bz2. Или просто перейти в ${CHROOTDIR} и собрать требуемый порт руками, устанавливая каждую зависимость, а затем продолжить сборку. Переходить в ${CHROOTDIR} нужно с помощью команды chroot, чтобы порты устанавливались и регистрировались в окружении той системы, релиз которой вы собираете.
- Какой-то определённый порт не собирается, даже если выполнить предыдущие рекомендации. Некоторые порты во время сборки определяют версию системы и в зависимости от неё выбирают соответствующие параметры сборки приложения, либо по версии системы выбирается, какие зависимости имеет порт. Система портов определяет версию через переменные ядра. Поэтому, хоть сборка порта и происходит в chroot-окружении, где все бинарные файлы и исходные коды от нужной версии ОС, для системы портов версия ОС будет той, в которой вы собираете релиз. Это следует помнить, так как версия системы влияет и на выбор индексного файла, и на то, какой файл будет скачиваться при выполнении make fetchindex, и какой файл будет генерироваться при выполнении make index. Это ограничение можно обойти, если определить переменные OSVERSION и OSREL. Формат OSVERSION можно посмотреть в переменной ядра kern.osreldate, OSREL определяется по первым цифрам вывода «uname -r». Подробнее всё это можно узнать, просмотрев скрипты в каталоге ports/Mk. Эти переменные можно поместить в ${CHROOTDIR}/etc/make.conf, тогда они будут использоваться при каждом запуске make в chroot-окружении.
Прочие ошибки
Искать способы исправления других ошибок вам придётся, видимо, самим. Можно попытаться обратиться в один из списков рассылки или форум, посвящённый FreeBSD. Расскажу только об одной ошибке, не относящейся к предыдущим пунктам. Я столкнулся с ней, когда попытался собрать релиз 4-й ветки, находясь в 5-й. Ошибка возникает, когда система пытается создать загрузочную область и образы загрузочных дискет. В 5-й версии было убрано псевдоустройство vn(4) и утилита vnconfig(8), их функционал перенесён в драйвер md(4) и mdconfig(8). Да, и ещё в 5-й версии используется devfs.
Итак, как решить проблему по созданию релиза 4-й версии FreeBSD в окружении 5-й? Не скажу, что способ правильный, но он рабочий, по пунктам:
- Нужно определить в make.conf переменные OSVERSION и OSREL в значение, которое они должны иметь в создаваемом релизе.
- Собираем мир, устанавливаем его и после завершения этапа release.2 прерываем сборку. Добавляем аналогичные опции в ${CHROOTDIR}/etc/make.conf. Это необходимо для корректной сборки портов. Продолжаем сборку релиза.
- После завершения этапа release.7 сборка должна прерваться с ошибкой в скрипте doFS.sh. Теперь нужно статически собрать утилиту mdconfig, чтобы исключить все зависимости от разделяемых библиотек. Для этого понадобятся исходные файлы текущей системы (той, в которой происходит сборка релиза), переходим в каталог src/sbin/mdconfig и запускаем команду «make -DNO SHARED depend all». В каталоге obj/usr/src/sbin/mdconfig будет статически скомпилированная утилита mdconfig. Копируем её в ${CHROOTDIR}/sbin/. Монтируем файловую систему devfs – «mount_devfs devfs ${CHROOTDIR}/dev». Теперь можно продолжить сборку релиза.
Я не проверял, будет ли этот способ работать при сборке релиза 5-й версии FreeBSD, находясь в окружении 4-й, но не вижу причин, по которым сборка может не удастся. Различие только в том, что собирать нужно будет не mdconfig, а vnconfig и вместо монтирования devfs создавать файлы устройств с помощью скрипта /dev/MAKEDEV.
Приступим к сборке
Процесс сборки релиза опишу именно так, как делаю его я. Начальные данные:
- Рабочий сервер, у которого есть доступ в Интернет, на нём находятся CVS-репозитарий и каталог distfiles с дистрибутивами для сборки портов.
- Ещё один сервер, на котором я ставлю эксперименты, он находится внутри локальной сети без доступа в Интернет.
- Доступ к CVS-репозитарию осуществляется по NFS.
- Собирать буду FreeBSD 5.3-STABLE, т.е. с CVS-тэгом RELENG_5.
Подготовка
# cd /usr/home
# mkdir release
# mount_nfs -o rdonly server:/usr/home/cvs /mnt
# cvs -Rd /mnt/ncvs co -Pr RELENG_5 src
# cd src/
# mv /etc/make.conf /etc/make.conf.old
# make -j8 buildworld
# cd release/
# vim start.sh
После сборки мира я создаю скрипт примерно со следующим содержанием:
make -j8 CHROOTDIR=/usr/home/release BUILDNAME=5.3-STABLE CVSROOT=/mnt/ncvs
RELEASETAG=RELENG_5 DOC_LANG=”en_US.ISO8859-1 ru_RU.KOI8-R” NOPORTREADMES=yes RELEASENOUPDATE=yes release
-j8 –максимальное количество make-процессов, которое может работать параллельно (сервер у меня двухпроцессорный, со SCSI RAID-5); RELEASENOUPDATE – это на будущее (чтобы не забыть), для цели «release» эта переменная не используется.
NFS-каталог server:/usr/ports/distfiles можно примонтировать сейчас в /usr/ports/distfiles или позже.
Сборка релиза
Теперь запускаем сборку релиза: sh start.sh. После получения исходных файлов системы и дерева портов из CVS можно прервать сборку и примонтировать distfiles:
# mount_nfs -o rdonly server:/usr/ports/distfiles /usr/home/release/usr/ports/distfiles
# vim start.sh
В файле start.sh исправляем цель release на rerelease и запускаем sh start.sh. Теперь можно заняться своими делами, но переодически посматривать за процессом сборки. Если каких-то портов не будет хватать, я запускаю на сервере make fetch-recursive для нужного порта и после завершения снова запускаю start.sh. Когда процесс закончится, вы увидите надпись «make rerelease for i386 finished …».
Перед сборкой ISO-образов в 5-й версии FreeBSD необходимо создать пакеты xorg и perl5. Если делать всё «по правилам», то для создания пакетов должен использоваться отдельный сервер, так как других лишних серверов у меня в наличии нет, я использую этот же.
Технология такая:
- Создаётся каталог ${CHROOTDIR}/usr/ports/packages.
- В chroot-окружении создаются необходимые пакеты вместе со всеми зависимостями при помощи make package-recursive.
- По имеющимся пакетам создаётся файл INDEX.
- Каталог packages копируется в ${CHROOTDIR}/R/cdrom/disc1/.
По первым двум пунктам вроде бы вопросов быть не должно, а вот о создании файла INDEX расскажу подробнее.
Я использовал для его создания свой скрипт, поэтому не заметил, как в версии 5.3 появился скрипт src/release/scripts/mkpkgindex.sh.
Мой скрипт выглядит так:
#!/usr/local/bin/perl
foreach(`ls All/`) {
chop;
s/\.t.z$//;
`grep -E "^$_" ../INDEX-5 >> INDEX`;
}
Итак, для минимального дистрибутива нужны пакеты perl и xorg, создаём их и копируем на «будущий» диск:
# mkdir /usr/home/release/usr/ports/packages
# cp mkindex.pl /usr/ports/packages
# chroot /usr/home/release
# cd /usr/ports/lang/perl5.8 && make package-recursive
# cd /usr/ports/x11/xorg && make package-recursive
# cd /usr/ports/x11/xorg-manpages && make package-recursive
# cd /usr/ports/packages
# ./mkindex.pl
# rm mkindex.pl
# cp –PRv /usr/ports/packages /R/cdrom/disc1/
# exit
Теперь можно создавать ISO-образы, исправляем файл start.sh, добавив переменную MAKE_ISOS=yes, и запускаем start.sh. Установится порт cdrtools и создадутся два образа диска.
На этом всё.
Заключение
В завершение хотелось бы упомянуть о возможностях создания нестандартного дистрибутива, имеющихся в сборочных скриптах релиза системы.
В первую очередь это настройка sysinstall при помощи конфигурационного файла install.cfg. На эту тему в Интернете можно найти множество статей, ну и конечно же не надо забывать про мануал sysinstall(8).
Также есть возможность применения сторонних патчей к исходным файлам системы во время создания релиза. Обратите внимание на переменные LOCAL_PATCHES, PATCH_FLAGS и LOCAL_SCRIPT в мануале release(7).
Ну и если вам необходимо что-то действительно необычное, обратите внимание на сборочные скрипты таких проектов, как Frenzy, FreeSBIE, LiveBSD.