Итак, в наличии имеем:
-
Сервер на Debian 8.5 Jessie, с установленным и работающим LTSP (с клиентами i386):
-
Linux server 3.16.0-4-amd64 #1 SMP Debian 3.16.7-ckt25-2+deb8u3 (2016-07-02) x86_64 GNU/Linux
-
- Raspberry Pi 3
- Добавить в LTSP образ клиента другой архитектуры
-
Установить Raspbian 8 (jessie) в качестве ОС для тонкого клиента на Raspberry Pi 3
-
Поставить последнюю актуальную версию ядра (4.4.20)
-
На момент написания статьи, в сети можно было найти несколько решений для данной или похожей задачи, как частичных, так
и "готовых". Например, довольно популярный в этой сфере проект PiNet или универсальный boot-loader для RPi - BerryBoot, который включает в себя возможность создания из устройства LTSP-клиента (для уже готового сервера). Так или иначе, все эти варианты по разным причинам, были для меня неподходящими, да и хотелось самому поглядеть
на это по-внимательнее, с разных сторон.
Обязательными, кроме самого LTSP, будут - SSHD, NBD, NFS, TFTPD-HPA, DHCPD.
Установка и настройка данных компонентов достаточно популярно описана на различных ресурсах в сети, поэтому тут, в полном объеме, приводиться не будет. Несколько полезных ссылок по этой теме я оставлю в конце статьи.
1.
Настройка серверной стороны
Для начала, установим на сервер пакеты, позволяющие создавать образы LTSP-клиентов с архитектурой, отличной от amd64 и i386:
apt-get install qemu-user-static binfmt-supportДобавляем GPG-ключ репозитория Raspbian:
wget http://archive.raspbian.org/raspbian.public.key -O - | gpg --importСоздаем файл конфигурации для клиента:
gpg --export 90FDDD2E >> /etc/ltsp/raspbian.public.key.gpg
/etc/ltsp/ltsp-raspbian.conf
DEBOOTSTRAP_KEYRING=/etc/ltsp/raspbian.public.key.gpgПосле этого можно запускать создание клиентской файловой системы:
DIST=jessie
MIRROR=http://mirrordirector.raspbian.org/raspbian
SECURITY_MIRROR=none
UPDATES_MIRROR=none
LOCALE="$LANG UTF-8"
KERNEL_PACKAGES=linux-image-4.4
ltsp-build-client --arch armhf --config /etc/ltsp/ltsp-raspbian.conf --prompt-rootpass 'pass'
2. Подготовка и создание образа
Как только создание файловой системы будет завершено, заходим в chroot:
ltsp-chroot --arch armhfВ первую очередь добавим репозиторий raspberrypi.org, в котором содержится утилита rpi-update.
/etc/apt/sources.list
deb http://mirrordirector.raspbian.org/raspbian jessie main contrib non-free rpiМонтируем proc:
deb http://archive.raspberrypi.org/debian/ jessie main
mount -t proc proc /procОбновляем репозитории (пока нет GPG-ключа, ругнется на его отсутствие):
apt-get updateИнсталлируем окружение и пакет для обновления ядра RPi:
apt-get install vim wget less
wget http://archive.raspberrypi.org/debian/raspberrypi.gpg.key
apt-key add raspberrypi.gpg.key
apt-get upgrade
apt-get install lxde rpi-updateОчистим /boot и загрузим актуальное ядро:
rm -rf /boot/*Есть важный момент, относительно команды rpi-update. Эта утилита является bash-скриптом, работающим через API github.com для единственного репозитория rpi-firmware, который содержит в себе все последние актуальные ядра и служебные файлы для работы Raspberry Pi. В конце статьи, я подробнее опишу некоторые аспекты, которые могут сильно помочь вам в случае проблем, а пока, просто продолжим.
rpi-update
Переименовываем файл ядра:
mv /boot/kernel7.img /boot/kernel7.img-4.4.20-v7+Допишем модуль для обязательной загрузки (!):
echo "overlay" >> /etc/initramfs-tools/modulesПолучив все нужные пакеты и обновив ядро на текущее (в моем случае 4.4.20), нужно собрать initrd.img, но тут есть тонкость (возможно, что это будет исправлено в дальнейшем, но данная ошибка стоила мне пары часов). Надо изменить файл скрипта инициализации, который запускается на этапе монтирования NBD-образа в клиентской системе.
Для этого, после создания initrd.img, перенесем его в пустой каталог /home и распакуем:
update-initramfs -c -k 4.4.20-v7+Нас интересует скрипт ./scripts/init-bottom/ltsp
mv initrd.img-4.4.20-v7+ /home && cd /home
gzip -cd initrd.img-4.4.20-v7+ | cpio -i
Заменяем содержимое файла на следующее и запаковываем в каталог /boot, не забыв удалить сам образ. Настоятельно рекомендую заменять содержимое файла явно, через редактор, если есть такая возможность. По неясной для меня причине, варианты заливки через wget или curl, приводят к kernel panic при последующей загрузке RPi. Интересно, что при сравнении через diff\vimdiff, или визуально - никаких отличий нет, но в hex-виде, имеются лишние биты, что, вероятно, и является причиной проблемы.
Производим действия по замене, не забыв удалить сам файл образа из директории перед упаковкой обратно и очистив директорию от остального, после:
rm /home/initrd.img-4.4.20-v7+Можно еще немного изменить файл конфигурации образа:
find | cpio -H newc -o |gzip -9 > /boot/initrd.img-4.4.20-v7+
rm -rf /home/*
/etc/lts.conf
[default]Все возможные переменные этого файла есть в мануале, но стоит иметь ввиду, что часть из них может быть несовместима с Raspbian.
LTSP_CONFIG = True
SOUND = True
LOCALDEV = True
REMOTE_APPS = True
HOTPLUG = True
NBD_SWAP = True
[ff:ff:ff:ff:ff:ff]
LDM_LANGUAGE = "en_US.UTF-8"
LDM_AUTOLOGIN = True
LDM_USERNAME = user
LDM_PASSWORD = pass
Чтобы в логе dmesg не мелькала ошибка "Failed to start Load Kernel Modules", в файле /etc/modules-load.d/modules.conf, нужно закомментировать строку (в моем случае, подсистема печати мне нужна не была):
#lpВыходим из chroot и собираем образ:
exitУбедимся, что существует /etc/nbd-service/conf.d/ltsp_armhf.conf со следующим содержанием:
ltsp-update-image armhf
[/opt/ltsp/armhf]Перезагружаем NBD-сервис:
exportname = /opt/ltsp/images/armhf.img
readonly = true
authfile = /etc/ltsp/nbd-server.allow
systemctl restart nbd-serverТакже стоит проверить и /etc/ltsp/nbd-server.allow на предмет корректных ip-адресов, которым разрешен доступ к серверу по NBD.
3. Настройка клиентской стороны
mkfs.fat /dev/sdxКопируем на нее файлы из директории сервера /opt/ltsp/armhf/boot
Также создаем там 2 файла:
cmdline.txt
dwc_otg.lpm_enable=0 console=serial0,115200 kgdboc=serial0,115200 console=tty1 init=/sbin/init-ltsp nbdroot=1.1.1.1:/opt/ltsp/armhf root=/dev/nbd0 elevator=deadline rootwaitconfig.txt
Файл конфигурации настраивается в зависимости от многих факторов, поэтому я привожу относительно универсальный, который, впоследствии стоит подкорректировать. Основной момент - в самом конце, где указываются файлы kernel7.img и initrd.img.
Больше информации по файлам конфигурации можно найти тут.
Размонтируем носитель и запускаем Raspberry.
Система должна будет загрузиться до окна логина LXDE.
На этом вся настройка завершена. В остальном, все соответствует с настройкой обычных LTSP-клиентов.
Команда uname -a должна выводить:
Поскольку связка, описанная в статье, не особо популярна, то и информации по отладке данной конструкции найти не просто. В этом, и любых других похожих случаях, всегда стоит исходить из того, что есть в наличии - логи сервера, сетевой трафик, утилиты для диагностики и прочие инструменты, которые могут пролить свет на происходящее.
По ходу настройки, я сталкивался с различными проблемами, которые попытаюсь описать далее.
1. RPi зависает на этапе инициализации. На мониторе тест GPU-Rainbow:
2. RPi проходит этап инициализации, но, при попытке подгрузки ядра вылетает в консоль (initramfs) [BusyBox v1.22.1]:
Причин этой проблемы очень много, поэтому перечислять все, просто нет смысла. Полезнее, на мой взгляд, объяснить, как это можно диагностировать.
В большинстве случаев, на экран будет выведено, на каком этапе инициализации ядра произошла ошибка. Если вы используете переходник HDMI-VGA\DVI, то, чтобы вместить в монитор максимальное количество выходной информации, стоит заранее откорректировать разрешение вывода в файле config.txt. Делается это с помощью указания значения для переменных hdmi_group и hdmi_mode. К примеру, для монитора с разрешением 1280x1024 нужно использовать значения hdmi_group=2, hdmi_mode=36. Таблицы, по которым можно выбрать нужные значения, находятся на официальном сайте.
90% падений на этапе инициализации ядра происходит в процессе выполнения скриптов из директории ./scripts/, которую мы видели ранее, когда изменяли файл ltsp. Но, когда вы находитесь в консоли (initramfs), никаких действий по распаковке делать не нужно. Просто запустите тот скрипт, который выдал ошибку при загрузке, и изучите его вывод. Вероятнее всего, будет выведен и номер строки, на которой эта ошибка произошла. Если это произошло, то с помощью vi, проверьте ее содержание - это может дать подсказку о том, из-за чего загрузка была прервана.
Обязательно проверьте, получил ли клиент IP-адрес с помощью ifconfig. Можно попробовать пропинговать сервер, но учтите, что остановить пинг ничем, кроме перезагрузки, не получится. Keyboard Interrupt не перехватываются.
Если проблемы в каком-то модуле ядра, то стандартные lsmod, modprobe и прочие инструменты для работы с модулями также доступны. Порой, только этого достаточно, чтобы понять, что модуль, например, не найден или находится не там, где нужно.
Если в корневом каталоге имеется каталог /rofs, и в нем находятся папки с NBD-образа сервера, значит этап монтирования прошел успешно.
Вывод dmseg в отладочной консоли, к сожалению, особой информацией не блещет, но есть и другой способ. О нем, немного позже.
Очень помогает также, параллельно наблюдать текущий вывод логов сервера, в процессе загрузки RPi. Например, через journalctl -f.
Это дает возможность в реальном времени видеть, когда возникла ошибка, обратился ли клиент к нужным файлам, или, например, не смог их найти. Наиболее полезным тут, может оказаться лог NBD-сервера.
К примеру, ошибка "Read failed: Bad file descriptor", может возникнуть, если скрипт ltsp в образе initrd.img не изменять.
Ошибка "virststyle ipliteral" - при использовании wget для изменения скрипта ltsp, что подводит нас к следующему пункту.
3. При попытке загрузки системы, ядро вываливается в ранее упомянутый Kernel Panic.
Linux user 4.4.20-v7+ #908 SMP Wed Sep 7 14:44:27 BST 2016 armv7l GNU/Linux
4. Troubleshooting
По ходу настройки, я сталкивался с различными проблемами, которые попытаюсь описать далее.
1. RPi зависает на этапе инициализации. На мониторе тест GPU-Rainbow:
- Неверные данные\нечитаемые символы в файлах config.txt или cmdline.txt
- Проблемы ФС карты памяти
- Различные версии бинарных файлов
- Неверно указаны версии kernel7.img\initrd.img в config.txt
- https://www.raspberrypi.org/forums/viewtopic.php?f=28&t=58151
- http://elinux.org/R-Pi_Troubleshooting
2. RPi проходит этап инициализации, но, при попытке подгрузки ядра вылетает в консоль (initramfs) [BusyBox v1.22.1]:
Причин этой проблемы очень много, поэтому перечислять все, просто нет смысла. Полезнее, на мой взгляд, объяснить, как это можно диагностировать.
В большинстве случаев, на экран будет выведено, на каком этапе инициализации ядра произошла ошибка. Если вы используете переходник HDMI-VGA\DVI, то, чтобы вместить в монитор максимальное количество выходной информации, стоит заранее откорректировать разрешение вывода в файле config.txt. Делается это с помощью указания значения для переменных hdmi_group и hdmi_mode. К примеру, для монитора с разрешением 1280x1024 нужно использовать значения hdmi_group=2, hdmi_mode=36. Таблицы, по которым можно выбрать нужные значения, находятся на официальном сайте.
90% падений на этапе инициализации ядра происходит в процессе выполнения скриптов из директории ./scripts/, которую мы видели ранее, когда изменяли файл ltsp. Но, когда вы находитесь в консоли (initramfs), никаких действий по распаковке делать не нужно. Просто запустите тот скрипт, который выдал ошибку при загрузке, и изучите его вывод. Вероятнее всего, будет выведен и номер строки, на которой эта ошибка произошла. Если это произошло, то с помощью vi, проверьте ее содержание - это может дать подсказку о том, из-за чего загрузка была прервана.
Обязательно проверьте, получил ли клиент IP-адрес с помощью ifconfig. Можно попробовать пропинговать сервер, но учтите, что остановить пинг ничем, кроме перезагрузки, не получится. Keyboard Interrupt не перехватываются.
Если проблемы в каком-то модуле ядра, то стандартные lsmod, modprobe и прочие инструменты для работы с модулями также доступны. Порой, только этого достаточно, чтобы понять, что модуль, например, не найден или находится не там, где нужно.
Если в корневом каталоге имеется каталог /rofs, и в нем находятся папки с NBD-образа сервера, значит этап монтирования прошел успешно.
Вывод dmseg в отладочной консоли, к сожалению, особой информацией не блещет, но есть и другой способ. О нем, немного позже.
Очень помогает также, параллельно наблюдать текущий вывод логов сервера, в процессе загрузки RPi. Например, через journalctl -f.
Это дает возможность в реальном времени видеть, когда возникла ошибка, обратился ли клиент к нужным файлам, или, например, не смог их найти. Наиболее полезным тут, может оказаться лог NBD-сервера.
К примеру, ошибка "Read failed: Bad file descriptor", может возникнуть, если скрипт ltsp в образе initrd.img не изменять.
Ошибка "virststyle ipliteral" - при использовании wget для изменения скрипта ltsp, что подводит нас к следующему пункту.
3. При попытке загрузки системы, ядро вываливается в ранее упомянутый Kernel Panic.
Эта проблема возникла после написания статьи, во время проверки написанного и параллельного моделирования, чем довольно сильно меня удивила.
Сложность была в том, что при kernel panic система не дает возможности выполнять какие-либо команды, а, из-за большого размера вывода, становится полностью непонятно, что именно послужило причиной.
Логи сервера в этот раз, ничем не помогли, равно, как и логи сетевого трафика, снятого через tshark. Я видел, что процесс инициализации вроде как прошел, и файловая система смонтировалась. Видел и обращение к NBD-образу, но было совершенно не очевидно, в чем была сама причина.
Смазало картину еще и то, что за время, пока писалась статья, в основную ветку ядра было замерждено 2 коммита, которые меняли довольно большое количество файлов, включая бинарные файлы RPi и приличное количество модулей ядра.
В логах NBD-сервиса сервера в больших количествах сыпались "virststyle ipliteral", напоминающие какие-то проблемы на сетевом уровне.
В первую очередь были проверены все интерфейсы, просмотрен трафик, но ни битых пакетов, ни даже ритрансмишенов найдено не было, равно как и дропнутых пакетов на интерфейсе.
Как я упоминал ранее, у скрипта rpi-update есть пара особенностей, одна из которых дает возможность скачать версию ядра именно на том коммите, который вам нужен, если в релизе возникли проблемы.
Именно это и было сделано. Через
rpi-update b0ef6e25679d3612a980708cf4c3907ce6e13e84я получил именно ту версию файлов ядра и модулей, которые использовались в процессе настройки и отладки. Далее, как обычно, создание initrd.img, распаковка, замена скрипта ltsp через wget, и....
Разумеется, я получил тот же самый kernel panic...
Гипотеза с "битым" файлом в обновленной версии одного из последних коммитов ядра отпала, и стало ясно, что либо я что-то упустил, и забыл добавить в статью, либо что-то из этого я сделал по-другому.
Для того, чтобы относительно прояснить ситуацию, нужен был полный лог загрузки, от инициализации RPi, до kernel panic. А поскольку программно этого было не сделать, на помощь пришел... разумеется UART. Кто читал мои прошлые статьи, тот знает, что его я ищу в первую очередь :)
Разумеется для такой проработанной машинки, как Raspberry Pi, было ясно, что UART есть, и выведен на отдельные пины GPIO.
Беглый поиск привел к одному обсуждению на github, где была нужная мне информация. 14 и 15 пины GPIO, включение в config.txt строки enable_uart=1, и вот minicom уже выводит все, что я хотел увидеть.
В качестве USB-to-TTL я использовал мой традиционный шнурок от телефона на PL-2303. Команда на запуск minicom:
minicom -l -8 -c on -s -D /dev/ttyUSB3Правда особой радости это не принесло. Судя по сетевому трафику и логу:
[ 7.470081] nbd: registered device at major 43было видно, что NDB-образ монтировался, но дальше никакой полезной информации не было. Полный лог тут.
[ 7.686706] squashfs: version 4.0 (2009/01/31) Phillip Lougher
[ 9.344819] Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000200
[ 9.344819]
[ 9.375963] CPU: 1 PID: 1 Comm: init-ltsp Not tainted 4.4.20-v7+ #909
Мелькнуло несколько мыслей о, возможно, аппаратном происхождении данной проблемы, но это было слишком маловероятно. Дядюшка Оккама не раз подтверждал свою компетентность, поэтому, для очистки совести, я успешно запустился с другой SD локально, чем подтвердил, что надо искать причину в другом месте.
Самый простой способ - просмотреть history командной строки сервера, и еще раз проверить все шаги.
Единственным шагом, который я решил изменить - было использование wget для более быстрого изменения скрипта init-bottom/ltsp. Проверка этой теории и решила данную проблему.
Как я уже писал ранее, я пробовал найти отличия в этих двух вариантах, пробовал использовать curl (надо было бы еще через python.requests попробовать), но визуальных различий не было, а побитовое сравнение давало слишком большие расхождения в, теоретически, одинаковых, текстовых файлах. Размышления об истинной причине оставлю читателям данной статьи. Если у кого-то возникнет желание смоделировать данную ситуацию, и найти ответ - было бы интересно его услышать.
4. Система загружается полностью, запускается окружение, но интерфейсы управления и USB-порты не работают.
Данное поведение характерно для ситуации, когда версия модулей ядра каталога /lib/modules вашего образа, не соответствуют версиям модулей запущенного ядра Raspberry.
Чтобы избежать это проблемы, всегда очищайте каталог /boot вручную, перед установкой новой версии ядра. В теории, это автоматически делает утилита rpi-update (вместе с автоматическим бэкапом), но лучше все же, делать это самому.
К слову, если у вас возникла необходимость принудительно обновить ядро через rpi-update, то удалите скрытый файл /boot/.firmware_revision прежде чем ее запускать.
На этом я пожалуй закончу данную статью, и, как и обещал в ее начале, приведу несколько полезных ссылок.
- Настройка LTPS-сервера
- Терминальные решения на основе LTSP
- Доступные переменные конфигурационного файла lts.conf*
- Описание процесса настройки UART на Raspberry Pi
- Описание процесса работы ядра в Raspbian (обратите внимание на то, каким образом идет замена файла ltsp)
- Репозиторий последних версий ядра Raspberry Pi (github.com)
- RPI-update (github.com)
- Troubleshooting проблем запуска Raspberry Pi
Читать дальше...
Комментариев нет:
Отправить комментарий