Собственный дистрибутив на базе Debian Linux
Потребовалось как-то по работе сделать кастомный дистрибутив. Дистрибутив должен умещаться на один диск и содержать в себе все, что требуется для простого десктопа + некоторые мелочи.
Для начала нам надо установить эталонную систему. Берем с сайта debian.org дистрибутив. Например NetInstall. Скачиваем и устанавливаем на машину(или на виртуальную машину, кому как удобнее). Например я ставлю базовую систему, а затем все устанавливаю руками.
Итак. Система установлена. Устанавливаем требуемые пакеты.
apt-get install gnome gdm openoffice.org xfonts-base xorg xserver-xorg
После устанавливаем пакет «apt-move»
открываем настройки /etc/apt-move.conf и указываем в пункте LOCALDIR путь до того места, где у нас будет храниться будущий дистрибутив. У меня путь был указан в /home/debian. Сохраняем конфиг и выполняем команду apt-move update
По указанному выше пути(/home/debian/distrib) появился каталог pool. В нем будут лежать пакеты, которые в итоге окажутся на диске.
Монтируем CD(в нем ведь все еще есть диск Debian NETINSTALL) и копируем все, что на нем есть в каталог /home/debian/distrib.
Скачиваем с серверов debian.org файлы override. . Я например брал с ftp.fr.debian.org/debian/indices. Версия текущего дистрибутива etch, поэтому и скачиваем файлы, с названиями override.etch.*
Разархивируем gunzip’ом и кладем например в /home/debian/indices
В каталоге /home/debian создаем файл с именем apt.conf и заполняем его текстом по примеру с wiki.debian.org/DebianCustomCD/PoolAptConf. В этом-же каталоге создаем файл с названием дистрибутива (например mycd.conf) и заполнем его по примеру с wiki.debian.org/DebianCustomCD/PoolPackagesGzConf
Запускаем apt-ftparchive -c apt.conf generate mycd.conf
Ну а теперь самое главное. в каталоге /home/debian/distrib/install.i386 создаем файл preseed.conf
В него вписываем инструкции для инсталлятора Debian.
Пример заполнения файла preseed.conf
#заставляем инсталлер автоматически выбирать интерфейс
d-i netcfg/choose_interface select auto
#или выбираем конкретный
#d-i netcfg/choose_interface select eth1
# устанавливаем таймаут для DHCP(в данном случае, если у вас DHCP сервер медленный)
d-i netcfg/dhcp_timeout string 100
#если по DHCP ничего не получили, выводим опцию с возможностью указать настройки сети руками
d-i netcfg/dhcp_failed note
d-i netcfg/dhcp_options select Configure network manually
#название хостнейма данной машины и домена
d-i netcfg/get_hostname string office
d-i netcfg/get_domain string local
# Установка часов
d-i clock-setup/utc boolean true
d-i time/zone string Europe/Moscow
#Устанавливаем базовую систему
tasksel tasksel/first multiselect standard
#возможны разные варианты 🙂
#tasksel tasksel/first multiselect standard, web-server
#tasksel tasksel/first multiselect standard, kde-desktop
# Ну и индивидуальныем пакеты
d-i pkgsel/include string ssh xorg xserver-xorg gdm gnome openoffice.org xfonts-base xfonts-100dpi xfonts-75dpi xfonts-encodings xfonts-scalable xfonts-utils
# Показываем сообщение о предстоящем ребуте
d-i finish-install/reboot_in_progress note
Сохраняем этот файл и открываем для редактирования isolinux/isolinux.cfg
в нем дописываем
LABEL installseed
kernel /install.386/vmlinuz
append vga=normal file=/cdrom/install.386/preseed.cfg initrd=/install.386/initrd.gz —
выходим в директорию /home/debian/distrib
выполняем команду
find . -type f -print0 | xargs —null md5sum > md5sum.txt
и собственно начинаем процесс создания ISO образа:
genisoimage -r -V «Debian 4.0 r4a i386 custom» -o /home/debian/debiancustom_40.iso -J -cache-inodes -b isolinux/isolinux.bin -c isolinux/boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table /home/debian/distrib
После того, как программа закончит работу, в /home/debian/ можно будет найти .iso файл с нашим кастомным дистрибутивом. Теперь его или пишем на болванку или ставим на виртуальную машину.
Делаем из Linux From Scratch свой универсальный дистрибутив
Так уж случилось, что пару лет назад по долгу службы на команду разработчиков, к которой я отношусь, свалилась неожиданная задача — разработка системы управления оборудованием (в этом-то как-раз неожиданности нет, ибо направление разработок такое) с управляющим PC под Linux.
Разработки линуксовой части велись (да и ведутся) под Ubuntu, в среде Code::Blocks. Но, как показала практика, для качественной работы нужно что-то гораздо более легкое с гарантированным временем отклика. Для работы было достаточно консоли, так как задачи организации пользовательского интерфейса решались на подключаемом по TCP/IP удаленном компьютере.
Тогда и пришла идея использовать дистрибутив Linux собственной сборки, чем (сборкой дистрибутива), собственно, в свободное время я и занялся. Выбор пал на LFS. Про то что такое LFS уже неоднократно писали даже на Хабре, я же опишу решение нескольких дополнительных (кроме простенького Linux’а) задач, вставших передо мной в нашем конкретном случае.
Поначалу такая задача была одна — использовать real-time ядро.
Однако дальше, когда идея USB-флешки с дистрибутивом, пришлась всем по душе, то появились задачи размножения флешек и запуска системы на различных компьютерах (тестовых стендов много, имея свою флешку суешь в карман и иди к любому). Вот тут и появились проблемы — LFS не обладает 100% переносимостью с одного компьютера на другой. Для ее адаптации к конкретному компьютеру нужно править некоторые скрипты, что в условиях команды вчерашних Windows-кодеров проблематично (на виртуалку с Ubuntu некоторые пересели, но консоль и скрипты — это беда). Размножение системы также требует повторения некоторых манипуляций, проделываемых в процессе сборки (тот же GRUB установить).
Естественно, решение всех задач есть на просторах интернета, но, думаю, сбор некоторой информации в одном месте никому не помешает.
Итак, конкретные задачи были следующие…
1. Использование ядра Linux с real-time патчем
Это была одна из самых легких задач. Процесс сборки прошел по книге LFS с единственным исключением — вместо штатного для книги ядра было взято 2.6.33.9 и RT-патч для него. Везде, где происходили манипуляции с ядром (установка Linux Headers и ядра непосредственно), работаем с нашей пропатченной версией.
Также не лишним будет сказать, что дистрибутив собирался без подключения swap-раздела (2Гб ОЗУ это в нашем случае — выше крыши, наличие swap не желательно по причине его негативного влияния на гарантированное время отклика, да и для флешки он крайне губителен) и представлял собой один единственный раздел ext2fs.
2. Автоматический вход в систему (в условиях разработки безопасность нам не важна, да и систему пускаем под рутом по ряду причин)
Идея автоматического входа была взята отсюда.
Был создан файл autologin.c следующего содержания:
Далее файл был скомпилирован командой:
gcc autologin.c -o /usr/local/sbin/autologin
Далее было решено, что двух консолей с автоматическим входом хватит (одна для запуска системы, другая для всего остального, если понадобится).
В файле /etc/inittab строчки:
1:2345:respawn:/sbin/agetty tty1 9600
2:2345:respawn:/sbin/agetty tty2 9600
были заменены на:
1:2345:respawn:/sbin/agetty -n -l /usr/local/sbin/autologin tty1 9600
2:2345:respawn:/sbin/agetty -n -l /usr/local/sbin/autologin tty2 9600
После этого никаких проблем с автоматическим входом не было, поэтому никаких дополнительных манипуляций со скриптами не было.
3. Отвязывание системы от порядка подключения дисков
У разных BIOS свои заморочки. Одни, например, считают, что первым является тот диск, с которого мы загружаемся. В этом случае наша флешка будет sda. Другие считают, что сначала должны идти жесткие диски, а потом другие устройства. В этом случае наша флешка будет иметь имя sdb, sdc и так далее.
В результате, то система не может загрузиться с диска, которого нет, то корневой каталог не может быть смонтирован по той причине, что в /etc/fstab указан не тот диск.
Все решается либо исправлением /boot/grub/grub.cfg и /etc/fstab под конкретную машину или использованием для загрузки и монтирования не имени диска (sda, sdb и т. д.), а UUID файловой системы, который для данной файловой системы на флешке будет уникален и, что самое главное — постоянен.
Проблема в том, что GRUB работать с UUID умеет, а ядро — нет, то есть напрямую монтировать корневую систему по UUID (не зная имени устройства) невозможно. Это не баг, а следствие идейных соображений Линуса Торвальдса, поэтому на такую возможность и в будущем надеяться не стоит. Тем не менее пути обхода есть — это initramfs.
Initramfs — временная файловая система, помогающая в загрузке и монтировании файловых систем настоящей системы.
В стандартную сборку LFS initramfs не входит, поэтому для ее построения воспользуемся рекомендациями из Gentoo Wiki и некоторыми собственными соображениями (вариант из Gentoo Wiki без изменений в моем случае проблему с именами дисков не решил, да и не заработал толком).
Для создания простейшей initramfs системы, монтирующей нашу основную по UUID нужна простейшая командная оболочка (shell) и скрипт init. Полный набор утилит командной строки достаточно громоздок для initramfs, поэтому часто для этой цели применяется busybox, который при скромных размерах и требованиях реализует некоторые, наиболее часто используемые утилиты.
Забираем последнюю версию busybox:
Распаковываем и конфигурируем:
tar jxf busybox-1.18.4.tar.bz2
cd busybox-1.18.4
make menuconfig
Конфигурирование производится при помощи меню (наподобие ядра Linux). В принципе, стандартной конфигурации хватает для наших нужд, но, на всякий случай, стоит проверить, что подключены следующие возможности:
Support for devfs — поддержка devfs для работы с /dev.
Build Busybox as a static library (no shared libraries) — статическая компоновка, чтобы не тянуть за собой кучу so-библиотек.
Support version 2.6.x Linux kernels — поддержка ядер линейки 2.6.
А также поддержка функциональности утилит: sh, cat, cut, findfs, mount, umount, sleep, echo, switch_root.
Теперь собираем дерево каталогов нашей файловой системы:
mkdir /usr/src/initramfs
cd /usr/src/initramfs
mkdir -p bin lib dev etc mnt/root proc root sbin sys
cp -a /dev/
Копируем busybox и создаем линки на утилиты:
cp /sources/busybox-1.18.4/busybox ./bin/
cd bin
ln -s busybox sh
ln -s busybox cat
ln -s busybox cut
ln -s busybox findfs
ln -s busybox mount
ln -s busybox umount
ln -s busybox sleep
ln -s busybox switch_root
cd ..
Осталось написать скрипт init:
Делаем наш скрипт исполняемым:
chmod +x /usr/src/initramfs/init
Собираем нашу временную файловую систему в архив:
cd /usr/src/initramfs
find . -print0 | cpio —null -ov —format=newc | gzip -9 > /boot/initrd.img-2.6.33-rt31
Обратим внимание на имя файла — оно должно соответствовать имени ядра (это нужно, чтобы GRUB его правильно подцепил). То есть, если ядро имеет имя vmlinux-2.6.33-rt31, то initramfs должен иметь имя initrd.img-2.6.33-rt31.
Теперь при выполнении grub-mkconfig GRUB обнаружит initramfs, а также включит в конфигурацию UUID корневой системы. Для проверки можно поправить /boot/grub/grub.cfg руками. Например конфигурацию:
menuentry «Linux 2.6.33-rt31» —class gnu-linux —class gnu —class os <
insmod ext2
set root='(hd0,1)’
search —no-floppy —fs-uuid —set 47029df8-8567-417d-b813-eedfe1ff8b0f
linux /boot/vmlinux-2.6.33-rt31 root=/dev/sda1 ro
>
menuentry «Linux 2.6.33-rt31» —class gnu-linux —class gnu —class os <
insmod ext2
set root='(hd0,1)’
search —no-floppy —fs-uuid —set 47029df8-8567-417d-b813-eedfe1ff8b0f
linux /boot/vmlinux-2.6.33-rt31 root=UUID=47029df8-8567-417d-b813-eedfe1ff8b0f ro
initrd /boot/initrd.img-2.6.33-rt31
>
UUID файловой системы можно узнать так (например, для /dev/sdb1):
blkid -p -o udev /dev/sdb1
Осталось поправить /etc/fstab, заменив строчку:
/dev/sda1 / ext2 defaults 1 1
UUID= 47029df8-8567-417d-b813-eedfe1ff8b0f / ext2 defaults 1 1
Также следует заметить, что для всех манипуляций выше необходимо, чтобы в ядре была включена поддержка devtmpfs (CONFIG_DEVTMPFS=y) и initramfs (CONFIG_BLK_DEV_INITRD=y).
4. Отвязывание системы от сетевой карты
Если в компьютере установлено более одной сетевой платы, то при параллельной загрузке модулей ядра не гарантируется постоянное назначение имен этим платам. Например, есть две платы. Плата_1 имеет имя интерфейса в системе eth0, Плата_2 — eth1. При очередной перезагрузке может получиться так, что Плата_1 станет eth1, а Плата_2 — eth0.
С этой целью в LFS производится привязка имени к конкретной плате. При загрузке на другом компьютере очень велика вероятность, что сеть не поднимется.
В моем конкретном случае плата на всех компьютерах одна и IP — статический (связь только с терминальным компьютером напрямую).
Поднятие сетевого интерфейса в LFS осуществляется скриптом /etc/rc.d/init.d/network. Допишем скрипт так, чтобы каждый раз при загрузке генерировался конфигурационный файл /etc/udev/rules.d/70-persistent-net.rules и при завершении работы этот файл удалялся. Подозреваю, что есть метод проще, но найденный метод заработал, а копаться в принципах работы Udev времени и особого желания на момент сборки системы не было.
В начало раздела start команды case добавляем:
А в конец секции stop (непосредственно перед ;;) добавляем:
Теперь при загрузке на любой системе имя сетевого интерфейса будет eth0 (кроме самых экзотических случаев) и сеть будет подниматься. Разумеется, каталог /etc/sysconfig/network-devices/ifconfig.eth0 с файлом ipv4 должен существовать. Содержимое этого файла описано в книге LFS.
5. Написание скрипта, производящего инсталляцию LFS на любую флешку
Осталось последнее — сделать архив системы и скрипт, который будет ее устанавливать на произвольный носитель.
Загружаемся в другой системе (не с флешки), монтируем флешку, например в /mnt/usb-os. Архивируем содержимое:
cd /mnt/usb-os
tar -cvjf
Пишем скрипт для установки install_usb-os.sh. В качестве параметра скрипт принимает имя устройства, на котором необходимо развернуть систему (например /dev/sdb). Скрипт сам создаст необходимый раздел и файловую систему (/dev/sdb1, если указано имя /dev/sdb), распакует архив и установит GRUB.
Запускаться он должен с правами root и, на самом деле, очень опасен.
В случае неверного указания имени устройства могут быть уничтожены все данные на рабочем диске!
Собираем собственную ОС на базе Linux
Архив номеров / 2006 / Выпуск №3 (40) / Собираем собственную ОС на базе Linux
|