logo ubuntu and windows
Изображение с сайта getwallpapers.com


История


В далёком 2013 году в одном банке использовались тонкие клиенты на основе DisklessUbuntu. С ними были некоторые проблемы, по-моему монтирование корневой ФС по сети в больших филиалах со слабой сетью работало не очень. Тогда мой хороший друг @deadroot сделал первую версию тонкого клиента, который грузился целиком в память, не требуя что-то монтировать по сети для работы.


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


Недавно у меня дошли руки сделать из этой кучи страшных ненадёжных скриптов достаточно удобное для использования решение:


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

Что умеет


  • Целиком грузится в память, не требует для работы монтировать корневую ФС по сети.
  • Построена на базе Ubuntu, практически любой софт можно ставить из её богатых репозиториев, и подключать сторонние если чего-то не хватило. Особенно приятно, что обновления безопасности прилетают в Ubuntu достаточно быстро.
  • Умеет монтировать поверх корневой ФС дополнительные оверлеи. Можно добавить какой-то софт только для некоторых рабочих станций, не собирая новый образ
  • Умеет zram — сжатие памяти, полезно для старых клиентов с небольшим количеством оперативки. Хотя и для новых как правило не помешает.
  • Из коробки собирается лёгкий десктоп(LXDE) с RDP-клиентом, адреса и параметры RDP серверов просто передаются с PXE-сервера через параметры при загрузке.
  • Можно поменять один параметр в конфиге и будет собираться минимальная консольная система без лишнего софта — основа для какой-нибудь вашей нестандартной сборки.
  • Если загрузка не прошла из-за проблем с сервером или сетью, будет недолго показывать сообщение об ошибке и пытаться загрузится снова. Удобно, что когда проблемы исправлены, рабочие станции поднимутся сами без лишних телодвижений.

В банке для удалённого подключения к тонкому клиенту пользователя использовался VNC(x11vnc для подключения к уже запущенной сессии Xorg). Это далеко не всем требуется(обычно хватает возможности подключения к сеансу RDP на сервере терминалов), и тут всё очень индивидуально в плане требований удобства/безопасности. Поэтому эту часть я выкладывать не стал.


Аналоги



Если Thinstation полностью устраивает — то лучше пользоваться им, это более старый и зрелый проект. Плюс он раза в полтора меньше по размеру, всё-таки это специально заточенная под минимальный объём сборка, а не слегка допиленная обычная Ubuntu.


Но версии софта в нём достаточно древние и его там мало. Если нужно что-то дополнительное, помимо клиентов RDP/Citrix/… — потребуется собирать это руками, и так при каждом обновлении.



kvaps указал в комментарии, что LTSP может скопировать образ squashfs в память и работать без монтирования ФС по сети: это настраивается переменной LTSP_NBD_TO_RAM. Для настройки используется chroot, что может быть менее удобно, особенно для настройки графического окружения и приложений. Также хороший зрелый проект, можно рассматривать как альтернативу.


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. Английская версия статьи в моём блоге.

