Alpine + Docker: поваренная книга разработчика

Автор: Евгений Голышев

Статья была опубликована в 237-м выпуске журнала Linux Format. По истечении полугода с момента публикации права на статью переходят ее автору, поэтому материал публикуется в нашем блоге.

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

Два из четырех фигурирующих здесь рецептов призваны уменьшить размер результирующих образов. Эта задача всегда была и остается актуальной для Docker’а, поэтому я начну свой небольшой сборник рецептов с рассказа об Alpine Linux, а потом перейду непосредственно к самим рецептам. Во-первых, именно Alpine является первым шагом на пути к компактным образам, а во-вторых, я хочу воспользоваться случаем и рассказать немного об этом дистрибутиве, чтобы исправить недостаток информации об Alpine на страницах этого журнала (по какой-то причине журнал до сих пор обходил этот дистрибутив стороной).

Немного об Alpine

Alpine Linux применяется в качестве основы для официальных Docker-образов; в свое время он вытеснил с этого места Ubuntu. Более того, в начале 2016-го стало известно, что Натанаэ́ль Копа [Natanael Copa], создатель дистрибутива, присоединился к команде Docker’а. При этом Alpine остается независимым дистрибутивом, подконтрольным только своему сообществу.

Вот список наиболее интересных, на мой взгляд, фактов:

  • Последняя на момент написания статьи версия дистрибутива – 3.8 – вышла 26 июня 2018 г., и ее официальный образ весит чуть больше 4 МБ.
  • Разработчики стараются придерживаться предсказуемого графика выпуска новых версий дистрибутива. Как правило, новые версии Alpine’а выходят два раза в год – в мае и декабре, но возможны и небольшие отклонения от графика. К примеру, версия 3.8 вышла с опозданием на пару месяцев.
  • Каждая версия дистрибутива поддерживается на протяжении двух лет. Так как новые версии Alpine’а выходят два раза в год, то одновременно поддерживается 3-4 версии дистрибутива.
  • В качестве системы инициализации используется OpenRC. Напомню, что стандартом де-факто сейчас является systemd.
  • Поддерживается 6 портов: x86 и x86_64, armhf и aarch64, ppc64le, s390x. Это меньше, чем у Debian, но больше, чем у Ubuntu.
Docker-образы на базе текущей и предыдущей версий Alpine’а весят чуть больше 4 МБ.

Статическая компоновка

Не вдаваясь в лишние подробности, сборку программы можно разделить на два этапа: получение объектных файлов (на каждую единицу компиляции – файл с расширением .c или .cpp – приходится по одному объектному файлу) и компоновку (или линковку, от англ. linkage). Суть компоновки заключается в создании исполняемого файла (или разделяемой библиотеки) из полученных ранее объектных файлов. Если у исполняемого файла есть зависимости в виде библиотек, то в задачи компоновщика также входит связывание [linking] исполняемого файла с этими библиотеками одним из двух способов: статически или динамически. Для статической компоновки необходимы статические библиотеки [static libraries], а для динамической – разделяемые [shared libraries].

Статическая библиотека представляет собой архив объектных файлов, который создается программой ar. Когда исполняемый файл компонуется со статической библиотекой, то составляющие данную библиотеку объектные файлы становятся частью этого исполняемого файла. Для сравнения, разделяемая библиотека представляет собой объектный файл. Когда исполняемый файл компонуется с разделяемой библиотекой, то в исполняемый файл попадает только информация об этой библиотеке. В таком случае, процесс компоновки будет осуществляться во время запуска программы динамически. (В силу ограниченного объема нашего урока, описание процесса компоновки пришлось сильно упростить, поэтому я настоятельно рекомендую обратиться к книгам «Linux API. Исчерпывающее руководство» Майкла Керриска и «Linux. Руководство программиста» Джона Фуско за более детальными подробностями по этому вопросу.)

С использованием разделяемых библиотек связан ряд преимуществ.

  • Использование разделяемых библиотек уменьшает расход дискового пространства и памяти: на диске хранится и в память загружается только одна копия разделяемой библиотеки, независимо от количества программ, которые ее используют.
  • Устранение ошибок и уязвимостей в разделяемой библиотеке не требует пересборки зависимых от нее программ (конечно, при условии, что исправления не затронули двоичную совместимость).

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

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

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

К примеру, у Memcached есть две основные зависимости – пакет libevent-dev с библиотекой для ассинхронного неблокируещего ввода/вывода, и cyrus-sasl-dev с библиотекой, реализующей вторую версию SASL API. libevent-dev зачем-то тащит за собой пакет python2, который в установленном виде занимает 39 МБ. Это именно те накладные расходы, о которых я говорил выше. Таким образом, официальный Docker-образ с Memcached’ом 1.5.10, последней на момент написания статьи версией демона, весит почти 59 МБ, т. к. постоянно таскает за собой Python 2, о котором даже никто не вспоминает, когда запускает Memcached. Docker-образ со статически скомпонованным с этими библиотеками Memcached’ом, весит чуть больше 10 МБ. Предлагаю в этом убедиться на примере образа Memcached’а из проекта MMB. Для этого сначала получите исходники всего проекта, а затем (предпочтительнее) соберите образ –

$ git clone https://github.com/tolstoyevsky/mmb.git
$ cd mmb/memcached
$ IMAGE_NAME=$(grep "image: " docker-compose.yml | awk -F': ' '{print $2}')
$ docker build -t ${IMAGE_NAME} .

или вытяните его с Docker Hub’а:

$ docker pull cusdeb/memcached:1.5.10-amd64

(На самом деле, в этом образе используется еще одна нехитрая оптимизация, о которой будет рассказано сразу в следующем рецепте. Без нее, конечно, размер образа был бы чуть больше.)

Образ со статически скомпонованным Memcached’ом весит почти в 6 раз меньше официального образа.

Но за эту оптимизацию приходится платить. Дело в том, что при использовании Alpine в качестве базового образа статическая компоновка осложняется тем, что у этого дистрибутива туговато со статическими библиотеками. К примеру, как libevent-dev, так и cyrus-sasl-dev содержат только разделяемые варианты библиотек. Наличие обоих вариантов библиотек в Alpine отдается на откуп сопровождающим этих библиотек, и если сопровождающий решил, что библиотека, от которой зависит докеризуируемая вами программа, должна быть доступна только в разделяемом виде, то придется самостоятельно готовить ее статический вариант на этапе сборки самого образа, что потребует дополнительных сил на написание Dockerfile’а.

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

Докеризация только серверных частей

При докеризации клиент-серверного программного обеспечения не оставляйте в образе клиентов. В противном случае образ будет включать компоненты, которые примутся всюду за ним таскаться, и более того, о которых никто не вспомнит при запуске целевого сервера (немного напоминает историю с Python 2 и Memcached из предыдущего рецепта).

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

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

Сборка образов под ARM на машине x86

Благодаря широкому распространению одноплатных компьютеров (Raspberry Pi, Orange Pi, Banana Pi и т. д.), устройства на базе 32- и 64-битного процессора ARM, на которых можно запустить полноценную операционную систему, сейчас доступны как никогда. Более того, Docker поддерживает ARM уже достаточно давно: начиная с версии 1.10, вышедшей 4 февраля 2016 г., появилась возможность собрать клиентскую и серверную части под ARM, а с версией 1.12.1, вышедшей 18 августа того же года, официальная поддержка пришла на ARM-машины под управлением Debian Jessie, Ubuntu Trusty и Raspbian Jessie.

Тем не менее, Docker-образы не всегда удобно собирать непосредственно на одноплатниках – для экономии времени сборку лучше производить на рабочей станции или ноутбуке, которые, как правило, представляют собой машины на базе процессора x86. Один из способов этого добиться – поместить в базовый образ [base image] целевого Docker-образа двоичные файлы средства эмуляции в режиме пользователя [user mode emulation binaries].

Как в производных от Debian дистрибутивах, так и в Fedora двоичные файлы средства эмуляции в режиме пользователя содержатся в пакете qemu-user-static. Модуль ядра binfmt_misc, доступный в Linux начиная с версии 2.1.43 (которая, кстати, вышла в июне далекого теперь 1997 г.), позволяет распознавать различные форматы исполняемых файлов и ассоциировать их с произвольными приложениями. Другими словами, для определенного формата исполняемого файла можно зарегистрировать эмулятор, и при каждой попытке запустить исполняемый файл этого формата передавать его эмулятору, а не запускать на текущем процессоре. (В производных от Debian дистрибутивах также потребуется установить пакет binfmt-support, в который вынесена функция регистрации эмуляторов.) Таким образом, если предназначенный для запуска на одноплатнике образ включает эти бинарники, то на машине на базе процессора, отличного от ARM, сборка образа и запуск контейнера на его основе будут осуществляться с привлечением эмулятора, а на ARM-машинах всё будет выполняться на реальном процессоре.

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

Предлагаю рассмотреть скрипт создания базового Docker-образа для ARM, который будет включать двоичные файлы средства эмуляции в режиме пользователя, чтобы любой образ на его основе мог быть без проблем собран и запущен на x86-машинах. Чтобы скрипт корректно отработал, в системе, где он будет запущен, должны находиться эти бинарники. В том случае, если этой системой является Fedora, установите пакет qemu-user-static, а если речь идет о Debian или Ubuntu, то qemu-user-static и binfmt-support.

А теперь перейдем к самому скрипту:

#!/bin/sh

set -e

if [ "$(id -u)" -ne "0" ]; then
  >&2 echo "This script must be run as root"
  exit 1
fi

mirror=http://dl-cdn.alpinelinux.org/alpine
alpine_ver=3.8
apk_ver=2.10.0-r3
chroot_dir=./alpine-baseimage

wget ${mirror}/v${alpine_ver}/main/armhf/apk-tools-static-${apk_ver}.apk

tar xzf apk-tools-static-${apk_ver}.apk

mkdir -p ${chroot_dir}/usr/bin

cp /usr/bin/qemu-arm-static ${chroot_dir}/usr/bin

./sbin/apk.static -X ${mirror}/v${alpine_ver}/main -U --allow-untrusted --root ${chroot_dir} --initdb add alpine-base
mknod -m 666 ${chroot_dir}/dev/full c 1 7
mknod -m 666 ${chroot_dir}/dev/ptmx c 5 2
mknod -m 644 ${chroot_dir}/dev/random c 1 8
mknod -m 644 ${chroot_dir}/dev/urandom c 1 9
mknod -m 666 ${chroot_dir}/dev/zero c 1 5
mknod -m 666 ${chroot_dir}/dev/tty c 5 0

IMAGE=$(sh -c "tar -C ${chroot_dir} -c . | docker import -")

docker tag ${IMAGE} alpine:${alpine_ver}-armhf

Обратите внимание на то, что версия утилиты apk-tools-static (в данном случае 2.10.0-r3) является плавающей – почти наверняка к выходу данной статьи в свет она изменится. Скрипту определенно следовало бы быть немного сложнее, чтобы учитывать эту особенность, но здесь он должен оставаться как можно более простым. Прежде чем переходить к его модернизации, проделайте следующее:

  • откройте http://dl-cdn.alpinelinux.org/alpine/v3.8/main/armhf/, чтобы подсмотреть текущую версию apk-tools-static;
  • обновите версию apk-tools-static в тексте скрипта;
  • запустите скрипт с правами суперпользователя (т.к. скрипту необходимо производить различные манипуляции с chroot-окружением).

В результате будет создан базовый Docker-образ alpine:3.8-armhf.

Напоследок стоит сказать, что эмулятор позволяет решить подавляющее большинство задач, но, к сожалению, не всё через него запускается или собирается. Тем не менее, случаи отказов являются большой редкостью и сильно зависят от того, насколько экзотическими являются те вещи, для запуска которых эмулятор привлекается. По моему опыту, запущенные через эмулятор FTP-сервер vsftpd и сборка watchtower ведут себя нестабильно. К счастью, на этом список известных мне проблем заканчивается.

Накладывание патчей при сборке образа

Между пакетами исходных текстов, на базе которых строятся двоичные пакеты дистрибутивов GNU/Linux, и Docker-образами есть много общего. Нередки случаи, когда докеризуемая программа собирается из исходников. Как и в случае с пакетами исходных текстов, в рамках Docker-образа, возможно, придется заняться полировкой исходных текстов целевой программы – из-за того, что разработчики ее оригинала не учитывали особенности той системы, в которой программу в данный момент хотят заставить работать. Этот рецепт посвящен распространению патчей в составе исходников Docker-образов, чтобы, как и в случае пакетов исходных текстов, внесенные в тот или иной кусок (известного) кода изменения находились у всех на виду и могли быть проанализированы и переиспользованы другими разработчиками.

В качестве примера хочу привести Docker-образ QEMU из проекта MMB: https://github.com/tolstoyevsky/mmb/tree/master/qemu. Одной из главных задач для MMB в один прекрасный момент стало предоставление пользователям компактного образа с эмулятором (а по совместительству и системой виртуализации) QEMU, поэтому в качестве основы для образа был выбран Alpine. Затем, чтобы не зависеть от версии QEMU, которая в тот или иной момент времени находится в репозитории дистрибутива, было принято решение собирать эмулятор из исходников. После этого выяснилось, что некоторые зависимости QEMU отсутствуют в Alpine, и возникла необходимость их тоже собирать из исходных текстов.

Библиотека numactl, одна из зависимостей QEMU, до версии 2.0.12 не собиралась в Alpine из-за того, что Musl, стандартная библиотека языка C, которую использует дистрибутив, нигдене объявляет макрос __GLIBC_PREREQ, фигурирующий в одном из модулей библиотеки. Таким образом, numactl оказалась зависима от Glibc. Чтобы исправить эту ситуацию, был взят на вооружение менеджер патчей quilt, тот самый, который используется в пакетах исходных текстов Debian.

