Тонкий бездисковый клиент на базе Ubuntu, не требующий монтирования ФС по сети
Изображение с сайта getwallpapers.com
История
В далёком 2013 году в одном банке использовались тонкие клиенты на основе DisklessUbuntu. С ними были некоторые проблемы, по-моему монтирование корневой ФС по сети в больших филиалах со слабой сетью работало не очень. Тогда мой хороший друг @deadroot сделал первую версию тонкого клиента, который грузился целиком в память, не требуя что-то монтировать по сети для работы.
Потом этот клиент активно допиливал я, там было сделано много полезных штук, специфичных именно для нашего сценария использования. Потом банк закрылся (отозвали лицензию), остатки исходников клиента переехали на мой гитхаб: thunclient. Пару раз я его слегка допиливал на заказ.
Недавно у меня дошли руки сделать из этой кучи страшных ненадёжных скриптов достаточно удобное для использования решение:
- Vagrant поднимает виртуалку, которую можно настраивать как обычную рабочую станцию.
- Одним скриптом из неё собирается готовые для загрузки по сети файлы, лишнее вырезается.
- Vagrant поднимает виртуальный PXE сервер и сетевой клиент для проверки получившейся сборки.
Что умеет
- Целиком грузится в память, не требует для работы монтировать корневую ФС по сети.
- Построена на базе Ubuntu, практически любой софт можно ставить из её богатых репозиториев, и подключать сторонние если чего-то не хватило. Особенно приятно, что обновления безопасности прилетают в Ubuntu достаточно быстро.
- Умеет монтировать поверх корневой ФС дополнительные оверлеи. Можно добавить какой-то софт только для некоторых рабочих станций, не собирая новый образ
- Умеет zram — сжатие памяти, полезно для старых клиентов с небольшим количеством оперативки. Хотя и для новых как правило не помешает.
- Из коробки собирается лёгкий десктоп (LXDE) с RDP-клиентом, адреса и параметры RDP серверов просто передаются с PXE-сервера через параметры при загрузке.
- Можно поменять один параметр в конфиге и будет собираться минимальная консольная система без лишнего софта — основа для какой-нибудь вашей нестандартной сборки.
- Если загрузка не прошла из-за проблем с сервером или сетью, будет недолго показывать сообщение об ошибке и пытаться загрузится снова. Удобно, что когда проблемы исправлены, рабочие станции поднимутся сами без лишних телодвижений.
В банке для удалённого подключения к тонкому клиенту пользователя использовался VNC (x11vnc
для подключения к уже запущенной сессии Xorg). Это далеко не всем требуется (обычно хватает возможности подключения к сеансу RDP на сервере терминалов), и тут всё очень индивидуально в плане требований удобства/безопасности. Поэтому эту часть я выкладывать не стал.
Аналоги
Почему бы просто не пользоваться Thinstation?
Если Thinstation полностью устраивает — то лучше пользоваться им, это более старый и зрелый проект. Плюс он раза в полтора меньше по размеру, всё-таки это специально заточенная под минимальный объём сборка, а не слегка допиленная обычная Ubuntu.
Но версии софта в нём достаточно древние и его там мало. Если нужно что-то дополнительное, помимо клиентов RDP/Citrix/… — потребуется собирать это руками, и так при каждом обновлении.
Vagrant vs chroot
Прошлые версии использовали chroot, как собственно и большинство похожих проектов, тот же Thinstation к примеру. Это несложно, но всё-таки запущенная в chroot отдельная программа не соответствует происходящему на реальной машине: нету взаимодействия с системным init, с другими программами и службами. Плюс Vagrant позволил сделать процесс создания клиента максимально простым: виртуалка настраивается как обычная машина.
Конечно, использование Vagrant приносит и некоторые сложности.
На машине должна работать служба virtualbox-guest-utils
, для работы общих папок. Кроме того, нужен менеджер загрузки (grub
), обязательный для машины с диском и бесполезный для загружаемого по сети клиента. Эти проблемы я решил, исключая из сборки все файлы этих пакетов. Поэтому на размер получившегося образа они не влияют.
Кроме того, для Vagrant обязателен работающий на машине ssh, пускающий пользователя со сгенерированным ключом. Я исключаю из сборки домашний каталог пользователя vagrant, используемого для настройки, вместе с его ssh ключами. Ключи для используемого при работе пользователя ubuntu можно положить в его домашний каталог.
Ну и для работы Vagrant генерирует настройки сетевых интерфейсов, которые будут ошибочными для реальной машины. Пришлось на время сборки подменять файл interfaces
, и написать скрипт, который на реальной машине генерирует конфиг для настройки всех доступных интерфейсов по DHCP.
Provisioning делается с помощью Ansible. Это очень удобный инструмент для конфигурации всяческого софта и железа. Но включать в итоговый образ Ansible и требующийся ему второй python с нужными библиотеками не хотелось бы: бесполезный балласт. Ставить Ansible на машину, где запукается виртуальное окружение, тоже не хочется: это усложнит работу.
Vagrant позволяет сделать хитрость: поставить Ansible на одну машину (тестовый PXE сервер), и с неё делать разворачивание других машин, в рамках той же playbook. Для этого машины должны иметь статический IP, чтобы прописать его в ansible inventory. Ну, а проблему с конфигурацией интерфейсов мы решили в прошлом пункте.
Непослушный кабачок
Squashfs — сжимающая read-only файловая система. Лежит в основе большинства существующих Linux LiveCD. Именно она позволяет создать достаточно компактный образ системы, помещающийся в оперативную память тонкого клиента.
Из итогового образа надо много чего вырезать: /tmp
, /run
, /proc
, /sys
, /usr/share/doc
и так далее.
Утилита mksquashfs
поддерживает аж 3 типа списков для исключения файлов: по полному пути, по маскам и по регулярным выражениям. Казалось бы, всё прекрасно. Но последние два варианта не поддерживают пути, начинающиеся с /
. У меня не получилось исключить все файлы внутри некотороый структуры папок, не исключая последнюю папку.
Мне быстро надоело с ней бороться, я просто нашёл find
-ом все файлы и папки, которые надо исплючить, и запихнул в один большой файл с исключениями по полному пути. Костыли.jpg. Но работает. Единственным артефактом этого подхода в итоговом образе остаётся одинокая папка /proc/NNN
, соответствующая номеру процесса mksquashfs, которого при создании списка исключений ещё не было. Сверху всё равно монтируется procfs.
Магия initrd
Чтобы не тянуть в составе ядра все необходимые драйвера и логику монтирования корневой ФС, Linux использует initial ramdisk. Раньше использовался формат initrd, в котором этот диск представлял собой настоящий образ файловой системы. В ядре 2.6 появился новый формат — initramfs, представляющий собой извлекаемый в tmpfs cpio-архив. Как initrd, так и initramfs могут быть сжаты для экономии времени загрузки. Многие названия утилит и имена файлов по-прежнему упоминают initrd, хотя он уже не используется.
В Debian/Ubuntu для создания initramfs используется пакет initramfs-tools. Он даёт следующие возможности для кастомизации:
- хуки — скрипты специального формата, которые позволяют добавлять в образ модули ядра и исполняемые файлы со всеми необходимыми им библиотеками
- скрипты в каталогах
init-bottom
,init-premount
,init-top
,local-block
,local-bottom
,local-premount
,local-top
, выполняемые в соответствующий момент загрузки. См. man initramfs-tools (8) - самое интересное — добавлять собственные скрипты загрузки, отвечающие за монтирование корневой ФС. Они должны определять shell функцию
mountroot()
, которая будет использована главным скриптом/init
. В составе уже естьlocal
для монтирования корня на локальном диске иnfs
для монтирования корня по сети. Используемый скрипт выбирается параметром загрузкиboot
.
Итого, чтобы примонтировать корневую ФС каким-то сильно хитрым образом, надо создать свой скрипт загрузки, определить в нём функцию mountroot()
, передать имя этого скрипта в параметре загрузки boot
и не забыть написать хуки, подтягивающие в initramfs все нужные скрипту программы и модули ядра.
Борьба за оверлеи
Для создания единой корневой файловой системы из нескольких используется OverlayFS. В первых версиях использовалась AUFS (она используется большинством линусковых LiveCD). Но её не приняли в ядро, и сейчас всем рекомендуют переходить на OverlayFS.
После монтирования настоящей корневой ФС в каталог внутри initramfs, будет запущена программа run_init
из состава klibc-utils
. Она проверит, что корневая ФС смонтирована внутри initramfs, отчистит initramfs (зачем зря терять память?) и переместит точку монтирования корневой ФС в /
, и запустит системный init. Подробности. Эта программа собрана в виде отдельного исполняемого файла, потому что скрипт, использующий любые внешние утилиты, сломается после отчистки initramfs.
Если корневая ФС собирается из нескольких оверлеев, смонтированных внутри initramfs, при работе run_init
эти точки монтирования пропадают, и она ломается. Эту проблему можно решить, переместив точки монтирования оверлеев внутрь корневой ФС, где они уже не пропадут. Рекурсия :) Делается так: mount --move olddir newdir
.
AppArmor пришлось отключить: её профили рассчитаны на прямое монтирование корневой ФС с одного устройства. При использовании OverlayFS она видит, что /sbin/dhclient
это на самом деле /AUFS/root/sbin/dhclient
, и профиль ломается. Единственный вариант её использовать — переписать все профили для всех приложений, и обновлять при необходимости.
Где нужна возможность записи
Под идее, Linux может спокойно работать, когда все ФС примонтированы только на чтение. Но многие программы рассчитывают на возможность записи на диск, приходится монтировать туда tmpfs:
/tmp
,/var/tmp
— понятно, нужны очень многим/var/log
— пишем логи/run
— без него не запустятся почти все сервисы/media
— монтированиие подключенных носителей/var/lib/system
— используется многими программами из systemd, в частностиsystemd-timesyncd
/var/lib/dhclient
— сюда dhclient записывает информацию о leases/etc/apparmor.d/cache
— если вы всё-таки поборете AppArmor, то ему надо будет писать файлы в/etc
. ИМХО отвратительно, для таких вещей есть/var
.
Итого
Если вы хотите собрать загружаемую по сети и работающую только из памяти сборку Ubuntu — вот тут есть готовый удобный конструктор: thinclient. Если потребуется помощь — пишите в ЛС или в тикеты на гитхабе, подскажу.
P.S. Английская версия статьи в моём блоге.