- Знакомьтесь — Linux From Scratch
- Вместо вступления
- Единственный бумажный дистрибутив
- Но почему начинать с него?
- Цифры и суровая правда жизни
- Состав дистрибутива
- Вместо заключения
- What is Linux From Scratch?
- Why would I want an LFS system?
- What can I do with my LFS system?
- Who’s who:
- Linux-дистрибутив from scratch для сборки Docker-образов — наш опыт с dappdeps
- В поисках приключений
- Теория
- Собираем дистрибутив dappdeps
- Собираем dappdeps/toolchain
- Используем Omnibus вместе с dappdeps/toolchain
- Собираем dappdeps/base
- Собираем dappdeps/gitartifact
- Собираем dappdeps/chefdk
- Собираем dappdeps/ansible
- Как использовать дистрибутив dappdeps?
- Дальнейшие работы и проблемы
- Какие проблемы с dappdeps?
- Чего не хватает в Dockerfile?
- Выводы
Знакомьтесь — Linux From Scratch
Вместо вступления
«Хочешь начать изучать GNU/Linux? Начни с Linux From Scratch.»
Единственный бумажный дистрибутив
LFS (здесь и далее аббревиатура от Linux From Scratch) – книга, написанная Герардом Бикмансом, описывающая процесс сборки минимального рабочего варианта GNU/Linux из исходных кодов. Почему бумажный? В свое время книгу можно было купить в твердом переплете, что делает «дистрибутив» несколько необычным, не правда-ли? Помимо самой книги, для сборки конечно-же понадобится интернет (или заранее загруженные исходные коды), свободный раздел на жестком диске, и любая операционная система на базе ядра Linux, имеющая компилятор. Лично я всегда использую либо уже собранный дистрибутив LFS, либо полную установку Slackware – в нем есть все необходимое, чего не скажешь про (например) Ubuntu. Конечно, всегда можно загрузить нехватающие пакеты, но ведь мы хотим только-только научиться линуксу? А Slackware даже в своей базовой установке и без конфигурирования предоставляет требуемый инструментарий.
Следует сказать сразу — дистрибутив собранный по книге LFS не умеет толком ничего. Вернее, ничего такого, что потребуется неискушенному пользователю. Он умеет включаться, выключаться, перезагружаться, использовать Ethernet подключение, но что самое главное — компилировать. Так существуют другие книги, теперь уже поддерживаемые сообществом — Beyond LFS позволяет собрать те самые, интересные пользователю, программы. От браузера и графической среды, до систем управления базами данных и DHCP сервера. Книга имеет свойство отставать в версии от базовой книги, но полученный от LFS опыт обычно достаточен, для самостоятельного разрешения конфликтов версий. Три другие книги — Automated LFS, Cross LFS и Hardened LFS полностью соответствуют названиям и выходят за пределы этой статьи. Впрочем, всегда можно почитать в интернете, правда?
Но почему начинать с него?
Все очень просто, если не сказать — до смешного. Помимо инструкций, в книге много теоретического (но крайне сжатого и как следствие — не заунывного) материала. Установите Ubuntu. Вы знаете что делает пакет Libtool? Или Gawk? После пары успешных сборок LFS, вы будете знать каждый пакет в своей системе и что самое главное — представлять, как они взаимодействуют. Да, состав системы минимальный, но это постоянно подогревает интерес к ее усовершенствованию. Приучит частенько обращаться за помощью к Google и читать англоязычную документацию. Поначалу вы не будете понимать и половины своих действий, внимательно перепечатывая или копируя блоки кода в консоль. Но в самом конце, осознание того, что эту неказистую, без поддержки графики и вашей мощной видеокарты, без новомодного Aero и сенсорных экранов, операционную систему вы собрали сами, придаст вам такой запас сил и гордость, что вы сами потянитесь за новыми знаниями и новыми победами. Я немного утрирую, но ощущения после первой перезагрузки в новую систему сложно забыть даже сейчас.
Цифры и суровая правда жизни
LFS вовсе не минимальный по своему весу дистрибутив. Для сборки потребуется порядка 3 свободный гигабайтов на диске (это помимо уже рабочей Linux-Based системы) — тем не менее, после определенных танцев, систему можно будет превратить в Live-CD. Если у меня будет такая возможность, я расскажу как это делается, в последствии. Компиляция всего и вся (с учетом необходимости создания конфигурационных файлов и прочего) занимает около двух дней. Это если с перерывами на сон, питание и отключение компьютера на ночь. С другой стороны, это зависит от производительности компьютера, на котором собирается система. Моей первой жертвой был ноутбук MSI X-340 — процессор Intel Core 2 Solo с тактовой частотой 1.2 гигагерца (вообще говоря, LFS рекомендуется собирать на одноядерной системе). Оперативная память — 2GB DDR2. Вполне достаточно даже более низких характеристик, а на быстром процессоре сборка пойдет значительно быстрее.
Кстати, приблизительное время сборки каждого из пакетов указано в учебнике. За единицу времени, принимается время сборки пакета Binutils — ассемблера, линкера и ряда других, более мелких утилит для работы с объектными файлами. На вышеуказанной системе это заняло три минуты.
Состав дистрибутива
Перечислять все входящие в LFS пакеты не имеет большого смысла. Список получится длинным, и практически ни о чем не говорящим новичку; избыточным для человека разбирающегося. В этом небольшом разделе я лишь хотел дать несколько общих советов. Во-первых, собирая пакет, не описанный в книге, загляните в репозиторий патчей — возможно его уже адаптировали для использования в LFS. Во-вторых, BLFS почти полностью состоит из опциональных пакетов — просто выбирайте те, которые вам нужны и смело добавляйте в свою сборку LFS. И третье: с самого начала используйте пакетный менеджер. В книге этому уделяется глава, но практические инструкции отсутствуют чуть менее, чем полностью. Я лишь могу порекомендовать Guarded Installation Tool – написанный на Bash’е скрипт, обеспечивающий минимальный набор команд, для управления пакетами, зависимостями и версиями. В LFS этого будет достаточно. В последствии вы можете скомпилировать из исходных кодов APT или DPKG — это неплохо освещено на форумах сообщества.
Вместо заключения
За прошедшие полтора года я сильно продвинулся в своем изучении. Продвинулся со стадии «Есть такая операционная система» до уровня «Подниму сервер под Gentoo за трое суток». Я еще очень далек от идеала и вряд ли могу даже называть себя настоящий линуксоидом — на домашнем компьютере уживаются старенькая Windows XP и Xubuntu 10.10, но знаете что я отвечаю, когда меня спрашивают с чего начать изучать линукс? Начните с Linux From Scratch.
Источник
What is Linux From Scratch?
Linux From Scratch (LFS) is a project that provides you with step-by-step instructions for building your own customized Linux system entirely from source.
Why would I want an LFS system?
Many wonder why they should go through the hassle of building a Linux system from scratch when they could just download an existing Linux distribution. However, there are several benefits of building LFS. Consider the following:
LFS teaches people how a Linux system works internally
Building LFS teaches you about all that makes Linux tick, how things work together and depend on each other. And most importantly, how to customize it to your own tastes and needs.
Building LFS produces a very compact Linux system
When you install a regular distribution, you often end up installing a lot of programs that you would probably never use. They’re just sitting there taking up (precious) disk space. It’s not hard to get an LFS system installed under 100 MB. Does that still sound like a lot? A few of us have been working on creating a very small embedded LFS system. We installed a system that was just enough to run the Apache web server; total disk space usage was approximately 8 MB. With further stripping, that can be brought down to 5 MB or less. Try that with a regular distribution.
LFS is extremely flexible
Building LFS could be compared to a finished house. LFS will give you the skeleton of a house, but it’s up to you to install plumbing, electrical outlets, kitchen, bath, wallpaper, etc. You have the ability to turn it into whatever type of system you need it to be, customized completely for you.
LFS offers you added security
You will compile the entire system from source, thus allowing you to audit everything, if you wish to do so, and apply all the security patches you want or need to apply. You don’t have to wait for someone else to provide a new binary package that (hopefully) fixes a security hole. Often, you never truly know whether a security hole is fixed or not unless you do it yourself.
What can I do with my LFS system?
A by-the-book LFS system is fairly minimal, but is designed to provide a strong base on which you can add any packages you want. See the BLFS project for a selection of commonly used packages.
Who’s who:
- Project Creator: Gerard Beekmans
- Managing Editor: Bruce Dubbs
- Editor: Ken Moffat
- Editor: Pierre Labastie
- Editor: DJ Lucas
- Editor: Douglas R. Reno
- Editor: Thomas Trepl
- Editor: Tim Tassonis
- Editor: Xi Ruoyao
- Plus numerous people who contribute to the book and its side projects.
© 1998-2021 Gerard Beekmans. Website design by Jeremy Huntwork & Matthew Burgess.
Источник
Linux-дистрибутив from scratch для сборки Docker-образов — наш опыт с dappdeps
Сборка образов для Docker на основе базового образа, как правило, предполагает вызов команд в окружении этого базового образа. Например — вызов команды apt-get, которая есть в базовом образе, для установки новых пакетов.
Часто возникает необходимость доустановить в базовую систему некоторый набор утилит, с помощью которых происходит установка или сборка некоторых файлов, которые требуются в итоговом образе. Например, чтобы собрать Go-приложение, надо установить компилятор Go, положить все исходные коды приложения в базовом образе, скомпилировать требуемую программу. Однако в итоговом образе требуется лишь скомпилированная программа без всего набора утилит, который использовался для компиляции этой программы.
Проблема известная: одним из путей её решения может быть сборка вспомогательного образа и перенос файлов из вспомогательного образа в результирующий. Для этого появились Docker multi-stage builds или образы-артефакты в dapp (Обновлено 13 августа 2019 г.: в настоящее время проект dapp переименован в werf, его код переписан на Go, а документация значительно улучшена). И данный подход идеально решает проблему подобную переносу результатов компиляции исходных кодов в итоговый образ. Однако он не решает все возможные проблемы…
Вот другой пример: для сборки образа используется Chef в локальном режиме. Для этого в базовый образ ставится chefdk, монтируются или добавляются рецепты, запускаются эти рецепты, которые настраивают образ, устанавливают новые компоненты, пакеты, файлы-конфиги и прочее. Аналогично может быть использована другая система управления конфигурациями — например, Ansible. Однако установленный chefdk занимает около 500 Мб и существенно увеличивает размеры итогового образа — оставлять его там нет смысла.
Но multi-stage builds в Docker уже не решат эту проблему. Что, если пользователю не хочется знать о том, каков побочный эффект работы программы — в частности, какие файлы она создает? Например, чтобы не держать лишние явные описания всех экспортируемых путей из образа. Хочется просто запустить программу, получить какой-то результат в образе, но чтобы программа и все окружение, нужное для ее работы, осталось вне итогового образа.
В случае с chefdk можно было бы монтировать директорию с этим chefdk в сборочный образ на время сборки. Но с этим решением есть проблемы:
- Не любая программа, нужная для сборки, устанавливается в отдельный каталог, который легко примонтировать в сборочный образ. В случае с Ansible надо монтировать Python в нестандартное место, чтобы не конфликтовать с системным Python, что уже может вызывать проблемы.
- Примонтированная программа будет зависеть от используемого базового образа. Если программа собрана для Ubuntu, то она может не запуститься в не предусмотренном для нее окружении — например, в Alpine. Даже chefdk, который является omnibus-пакетом со всеми своими зависимостями, все равно зависит от системного glibc и не будет работать в Alpine, где используется musl libc.
А что, если мы сможем подготовить некий статичный неизменный набор всех возможных полезных утилит, который будет так хитро слинкован, что будет работать в любом базовом образе, даже scratch? После подключения такого/таких образов в базовый, в итоговом образе останется лишь пустая директория mount-point, в которую были подключены эти утилиты.
В поисках приключений
Теория
Необходимо получить образ, в котором содержится набор программ в некоторой статически определенной нестандартной директории — например, /myutils . Любая программа в /myutils должна зависеть только от библиотек в /myutils .
Динамически скомпилированная программа в Linux зависит от местоположения линкера ld-linux в системе. Например, бинарник bash в ubuntu:16.04 скомпилирован так, что зависит от линкера /lib64/ld-linux-x86-64.so.2 :
Причем эта зависимость является статической и вкомпилирована в сам бинарник:
Таким образом, надо: а) скомпилировать условный /myutils/bin/bash так, чтобы он использовал линкер /myutils/lib64/ld-linux-x86-64.so.2 ; б) чтобы линкер /myutils/lib64/ld-linux-x86-64.so.2 был настроен на динамическую линковку библиотек из /myutils/
Первым шагом будет сборка образа toolchain , который будет содержать всё, что необходимо для сборки и последующей работы других программ в нестандартной root-директории. Для этого нам как нельзя кстати придутся инструкции проекта Linux From Scratch.
Собираем дистрибутив dappdeps
Почему набор образов нашего «дистрибутива» называется dappdeps? Потому что эти образы использует сборщик dapp — они собираются под нужды этого проекта.
Итак, наша конечная цель:
- Образ dappdeps/toolchain с компилятором GCC для сборки других приложений и библиотекой glibc.
- Образ dappdeps/base с набором программ и всех зависимых библиотек: bash, gtar, sudo, coreutils, findutils, diffutils, sed, rsync, shadow, termcap.
- Образ dappdeps/gitartifact с утилитой Git и всеми зависимостями.
- Образ dappdeps/chefdk с omnibus-пакетом chefdk, который содержит все зависимости Chef, в т.ч. интерпретатор Ruby.
- Образ dappdeps/ansible с утилитой Ansible, который содержит все зависимости, в т.ч. интерпретатор Python.
Образы dappdeps могут зависеть друг от друга. Например, при сборке dappdeps/base требуется toolchain и glibc из образа dappdeps/toolchain. После компиляции всех утилит в dappdeps/base для их работы в runtime будут требоваться файлы из dappdeps/toolchain.
Главное условие заключается в том, чтобы утилиты из этих образов располагались в нестандартном месте, а именно — в /.dapp/deps/ , и не зависели ни от каких утилит или библиотек в стандартных системных путях. Также в dappdeps-образах не должно быть никаких других файлов, кроме /.dapp/deps .
Такие образы позволят создавать на их основе контейнеры с томами, где содержатся утилиты, и монтировать их в другие контейнеры с использованием опции —volumes-from для Docker.
Собираем dappdeps/toolchain
Глава 5 «Constructing a Temporary System» руководства Linux From Scratch как раз описывает процесс построения временного chroot-окружения в /tools с некоторым набором утилит, которым затем собирается главный целевой дистрибутив.
В нашем случае немного переиначим директорию chroot-окружения. В параметре —prefix при компиляции будем указывать /.dapp/deps/toolchain/0.1.1 . Это та директория, которая будет появляться в сборочном контейнере, при монтировании в него dappdeps/toolchain — в ней содержатся все нужные утилиты и библиотеки. Нам требуются лишь GNU binutils, GCC и glibc.
Собирается образ с использованием Docker multi-stage builds. В образе на основе ubuntu:16.04 подготавливается все окружение и производится компиляция и установка программ в /.dapp/deps/toolchain/0.1.1 . Затем эта директория копируется в scratch-образ dappdeps/toolchain:0.1.1. Dockerfile можно найти здесь.
Итоговый образ dappdeps/toolchain — это и есть «temporary system» в терминологии LFS. GCC в данной системе все еще завязан на системные пути к библиотекам, однако мы не будем добиваться того, чтобы GCC работал в любом базовом образе. Образ dappdeps/toolchain — вспомогательный, он будет использоваться далее, в т.ч. для сборки уже реально независимых от общих системных библиотек программ.
Используем Omnibus вместе с dappdeps/toolchain
Для сборки таких проектов, как chefdk или GitLab, используется Omnibus. Он позволяет создать самодостаточные наборы (self-contained bundle) с программой и всеми зависимыми библиотеками, кроме системного линкера и libc. Все инструкции описываются читаемыми удобными Ruby-рецептами. Также у проекта Omnibus есть библиотека уже написанных рецептов omnibus-software.
Итак, попробуем описать сборку остальных dappdeps-дистрибутивов с использованием Omnibus. Однако, чтобы избавиться от зависимости от системного линкера и libc, будем собирать все программы в Omnibus с использованием компилятора из dappdeps/toolchain. В этом случае программы окажутся завязаны на glibc, который тоже есть в dappdeps/toolchain.
Для этого сохраним содержимое dappdeps/toolchain как архив:
Добавим этот архив через директиву Dockerfile ADD и распакуем содержимое архива в корень сборочного контейнера:
Перед запуском сборки через omnibus добавляем в переменную PATH путь /.dapp/deps/toolchain/0.1.1/bin в качестве приоритетного, чтобы использовался GCC из dappdeps/toolchain.
Результат работы Omnibus — это пакет (в нашем случае — DEB), содержимое которого распаковывается и переносится в /.dapp/deps/
Собираем dappdeps/base
Проект для Omnibus описывается с помощью файла проекта omnibus/config/projects/dappdeps-base.rb :
В этом файле указаны все зависимости Omnibus-пакета dappdeps-base и целевая директория для установки. Зависимости могут располагаться либо в отдельном репозитории (например, omnibus-software), либо в директории omnibus/config/software . Каждый файл в этой директории описывает инструкции по установке какого-то пакета/компонента. Для dappdeps-base в Omnibus написаны software-рецепты, отсутствующие в стандартном репозитории omnibus-software: acl , attr , coreutils , diffutils , findutils , gtar , rsync , sed , shadow , sudo , termcap .
Рассмотрим на примере rsync , как выглядит software-рецепт для Omnibus:
Директивой source указывается URL, откуда надо скачать исходные коды. Зависимости от других компонентов указаны директивой dependency по имени. Имя собираемого компонента задано директивой name . Каждый software-рецепт в свою очередь может указывать зависимости от других компонентов. Внутри блока build указаны стандартные команды сборки из исходных кодов.
Проект Omnibus и Dockerfile для dappdeps/base можно найти здесь.
Собираем dappdeps/gitartifact
В случае с dappdeps-gitartifact необходим лишь рецепт сборки Git, а он уже есть в omnibus-software — остается только подключить его в текущий Omnibus. В остальном все аналогично.
Проект Omnibus и Dockerfile для dappdeps/gitartifact можно найти здесь.
Собираем dappdeps/chefdk
Для chefdk тоже уже есть готовый проект Omnibus. Остается лишь добавить его в сборочный контейнер через Dockerfile и заменить стандартные пути установки chefdk /opt/chefdk на /.dapp/deps/chefdk/2.3.17-2 (наш путь установки будет включать в себя версию Chef).
Dockerfile для сборки dappdeps/chefdk можно найти здесь.
Собираем dappdeps/ansible
Для сборки Ansible также заводим Omnibus-проект, в котором устанавливаем интерпретатор Python, pip и описываем software-рецепт для Ansible:
Как видно, образ с Ansible представляет собой встроенный Python, pip и установленный через pip Ansible с зависимостями.
Проект Omnibus и Dockerfile для dappdeps/ansible можно найти здесь.
Как использовать дистрибутив dappdeps?
Чтобы пользоваться образами dappdeps через монтирование томов, предварительно для каждого образа необходимо создать контейнер и указать, какой том хранится в этом контейнере. Этого требует Docker на данный момент.
Контейнер называется dappdeps-toolchain : по этому имени все объявленные томы этого контейнера можно использовать для монтирования в другие контейнеры с помощью —volumes-from . Параметр-команду с произвольным текстом no-such-cmd требуется указать для Docker, но данный контейнер никогда не будет запущен — он так и останется в состоянии Created .
Создаем остальные контейнеры:
Вот мы и дошли до кульминации, ради которой задумывался весь этот сыр-бор. Итак, в качестве демонстрации возможностей установим в образ Alpine пакеты nginx и tree , запустив Ansible из dappdeps/ansible через Bash из dappdeps/base:
Финальный аккорд — создаем образ из получившегося контейнера и… видим, что от dappdeps в нем остались лишь пустые директории mount-point’ов!
Казалось бы, о чем еще можно мечтать.
Дальнейшие работы и проблемы
Какие проблемы с dappdeps?
Необходимо провести работу по уменьшению размеров dappdeps/toolchain. Для этого надо разделить toolchain на 2 части: часть, необходимая для сборки новых утилит в dappdeps, и часть с базовыми библиотеками типа glibc, которые необходимо монтировать в runtime уже для запуска этих утилит.
Для работы модуля Ansible apt в dappdeps/ansible пришлось добавить содержимое пакета python-apt в Ubuntu прямо в образ без пересборки. В этом случае модуль apt работает без проблем в базовых образах на основе DEB, но требуется наличие glibc определенной версии. Поскольку сам apt — это дистрибутиво-специфичный модуль, то такое допустимо.
Чего не хватает в Dockerfile?
Для использования тома из образа dappdeps/toolchain приходится сначала создавать архив этого образа, а затем добавлять его в другой образ через директиву Dockerfile ADD (см. раздел «Используем Omnibus вместе с dappdeps/toolchain»). Со стороны Dockerfile не хватает функционала, который бы позволял просто подключать директорию другого образа на время сборки как VOLUME , т.е. аналог опции —volumes-from для Dockerfile.
Выводы
Мы убедились, что идея работает и позволяет использовать в сборочных инструкциях GNU- и другие CLI-утилиты, запускать интерпретатор Python или Ruby, запускать даже Ansible или Chef в Alpine или scratch-образах. При этом писателю сборочных инструкций не требуется знать побочный эффект выполнения запускаемых команд и явно перечислять, какие файлы необходимо импортировать, как в случае с Docker multi-stage builds.
Результаты данной работы применяются и на практике: dapp использует dappdeps-образы в сборочных контейнерах. Например, Git из dappdeps/gitartifact используется для работы с патчами, и утилита Git с некоторой гарантией ведет себя одинаково во всех базовых образах. Однако то, как dapp использует dappdeps, выходит за рамки данной статьи.
Целью данной статьи было донести саму идею и показать на реальном практическом примере возможность ее применения.
Источник