К сожалению, quilt никак не может стать частью дистрибутива, вечно находясь в edge, тестируемой версии Alpine, поэтому менеджер патчей придется тоже собрать из исходников или написать примитивную альтернативу на базе программы patch и цикла for. Преимуществами использования quilt для управления патчами являются в первую очередь легкость отключения того или иного патча в серии или, при необходимости, изменение порядка их внесения.

Чтобы исправить сложившуюся с numactl ситуацию, можно воспользоваться самим quilt’ом, но для начала надо получить исходники библиотеки.

$ git clone https://github.com/numactl/numactl.git
$ cd numactl

Затем необходимо сообщить quilt’у о намерении создать новый патч:

$ quilt new adapt_to_musl.patch
$ quilt add syscall.c

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

Моим решением на скорую руку было просто-напросто удалить __GLIBC_PREREQ из условия

#if defined(__GLIBC__) && __GLIBC_PREREQ(2, 11)

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

#ifndef __GLIBC_PREREQ
# define __GLIBC_PREREQ(x,y) 0
#endif

И наконец, надо обновить патч, чтобы он отражал сделанные только что изменения.

$ quilt refresh

В результате всех этих манипуляций в корне дерева исходных текстов библиотеки будет создана директория patches с двумя файлами – adapt_to_musl.patch и series. Второй файл является так называемой серией патчей и представляет собой список, который на данный момент состоит всего из одного-единственного патча. Посредством редактирования можно повлиять на поведение quilt.

Теперь, чтобы созданный патч был применен к numactl во время сборки образа, необходимо позаботиться о том, чтобы директория patches находилась корне исходных текстов библиотеки. Затем, вызов quilt push -a инициирует накладывание всех патчей, указанных в серии.

GNU или не GNU/Linux

Такие дистрибутивы, как Debian или Ubuntu (и многие другие), используют GNU Coreutils и GNU C Library (Glibc) в качестве Unix-подобного окружения и стандартной библиотеки языка C соответственно. Таким образом, Проект GNU, как разработчик этих компонентов, играет важную роль в дистростроении, особенно если учесть тот факт, что для сборки вышеперечисленных частей используется компиляторы C/C++ из состава коллекции компиляторов от GNU (GNU Compiler Collection, GCC). В связи с этим Debian и Ubuntu следует называть дистрибутивами GNU/Linux, т.е. дистрибутивами, в которых различные компоненты Unix-подобной операционной системы от GNU в сочетании с ядром Linux образуют прочную основу, на которой строится всё остальное.

Было даже время, когда полнофункциональную Linux-подобную операционную систему не представлялось возможным создать без привлечения арсенала GNU. Несмотря на то, что достойная альтернатива Coreutils в лице Busybox существовала с незапамятных времен, свободные компиляторы C/C++ и альтернативные стандартные библиотеки языка C не всегда дотягивали до функциональности своих GNU’шных собратьев. Но времена изменились, и сейчас Musl является достойной альтернативой Glibc, а Clang не только научился конкурировать с компиляторами C/C++ из состава GCC, но и начал в чем-то их превосходить. Таким образом, в новых операционных системах на базе ядра Linux появилась возможность без значительного ущерба для функциональности свести к минимуму использование кода от GNU, компенсировав его аналогами, распространяющимися под пермессивной лицензией (Apache 2.0, все виды BSD, MIT и т.д.) или лучше «заточенными» под встраиваемые устройства. К числу таких операционных систем принадлежит Alpine. (Однако, справедливости ради, стоит заметить, что значительная часть дистрибутива собирается не чем иным, как GCC.) Таким образом, в данном конкретном случае от GNU/Linux остается только Linux, в результате чего полное название дистрибутива – Alpine Linux. Данный подход является семантическим именованием: взглянув на одно лишь имя операционной системы, возможно не только понять, что она построена на базе ядра Linux, но еще и то, что в ней, к примеру, используется нечто отличное от Glibc. Для сравнения, полным названием Debian является Debian GNU/Linux. Глядя на это название, можно с уверенностью сказать, что концентрация распространяющегося под лицензией GPLv3 кода в базовой системе очень высока. К сожалению, не все проекты по разработке Linux-подобных операционных используют семантиче­ское именование. Отличным примером является Gentoo Linux.

Devuan: плод нелюбви к systemd

Автор: Евгений Голышев

Статья была опубликована в 237-м выпуске журнала Linux Format. По истечении полугода с момента публикации права на статью переходят ее автору, поэтому материал публикуется в нашем блоге.

Кена Томпсона [Ken Thompson] однажды спросили, что бы он изменил в UNIX, если бы проектировал систему заново, и он ответил: «Я бы написал creat с буквой e» (англ. I’d spell creat with an e). Эта шутка, на мой взгляд, как ничто другое подчеркивает, насколько бережно Unix хранит традиции: за 40 с лишним лет так никто и не добавил букву e к имени системного вызова, хотя количество опечаток, допущенных при написании кода, велико и с каждым днем продолжает расти. В конце концов, кто может осмелиться сказать, что архитектурные решения патриархов Томпсона и Ритчи ошибочны?

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

И однажды в этот мир вторгся человек по имени Леннарт Пёттеринг [Lennart Poettering], со своим видением дистрибутивостроения, которое, по мистическому совпадению, в корне отличалось от того, к которому все успели привыкнуть за пару десятков лет. Всё бы ничего, но Леннарт родился с редкой способностью изменять всё вокруг, поэтому он легко начал воплощать свои задумки, а через крупнейшие дистрибутивы (Debian, Ubuntu, Red Hat Enterprise Linux, SUSE Linux Enterprise и т. д.) они стали добираться до согласных и несогласных с ним пользователей.

Леннарт полон неоднозначных идей того, каким образом дистрибутивы GNU/Linux можно сделать лучше.

Данная статья посвящена Devuan (читается как DevOne). Devuan является производным от Debian GNU/Linux дистрибутивом, основная цель которого заключается в альтернативном развитии Debian, как если бы он никогда не встретился с известным детищем Леннарта под названием systemd. Я написал эту статью главным образом по двум причинам.

Во-первых, Debian-подобные дистрибутивы – моя страсть, а Devuan, один из ярких представителей этого семейства, упоминался на страницах LXF только вскользь, поэтому я чувствую своим долгом исправить это недоразумение.

Во-вторых, я хочу помочь читателям разобраться в ситуации, сложившейся вокруг проекта systemd, а также понять, что побудило разработчиков Devuan потратить два с половиной года на то, чтобы «вытравить» systemd из Debian Jessie, получив таким образом первую стабильную версию производного дистрибутива.

В конце концов, если человек в один прекрасный момент поймет, что ненавидит systemd, то пусть хотя бы будет способен объяснить, почему.

К счастью или сожалению, материал не содержит информации, которая могла бы оказаться полезной при

  • установке, настройке и администрировании серверов под управлением Devuan;
  • администрировании серверов на базе Linux-подобных операционных систем, использующих systemd (если вы интересуетесь практиче­ской стороной systemd, то рекомендую обратиться к LXF199).

Хочу сразу отметить, что я не являюсь пользователем Devuan, предпочитая ему Debian (рабочая станция) и Ubuntu (ноутбук), и стараюсь занимать нейтральную позицию в спорах, связанных с действиями Леннарта Пёттеринга в общем и с systemd в частности. Но разочаровываться не стоит – я регулярно имею дело с Devuan при решении различных задач, одной из которых является разработка сервиса для кастомизации образов операционных систем для одноплатных компьютеров.

А теперь предлагаю начать обсуждение заявленных тем… но обо всём по порядку.

В погоне за ускорением загрузки

В конце 2000‐х несколько крупных производителей операционных систем на базе ядра Linux пришли к выводу, что необходимо ускорять время загрузки своих систем. Так как за загрузку операционной системы отвечает система инициализации, стало очевидно, что начинать нужно именно с нее. В течении долгого времени дистрибутивы GNU/Linux использовали для этих целей SysVinit, созданную Микéлем ван Смооренбургом [Miquel van Smoorenburg] и основанную на идеях оригинальной System V init. SysVinit сейчас считается классиче­ской системой инициализации и в данной статье противопоставляется всем другим.

SysVinit представляет собой систему инициализации, основанную на уровнях запуска (runlevel-based). Уровни запуска – это состояния, в которых может находиться система; система переходит из одного состояние в другое. SysVinit выполняет скрипты, ассоциированные с определенным уровнем, когда система входит в (или покидает) этот уровень. К примеру, когда система входит в уровень запуска N, то SysVinit запускает скрипты из /etc/rcN.d, где N — число от 0 до 6. Однако со временем накопилось много потребностей, которые классиче­ская система инициализации не могла удовлетворить в силу своей архитектуры (к примеру, слежение за своими дочерними процессами и их перезапуск в случае необходимости).

Недостатки SysVinit мотивировали компанию Canonical, которая является основным разработчиком Ubuntu, начать работу над альтернативной системой инициализации под названием Upstart, а достоинства launchd из Mac OS X и SMF из Solaris послужили основным источником ценных идей, которые помогли в решении поставленной задачи. Новая система инициализации оказалась эффективнее, чем SysVinit благодаря событийно-ориентированной архитектуре (event-driven architecture). Системные сервисы стало возможным запускать и останавливать на основе событий, а не уровней запуска. Данный подход предполагал генерацию событий при запуске или остановке системных сервисов, что позволило организовать привязку к ним других сервисов.

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

Upstart дебютировала в Ubuntu 6.10 «Edgy Eft» в конце 2006 г., заменив SysVinit (однако окочательно новая система инициализации была интегрирована в дистрибутив только к выходу Ubuntu 9.10 «Karmic Koala»). Затем на Upstart обратили внимание компании Google и Red Hat, интегрировав ее в свои продукты. Так Upstart нашла применение за пределами Ubuntu и начала использоваться в ChromeOS и Red Hat Enterprise Linux 6. На новую систему инициализации даже планировал перейти Debian.

При всех своих достоинствах, Upstart оказалась всего лишь трамплином для другой, более совершенной системы инициализации. Так, в апреле 2010 г. компания Red Hat, при участии разработчиков из Novell, IBM, Intel и Nokia, поставила перед собой цель создать систему инициализации, нацеленную на более интенсивную параллелизацию выполнения сервисов на этапе загрузки. Проект, получивший название systemd, возглавил Леннарт Пёттеринг. И уже в июле того же года была выпущена первая стабильная версия проекта.

На тот момент systemd получала не больше негативной критики, чем любой другой проект, позиционирующий себя как альтернативная система инициализации. Всё шло относительно гладко, несмотря на то что systemd (почти) сразу обратила на себя внимание своим агрессивным подходом к делам, связанным с обратной совместимостью. (К примеру, если Upstart была совместима со скриптами инициализации SysVinit, то новейшая система инициализации не была завязана ни на Bash, ни на любую другую оболочку.) Ситуацию не сильно усугубил и тот факт, что именно Леннарт был тем самым разработчиком звукового сервера PulseAudio, который в начале своего пути не отличался стабильностью работы (в основном из-за аудиодрайверов, а не ошибок в коде PulseAudio).

Проекту серьезно досталось немного позднее, когда в один прекрасный день systemd перестал позиционироваться как альтернативная система инициализации, превратившись в системный менеджер, в котором система инициализации стала всего лишь одним из многочисленных компонентов. О двух таких компонентах — journald и logind — я хочу рассказать прямо сейчас.

logind представляет собой систему управления пользовательскими сеансами. До того, как за решение этой задачи взялись разработчики systemd, дистрибутивы GNU/Linux для этих целей использовали ConsoleKit. В свое время ConsoleKit поставил перед собой амбициозную цель – поддержку более одного независимого рабочего места (multi-seat), что предполагает одновременную работу на одной системе нескольких независимых графических сеансов для различных пользователей. (Функция редкая, но в некоторых случаях очень востребованная.) Тем не менее, архитектура ConsoleKit не способствовала достижению заветной цели и проект оказался в глубокой стагнации. Так, без переизобретения велосипеда непростая задача поддержки более одного независимого рабочего места была решена в рамках компонента logind. Другими словами, лучшей альтернативы в мире Linux еще не видели. Более того, решение оказалось настолько удачным, что Энди Винго (Andy Wingo), один из разработчиков дистрибутива Guix, реализовал возможность обособленного использования logind. Проект получил название elogind (по аналогии с eudev). На elogind даже перешел Devuan, который, напомню, принципиально не использует systemd; но об этом немного позже.

journald представляет собой сервис журналирования событий. Этот компонент, в свою очередь, является отличным примером того, как разработчики systemd не только решили задачу, которая давно не нуждалась в решении, но и решили ее по-своему. Двумя основными отличиями journald от различных реализация syslogd, которые много лет занимались задачами журналирования в Unix, являются использование криптографиче­ских средств для гарантирования неизменности и целостности накопленных логов и хранение этих логов в двоичном виде. Традиционно логи на Unix-серверах хранятся в текстовом виде, поэтому хранение логов в двоичном виде воспринимается как недостаток, который обесценивает любые достоинства journald, даже такие крутые, как гарантию неизменности и целостности логов. Подробнее о journald можно узнать из LXF191/192 и LXF199.

Появление Devuan

В конце 2013-го – начале 2014-го Debian в очередной раз вернулся к актуальной для себя задаче – переходу на более современную систему инициализации. В каче­стве кандидатов рассматривались Upstart и systemd, и по результатам голосования победила последняя. Следом за Debian пошла Ubuntu.

По итогам интеграции systemd в Debian дистрибутив не только перешел на новую систему инициализации, но и оказался сильно завязанным на системном менеджере, который был призван управлять всеми аспектами работы системы. В итоге, systemd был поставлен в один ряд с ядром и стандартной библиотекой языка C, для которых в дистрибутиве не предусмотрены альтернативы. Jessie стал первым выпуском Debian, который не мог функционировать без systemd. Таким образом, группа разработчиков Veteran Unix Admins, которые были не согласны с этой политикой, выпустили в рамках проекта Devuan свободную от systemd версию Jessie. Чтобы этого добиться, разработчикам потребовалось внести изменение в 381 пакет. Так появился первый выпуск Devuan, позволивший пользователям Debian использовать или не использовать systemd в зависимости от их желания.

