10 октября 2016 г. 19:05
Написал soar

Готовим ThinStation

Введение

Сборка тонкого клиента, ориетнированного на определенные клиенты, сводится к следующим этапам:

  • Скачиваем полный репозиторий ThinStation
  • Собираем "толстый" (полный) образ
  • Загружаем тонкий клиент на толстом образе
  • Получаем список необходимых для этого клиента модулей ядра и пакетов
  • Исправляем конфиги сборки, оставив только самое необходимое (в том числе полученное на предыдущем этапе)
  • Собираем "тонкий" (облегченный) образ

Подготовка кухни

Сразу скажу, что есть и другой путь сборки - скачивание подготовленного .iso образа. Но мне это кажется не таким удобным, поэтому я буду описывать "правильный" вариант.

Скачивание репозитория

Вообще, для работы с ThinStation рекомендуется иметь базовые знания работы с Git. Просто потому, что ваши изменения нужно будет куда-то сохранять, а заблудиться в иерархии файлов, когда кухня уже распакована (не зная Git) - очень легко. Скачивание сводится к выполнению одной команды.

git clone --depth 1 git://github.com/Thinstation/thinstation.git -b 5.5-Stable

где 5.5-Stable - актуальная на данный момент ветка. К сожалению размер репозитория за последние годы очень сильно вырос - в 2014-ом году он занимал чуть более 2 ГБ, а сейчас скачать придется целых 8 ГБ. Поэтому во всех мануалах предлагают скачивать его с ключом --depth 1 - на вкус и цвет.

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

Подготовка chroot

В корневой директории скачанного репозитория лежит bash-скрипт setup-chroot, который сделает всю работу за вас. О нем нужно знать лишь то, что запускать его нужно от рута (т.к. он, например, монтирует в новое дерево служебные файловые системы), а во-вторых он имеет набор ключей, самым нужным из которых на первом этапе является ключ -a - не задавать вопросы, а скачивать всё что нужно автоматически.

soar@localhost $ cd thinstation
soar@localhost $ sudo ./setup-chroot -a

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

Толстый образ

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

Безусловно, есть вероятность, что для вашего железа это кто-то уже сделал. Вы можете зайти в директорию ts/build/machine и поискать там название вашей платформы. Если найдете - смело пропускайте этот пункт.

Настройка сборки

Первое, что вам понадобится, отредактировать файл ts/build/build.conf (изначально это симлинк на build.conf.example - сделайте копию) следующим образом (раскомментируйте строки):

...
# Пригодится, чтобы больше узнать о железе
package lshw
...
# Набор xorg-пакетов для практически любого видео
package xorg7-v4l
package xorg7-vesa
package xorg7-vmware
package xorg7-ati
package xorg7-nouveau
package xorg7-openchrome
package xorg7-intel
package xorg7-sis
...
# Системные утилиты, в том числе - hwlister, о нем ниже
package extensions
package extensions-x
...
# Изменение системы сжатия
param initrdcmd "gzip"
...
# Включает полный набор firmware - иногда не работает, но рекомендую раскоментировать
param allres true
param allfirmware true
...

Всё остальное сейчас не важно - настраивать тонко будем потом.

Отдельно остановлюсь на том, зачем нужно поменять систему сжатия со squashfs на gzip. Скрипт hwlister.sh, о котором пойдет речь ниже, использует очень интересную методику поиска загруженного firmware - он просто смотрит время доступа к файлам в /lib/firmware и на основе этого делает выводы, какие файлы были загружены. Но squashfs монтируется с параметром relatime, что приводит к тому, что время доступа к файлам не меняется и список firmware (чёрт, не знаю, как перевести это слово, не потеряв смысл) всегда пуст. Изменение режима сжатия на gzip - самый простой и быстрый способ вернуть скрипт к жизни не залезая в кишки. Я написал об этом разработчикам, но ответа пока не было.

Сборка