Комментарии (22)


  1. andvseti
    09.03.2018 16:04
    +2

    Это очень кстати. Спасибо за труд и за пояснения!


    1. selivanov_pavel Автор
      09.03.2018 16:20
      +2

      Пожалуйста, рад если это кому-то пригодится :)


  1. xlin
    09.03.2018 21:23

    А как дела с принтерами обстоят и с флешками?


    1. selivanov_pavel Автор
      09.03.2018 22:31

      С флешками всё хорошо, есть два варианта:



      С принтерами есть такие варианты:


      • проброс принтера с помощью FreeRDP. Сам такое делать не пробовал. Понадобится добавить в образ CUPS с драйверами для нужных принтеров, какой-нибудь скрипт для запихивания принтера в CUPS при загрузке, и возможно некоторое количество магии(а может наоборот сразу заработает).
      • в банке это делалось так: все принтеры были сетевые, они пробрасывались из филиала по VPN на принт-сервер, откуда их забирал сервер терминалов. Было настроено, чтобы пользователь из соответствующего OU видел только свои принтеры. Немного повышает требования к толщине канала, но думаю в 2018 году уже не осталось офисов, сидящих на модемном соединении.


      1. AndreyAf
        10.03.2018 09:27
        +1

        с принтерами вообще все просто,
        достаточно открыть порт 9100 например xinetd + скрипт с cat или обычный nc и сделать редирект в /dev/usb/lp0, можно так же предусмотреть файл блокировки и spool

        драйвер я ставлю стандартные ps или pcl5, pcl6 в зависимости от модели притера что он понимает


  1. kovserg
    09.03.2018 22:33

    Какие минимальные требования к железу?


    1. selivanov_pavel Автор
      09.03.2018 23:05

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


      Собираемый по-умолчанию образ с LXDE работает на 1 Гб оперативки, сам образ весит 330 Мб(вместе с initrd и ядром). Причём top после загрузки показывает, что занято 400 Мб, а ещё 600 осталось для программ, а с учётом zram там поместится раза в полтора больше.


      Если убрать LXDE, оставить только X и какой-нибудь легковесный WM, то возможно получится завестись на 768 Мб.


      1. kovserg
        09.03.2018 23:52

        Я использовал для тонкого клиента slitaz он менее прожорлив.
        image


        1. selivanov_pavel Автор
          10.03.2018 00:02

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


          Впрочем, сейчас железо меньше чем с гигабайтом памяти просто не найдёшь.


          1. kovserg
            10.03.2018 01:24

            Если бы не кривая реализация BT в андройдах, то планшеты для тонкого клиента сгодились бы.


  1. kvaps
    09.03.2018 23:02
    +1

    Вау, я крайне удивлен что ни одного упоминания про LTSP.
    Простое и проверенное решение, а главное уже готовое и умеет все тоже самое что описанно в статье.


    Я использую несколько модифицированную версию для фермы серверов.
    Squashed образ грузится с nbd-сервера и копируется в RAM.


    LTSP-сервер вместе с клиентским образом и всеми необходимыми изменениями собирается автоматически из Dockerfile после пуша в корпоративный git-репозиторий.


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


    1. selivanov_pavel Автор
      09.03.2018 23:12

      Оно вроде требует монтировать ФС по сети, или нет? Описанный в статье велосипед родился как раз из-за требования так не делать.


      1. kvaps
        09.03.2018 23:34

        Вовсе нет, переменная LTSP_NBD_TO_RAM позволяет скопировать Squashed-образ в RAM при загрузке, все дополнительные фс (например локальные диски) можно настроить через переменные FSTAB, есть целая куча встроенных возможностей.


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


        1. selivanov_pavel Автор
          09.03.2018 23:50

          Если бы этот комментарий появился в 2013 году, описанное в статье решение вероятно не было бы создано :)


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


          Сейчас уже поздно, завтра добавлю к списку альтернатив помимо Thinstation ещё и LTSP.


          1. kvaps
            10.03.2018 00:07

            Не отчаивайтесь, зато теперь вы знаете как работает initramfs — а это уже половина дела, все что вы описали в статье применимо так же и к LTSP.
            LTSP просто предоставляет вам набор скриптов который сильно упрощает жизнь. :)


            К примеру:
            ltsp-chroot — что бы установить софт или внести другие изменения.
            ltsp-update-image — и у вас новый squashed образ.
            ltsp-update-kernels — и у вас готовый конфиг с новыми ядрами для pxelinux.


            1. selivanov_pavel Автор
              10.03.2018 10:28

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


              Кстати, посмотрел, LTSP_NBD_TO_RAM в LTSP добавили как раз 5 лет назад, возможно на момент создания thinclient этого ещё не было в стабильной версии или оно не упоминалось в документации.


            1. selivanov_pavel Автор
              10.03.2018 12:45

              Обновил статью, указал LTSP в списке альтернативных решений.


    1. deadroot
      10.03.2018 01:29

      В 2013-2014 LTSP был достаточно сырым. После нескольких тестов решили от него отказаться.

      Ну и еще пара особенностей, которые нужны были:
      1. Поддержка вебкамер и вообще любого железа в потенциале (мало ли, что бизнес запросит).
      2. Настройка окружения пользователя. Например, браузеров и т. п. Это так и не понадобилось, но, опять же, мысли были.


      1. kvaps
        10.03.2018 09:45

        Так, а в чем проблема то? Все что можно установить в chroot все тоже самое можно сделать и тут.
        Поддержка железа такая же как и на десктопной версии Ubuntu.
        Касательно настройки окружения — достаточно просто положить необходимые конфиги в домашнюю директорию пользователя.


      1. Frankenstine
        10.03.2018 12:53

        Насколько я помню, в LTSP ещё отваливалась сессия без возможности восстановления в случае разрыва сетевого соединения с сервером, то есть выдёргивание свича равносильно резету куче машин.


        1. selivanov_pavel Автор
          10.03.2018 12:58

          Да, именно та проблема с монтированием ФС по сети, которая и вызвала появление этого решения. Впрочем, kvaps сверху написал комментарий, что LTSP тоже оказывается это умеет.


        1. kvaps
          10.03.2018 13:35

          Такая проблема происходит при использовании stateful-протоколов, NBD — как раз один из них.
          При использовании stateless NFSv3 эта проблема отсутсвует, но есть ряд крайне неприятных багов при использовании OverlayFS над NFS.
          В сообществе мне еще советовали попробовать AOE, но руки так и не дошли. Squashed образ и правда занимает относительно немного места в памяти, а работает гораздо стабильнее чем по сети.