Второй в истории лидер проекта Debian является пользователем Devaun.

В качестве системы инициализации в Devuan по умолчанию используется SysVinit, которая сейчас более-менее активно развивается. Дело в том, что версия 2.89, выпущенная 28‐го марта 2018-го года, была первой за 8 лет стагнации проекта, но с приходом Джесса Смита [Jesse Smith], который занял пустующее кресло сопровождающего проекта, ситуация резко изменилась.

Различные новостные ресурсы, освещающие достижения открытого и свободного программного обеспечения, позиционируют Devuan как борца за независимость от systemd (отчасти это объясняется тем, что самыми оперативными оказываются информационные источники, пополняемые сообществом, а профессиональные издания, как правило, чуть медленнее реагируют на события и потому постоянно догоняют первых по горячим следам). С одной стороны, это позволило быстро обратить на проект внимание, сделав Devuan звездой мира СПО. С другой стороны, это искажает оригинальные цели проекта – свободе выбора системы инициализации. Сейчас с полной уверенностью можно утверждать, что Devuan добился этой цели: во втором выпуске дистрибутива, известном под кодовым именем ASCII, который вышел 9 июня 2018 г., пользователям помимо SysVinit предлагается OpenRC, которая используется в Alpine Linux и Gentoo.

Заключение

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

Что касается фактов, то systemd является образцом отлично написанного системного программного обеспечения, а в сообществе разработчиков царят мир, гармония и взаимоуважение. Леннарт, в свою очередь, является примером отличного лидера, который, ко всему прочему, еще и невероятно креативен – пишет много и интересно, освещая различные особенности Unix в своем блоге. Но чутье подсказывает, что где-то здесь должен быть подвох. И он действительно есть. Когда в рамках systemd решаются какие-то задачи, то разработчики, как правило, подходят к их решению по-своему и без оглядки на обратную совместимость, посягая на святая святых – традиции Unix (к примеру, я почувствовал легкое недомогание, когда первый раз прочитал новость о том, что journald предлагает хранить логи в двоичном виде). Именно здесь и начинаются конфликты интересов. Но не все решения разработчиков systemd так уже плохи. Об этом свидетельствует тот факт, что некоторые компоненты systemd уже используются в дистрибутивах, которые сознательно отказываются от systemd как от системы инициализации.

Имена доверьте авторам

Есть иногда у некоторых проектов такие имена, глядя на которые, не сразу возможно угадать их правильное произношение (под «правильным» я в данном случае подразумеваю принятое в сообществе). Обычно в таких случаях необходимо обратиться к FAQ’у или истории этих проектов. В каче­стве примера на ум приходят web-сервер Lighttpd (читается как lighty) и Nginx (читается как engine-x). (Очень забавно, когда люди с серьезным выражением лица рассказывают о некоем web-сервере под названием Энгинкс.) Но история с именами на этом не заканчивается.

Есть и такие проекты, авторы которых настаивают на строгом написании названий своих детищ. Однако все мы привыкли к тому, что слова, с которых начинаются предложения, пишутся с большой буквы. А еще мы помним, что с заглавной буквы всегда пишутся все имена собственные. Но в школе обычно не учат тому, что всем этим правилам нужно следовать только в том случае, если они не противоречат правилу написания конкретного имени. В каче­стве примеров проектов, с которыми нужно считаться с этой точки зрения, хочу привести ownCloud и, конечно, systemd. Таким образом, в каком бы месте предложения ни стояли эти имена, после какого бы знака препинания не шли – они должны сохранять авторский вариант написания.

В заключение, думаю, что нужно сказать пару слов о том, почему следует писать systemd с маленькой буквы несмотря на то, что это имя собственное. Дело в том, что проект был назван по имени основного исполняемого файла, который запускается первым при загрузке системы, становясь процессом с номером 1 и родителем всех остальных процессов. Традиционно (и тут тоже традиции!) в UNIX и C, родного для этой ОС языка, использовался нижний регистр, поэтому одна из ключевых программ в Linux-подобной операционной системе могла быть названа только systemd, а не Systemd, SystemD или как-то еще.

Raspbian: сборка образа. Часть 2

Автор: Евгений Голышев

Статья была опубликована в 236-м выпуске журнала Linux Format. По истечении полугода с момента публикации права на статью переходят ее автору, поэтому материал публикуется в нашем блоге.

Тема сборки минимально функциональной версии Raspbian продолжается. Эту часть я хочу начать с обсуждения несправедливо забытого термина JeOS (читается как juice). Этот термин, который, кстати, расшифровывается как «just enough operating system», был очень популярен лет десять назад, когда производители коммерческих дистрибутивов GNU/Linux были увлечены разработкой инструментов для индивидуальной настройки [customization] операционных систем. JeOS – это подход, согласно которому для решения какой-то конкретной задачи достаточно взять минимально возможную версию операционной системы и установить поверх нее зависимости, необходимые для запуска одного конкретного приложения, решающего эту задачу. Таким образом, решение получается самодостаточным и максимально эффективным с точки зрения производительности. В зависимости от инструмента, помогающего кастомизировать операционные системы, конечный результат был пригоден для запуска как на реальной, так и в виртуальной машине. Самым ярким из этих инструментов был SUSE Studio, который неоднократно освещался на страницах Linux Format (к примеру, в LXF125 и LXF138). Он одним из первых начал широко использовать термин «виртуальное устройство [virtual appliance]», который означает операционную систему узкого назначения вкупе с целевым приложением, пригодную для запуска в виртуальной машине. Но времена меняются, и всё чаще для решении какой-то конкретной задачи выделяется целый Raspberry Pi или любой другой одноплатный компьютер. Терминология здесь еще недостаточно проработана, и необходимо это исправить. Назовем, к примеру, подготовленный в первой части этого руководства образ минимально функциональной версии Raspbian термином JeOSI (предлагаю произносить его как juicy). По аналогии с JeOS новый термин расшифровывается как «just enough operating system image». С переводом будет немного сложнее. Дело в том, что словосочетание «just enough» в данном случае является наречием, а не существительным, что делает эти термины больше похожими на слоганы, чем на что-то другое. После нескольких часов размышлений я не придумал ничего лучше, чем «достаточно просто операционной системы» [можно также перевести как «ОС в обрез», – прим. ред.] для JeOS и «достаточно просто образа операционной системы» для JeOSI (как рекламный слоган популярного в 1990-х растворимого напитка Invite – «просто добавь воды»). На самом деле куда важнее, что JeOSI может сбить с толку тем, что перекликается с сетевой моделью OSI или некоммерче­ской организацией Open Source Initiative, название которой также часто сокращается до OSI; но более удачного термина я пока предложить не могу.

Итак, пора определиться с целями второй части учебника.

Показанный в первой части подход позволяет собирать минимально функциональные образы для Raspberry Pi на основе Debian-подобных дистрибутивов. Прежде чем написать статью, я успел опробовать этот подход на Raspbian, Devuan и Ubuntu. Однако именно Raspbian использовался в качестве примера для демонстрации сборки образа на протяжении всей первой части руководства, т.к. он является «родным» для RPi. Таким образом, выбор в пользу этого дистрибутива был исключительно символическим, и нет никаких препятствий в использовании любого другого основанного на Debian дистрибутива. Экспериментируйте.

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

$ sudo mkdir /mnt/boot /mnt/rootfs
$ LOOP_DEV=$(sudo losetup --partscan --show --find raspbian-stretch.img)
$ sudo mount ${LOOP_DEV}p1 /mnt/boot
$ sudo mount ${LOOP_DEV}p2 /mnt/rootfs

А теперь – вперед.

Поддержка сети

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

SUSE Studio был пионером и отличным примером решения в области кастомизации операционных систем.
SUSE Studio был пионером и отличным примером решения в области кастомизации операционных систем.

Raspbian использует системный менеджер systemd, который, помимо всего прочего, пытается решить проблему с именованием сетевых интерфейсов. Таким образом, в рамках systemd 197, который вышел 7 января 2013 г., в udev была добавлена поддержка т. н. «предсказуемых имен сетевых интерфейсов [Predictable Network Interface Names]». И первым делом я предлагаю отключить эту поддержку. Во-первых, по иронии судьбы предсказуемые имена не всегда предсказуемы. Во-вторых, в случае RPi и его единственного проводного сетевого интерфейса нет никакой проблемы с именованием. В-третьих, я хочу, чтобы этот учебник был применим ко всем производным от Debian дистрибутивам, которые могут по тем или иным причинам не использовать systemd.

Чтобы отключить поддержку предсказуемых имен сетевых интерфейсов, необходимо добавить два следующие параметра к командной строке ядра, которая содержится в cmdline.txt на загрузочном разделе.

net.ifnames=0 biosdevname=0

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

$ sudo sh -c "echo 'net.ifnames=0 biosdevname=0 $(cat /mnt/cmdline.txt)' > /mnt/boot/cmdline.txt"

Если на данном этапе вы запишете образ на SD-карту, загрузите с нее устройство и выполните

$ ip link show

то увидите в списке всем хорошо знакомый eth0.

Следующим шагом необходимо разобраться с «именем хоста [hostname]». Дело в том, что debootstrap (см. первую часть учебника) назвал chroot-окружение именем системы, в которой оно собиралось, поэтому две ваши машины – RPi и та, на которой происходила сборка окружения – сейчас имеют одинаковые имена. Это необходимо исправить. Для этого придумайте новое имя для RPi и добавьте его в /etc/hostname и /etc/hosts, к примеру, следующим образом:

$ sudo sh -c "echo raspbian-jeosi > /mnt/rootfs/etc/hostname"
$ sudo sed -i '2i 127.0.0.1\traspbian-jeosi' /mnt/rootfs/etc/hosts

В данном случае в качес тве имени использовалось raspbian-jeosi. Назовите машину с учетом ваших личных вкусов и предпочтений, но помните, что согласно RFC 952 имена хостов не должны превышать 24-х символов; в качестве символов, из которых разрешается составлять имена – буквы латинского алфавита в верхнем и нижнем регистре, цифры, знак минуса (-) и точка (.).

В заключение необходимо установить пакеты, которые содержат DHCP-клиент, ряд вспомогательных утилит типа ping, всё необходимое для взаимодействия устройства с другими устройствами в сети (на базе стека протокола TCP/IP, разумеется) и SSH-сервер, ради которого всё это затевалось

$ sudo chroot /mnt/rootfs apt-get update
$ sudo chroot /mnt/rootfs apt-get install netbase net-tools iscdhcp-client inetutils-ping openssh-server

а также отредактировать конфигурационный файл /etc/network/interfaces, добавив в него следующее содержимое:

auto lo
iface lo inet loopback
auto eth0
allow-hotplug eth0
iface eth0 inet dhcp

И хотя после всех этих манипуляций образ наконец имеет всё необходимое, чтобы работать в Сети, остается одна небольшая про­блема с подключением к SSH-серверу. Дело в том, что OpenSSH, который достался дистрибутиву от Debian, сконфигурирован таким образом, чтобы не разрешать вход от имени root с парольной аутентификацией. Решить эту проблему можно одним из двух способов: либо измените в /etc/ssh/sshd_config текущее значение PermitRootLogin на yes, включив тем самым возможность входа как root по паролю (что настоятельно не рекомендуется), либо перейдите к следующему разделу и заведите еще одного пользователя в системе, на которого не будет накладываться это ограничение.

Пользователи

На данный момент в системе есть только один пользователь – root. Этого было достаточно, чтобы, не отвлекаясь на лишние детали, убедиться, что новоиспеченная система находится в рабочем состоянии. Теперь настало время обратить внимание на опущенные ранее детали.

В настоящее время разработчики дистрибутивов делятся на два лагеря, когда речь заходит о пользователе root (напомню, что по отношению к нему часто используется термин «суперполь­зователь»). Более консервативные следуют практике наличия по крайней мере двух учетных записей в системе: root’а для административных задач и непривилегированного пользователя для всех остальных. Этот канонический подход дошел до нас в почти неизменном виде с самых первых версий UNIX; но он не лишен недостатков. На мой взгляд, наиболее ярким из них является необходимость постоянного контроля за тем, чтобы сессия root’а была вовремя закрыта. Таким образом, всегда есть риск забыться и продолжить решение своих повседневных задач от лица суперпользователя, что может привести к серьезным негативным последствиям – ведь безграничные возможности, скрытые в учетной записи root’а, обладают поистине разрушительной силой, которую не каждый способен держать под контролем.

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

  • не устанавливать пароль для суперпользователя, оставляя его учетную запись заблокированной, или заблокировать учетную запись явно, указав вместо пароля (или перед паролем) восклицательный знак (!);
  • установить программу sudo;
  • добавить первого пользователя (т. е. того, который был создан во время установки системы) в группу sudo, дав ему тем самым неограниченные права при выполнении любых операций в системе.
Получив дополнительные права через sudo, не забывайте, что с великой силой приходит великая ответственность.
Получив дополнительные права через sudo, не забывайте, что с великой силой приходит великая ответственность.

С одной стороны, такая практика противоречит философии программы – дать каждому пользователю как можно меньше привилегий, но так, чтобы их было достаточно для решения поставленных перед этими пользователями задач; а с другой – помогает решить проблему с закрытием сессии root.

В официальном образе Raspbian используется именно этот подход. Какой подход будет использоваться в вашем образе – решать вам, но для начала необходимо завести в системе еще одного пользователя, от имени которого будет осуществляться вход, в том числе и через SSH.

Итак, чтобы создать пользователя с именем pi, выполните

$ sudo chroot /mnt/rootfs useradd -m -s /bin/bash pi

Заметьте, что useradd и другие смежные с ней программы, в том виде, в котором они используются в этом разделе, специфичны для всех систем, в которых используется shadow для управления пользователями и группами. В отличие от coreutils, интерфейс программ (и даже название самих программ) для управления пользователями и группами сильно варьируется от реализации к реализации, поэтому всё описанное здесь может отличаться от того, что принято в других Unix-подобных операционных системах и даже других дистрибутивах GNU/Linux.