Сборка любого образа выполняется в chroot - так что не забываем в него зайти. Для сборки толстого образа существует также специальный параметр --allmodules, который включает в образ все доступные модули ядра, что также пригодится на неизвестном железе.

soar@localhost $ sudo ./setup-chroot
[root@TS_chroot]/# cd build
[root@TS_chroot]/build# ./build --allmodules --license ACCEPT --autodl

После того, как процесс завершится, в директории boot-images можно будет найти варианты образа - iso, pxe и syslinux. Можно использовать любой и загружать клиент любым удобным способом.

Сбор информации

Когда подопытное железо успешно загрузилось, необходимо зайти в консоль любым удобным способом и написать:

hwlister.sh

Это обычный bash-скрипт, после выполнения которого вы обнаружите несколько файлов:

  • /firmware.list - список необходимых firmware
  • /module.list - список необходимых модулей ядра
  • /package.list - список необходимых пакетов, учитывая архитектуру будет содержать только xorg7-* пакеты
  • /vbe_modes.list - если используется uvesafb, этот файл будет содержать список поддерживаемых режимов

Некоторые файлы могут отсутствовать, если ничего подходящего не найдено

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

Этот же скрипт попытается загрузить файлы на ваш tftp-сервер, указанный в конфиге, однако, надеюсь, у вас запись на tftp, как и у меня, запрещена. Поэтому забираем файлы с тестируемой системы любым способом и кладем в директорию ts/build/machine/MACHINENAME, где MACHINENAME - кодовое имя, которое вы дадите своему железу.

Тонкий образ

Сборка тонкого образа - это всегда балансирование на границе между функциональностью и объемом. Меньше объем - быстрее загрузка бездисковых рабочих станций по сети, быстрее старт системы, меньше оперативной памяти требуется клиентам. Лично у меня стояла задача сделать образ минимального объёма всего под одну задачу - терминальный RDP-клиент. Об этом я и буду рассказывать.

Итак, у нас есть офис, набор тонких клиентов, подключенных проводами, получающих адрес по DHCP, загружающихся по PXE и стартующих одно единственное приложение - RDP-клиент.

Конфигурация сборки - build.conf

Как я уже писал выше, первый этап в конфигурации сборки - это правка файла build.conf. Он определяет какие пакеты будут включены в образ и некоторые другие параметры сборщика.

  • Все строки начинающиеся с machine - комментируем. Должно остаться только то, что используется у вас. Нужно отметить, что в конфигурации можно держать активными сразу несколько профилей - тогда получится образ, запускающийся на любом из них (в теории, если нет конфликтов).
  • Скорее всего вам не понадобятся файловые системы кроме vfat и ntfs - поэтому в блоке файловых систем можно смело комментировать строки isofs, udf, ext*.
  • Так как мы создали профиль для своего железа, содержащий необходимые пакеты xorg7, то все строки содержащие package xorg7-* - можно смело комментировать.
  • Смело комментируем все пакеты локалей package locale-* кроме, конечно ru_RU, и, по желанию, en_US - нужна она или нет вопрос спорный.
  • Если вам нужен удаленный доступ к рабочим станциям - включите package sshd
  • Если вам нужны смарт-карты и USB-токены - включите package ccidreader
  • Если вы собираетесь вырезать оконный менеджер, рабочий стол и показывать пользователю только одно приложение (например FreeRDP) - включите пакет package automount для автоматического монтирования любых USB-устройств. При этом package udisks можно смело выключить.
  • Если вам не нужен интерфейс для Wi-Fi соединений и другие рюшечки - закомментируйте package networkmanager и включите package autonet. Но будьте готовы к тому, что придется покопаться в его внутренностях - это скриптовая обвязка для системных утилит и в некоторых сетях может работать не совсем так, как ожидается.
  • Чтобы максимально облегчить образ, включаем package openbox и выключаем package gtk-*, package icons-*, package fonts-*.

Что касается пакетов в разделе Applications - здесь выбор полностью за вами. Всё описанное выше применимо к тонким клиентам, где пользователь не будет видеть своего рабочего стола (RDP, VNC, etc) и для использования, например, локального браузера - многое из перечисленного выше придется оставить.