Опция -m позволяет указать, что в /home необходимо создать домашнюю директорию пользователя с его именем, а опция -s говорит, что оболочкой по умолчанию должна стать Bash. Можно было бы посредством опции -p указать пароль, но это небезопасно. Во-первых, вышеприведенная команда останется в истории, к которой может получить доступ администратор или злоумышленник, если пользователь, от имени которого она выполнялась, будет скомпрометирован. Во-вторых, процесс создания пользователя будет фигурировать в списке процессов, из которого администратор может получить все переданные useradd параметры. В-третьих, пароль на экране может быть просто подсмотрен из-за плеча. Даже если эта и другие приведенные в данной статье команды выполняются на персональной машине в пустом и запертом помещении, я всё равно предлагаю пойти правильным путем и установить пароль пользователя отдельно. В конце концов, чтобы стать настоящим хакером, нужно быть немного шизофреником.

$ sudo chroot /mnt/rootfs passwd pi

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

$ sudo chroot /mnt/rootfs apt-get update
$ sudo chroot /mnt/rootfs apt-get install sudo
$ sudo chroot /mnt/rootfs usermod -aG sudo pi

Теперь пользователь pi может временно заимствовать права суперпользователя для тех вещей, которые выполняются через sudo.

Финальный штрих – блокировка учетной записи root. Для этого выполните

$ sudo chroot /mnt/rootfs passwd -l root

Данная команда поставит восклицательный знак (!) перед хэшем пароля, и это обозначит, что учетная запись заблокирована.

Загляните в «теневой файл паролей [shadowed password file]» /etc/shadow, чтобы в этом убедиться.

На этом тему можно было бы считать закрытой, но остается еще одна вещь, которая не дает мне покоя. Дело в том, что за полтора десятка лет существования Ubuntu успело подрасти целое поколение линуксоидов, которые убеждены, что sudo – это не более чем просто способ временного получения прав суперпользователя, и если оставить всё как есть, то наш учебник только поддержит это распространенное заблуждение. Я хочу внести свой посильный вклад в то, чтобы исправить сложившуюся ситуацию. Для этого я предлагаю решить одну простую, но очень актуальную для RPi задачу, суть которой заключается в том, чтобы дать пользователю возможность выключать или перезагружать свое устройство через Android-приложение нажатием одной кнопки. Таким образом, потребуется посредством sudo дать пользователю право выполнять команды systemctl poweroff и systemctl reboot. Более того, необходимо не только дать пользователю право выполнения этих команд, но и при этом не спрашивать его пароль. Дело в том, что Android-приложение SSH Button, которое я предлагаю использовать для создания кнопок выключения и перезагрузки устройства, как и любое подобное приложение, очень примитивно. Оно подключается к SSH-серверу, выполняет указанную программу и анализирует код завершения [exit status]. Если в результате своей работы программа переходит в режим, в котором она ожидает каких-то действий от пользователя (к примеру, ввода пароля), то SSH Button считает выполнение программы неудачным. Всё это создает идеальные условия для того, чтобы копнуть чуть глубже возможности, предлагаемые sudo, чем я и предлагаю заняться прямо сейчас.

Правила, согласно которым sudo принимает решение, кто и что в праве делать, находятся в /etc/sudoers. Однако, вместо того чтобы редактировать этот файл напрямую, я предлагаю добавить новое правило в виде отдельного файла через подключаемую директорию /etc/sudoers.d. Создайте файл, к примеру, с именем shutdown со следующим содержимым.

pi raspbian-jeosi =NOPASSWD: /usr/bin/systemctl halt,/usr/bin/systemctl reboot

Все файлы в этой директории должны иметь права 0440 (-r–r—–).

$ sudo chmod 0440 /mnt/rootfs/etc/sudoers.d/shutdown

Теперь установите SSH Button на любое устройство под управлением Android. Затем создайте в нем кнопку и укажите в ее настройках адрес вашего RPi, логин и пароль пользователя pi и одну из разрешенных этому пользователю команд — systemctl poweroff или systemctl reboot.

Удобный способ выполнить любую команду на RPi с мобильного устройства.
Удобный способ выполнить любую команду на RPi с мобильного устройства.

Этот подход особенно удобен, когда вам нужно подключиться к RPi только для того, чтобы корректно завершить все процессы на нем перед его выключением. Читайте следующий раздел, в котором пойдет речь о том, как сделать корневую файловую систему доступной только на чтение и, таким образом, избавиться от необходимости т.н. корректного завершения работы.

Доступный только на чтение корень

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

Доступный только на чтение корень – это умышленное ограничение функциональности системы для повышения ее живучести. Но основная проблема здесь не в том, как этого добиться, а в том, как минимизировать последствия от этого ограничения: ведь будет очень обидно, если демон, предназначенный для решения той самой единственной задачи, потеряет способность вести журнал… Тому, как сделать корень доступным только на чтение и не терять логи в этих условиях, посвящена большая часть этого раздела. (В силу того, что материал руководства выходит далеко за пределы журналирования, разговор на эту тему не будет исчерпывающим, поэтому я настоятельно рекомендую обратиться к статье «Записки демонов» из LXF91 за более детальными подробностями по этому вопросу.)

Традиционно в Unix-подобных операционных системах журналированием занимается демон syslogd. В Debian, к примеру, используется одна из его реализаций под названием rsyslog. Когда сообщение доходит до syslogd, возможен один из пяти вариантов развития событий:

  • сообщение может быть добавлено в файл;
  • сообщение может быть выдано на терминал любого указанного пользователя;
  • сообщение может быть записано в FIFO (именованный канал);
  • сообщение может быть перенаправлено syslogd, находящемуся на другой машине;
  • сообщение может быть проигнорировано.

Самый распространенный первый вариант не подходит, т.к. корневая файловая система доступна только на чтение. Можно, конечно, передавать логи на другую машину, но в некоторых случаях наличие еще одной машины может оказаться избыточным. К счастью, другая реализация syslogd, разработанная в рамках проекта Busybox, поддерживает использование так называемого «кольцевого буфера [circular buffer]» для хранения логов. Таким образом, создается иллюзия того, что логи пишутся в обычном режиме так, как если бы корневая файловая система не была доступна только на чтение.

В Raspbian (и других дистрибутивах, основанных на Debian) реализация syslogd от проекта Busybox находится в пакете busybox-syslogd.

$ sudo chroot /mnt/rootfs apt-get install busybox-syslogd

Если в системе уже был установлен rsyslog, то установка busybox-syslogd приведет к его удалению, т. к. busybox-syslogd возьмет на себя функции rsyslog.

Теперь логи подавляющего большинства демонов в вашей системе будут сохраняться в памяти. busybox-syslogd сконфигурирован в Raspbian таким образом, чтобы использовать для этих целей буфер размером 64 КБ. Однако, если произойдет внезапное отключение питания, то все логи вылетят в трубу. Конечно, самым надежным во всех случаях подходом является хранение логов на отдельной машине, и busybox-syslogd это тоже умеет.

Несмотря на то, что подавляющее большинство демонов используют syslogd для журналирования, есть демоны, которые занимаются этим вопросом самостоятельно. К примеру, Nginx ведет два типа журналов: журнал доступа [access log] и журнал ошибок [error log], и по умолчанию не пользуется услугами syslogd для этих целей. Тем не менее, модуль Nginx ngx_http_log_module, который занимается журналированием, поддерживает использование syslogd. В Debian и его производных Nginx сконфигурирован так, чтобы журнал доступа сохранялся в /var/log/nginx/access. log, а журнал ошибок – в /var/log/nginx/error.log. Очевидно, что в файловой системе, доступной только на чтение, Nginx не сможет писать в access.log и error.log, поэтому необходимо попросить web-сервер регистрировать события через syslogd. Для этого отредактируйте /etc/nginx/nginx.conf так, чтобы директивы access_log и error_log выглядели следующим образом.

access_log syslog:server=unix:/dev/log,nohostname;
error_log syslog:server=unix:/dev/log,nohostname;

И в заключение, необходимо сделать корневой раздел доступным только на чтение. Для этого добавьте параметр ro к командной строке ядра, которая находится в файле cmdline.txt на загрузочном разделе.

Сборка ядра

Сейчас система основана на Raspbian’овском ядре, которое латается и пакетируется самими разработчиками одноплатников, поэтому оно, как ни что другое, отлично поддерживает RPi. Пакет raspberrypi-kernel, установка которого обсуждалась в первой части этого руководства, содержит ядро, предназначенное для решения широкого круга задач, что открывает огромные возможности для оптимизаций. Таким образом, в данном разделе речь пойдет о том, как самостоятельно собрать ядро – для того, чтобы получить возможность подогнать его под решение конкретной задачи.

Теперь осталось определиться, у кого мы возьмем ядро, которое будем здесь собирать: у Линуса, разработчиков RPi или у когото еще. Предлагаю собирать именно «ванильное» ядро, т. к. этот подход является наиболее дистрибутивонезависимым. Базовая поддержка RPi появилась в upstream’е достаточно давно. К примеру, RPi 2 поддерживается Linux’ом, начиная с версии 4.5, которая вышла 13-го марта 2016 г., а RPi 3 – начиная с версии 4.8, которая вышла 2 октября того же года. Безусловно, RPi поддерживается «ванильным», т. е. стандартным, ядром не настолько хорошо, как Raspbian’овским, но для решения некоторых задач этим можно пренебречь.

В качестве примера я возьму Linux 4.14 — последний на момент написания статьи выпуск с длительным сроком поддержки. Так, этот раздел не потеряет своей актуальности ни на грамм, пока не закончится жизненный цикл выпуска 4.14, которое при участии Google будет сопровождаться до 2023 г. Тем не менее, я призываю вас брать самую последнюю на момент чтения этого руководства версию ядра и применять к ней описанные здесь рецепты.

А теперь к делу. Загрузите с kernel.org архив с интересующей вас версией ядра Linux. К примеру, им оказался linux-4.14.52.tar.xz. Распакуйте его.

$ tar xJvf linux-4.14.52.tar.xz

Затем установите необходимые для конфигурации и сборки ядра пакеты (в Debian и производных от него это build-essential, libncurses5-dev и gcc-arm-linux-gnueabihf). После этого выполните следующие команды, чтобы приступить к конфигурации целевого ядра, ради которой мы здесь все собрались.

$ ARCH=arm make bcm2835_defconfig
$ ARCH=arm make menuconfig
menuconfig поможет сконфигурировать ядро для решения конкретной задачи.
menuconfig поможет сконфигурировать ядро для решения конкретной задачи.

На этом этапе, как правило, из ядра выкидывается всё, что не способствует выполнению поставленной задачи, и добавляется всё, чего не хватает. Руководство не предлагает в каче­стве примера какую-то конкретную задачу, поэтому здесь всё зависит от вас. Я настоятельно рекомендую не зацикливаться на этом шаге, двинуться дальше, а потом еще раз вернуться к этому разделу после просмотра доклада «Tuning Linux For Embedded Systems: When Less is More» Даррена Гарта [Darren Hart], где он ставит перед собой цель собрать минимально возможное ядро для встраиваемого устройства.

После того как с конфигурацией будет покончено, можно начинать сборку:

$ ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- chrt -i 0 make -j4 deb-pkg

В результате получится несколько Deb-пакетов. Сейчас нас интересует linux-image, полное название у которого в моем случае – linux-image-4.14.52_4.14.52-1_armhf.deb. Установите его, предварительно удалив пакет с Raspbian’овским ядром:

$ sudo chroot /mnt/rootfs apt-get purge raspberrypi-kernel
$ sudo cp linux-image-4.14.52_4.14.52-1_armhf.deb /mnt/rootfs
$ sudo chroot /mnt/rootfs dpkg -i linux-image-4.14.52_4.14.52-1_armhf.deb
$ sudo rm /mnt/rootfs/linux-image-4.14.52_4.14.52-1_armhf.deb

Затем удалите с загрузочного раздела всё, что относилось к Raspbian’овскому ядру и загрузите на него новое ядро и dtb-файлы:

$ sudo rm /mnt/boot/{cmdline.txt,bcm283*,kernel7.img}
$ sudo cp /mnt/rootfs/boot/vmlinuz-4.14.52 /mnt/boot/zImage
$ sudo cp /mnt/rootfs/usr/lib/linux-image-4.14.52/bcm283* /mnt/boot

Для загрузки «ванильного» ядра потребуется загрузчик, в качестве которого я предлагаю использовать Das U-Boot. Последней стабильной версией U-Boot на момент написания статьи является 2018.05. Загрузите архив с исходниками U-Boot, укажите файл конфигурации, которая соответствует вашему устройству, и запустите сборку.

Что касается файла конфигурации, то в моем случае это rpi_3_32b_defconfig, т. к. я хочу запустить на Raspberry Pi 3 Model B собранное под ARMv7 ядро Linux, поскольку мы имеем дело с 32-битной пакетной базой Raspbian. Подсмотрите в директории configs все возможные варианты.

$ curl -O ftp://ftp.denx.de/pub/u-boot/u-boot-2018.05.tar.bz2
$ tar xjvf u-boot-2018.05.tar.bz2
$ cd u-boot-2018.05
$ ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- chrt -i 0 make rpi_3_32b_defconfig
$ ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- chrt -i 0 make -j4

Теперь подготовьте файл boot.scr со следующим содержимым:

mmc dev 0
setenv fdtfile bcm2837-rpi-3-b.dtb
setenv bootargs earlyprintk console=tty0 console=ttyAMA0
root=/dev/mmcblk0p2 rootwait init=/bin/systemd
fatload mmc 0:1 ${kernel_addr_r} zImage
fatload mmc 0:1 ${fdt_addr_r} ${fdtfile}
bootz ${kernel_addr_r} - ${fdt_addr_r}