Остается не забыть вернуть param initrdcmd "squashfs" и убрать 3 строки в самом конце: package alltimezone, param allres true и param allfirmware true - в тонком образе это нам не пригодится.

Runtime-конфигурация - thinstation.conf.buildtime

Файл thinstation.conf.buildtime является по своей сути bash-скриптом, предоставляющим переменные окружения для всех скриптов запуска. Перед тем, как начать его редактировать, стоит заглянуть в директорию ts/build/conf (github) - здесь собраны кусочки конфигураций для каждого пакета, включающие в себя пояснения и все доступные переменные.

Давать какие-то универсальные советы - сложно. Настройка будет зависеть от вашего окружения и используемых пакетов. Приведу лишь пример для RDP-сессии.

# У пользователя не будет локального UI, так что локально выкручиваем громкость на максимум
AUDIO_LEVEL=100
MIC_LEVEL=100

# Для бездисковых станций резонно собирать логи в одном месте
SYSLOG_SERVER=syslog.example.com

# Локаль и таймзона
LOCALE=ru_RU.UTF8
TIME_ZONE=Europe/Moscow

# Кнопки "Безопасного извлечения устройства" также не будет - поэтому включаем обязательно
USB_STORAGE_SYNC=ON
DISK_STORAGE_SYNC=ON
# Монтировать устройства нужно в директорию, которую мы потом пробросим в удаленную сессию
USB_MOUNT_DIR=/mnt/usb
# Для поддержки кириллицы на съемных накопителях, я для себя вывел вот такой набор параметров. Он точно подходит для FAT32/NTFS разделов и FreeRDP
USB_MOUNT_OPTIONS="rw,nosuid,nodev,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=ascii,shortname=mixed,showexec,utf8,flush,errors=remount-ro"
DISK_MOUNT_OPTIONS="rw,nosuid,nodev,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=ascii,shortname=mixed,showexec,utf8,flush,errors=remount-ro"

# Если выключили NetworkManager и включили autonet - обязательно настройте сеть
NET_USE_DHCP=ON

# Нулевая сессия должна быть оконным менеджером
# Можно попробовать обойтись без него и даже вообще без Иксов
# но это тема для отдельной статьи
SESSION_0_TITLE=Desktop 
SESSION_0_TYPE=openbox 
SESSION_0_AUTOSTART=ON 

# Главная рабочая сессия
# Список параметров FreeRDP - пожалуй также повод для отдельной статьи
SESSION_1_TITLE=RemoteDesktop 
SESSION_1_TYPE=freerdp 
SESSION_1_AUTOSTART=ON 
SESSION_1_FREERDP_SERVER=rdp.example.com
SESSION_1_FREERDP_OPTIONS="+decorations +fonts +aero ..."

Сборка тонкого образа

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

soar@localhost $ sudo ./setup-chroot
[root@TS_chroot]/# cd build
[root@TS_chroot]/build# ./build --license ACCEPT --autodl

И это всё. В зависимости от того, что вы указали в build.conf, вы получите готовые образы для загрузки по PXE, с CD-ROM, жесткого диска или флешки. При описанной конфигурации можно добиться образа размером ~90 MB и времени загрузки по PXE (от включения питания до рабочего стола) около 1 минуты. С локального диска и того быстрее.

Другие возможности

Нужно отметить, что всё, о чем я писал выше - советы по сборке универсального образа. Я добился того, чтобы с одного образа можно было загрузить любой ПК из присутствующих в зоопарке компании. Но может получиться так, что вам понадобится сделать несколько вариантов клиентов, с разными настройками или, например, разными адресами серверов. В таком случае обратите внимание, что ThinStation умеет как скачивать дополнительные конфигурационные файлы во время загрузки, так и докачивать дополнительные модули. Это очень хорошо описано в документации, и я на этом останавливаться не буду.

Полезные заметки