Этот файл содержит список команд, которые U-Boot должен выполнить. Такой способ конфигурации U-Boot делает загрузчик очень гибким.

Есть три места, на которые в этом пункте стоит обратить пристальное внимание.

  • 2-я строка содержит имя DTB-файла, соответствующего устройству, на котором планируется загрузка ядра. Указанный в этой строке файл, в числе прочих DTB-файлов, был получен при сборке ядра и уже находится на корневом и загрузочном разделах. Укажите здесь DTB-файл, соответствующий вашему устройству.
  • 3-я строка содержит командную строку ядра.
  • 4-я строка содержит имя двоичного файла ядра (zImage).

Теперь необходимо скомпилировать boot.scr в boot.scr.uimg. Для этого потребуется программа mkimage, которую можно найти в пакете u-boot-tools во всех Debian-подобных дистрибутивах или в uboot-tools в Fedora.

$ mkimage -A arm -O linux -T script -C none -n boot.scr -d boot.scr boot.scr.uimg

Наконец, нужно перекинуть двоичный файл загрузчика, который был получен после сборки U-Boot, и boot.scr.uimg на загрузочный раздел:

$ sudo u-boot.bin /mnt/boot/kernel7.img
$ sudo boot.scr.uimg /mnt/boot

Очистка образа

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

Существует идиома, которая решает эту проблему. Она стала популярной за счет повсеместного использования в Dockerfile’ах, т.к. Docker-образы, как и образы для одноплатников, тоже нуждаются в очистке от «строительного мусора». Выглядит эта идиома следующим образом.

$ sudo apt-get clean
$ sudo rm -rf /var/lib/apt/lists/*

Первая команда удаляет кэшированные двоичные пакеты, а вторая – индексные файлы. В нашем случае эти команды будут выглядеть так:

$ sudo chroot /mnt/rootfs apt-get clean
$ sudo rm -rf /mnt/rootfs/var/lib/apt/lists/*

Заключение

Несмотря на то, что есть достаточно большое количество скриптов, автоматизирующих весь описанный в этом руководстве процесс, я убежден в большой пользе понимания, что у них под капотом. Во-первых, эти скрипты нуждаются в контрибьюторах – и где как не здесь их выращивать. Во-вторых, спускаясь на уровень сборки своей системы, хоть и на основе двоичных пакетов, есть возможность лучше понять GNU/Linux, а где как не здесь этим заниматься. Я старался выжимать максимум из каждого раздела этой части руководства, выстраивая как можно более длинные цепочки из смежных тем для того, чтобы сделать материал как можно более энциклопедическим. Теперь ваша очередь – сдуйте пыль со своего одноплатника и выжмите из него максимум. Удачного хакинга.

Запуск сборок Ubuntu 18.04 «Bionic Beaver» (64-bit) для Raspberry Pi 3 в эмуляторе

Тестируйте сборки на основе Ubuntu 18.04 «Bionic Beaver» (64-bit) для Raspberry Pi 3 в эмуляторе прямо в браузере. Соберите сборку на основе Ubuntu 18.04 (64-bit) в онлайн конструкторе образов, затем в панели управления нажмите кнопку «Запустить в эмуляторе».

Запуск Ubuntu в эмуляторе Raspberry Pi
Запуск Ubuntu в эмуляторе Raspberry Pi

Дождитесь когда CusDeb распакует вашу сборку и передаст эмулятору.

Распаковка образа
Распаковка образа

Подождите несколько минут, пока эмулятор запустит сборку, и вы увидите  приглашение авторизации. Ваша сборка работает в эмуляторе Raspberry Pi 3.

Приглашение операционной системы
Приглашение операционной системы

Введите имя пользователя и пароль, которые вы задали на этапе конфигурирования сборки. Если вы не меняли пользователя и пароль по умолчанию, то введите имя пользователя cusdeb и пароль secret.

Успешный вход в систему
Успешный вход в систему

Работайте с эмулятором, как будто вы подключились к Raspberry Pi 3 по ssh. Например, запустите команду top, чтобы увидеть список запущенных процессов.

Результат выполнения команды top
Результат выполнения команды top

Чтобы предоставить нашим пользователям возможность тестирования сборок без необходимости запускать их на реальном железе, мы разработали микросервис Orion, который эмулирует Raspberry Pi 3 при помощи QEMU и использует веб-эмулятор терминала на основе Gits для взаимодействия с пользователем.

Исходный код микросервиса Orion опубликован на  GitHub: https://github.com/tolstoyevsky/orion

2018 год c командой разработчиков CusDeb

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

  • Открыли исходный код бóльшей части компонентов конструктора. Теперь мы ведем разработку на GitHub.
  • Перестали заниматься Nextcloud и сфокусировались на CusDeb.
  • У нас появился бизнес-ангел – Игорь Вульфович, сооснователь компании WIS Software, который оказывает всяческую поддержку проекту.
  • Совместно с компанией Northern.tech интегрировали обновления по воздуху (OTA software updates) в конструктор.
  • К команде присоединились новые участники и вместе мы реализовали функцию тестирования образов операционных систем онлайн.

А теперь обо всем по порядку.

Открытый исходный код и Nextcloud

CusDeb начинался как проект с закрытым исходным кодом и долгое время оставался таким до тех пор, пока мы не осознали, что разрабатываем проект для настоящих хакеров, для которых важна открытость используемых ими инструментов. Таким образом, мы сначала опубликовали исходный код фреймворка для создания RPC-серверов Shirow, а затем и исходный код других компонентов CusDeb, таких как BlackMagic, Dominion и django-cusdeb-firmwares.

Зимой мы активно работали над образом SD-карты с предустановленным Nextcloud для Raspberry Pi 2 и 3 и тратили много времени на техническую поддержку пользователей этого решения. Это был наш первый продукт, т.е. вещь, за которую нам отдавали деньги. Было очень приятно, что образ для SD-карты, который мы сделали на базе Nextcloud, пользуется спросом, и мы просто кайфовали от происходящего.Тем не менее, мы мало времени уделяли CusDeb’у, но на первых порах не замечали этого.

Devuan, инновационный конвент и OS Day

Весна для нас началась с добавления поддержки операционной системы Devuan 1 «Jessie» в один из центральных компонентов CusDeb’а – Pieman, а затем и в сам CusDeb. Devuan – это ответвление от Debian GNU/Linux, основная цель которого – дать пользователям привычный Debian, но непривязанный к системному менеджеру systemd. Более подробно о Devuan мы писали в этой статье.

Дальше было интереснее – в апреле мы заняли третье место в молодежном инновационном конвенте Ростовской области в номинации лучший инновационный продукт, за что благодарим комитет по молодежной политике Ростовской области. Призовые деньги были отложены на поездку на Embedded Linux Conference 2019.

Денис Мосолов и Евгений Голышев

Знаменательным событием конца весны стала поездка в Москву на конференцию OS Day 2018 в роли слушателей. Это мероприятие является крупнейшим в области системного программирования в масштабах страны, поэтому для нас стало очевидно, что необходимо туда возвращаться в следующем году, но уже с докладом. Все интересные моменты, связанные с OS Day 2018, мы зафиксировали в этой статье.

Alpine Linux, снова Nextcloud и бизнес-ангел

В начале лета мы добавили Alpine Linux в Pieman. Это событие стало знаковым, т.к. один из центральных компонентов CusDeb’а стал еще более универсальным, чем был прежде. Дело в том, что Alpine не принадлежит к семейству Debian-подобных дистрибутивов, на которое был с самого начала рассчитан Pieman.

В середине лета произошло еще одно важное событие – мы осознали, что не можем разрываться между конструктором и образом c Nextcloud для Raspberry Pi. У Nextcloud есть большое дружное сообщество разработчиков, а у CusDeb есть только наша маленькая сплоченная команда, – если мы не будем разрабатывать CusDeb, то никто другой не будет. Таким образом, выбор был сделан в пользу конструктора. Тем не менее, мы внесли на прощание небольшой вклад в разработку Nextcloud и отправились в Берлин на Nextcloud Hackweek, где в течение недели работали над новыми возможностями Nextcloud Server и Nextcloud Talk. На этом мы поставили точку в работе над Nextcloud и сфокусировались на CusDeb.

 

Nextcloud community, group photo

В конце лета CusDeb нашел бизнес-ангела в лице Игоря Вульфович. Это событие придало новые силы и мы с удвоенной энергией взялись за конструктор. В это время к команде присоединилась Полина Попова и сразу решила проблему с переключением языка на сайте, а также другие проблемы с локализацией.

Акселерация в Южном IT-парке, Mender, тестирование образов онлайн

В начале сентября стартовала акселерационная программа в Южном IT-парке, где мы под надзором опытного Богдана Одарченко продолжили свой путь в продуктостроении. С этого момента события начали происходить с утроенной скоростью.

Мы рассказали об онлайн конструкторе редактору журнала MagPi и вскоре он опубликовал заметку о CusDeb, которая привлекла внимание разработчиков Mender. Mender – это менеджер обновлений для Интернета вещей с открытым исходным кодом. Эйстейн (Eystein) и Ралф (Ralph), разработчики Mender, связались с нами и предложили вместе интегрировать Mender в CusDeb. Задача оказалась не из легких, но в итоге мы справились. Теперь пользователи CusDeb могут генерировать Mender-совместимые образы с поддержкой обновлений по воздуху (OTA software updates), а также сами обновления в виде Mender-артефактов.

В начале ноября к команде присоединились Даниил Молотков и Денис Гаврилюк, студенты Ростовского-на-Дону колледжа связи и информатики. Ребята выбрали CusDeb для прохождения преддипломной практики. Даниил и Денис быстро разобрались в устройстве конструктора и взялись за реализацию функции тестирования образов онлайн. В конце декабря, спустя месяц усердной работы и поддержке WIS Software, ребята запустили новую фичу. Теперь пользователям CusDeb не обязательно тестировать свои сборки на реальном железе – на панели управления появилась кнопка «Запуск в эмуляторе», при нажатии на которую на сервере запускается QEMU для эмуляции Raspberry Pi и образ на базе Ubuntu, а в браузере открывается эмулятор терминала, через который возможно работать с запущенным в эмуляторе образом. Вот короткое видео, как это работает:

Сейчас тестирование в эмуляторе доступно для сборок на основе Ubuntu 18.04 «Bionic Beaver» (64-bit) под Raspberry Pi 3. Даниил и Денис продолжат работу над этой функцией в рамках своего дипломного проекта в 2019 году.

В конце года к команде присоединился Евгений Пятибратов. Он увлекается дизайном и разработкой веб-интерфейсов, а еще заканчивает написание книги о React. Евгений взялся переделать интерфейс CusDeb, чтобы сделать процесс создания образов в конструкторе удобным и интуитивно понятным для пользователей.

По итогам года жюри конкурса «Лучший ИТ-проект Ростовской области» присудили нам третье место в номинации «Лучший веб-портал или веб-сайт с интернет-сервисами» и вручили награду от Министерства связи и информатики Ростовской области. Мы благодарны Южному IT-парку и Министерству связи и информатики Ростовской области за поддержку и доверие. Призовые деньги также были отложены на поездку на Embedded Linux Conference 2019.

С наступающим новым годом,
команда разработчиков CusDeb.

Raspbian: сборка образа. Часть 1

Автор: Евгений Голышев

Статья была опубликована в 235-м выпуске журнала Linux Format. По истечении двух месяцев с момента публикации статьи материал публикуется в нашем блоге.

Вместе с Raspberry Pi (он же – RPi) и другими одноплатными компьютерами развиваются инструменты кастомизации образов для этих устройств. Отличными примерами являются rpi23‐gen-image и pi-gen. Для своего про­екта выходного дня, Pieman, я проделал т.н. Customer Development, чтобы понять, почему люди предпочитают собирать образы самостоятельно вместо использования уже готовых. Оказалось, что многие из тех, с кем мне удалось пообщаться, убеждены, что сборка минимально функциональной операционной системы под конкретную задачу будет работать быстрее, расходовать меньше ресурсов и даже поможет продлить срок жизни SD-карты. С этим сложно поспорить, т.к. чем больше запущено программ, тем

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

Также многие опрошенные указывают на то, что, приблизившись вплотную к сборке образов, вы получаете отличную возможность прокачать себя в Linux. В конце концов, одноплатники воскрешают очарование, испытанное при первом знакомстве с компьютером. Именно тому, что происходит под капотом таких инструментов, как rpi23‐gen-image и pi-gen, я хочу посвятить большую часть нашего урока.

Условимся, что сборка будет происходить на машине x86. Иначе процесс сборки хоть и незначительно, но упростится, оставив несколько интересных тем за бортом.

Всё описанное в этой статье было протестировано в Debian Stretch и Fedora 28.

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

Немного теории

На SD-карте, с которой будет осуществляться загрузка одноплатника, должно быть как минимум два раздела, где первый используется в качестве загрузочного, а второй – для хранения корневой файловой системы. На первом разделе должна использоваться FAT32, а на втором – любая POSIX-совместимая файловая система, которая удовлетворяет заданным условиям и личным предпочтениям. При включении RPi запускается первая ступень загрузки. На этом этапе загрузочный раздел монтируется загрузчиком, находящимся где-то в недрах SoC [System on a Chip, система на кристалле] BCM2836. Стоит отметить, что этот загрузчик закладывается еще на этапе производства и не может быть ни изменен, ни заменен. Затем за дело берется специальное ядро на графическом процессоре RPi, и загружает файл bootcode.bin с загрузочного раздела в L2‐кэш. Таким образом запускается вторая ступень загрузки. (Может показаться немного странным, что работа RPi начинается с графиче­ского, а не центрального процессора, но так уж устроен SoC BCM2836.) На этом этапе, опуская лишние подробности, загружается прошивка графиче­ского процессора start.elf, которая позволяет запустить kernel7.img и передать управление центральному процессору. kernel7.img может быть как образом ядра Linux, так и программой, специально написанной для RPi для запуска на «голом железе». start.elf использует config.txt для хранения параметров, которые передаются kernel7.img при запуске.

Этот процесс в том или ином виде уже был описан в различных книгах и статьях, которые еще на протяжении долгого времени будут с нами. Дело в том, что сейчас этот процесс незначи­тельно отличается от первоначального, но чтение руководств, которые содержат устаревшие сведения по этапам загрузки RPi, может натолкнуть на мысль, что в данную статью закралась ошибка. Таким образом, стоит отдельно сказать, что до 19‐го октября 2012 г. прошивка RPi включала файл loader.bin, который загружался между bootcode.bin и start.elf и запускал третий этап загрузки. С тех пор этот файл больше не требуется, и загрузка одноплатника ста­ла двухуровневой.

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

Подготовка образа SD-карты

 Cоздадим образ SD-карты размером 8 ГБ:

$ dd if=/dev/zero of=raspbian-stretch.img bs=1024 seek=$(( 1024 * 1024 * 8 )) count=1

В данном примере dd пропускает 8 миллионов блоков размером 1 КБ, а затем заполняет 1 КБ нулями. В результате получится то, что называют разреженным файлом [sparse file]. Этот подход позволяет отводить место только тогда, когда это действительно необходимо. Тогда пустой образ SD-карты размером 8 ГБ по факту будет занимать минимально возможное пространство на диске, т.е. размер блока файловой системы (4 КБ, как правило).

Затем необходимо создать таблицу разделов на будущей SD-карте и разбить ее на два раздела. Linux поддерживает несколько таблиц разделов, но исторически сложилось, что по умолчанию используется MS-DOS. Ее основной характеристикой является поддержка 4‐х первичных разделов, но если по какой-то причине этого количе­ства окажется недостаточно, один из этих 4‐х разделов можно сделать расширенным. Расширенный раздел может содержать до 12 логических. Таким образом, в первом случае мы получаем в свое распоряжение до 4 разделов, а во втором – до 15 (3 + 12, не считая расширенного, т.к. он является всего лишь «контейнером» для логических). Эти разделы в равной степени могут использоваться для хранения как данных, так и области подкачки.

Создадим таблицу разделов и разобьем образ SD-карты на два раздела:

$ sudo parted raspbian-stretch.img mktable msdos
$ sudo parted raspbian-stretch.img mkpart p fat32 4MiB 54MiB
$ sudo parted -s raspbian-stretch.img -- mkpart primary ext2 58MiB -1s

Как уже говорилось выше, эти два раздела являются необходимыми. Остальные разделы (для /home, области подкачки [swap] и пр.) могут быть созданы при желании, как и на любой другой машине под управлением GNU/Linux.

В последнем случае -1s использовалось в качестве индикатора последнего сектора и позволило сказать parted, что необходимо создать раздел, начиная с 58‐го МБ (c 118784‐го сектора) и заканчивая последним сектором на диске. Однако этот индикатор выглядит с точки зрения parted как опция, так что в этой командной строке, в отличие от предыдущей, использовалось --. Иначе программа завершилась бы, выбросив «parted: invalid option – '1'».

В данном примере я создал загрузочный раздел размером 50 МБ, выровняв его по границе 4 МБ. Что касается размера этого раздела, то к нему не предъявляется жестких требований, однако его необходимо сделать таким, чтобы в него поместились образ ядра, DTB-файлы, о которых речь пойдет в разделе «Заполнение загрузочного раздела», и описанные в начале статьи двоичные фрагменты-блобы. Лично мне показалось, что полсотни мегабайт должно хватить с головой, но при желании к этому вопросу можно подойти более педантично. Что касается выравнивания, то все разделы SD-карты рекомендуется выравнивать по границе 4 МБ. Стоит заметить, что пренебрежение этой рекомендацией может привести к падению производительности операций ввода-вывода. За подробностями рекомендую обратиться к статье Арнда Бергманна [Arnd Bergmann] «Optimizing Linux with cheap flash drives».

Заключительным этапом подготовки образа SD-карты станет форматирование разделов. Дело в том, что mkpart только устанавливает идентификатор типа файловой системы, но не форматирует разделы. Эти идентификаторы затем используются другими программами для сообщения пользователю, что собой представляет тот или иной раздел. В качестве идентификатора типа файловой системы для первого раздела использовалась FAT32, а для второго – ext2. Теперь выполним

$ /sbin/fdisk -lu raspbian-stretch.img

чтобы увидеть, что в конце концов получилось. Вывод fdisk будет достаточно информативным. Сначала убедитесь, что каждый раздел находится на своем месте и занимает указанное количество блоков, а затем обратите внимание на то, что первый помечен как W95 FAT32 (LBA), а второй – Linux. Стоит отдельно отметить, что для ext2, ext3, ext4 и большинства других файловых систем, которые считаются для Linux родными, используется один и тот же идентификатор 0x83. Таким образом, часто можно встретить примеры, когда в качестве типа файловой системы указывается ext2, но на деле используется ext4 или что-либо еще.

fdisk предлагает наглядную картину структуры будущего образа.

Чтобы начать форматирование разделов образа, их сначала необходимо подготовить. Для этого предлагаю воспользоваться программой losetup, которая прочитает таблицу разделов образа, ассоциирует одно из устройств обратной связи [loop device] с целым образом и, наконец, создаст виртуальные блочные устройства для каждого раздела. После этого каждый раздел может быть

отформатирован посредством любой программы из семейства mkfs.*. К примеру, команда

$ LOOP_DEV=$(sudo losetup --partscan --show --find raspbian-stretch.img)

создаст два блочных устройства – ${LOOP_DEV}p1 и ${LOOP_DEV}p2, соответствующие загрузочному и корневому разделу соответственно. Теперь их можно отформатировать следующим образом:

$ sudo mkfs.vfat ${LOOP_DEV}p1
$ sudo mkfs.ext4 ${LOOP_DEV}p2

и перейти к начинке для них.

debootstrap устанавливает базовую систему Debian в указанную директорию.

Подготовка chroot-окружения

debootstrap устанавливает базовую систему Debian в указанную директорию и позволяет формировать chroot-окружение на основе указанного выпуска Debian, Ubuntu или любого другого Debian-подобного дистрибутива. Посредством одного из параметров можно указать адрес репозитория дистрибутива, поэтому debootstrap не ограничивается дистрибутивами Debian и Ubuntu, позволяя строить chroot-окружения на основе Devuan, Raspbian и пр. Таким образом, в простейшем случае программе необходимо передать следующие параметры.

  • Кодовое имя дистрибутива (codename) или имя его статуса (status name). В каче­стве кодового имени могут быть использованы, к примеру, jessie, stretch, buster или sid (возможно использование кодовых имен не только Debian, но и Ubuntu), а в качестве имени статуса – соответствующие вышеприведенным кодовым именам oldstable, stable, testing и unstable.
  • Директория, которая будет играть роль корня будущего chroot-окружения.
  • (опционально) Адрес репозитория, который будет использоваться в каче­стве источника двоичных пакетов.

К примеру, $ sudo debootstrap stretch stretch создаст chroot-окружение на базе Debian Stretch (первый параметр), корнем которого будет директория stretch (второй параметр). В данном случае источником двоичных пакетов формально будет https://deb.debian.org/debian, а на деле – одно из ближайших к пользователю зеркал.

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

  1. Сообщить debootstrap’у, что формирование chroot-окружения должно производиться на основе Raspbian. Для этого в качестве третьего параметра команды нужно указать адрес репозитория дистрибутива – http://archive.raspberrypi.org/debian, а также целевую архитектуру – armhf (32‐битная архитектура ARM с аппаратной поддержкой операций с плавающей запятой).

  2. Посредством опции --foreign разделить процесс подготовки chroot-окружения на две ступени. Иначе debootstrap упадет на этапе конфигурирования пакетов, т. к. целевая архитектура отличается от архитектуры хоста (т. е. машины, на которой выполняется команда). Дело в том, что этот этап требует вовлечения низкоуровневого пакетного менеджера dpkg и других программ из самого chroot-окружения, которые собраны под архитектуру, отличную от x86. Разделение процесса сборки chroot-окружения на две ступени позволяет сначала довести до конца всё то, что можно сделать средствами хоста, отложив все этапы, которые требуют запуска различных программ и скриптов из самого chroot-окружения. Таким образом, появляется возможность втиснуть между запусками первой и второй ступени добавление двоичного эмулятора, чтобы дать всем программам из chroot-окружения шанс выполниться на процессоре хоста.

  3. Посредством опции --keyring передать debootstrap’у связку ключей для проверки подписей. debootstrap по-взрослому относится к работе с двоичными пакетами, что предполагает проверку их цифровых подписей. Если команда выполняется в любой системе, отличной от Raspbian, то debootstrap упадет на этапе получения списка пакетов, т.к. не сможет найти публичный ключ, закрытым ключом которого был подписан этот список.

  4. Посредством опции --variant=minbase сообщить debootstrap’у, что вам необходимо минимально возможное chroot-окружение. Из всех перечисленных опций эта является наименее критичной, но она позволяет получить минимальную систему, которая гарантированно не будет содержать ничего лишнего. Установка всего необходимого вручную позволит приблизиться к пониманию того, как устроена система.

Первая ступень только загружает и распаковывает пакеты.

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

$ curl http://archive.raspbian.org/raspbian.public.key -O

Файл raspberrypi.gpg.key представляет собой публичный ключ в ASCII-совместимом формате [ASCII-armored format], который можно, например, хранить в Git-репозитории и распространять вместе со скриптом, собирающим образы.

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

$ gpg --no-default-keyring --keyring=$(pwd)/keyring.gpg --import raspbian.public.key

В текущей директории появится файл keyring.gpg. Теперь есть всё необходимое для того, чтобы запустить первую ступень подготовки chroot-окружения на основе стабильного выпуска Raspbian.

Запустите debootstrap в той же директории, в которой выполнялась gpg, следующим образом:

$ sudo debootstrap --arch=armhf --foreign --keyring=$(pwd)/keyring.gpg --variant=minbase stretch stretch http://archive.raspbian.org/raspbian

В обоих случаях значением опции –keyring должен быть полный путь к связке ключей, поэтому использовалась запись $(pwd)/keyring.gpg, которая раскрывается в полный путь.

Перед запуском второй ступени необходимо подготовить двоичные файлы средства эмуляции в режиме пользователя [user mode emulation binaries], чтобы программы, собранные под архитектуру ARM из chroot-окружения, могли выполняться на процессоре хоста x86. Как в производных от Debian дистрибутивах, так и в Fedora эти бинарники содержатся в пакете qemu-user-static. Модуль ядра binfmt_misc, доступный в Linux, начиная с версии 2.1.43 (которая, кстати, вышла в июне далекого теперь 1997 г.), позволяет распознавать различные форматы исполняемых файлов и ассоциировать их с произвольными приложениями. Другими словами, для определенного формата исполняемого файла можно зарегистрировать эмулятор и при каждой попытке запустить исполняемый файл, который имеет этот формат, передавать его эмулятору, а не запускать на текущем процессоре. В производных от Debian дистрибутивах также потребуется установить пакет binfmt-support, в который вынесена функция регистрации эмуляторов.

После установки qemu-user-static в Debian и Fedora и binfmt-support – только в Debian необходимо будет скопировать двоичный файл средства эмуляции в chroot-окружение и запустить вторую ступень.

$ sudo cp /usr/bin/qemu-arm-static stretch/usr/bin
$ sudo chroot stretch /debootstrap/debootstrap --second-stage

Вторая ступень запускает для установки пакетов dpkg и другие программы из chroot-окружения.

Несмотря на то, что в качестве источника двоичных пакетов был указан http://archive.raspbian.org/raspbian, вместо него в /etc/apt/sources.list будет фигурировать http://deb.debian.org/debian.

Это следует исправить:

$ sudo sh -c "echo deb http://archive.raspbian.org/raspbian stretch main > stretch/etc/apt/sources.list"

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

$ sudo chroot stretch passwd

Установка ядра

В предыдущем разделе мы создали chroot-окружение, всё множество пакетов которого называется в терминологии Debian базовой системой (см. подробнее раздел 3.7 руководства Debian Policy). Ядро не входит в это множество, т. к. для функционирования системы, как бы это ни было странно, ядра не требуется. В этом можно убедиться, выполнив, к примеру,

$ sudo chroot stretch bash
# ls

Не имея собственного ядра, chroot-окружение может использовать возможности хостового. (По тому же принципу, но с бóльшим уровнем изоляции, работают Docker-контейнеры и другие средства виртуализации на уровне операционной системы.) Но для того, чтобы это chroot-окружение вышло за рамки своих скромных возможностей и превратилось в полноценную систему, в него необходимо установить ядро.

Пакет с ядром Raspbian находится в репозитории http://archive.raspberrypi.org/debian/. Его адрес необходимо добавить в /etc/apt/sources.list, а его публичный ключ – в список доверенных ключей.

$ sudo sh -c "echo deb http://archive.raspberrypi.org/debian stretch main >> stretch/etc/apt/sources.list"
$ curl http://archive.raspberrypi.org/debian/raspberrypi.gpg.key -O
$ sudo cp raspberrypi.gpg.key stretch
$ sudo chroot stretch apt-key add raspberrypi.gpg.key
$ sudo rm stretch/raspberrypi.gpg.key

Теперь обновите индексы и установите пакет с ядром:

$ sudo chroot stretch apt-get update
$ sudo chroot stretch apt-get install raspberrypi-kernel

Заполнение корневого раздела

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

Всё, что сейчас требуется – это смонтировать корневой раздел ${LOOP_DEV}p2 и скопировать на него всё содержимое chroot-окружения.

$ sudo mount ${LOOP_DEV}p2 /mnt
$ sudo rsync -apS stretch/ /mnt
$ sudo umount /mnt

Заполнение загрузочного раздела

Linux-подобные операционные системы на машинах x86, как правило, полагаются на директорию /boot – именно там загрузчик ищет двоичный файл ядра. Но на RPi и других одноплатниках эта директория будет формальностью. Как было сказано выше, бинарник ядра должен быть расположен на загрузочном разделе, который монтируется при старте машины. Таким образом, первым делом необходимо смонтировать загрузочный раздел и скопировать на него ядро, а затем создать файл с командной строкой ядра.

$ sudo mount ${LOOP_DEV}p1 /mnt
$ sudo cp stretch/boot/kernel7.img /mnt
$ sudo sh -c "echo console=serial0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 rw rootwait init=/bin/systemd > /mnt/cmdline.txt"

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

$ sudo cp stretch/boot/*.dtb /mnt

Наконец, надо загрузить блобы и поместить их туда же.

$ cd /mnt
$ export BOOT_ADDR=https://github.com/raspberrypi/firmware/raw/master/boot
$ sudo curl $BOOT_ADDR/bootcode.bin -OL
$ sudo curl $BOOT_ADDR/start.elf -OL

Заключение

Для записи полученного образа на SD-карту я настоятельно рекомендую использовать программу Etcher.

На нашем уроке в основном использовался стандартный инструментарий, который должен быть в каждой Linux-подобной операционной системе. Я попытался подробно рассказать о том, как можно с его помощью собрать минимально функциональную систему, которая, тем не менее, будет способна загрузиться на абсолютно любой модели Raspberry Pi. И хотя от такой системы в ее нынешнем виде сейчас мало толку, ее сборка должна была приоткрыть завесу тайны над тем, как работают некоторые инструменты, которые лежат в основе rpi23‐gen-image, pi-gen и даже инсталляторов Debian и Ubuntu. Несмотря на то, что эту систему можно расширить необходимыми для решения конкретной задачи пакетами, она всё еще является привязанной к Raspbian’овскому ядру, в ней отсутствует поддержка сети и т.д. Этим и другим темам будет посвящена вторая часть данного учебника.

Nextcloud: не только хранилище

Автор: Евгений Голышев

Статья была опубликована в 233-м выпуске журнала Linux Format. По истечении двух месяцев с момента публикации статьи материал публикуется в нашем блоге.

Nextcloud – это платформа, которая позволяет развернуть на своих вычислительных мощностях альтернативу сразу нескольким коммерческим сервисам. В арсенале этой платформы имеются средства для синхронизации контактов, календарей, списков дел и SMS между различными устройствами, а также голосовые и видеозвонки. Однако Nextcloud – это в первую очередь платформа для создания Dropbox-подобного хранилища на своем собственном сервере. В данной статье я хочу рассказать о том, что предлагает Nextcloud 13, и от каких публичных сервисов можно отказаться прямо сейчас; но для начала немного истории.

Сначала был ownCloud

Nextcloud начинался как проект под названием ownCloud и сейчас является его логическим продолжением. ownCloud был анонсирован на конференции Camp KDE 2010 и стартовал в рамках инициативы KDE Social Desktop (о которой подробно рассказыва-
лось в LXF122). Проект задумывался как свободный аналог закрытых решений, самыми известными из которых на тот момент были Dropbox и Ubuntu One.

Первая стабильная версия платформы со знаковым номером 1.0 вышла 24-го июня 2010-го года – через полгода после анонса проекта. Еще примерно через полгода в США была зарегистрирована компания ownCloud Inc. с целью упрощения привлечения инвестиций и финансирования ключевых разработчиков проекта. Это событие ознаменовало превращение ownCloud в полностью самостоятельный проект.

Однако выбранная компанией бизнес-модель, предполагавшая наличие двух версий платформы – Community Edition и Enterprise Edition, в основном требовала концентрации на коммерче­ской версии платформы Enterprise Edition, с целью получения максимальной прибыли: это задача любого бизнеса, и дело тут не в цинизме. Разработчики испытывали сильный дискомфорт, поскольку сложно было уделять внимание одной версии без ущерба для другой. И 2 июня 2016 г. ключевые разработчики проекта приняли решение форкнуть ownCloud с целью его альтернативного развития. Новый проект, получивший имя Nextcloud, вобрал в себя все коммерче­ские функции, ранее доступные в ownCloud Enterprise Edition. Более того, вскоре после форка была создана компания Nextcloud GmbH, с целью предоставления коммерче­ской техниче­ской поддержки всем тем, кто использует платформу на производстве или создает собственные продукты на базе Nextcloud. Таким образом, разработчикам теперь не надо разрываться между двумя версиями платформы, а текущая бизнес-модель компании стала ближе по духу к философии свободного программного обеспечения.

Почему Nextcloud популярен

Не секрет, что сейчас Nextcloud является одним из популярнейших представителей серверного программного обеспечения. Тем не менее, когда разработчики ownCloud только пришли на рынок со своим продуктом, на первых порах он не был востребован широким кругом пользователей, но немного позднее произошли несколько независимых друг от друга событий, которые сильно подогрели интерес сообщества сначала к ownCloud, а затем и к Nextcloud. Вот, на мой взгляд, наиболее значимые из них.

  • Откровения Эдварда Сноудена в 2013-м году заставили многих задуматься о том, что необходимо полностью или частично ограничить себя в использовании публичных Интернет-сервисов.
  • Одноплатный компьютер Raspberry Pi, появившийся в 2012-м году, породил целую волну достаточно мощных устройств, которые могут использоваться в качестве сервера и в буквальном смысле доступны каждому. К примеру, сегодня легко можно найти 50-долларовый одноплатник с 4-х ядерным процессором, 2 ГБ ОЗУ и нативной поддержкой SATA. Этого будет вполне достаточно для того, чтобы построить на базе Nextcloud полноценный NAS и обслуживать десяток пользователей.
  • Некоммерческий удостоверяющий центр Let’s Encrypt в конце 2015-го года начал выдавать TLS-сертификаты всем желающим.

Первое событие стало отличным источником мотивации, а последние два в несколько раз снизили затраты на приобретение и сопровождение домашнего сервера. Несмотря на то, что по очевидным причинами решение на базе Raspberry Pi и Nextcloud является медленным и ненадежным, оно очень популярно в силу своей простоты.

Установка Nextcloud

Пришло время установить Nextcloud и начать обзор его возможностей. Возможности платформы – это основная тема статьи, а установка является скорее ее побочной частью. Таким образом, для того чтобы не погружаться в установку сильно глубоко, я предлагаю развернуть Nextcloud посредством Docker. Для этих целей я специально подготовил проект MMB, который поможет развернуть платформу на машине на базе 32-битного процессора ARM (Raspberry Pi, Orange Pi, Banana Pi и т.д.) или 64-битного процессора x86 (рабочие станции и серверы). Я уже описывал установку Nextcloud при помощи MMB в одной из предыдущих статей.

Apps: модульная природа Nextcloud

Сначала ownCloud задумывался как свободный аналог Dropbox и Ubuntu One, но 30-го января 2012-го года вышел ownCloud 3.0 с поддержкой так называемых приложений (apps), которые существенно расширяли базовый функционал платформы, превращая ее в Dropbox-подобное хранилище на стероидах. С тех пор было выпущено большое количество как официальных, так и сторонних приложений. Сейчас, такие функции Nextcloud, как голосовые и видеозвонки, синхронизации контактов, календарей, списков дел и SMS между различными устройствами, а также много других возможностей, реализованы именно в виде приложений. Казалось бы, что может быть проще, чем открыть PDF-файл, не покидая веб-интерфейс платформы. Но даже такая функция не предусмотрена базовой версий Nextcloud и реализована в виде отдельного приложения.
Nextcloud делит приложения на те, которые сразу доступны после установки, и на те, которые необходимо предварительно активировать. Так, сразу после установки Nextcloud и создания учетной записи администратора, такие приложения, как галерея, видеопроигрыватель и просмотрщик документов в формате PDF, будут доступны сразу. Дело в том, что эти приложения считаются относительно простыми, т.к. не требуют изменения базы данных, и могут быть молча активированы без видимых последствий для всей платформы. Однако, такие приложения, как Calendar и Contacts (синхронизация календарей и контактов между различными устройствами), требуют внесения изменений в базу данных, что уже является чем-то серьезным. Активация таких приложений должна быть инициирована администратором платформы. Все приложения, которым будет посвящена оставшаяся часть статьи потребуют активации после установки Nextcloud, поэтому кликните на том месте, где должна быть аватарка (в правом верхнем углу экрана), и в появившемся меню выберите Приложения (Apps). Затем в списке Ваши приложения (Your apps) найдите Calendar, Contacts, Notes, Phone Sync и Talk и нажмите напротив каждого из них Включить (Enable). А теперь я предлагаю перейти к обзору основных возможности Nextcloud – синхронизация файлов между различными устройствами, а затем рассмотреть каждое из этих приложений по отдельности.

File Sync

Nextcloud – это в первую очередь Dropbox-подобное хранилище, поэтому его основные задачи – организация приватного или общего доступа к файлам, синхронизация их между различными устройствами и т.д. Доступ к файлам может быть организован как через веб-интерфейс, так и через клиенты. Официальные клиенты доступны для всех популярных мобильных и настольных операционных систем. Более того, Nextcloud предоставляет доступ к файлам по протоколу WebDAV, поэтому для работы с хранилищем достаточно возможностей штатного файлового менеджера. WebDAV (Web-based Distributed Authoring and Versioning) представляет собой расширение протокола HTTP. Его основной задачей является предоставление клиентам возможности монтирования разделяемых ресурсов (shares) поверх HTTP так, чтобы их поведение принципиально не отличалось от таких сетевых файловых систем, как NFS или SMB. Однако стоит заметить, что по сравнению с файловыми менеджерами со встроенной поддержкой WebDAV официальные клиенты предлагают расширенные возможности работы с хранилищем. Поэтому я настоятельно рекомендую установить клиенты Nextcloud на все свои устройства для удобства работы с платформой.

Calendar и Contacts

Приложение Calendar не уступает конкуренту от Google.

Синхронизация календарей и контактов между различными устройствами являются одной из самых востребованных возможностей после синхронизации файлов. Как ни странно, Google давно преуспел в решении этой задачи, а Nextcloud предлагает в лице приложений Calendar и Contacts альтернативу известному сервису. Эти приложения организуют доступ к календарям и контактам по протоколам CalDAV и CardDAV, которые являются расширениями WebDAV (см. выше). Существует достаточно много клиентов, поддерживающих эти протоколы, но среди них нет официального. Тем не менее, разработчики Nextcloud рекомендуют использовать свободный клиент под названием DAVdroid, поддерживающий сразу оба протокола. DAVdroid позволяет синхронизировать штатные календарь и записную книжку в операционной системе с сервером. При этом синхронизация является двухсторонней, т.е., например, любое изменение на сервере приводит к изменению в штатном календаре, и наоборот.
Для того чтобы начать синхронизации, сначала установите на мобильное устройство DAVdroid и официальный клиент Nextcloud. Затем запустите последний, авторизуйтесь, перейдите в настройки приложения и выберете в меню «Синхронизировать календарь и контакты» (Sync calendar & contacts). После этого запустится DAVdroid и вам потребуется ввести только пароль от учетной записи Nextcloud, т.к. все остальные параметры для подключения к DAV-серверу будут предзаполнены. На первых порах DAVdroid будет задавать много вопросов. Он спросит, нужно ли отключить оптимизацию батареи, предложит установить приложение OpenTask для синхронизации ко всему прочему еще и списка задач, потребует разрешение на доступ к штатному календарю и записной книжке. Отнеситесь к этому с пониманием. После того как настройка будет закончена, вы сможете указывать в DAVdroid, какие из существующих календарей синхронизировать с сервером, а также создавать новые. К этому моменту DAVdroid установлен и настроен, поэтому не составит никакого труда заставить его синхронизировать штатную записную книжку с сервером.

Phone Sync

Приложение Phone Sync – единая точка сбора SMS со всех ваших устройств.

Примечательно, что Google, будучи разработчиком Android, не предлагает решение доступное прямо «из коробки» в своей операционной системе для синхронизации SMS с удаленным сервером. Очевидно, что это достаточно редкая необходимость, востребованная в узких кругах. Тем не менее, эта задача решается достаточно изящно посредством SMS Backup+ – свободного приложения под Android, которое позволяет синхронизировать SMS с gmail’овским ящиком. Однако, несмотря на то, что решение выглядит красиво, в качестве хранилища используется неподконтрольный пользователю публичный сервис, поэтому я расскажу об альтернативном подходе, который предлагает Nextcloud. Для этих целей в арсенале платформы есть официальное приложение и мобильный клиент под названием Nextcloud SMS. Клиент ставится на все мобильные устройства, которые требуется синхронизировать с сервером, но к счастью или сожалению, синхронизация SMS с сервером не является двусторонней, как например, в случае синхронизации календарей и записной книжки. Таким образом, каждое новое сообщение, приходящее на устройство, синхронизируется с сервером, но удаление этого сообщения на сервере, не приводит к его удалению на устройстве.

Notes

Приложение Notes отлично дополняет арсенал платформы функ­цией ведения
заметок.

Возможность ведения заметок в Nextcloud присутствует, но по своим возможностям не дотягивает ни до Google Keeps, ни тем более до Evernote. Однако, функция реализована в платформе настолько просто и красиво, что мало кого оставит равнодушным. Дело в том, что каждая заметка представляет собой обычный текстовый файл, а приложение и официальный мобильный клиент Nextcloud Notes являются простыми редакторами этих файлов. Таким образом, заметки автоматически получают в свое распоряжение все возможности платформы: шифрование, общий доступ, версионирование и т.д. При этом для пользователя все максимально прозрачно, т.е. он работает с сущностью «заметка», а не «файл».

Talk

Приложение Talk претендует на звание лучшей альтернативы Skype и Hangouts.

Возможность совершения голосовых и видеозвонков уже существует в Nextcloud достаточно давно, но она была признана готовой только в 13-й версии платформы, и теперь известна всему миру под именем Nextcloud Talk. В арсенале платформы есть как официальное приложение, так и клиенты под Android и iOS. Стратегической целью разработчиков является обеспечение паритета в функциональности с такими известными в этой области решениями, как Skype и Hangouts. Однако, на данный момент возможности мобильных клиентов немного отстают от того, что предлагает приложение, ограничиваясь только голосовыми и видеозвонками. Тем не менее, платформа предлагает следующие возможности:

  • end-to-end шифрование. Даже в случае компрометации сервера данные не поддаются анализу, т.к. шифруются на стороне клиента.
  • Возможность организации групповых аудио и видеоконференций.
  • Возможность приглашения участников, у которых нет учетных записей в системе.
  • Возможность предоставления доступа к содержимому экрана.

К особенностям платформы можно отнести организацию канала связи при помощи WebRTC и кодирование звука и видео с помощью H.265.
Для использования Nextcloud Talk в локальной сети не требуется никакой настройки, однако от него в этом случае будет мало толка. Для полноценного общения через Интернет понадобятся STUN- и TURN-сервер. STUN (Session Traversal Utilities for NAT) позволяет установить прямое соединение между двумя и более участниками, находящимися за NAT. Официальное приложение по умолчанию использует для этих целей stun.nextcloud.com:443, поэтому в ряде случаев от администратора Nextcloud не требуется никаких действий. Тем не менее, STUN-сервера может оказаться недостаточно, и первым сигналом этому будет черный экран при установлении соединения между участниками. Дело в том, что STUN-сервер бессилен, если участники находятся за так называемым симметричным NAT. В этом случае на помощь приходит TURN-сервер. TURN (Traversal Using Relay NAT) расширяет возможности STUN, позволяя установить прямое соединение между двумя и более участниками, которые находятся за симметричным NAT. Возможно в недалеком будущем ситуация изменится, но сейчас TURN-север необходимо поднимать для своих нужд самостоятельно. Для этих целей я настоятельно рекомендую использовать coturn. Для того чтобы собрать и запустить свежую версию coturn, я предлагаю снова обратиться к MMB.

Заключение

Nextcloud имеет богатую историю и такой же богатый список возможностей, и я думаю, что у меня получилось уделить достаточное количество внимания как первому, так и второму. Основная мысль, которую я хотел выразить в этой статье, заключается в том, что Nextcloud способен решать широкий спектр различных задач, которые выходят далеко за рамки хранилища файлов в стиле Dropbox. Таким образом, если вы обзаведетесь сервером на базе Nextcloud, перед вами откроются большие возможности, которые будут расти от релиза к релизу платформы. Основной недостаток и главное преимущество Nextcloud являются двумя сторонами одной медали: для того чтобы ваш экземпляр платформы всегда оставался безопасным и находился в актуальном состоянии, вам необходимо самостоятельно следить за выходом обновлений и своевременно их устанавливать, не забывая перед этим делать резервные копии базы данных и т.д., но это необходимое условие для того, чтобы всегда держать все свои данные под контролем. Удачного хакинга и не забывайте, что с великой силой приходит великая ответственность.

OS DAY 2018

В июне 2017 я впервые узнал о ежегодной конференции разработчиков операционных систем OS DAY. В марте 2018 обновилась информация на официальной странице мероприятия https://osday.ru и стартовал приём заявок на доклады по вопросам обеспечения надёжности программно-аппаратных систем на этапах проектирования, разработки, эксплуатации и сопровождения. Мы с Женей зарегистрировались в качестве участников, решили поехать поучиться у коллег. Конечно хотелось заявиться с презентацией и рассказать о CusDeb, но, сказать по правде, я постеснялся.

Зато Женя не постеснялся написать разработчикам Debian, поинтересовался не собирается ли кто из них посетить OS DAY 2018, предложил бух…..поговорить о разработке Debian и подписать ключи. На предложение откликнулись Сергей и Дмитрий. Сергей, сказал, что у него не будет времени на конференцию, но он готов встретиться, чтобы подписать ключи. Дмитрий, ознакомившись с программой конференции, зарегистрировался.

17 мая в 9 утра мы были в главном здании академии наук в Москве. Вход по паспортам, нас проверяли по спискам, всё строго. Докладчики выступали в зелёном зале на третьем этаже, свободных мест не было, собралось более 200 участников. В перерывах мы рассматривали стенды разработчиков отечественных операционных систем. На мой взгляд, самый живой стенд получился у ребят из Базальт СПО. Несколько железяк под управлением base ALT отлично транслировали месседж — “Платформа base ALT работает на отечественном железе”.

На обеде познакомились с Дмитрием Шечневым, и я впервые в жизни увидел, как происходит подписание GnuPG ключей у разработчиков Debian. Согласно правилам Женя передал Диме отпечаток (fingerprint) ключа и длину ключа, распечатанные на бумаге, затем показал паспорт. Дима проверил паспорт и убедившись, что Женя является тем, за кого себя выдаёт, загрузил ключ по отпечатку с сервера публичных ключей, подписал его и отправил на этот же сервер.

Дмитрий Шечнев и Евгений Голышев
Дмитрий Шечнев и Евгений Голышев

После обеда подошли к Дмитрию Завалишину, чтобы сфотографироваться с легендой. Рассказали ему коротко о нашем конструкторе операционных систем для одноплатных компьютеров, сказали, что постеснялись отправить заявку на доклад. Оказалось, у него дома валяется Raspberry Pi, и он посоветовал нам прислать заявку на доклад через год на OS DAY 2019.

Евгений Голышев, Дмитрий Завалишин, Денис Мосолов
Евгений Голышев, Дмитрий Завалишин и Денис Мосолов

А вот цитаты из докладов первого дня, которые я записал в блокнот:

Не нужно распространять ПО в контейнерах, это небезопасно. Просто задумайтесь, кто будет обновлять операционную систему внутри изолированного окружения?
Дмитрий Державин, о том, кто отвечает за обновление ОС внутри Docker-контейнера.

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

Второй день конференции получился более дискуссионным, чем первый. Я узнал, что в России есть федеральная служба по техническому и экспортному контролю (ФСТЭК России). Эта служба утвердила требования безопасности информации к операционным системам. Требования применяются к операционным системам, используемым в целях обеспечения защиты информации, содержащей сведения, составляющие государственную тайну, и иной информации ограниченного доступа при ее обработке в информационных системах. В соответствии с требованиями выделяются три типа операционных систем: операционная система общего назначения (тип «А»), встраиваемая операционная система (тип «Б»), операционная система реального времени (тип «В»). Для дифференциации требований к функциям безопасности операционных систем выделяются шесть классов защиты операционных систем. Самый низкий класс – шестой, самый высокий – первый. Из доклада Петра Девянина я узнал, что Astra Linux Special Edition является операционной системой типа «А» и второго класса защиты. Astra Linux Special Edition стала первой операционной системой, сертифицированной по новым требованиям ФСТЭК России.

После обеда речь зашла об образовании. Георгий Курячий, Вартан Падарян и Роман Симаков рассказали каждый о своём опыте подготовки системных программистов, после чего конференция плавно перетекла в самое обыкновенное обсуждение образования в нашей стране. Высказались почти все, кто был на тот момент в зале. В какой-то момент микрофон взял Алексей Мандрыкин из компании Fast Reports и немного рассказал, как решается проблема подготовки будущих специалистов в Ростове-на-Дону.

Цитата из докладов второго дня конференции:

Если рассказывать студенту только об одной архитектуре, то через некоторое время он может даже не представлять, что существуют другие архитектуры.
Георгий Курячий, о синдроме утёнка.

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

В заключении, поделюсь выводом, который я сделал для себя после OS DAY 2018. Пора прекращать верить в магических человечков, которые магическим образом выполняют работу системы, а брать и изучать, как всё работает на различных уровнях абстракции, вплоть до транзисторов.

Денис Мосолов

Debian vs. Devuan

Devuan (читается как DevOne) является производным от Debian GNU/Linux дистрибутивом. Принципиальное отличие между Debian и Devuan заключается в отсутствии привязки к systemd у последнего. Несмотря на то, что дистрибутиву уже три года, все еще остается много вопросов относительно его появления и назначения. После того как наша команда добавила возможность кастомизации Devuan сначала в Pieman, а затем в CusDeb, я наконец хочу немного рассказать об этом дистрибутиве в нашем блоге. Основным источником разногласий, которые вылились в работу над Devuan, является переход Debian и Ubuntu на использование системы инициализации systemd. Но обо всем по порядку.

Ускорение загрузки

В конце 2000-х несколько крупных производителей операционных систем на базе ядра Linux пришли к выводу, что необходимо ускорять время загрузки своих систем и, как следствие, брать на вооружение альтернативную систему инициализации. Так как именно система инициализации отвечает за загрузку операционной системы, то стало очевидно, что начинать нужно именно с нее. В течении долгого времени дистрибутивы GNU/Linux использовали для этих целей SysVinit, которая сейчас считается классической системой инициализации и в данной статье противопоставляется всем другим.

Компания Canonical, которая является основным разработчиком дистрибутива Ubuntu, начала работу над альтернативной системой инициализации под названием Upstart в середине 2000-х. Upstart дебютировала в Ubuntu 6.10 «Edgy Eft», заменив SysVinit, но окочательно новая система иницализации была интегрирована в дистрибутив только к выходу Ubuntu 9.10 «Karmic Koala». Затем на Upstart обратили внимание компании Google и Red Hat, интегрировав ее в свои продукты. Так, Upstart начала использоваться в ChromeOS и Red Hat Enterprise Linux 6. На новую систему инициализации даже планировал перейти Debian.

Upstart оказалась эффективней чем SysVinit благодаря событийно-ориентированной архитектуре. Системные сервисы стало возможным запускать и останавливать на основе событий, а не уровней запуска (runlevels), указанных в скриптах инициализации. Данный подход предполагал генерацию событий при запуске или остановке системных сервисов, что позволило организовать привязку к ним других сервисов. Таким образом, для определения последовательности запуска сервисов и оценки возможности их выполнения в параллельном режиме был взят на вооружение метод учета зависимостей. Работа в тандеме с ядром, которое также стало развиваться в сторону событийно-ориентированной архитектуры, благодаря чему появилась возможность асинхронной загрузки драйверов, заметно сократила время от нажатия кнопки включения до полной готовности всей системы.

Недостатки SysVinit мотивировали разработчиков Upstart приступить к созданию альтернативной системы инициализации, а достоинства launchd из Mac OS X и SMF из Solaris послужили основным источником ценных идей, которые помогли в решении поставленной задачи. Однако, по воле судьбы или случайному стечению обстоятельств, Upstart оказалась всего лишь трамплином для другой, более совершенной системы инициализации. Так, компания Red Hat, при участии разработчиков из Novell, IBM, Intel и Nokia, поставила перед собой цель создать систему инициализации, нацеленную на более интенсивную параллелизацию выполнения сервисов на этапе загрузки. Проект получил название systemd и отличался более радикальным подходом к решению некоторых задач. К примеру, если Upstart была совместима со скриптами инициализации SysVinit, то systemd предлагала для этих целей новый синтаксис и, таким образом, не была завязана ни на Bash, ни на любую другую оболочку. Тем не менее, на этам этапе все было хорошо. Волна критики накатила на проект немного позднее, когда стало очевидно, что акцент разработчиков немного сместился в сторону, отличную от первоначальных целей. Так, в один прекрасный момент проект под названием systemd стал позиционироваться не как «альтернативная система инициализации», а как «системный менеджер». Система инициализации в этом проекте стала одним из многочисленных компонентов, а весь проект стал нацелен на управление различными аспектами работы системы. Также стоит заметить, что немалая доля критики свалилась на systemd из-за того, что лидером проекта является не кто иной, как сам Леннарт Поттеринг (Lennart Poettering), в прошлом разработчик звукового сервера PulseAudio, который в начале своего пути не отличался стабильностью работы (в основном из-за аудиодрайверов, а не ошибок в коде PulseAudio), что и является причиной ненависти как к проекту, так и к его автору.

Появление Devuan

Наконец, я подобрался к основным мотивам, которые заставили разработчиков Devuan форкнуть Debian с целью его альтернативного развития. В конце 2013-го – начале 2014-го Debian в очередной раз вернулся к актуальной для себя задаче – переходу на более современную систему инициализации. В качестве кандидатов рассматривались Upstart и systemd, и по результатам голосования победила последняя. Следом за Debian пошла Ubuntu. Стоит заметить, что выбор стоял между системой инициализации и системным менеджером, одним из компонентов которого являлась система инициализации. По итогам интеграции systemd в Debian дистрибутив не только перешел на новую систему инициализации, но и оказался сильно завязанным на системном менеджере, который был призван управлять всеми аспектами работы системы. В итоге, systemd был поставлен в один ряд с ядром и стандартной библиотекой языка C, для которых в дистрибутиве не предусмотрены альтернативы. Jessie стал первым выпуском Debian, который не мог функционировать без systemd. Поэтом группа разработчиков Veteran Unix Admins, несогласная с этой политикой, выпустила в рамках проекта Devuan свободную от systemd версию Jessie. Для того чтобы этого добиться, разработчикам потребовалось внести изменение в 381 пакет. Так появился первый выпуск Devuan, позволивший пользователям Debian использовать или не использовать systemd в зависимости от их желания.

Заключение

Я написал эту статью в первую очередь для того, чтобы помочь нашим пользователям разобраться в причинах появления еще одного производного от Debian дистрибутива. При этом я не являюсь противником ни systemd, ни Леннарта Поттеринга, и остаюсь пользователем Debian (рабочая станция) и Ubuntu (ноутбук). Тем не менее, я считаю, что безальтернативная поставка systemd в крупнейших дистрибутивах является крайностью. В связи со всем этим я попытался сделать статью как можно более беспристрастной. Но теперь наконец я могу сказать, что я восхищаюсь разработчиками Devuan и ценю проделанную ими работу, и рад заявить, что CusDeb поддерживает этот дистрибутив.