Очистка кухни

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

  1. Не забыть выйти из chroot
  2. Убедиться, что сохранили все свои изменения в Git
  3. Отмонтировать все системные ФС внутри кухни: umount -R thinstation/*
  4. Запустить скрипт очистки: sudo ./setup-chroot -a
  5. Удалить всё, что осталось: git clean -dx - это удалит все несохраненные файлы

Добавление своих пакетов

Если вы собираетесь привносить в проект что-то свое, нужно знать о том, что в терминологии ThinStation, а вернее в терминологии CRUX Linux, на котором базируется TS, существует два базовых понятия:

  • package (далее "пакет") - некая абстракция, указывающая на то, что необходимо установить в будущий образ. Пакет может содержать кусок дерева файловой системы, отдельные файлы, или даже просто один конфигурационный файл, указывающий, например на зависимости.
  • port (далее "порт") - подобие *.deb иди *.rpm пакета, с одним важным отличием: архив со скомпилированными файлами не содержит правил установки, а представляет из себя просто кусок дерева файловой системы. Любые правила (скрипт компиляции, скрипты пост-установки, и т.д.) лежат рядом с архивом и легко редактируются.

Когда вы хотите дополнить образ чем-то своим, первое, о чем стоит задуматься, а что именно вам нужно? Если вы хотите добавить в образ пару текстовых конфигов - просто создайте свой пакет, включите его build.conf - и этого будет более чем достаточно. Если же вам нужно собирать бинарные файлы - то вам понадобится сделать свой порт.

Создание своего порта

Первое, что я рекомендую сделать, это создать для своих поделок отдельную директорию. Для этого нужно в файл ts/etc/prt-get.conf добавить строку:

prtdir /ts/ports/yourproject

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

Теперь вам понадобится создать один единственный bash-скрипт, который будет отвечать за сборку порта: /ts/ports/yourproject/portname/Pkgfile. Образец можно посмотреть здесь, а можно подсмотреть в любом другом порте. Базовый вариант выглядит так:

name=mdetect-TS
version=0.5.2.3
release=1
source=(http://ftp.de.debian.org/debian/pool/main/m/$name/$name-$version.tar.bz2)

build() {
    cd $name-$version

    ./configure --prefix=/usr \
            --exec-prefix=/ \
            --sysconfdir=/etc \
            --mandir=/usr/man \
            --disable-extras

    make
    make DESTDIR=$PKG install
}

Давайте разберемся, что он делает (на самом деле делает не он, он лишь определяет стадию сборки):

  1. Скачивает файлы, заданные в source (в данном случае - http://ftp.de.debian.org/debian/pool/main/m/mdetect-TS/mdetect-TS-0.5.2.3.tar.bz2), их может быть несколько
  2. Распаковывает все скачанные файлы в рабочую директорию
  3. Выполняет configure + make
  4. Делает make install из директории /ts/ports/yourproject/portname/work/src в /ts/ports/yourproject/portname/work/pkg
  5. Полученное содержимое директории pkg упаковывается в архив. Это и будет наш порт, готовый для установки.

Проверим наши предположения. Чтобы выполнить первую сборку, необходимо сделать следующее:

[root@TS_chroot]/# cd ts/ports/yourproject/portname/
[root@TS_chroot]/ts/ports/yourproject/portname# pkgmk -kw
=======> Building '/ts/ports/yourproject/portname/portname#0.5.2.3-1.pkg.tar.xz'.
...
=======> WARNING: Footprint not found, creating new.
=======> Building '/ts/ports/yourproject/portname/portname#0.5.2.3-1.pkg.tar.xz' succeeded.

Конечный файл portname#0.5.2.3-1.pkg.tar.xz (имя файла формируется с учетом переменных заданных в начале скрипта) представляет из себя готовый к установке порт и содержит в себе дерево файловой системы. Остается включить его в образ - о том, как это сделать, смотрите ниже.

Нужно также отметить, что если у вас в директории с портом присутствуют файлы .footprint или .md5sum - сборка может "провалиться" с ошибкой из-за несоответствия дерева файлов или md5-сумм. Можно эти файлы удалить перед сборкой и они сгенерируются автоматически, а можно выполнить следующую последовательность действий:

[root@TS_chroot]/ts/ports/yourproject/portname# pkgmk -kw
[root@TS_chroot]/ts/ports/yourproject/portname# pkgmk -uf
[root@TS_chroot]/ts/ports/yourproject/portname# pkgmk -um

Порт собран - самое время установить его в систему. На данном этапе речь идет о системе рабочей, той, где вы собираете ThinStation.

[root@TS_chroot] # prt-get install portname
prt-get: installing /ts/ports/yourproject/portname
=======> Package '/ts/ports/yourproject/portname/portname#0.5.2.3-1.pkg.tar.xz' is up to date.
prt-get: installing portname 0.5.2.3-1

-- Packages installed
portname

prt-get: installed successfully

Теперь архив распакован в систему и все файлы доступны для использования. Однако эти файлы не появятся в собранном вами образе - для этого вам кроме порта, понадобится еще и пакет.

Создание пакета

Минимальный пакет может выглядеть так:

soar@localhost$ tree ts/build/packages/mypackage/
ts/build/packages/mypackage/
├── dependencies
├── etc
│   └── somefile-placed-in-etc

Где в файле dependencies достаточно указать одну строку - base. Считается хорошим тоном, чтобы все пакеты зависели хотя бы от пакета base, но вы можете добавить больше правил. Этого уже достаточно, чтобы при включении строки package mypackage в ваш build.conf - файл somefile-placed-in-etc попал в директорию /etc вашего готового образа.

Что же делать, если мы хотим, чтобы наш пакет, кроме обычных файлов, включал в сборку наш порт? Для этого нам понадобятся всего два скрипта. Скрипт установки ts/build/packages/mypackage/build/install:

#!/bin/sh

# mypackage - имя нашего порта
export PACKAGE=mypackage
export PORTS=$PACKAGE
repackage -e

returnval=$?

exit $returnval

И скрипт удаления ts/build/packages/mypackage/build/remove:

#!/bin/sh

# mypackage - имя нашего порта
export PACKAGE=mypackage
repackage -c

Теперь, при запуске магического ./build наш порт будет добавлен в дерево файлов образа и появится в сборке. Конечно, если вы не забыли добавить этот пакет в build.conf.

Обновления

Нужно отметить, что все манипуляции описанные выше возможны благодаря магии системы портов CRUX. Порт устанавливается и удаляется из системы на основе данных из .footprint и важно следить, чтобы этот файл всегда был актуален. Есть и другие подводные камни - прерванный процесс установки, ошибка во время установки и еще множество различных непредусмотренных действий могут легко привести к тому, что в вашей системе (и как следствие - образе) будут файлы, которые вы совсем не ожидаете увидеть. Я периодически делаю полную очистку кухни, чтобы быть уверенным в том, что система будет вести себя as-expected. Однако, иногда на это времени не хватает. Поэтому, если какой-то пакет и/или порт стал неконсистентным, я для себя вывел следующую последовательность команд, которая в 99% случаев очистит все лишние файлы и всё-таки приведет дерево в порядок:

# Обновляем порт, футпринт и мд5
prt-get update mypackage
prt-get update -uf mypackage
prt-get update -um mypackage
# Очищаем билд (выполняет содержимое файла remove, описанного выше)
./build --removeall
# Удаляем порт
prt-get remove mypackage
# Обновляем пакет (при наличии .dna файла)
update mypackage
# Устанавливаем порт
prt-get install mypackage
# Запускаем сборщик
./build --license ACCEPT --autodl

Что еще почитать

Вот пара достаточно интересных статей, которые когда-то мне очень помогли:

Ну и на всякий случай, моя ветка ThinStation на GitHub, где можно найти некоторые дополнительные профили и изменения, может быть пригодится:

Комментарии