Docker минимальный образ linux

Программист — это звучит гордо

Если пройтись по образам на docker hub, то подавляющее большинство окажется построенными на базе Ubuntu и Debian. Хотя уже встречаются робкие попытки, в официальных сборках популярных пакетов, выкладывать альтернативные образы на базе Alpine linux. Но в общей массе это капля в море. А между тем Alpine подходит для базового образа куда лучше, чем такие знакомые и родные Debian с Ubuntu.

Размер имеет значение

Если сравнивать размеры этих трёх дистрибутивов, то получим вот такую картину:

Дистрибутив Размер % от ubuntu:14.04
ubuntu:14.04 187.9 MB 100. %
ubuntu:16.04 126.6 MB 67.4 %
debian:wheezy 84.91 MB 45.1 %
debian:jessie 125.1 MB 66.5 %
alpine:3.3 4.793 MB 2.55 %
alpine:3.4 4.795 MB 2.55 %

Представители Ubuntu осознавая, что разница в 40 раз вызывает вопросы, оправдываются. Один из основных аргументов в том, что у автора канал хороший и поэтому образ скачивается быстро, остаётся только порадоваться за него. Второй аргумент в том, что базовый образ мол скачивается один раз и благодаря особенностям файловой системы Docker линкуется в остальные. Скажем честно — аргумент лукавый, по опыту, если скачать десяток произвольных контейнеров, есть вероятность собрать на диске всю коллекцию версий Ubuntu и Debian.

Набор софта

У меня наиболее часто используемые операции в базовом образе это: скачать (wget/curl), разархивировать (unzip/tar), отредактировать (nano/vi) и посмотреть (less). Судя по размеру, alpine должен быть лишён всех этих базовых полезных возможностей. Однако реальность удивляет:

Дистрибутив wget/curl unzip nano/vi less
ubuntu:14.04 vi +
ubuntu:16.04
debian:wheezy
debian:jessie
alpine:3.3 wget + vi +
alpine:3.4 wget + vi +

А что же тогда хранят образы Ubuntu и Debian? Вот набор наибольших по размеру вещей (в зависимости от версий набор варьируется): systemd, udev, python3+python2, bash и т.п. Уж конечно без этого мы собирая образ обойтись никак не могли.

Менеджер пакетов

У Alpine есть легковесный apk, с кучей свежих пакетов на все случаи жизни. Хотя тут он убунтовскомим репозиториям по количеству софта уступает. Но зато под реалии docker’а подходит куда лучше. Давайте разберём типичную проблему: в Dockerfile мы ставим пакет, что-то с его помощью делаем и удаляем, что бы не болтался и не раздувал размер образа. Конкретный пакет не важен, пусть это будет «zip»:

apk add --no-cache zip apk del zip

Размер образа до выполнения скрипта: 4.795 MB, после — 4.813 MB, разница 18 KB. Накладные расходы есть, но терпимо, ключ «–no-cache» помог не выкачивать индекс пакетов, а воспользоваться тем, что лежит в сети.

Читайте также:  Linux start process and get pid

Теперь то же для apt (ubuntu:16.04), тут обойтись без обновления индекса у меня не вышло:

apt-get update apt-get install zip apt-get remove zip

Размер образа после этого изменился с 126 MB до 166 MB, разница в 40 MB! Неплохой «штраф» за то, что пакетом попользовался 5 сек. Вычистить конечно мусор вручную можно, но повозиться прийдётся не слабо и всё равно разницу в 18 KB вы врядли получите.

Система инициализации

По умолчанию используется гентушный OpenRC, а у конкурентов в последних версиях горячо любимая многими systemd. Не подумайте, что я недолюбливаю творение Поттеринга, напротив по моему, от того, что во всех дистрибутивах будет единообразный способ инициализации, все только выиграют. Но для контейнеров это перебор, уж очень сложно и неудобно там делать многие вещи. Да и по моему никто внутри докера systemd и не использует, не видел ни разу. OpenRC меня кстати тоже не впечатлил, он писался не для docker и не помогает решать специфические задачи, которые возникают в контейнерах.

  • «Повесить» набор скриптов на старт и остановку контейнера.
  • Декларативно описать назначение прав на директории и файлы, вместо беспорядочных chmod и chown в разных местах.
  • Для каждого сервиса можно написать скрипт запуска под нужным пользователем, и скрипт, который будет выполняться при завершении контейнера.
  • Ну и конечно единообразное логирование каждого шага.

Документация

В целом проблем с Alpine не возникает, в основном из-за простоты системы, но если что — всегда на помощь прийдёт wiki, по наполнению она конечно поменьше знаменитой archwiki, но решение большинства проблем в ней есть. Проблемы с установкой ПО, проще всего решаются поиском на docker hub, с большой вероятностью кто-то уже написал нужный Dockerfile, где можно подсмотреть решение. Так же существует forum и судя по всему достаточно активный, но настолько сложных проблем, что бы туда написать у меня ещё не возникало.

Источник

Самый маленький Docker-образ — меньше 1000 байт

Прим. перев.: Автор этого материала — архитектор в Barclays и Open Source-энтузиаст из Великобритании Ian Miell. Он задаётся целью сделать удобный образ Docker (со «спящим» бинарником), который не нужно скачивать, а достаточно просто копировать через copy & paste. Методом проб, ошибок и экспериментов с Assembler-кодом он достигает цели, подготовив образ размером менее килобайта.

Читайте также:  Первый запуск кали линукс

Как я к этому пришёл?

Однажды коллега показал Docker-образ, который он использовал для тестирования кластеров Kubernetes. Он ничего не делал: просто запускал под и ждал, пока вы его убьёте.

И тут мне стало любопытно, какой же минимальный образ Docker я смогу создать. Хотелось получить такой, что можно было бы закодировать в base64 и отправлять буквально куда угодно простым copy & paste. Поскольку Docker-образ — это просто tar-файл, а tar-файл — это всего лишь файл, всё должно получиться.

Крохотный бинарник

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

 SECTION .data msg: db "Hi World",10 len: equ $-msg SECTION .text global _start _start: mov edx,len mov ecx,msg mov ebx,1 mov eax,4 int 0x80 mov ebx,0 mov eax,1 int 0x80
nasm -f elf64 hw.asm -o hw.o ld hw.o -o hw strip -s hw

Получается бинарник в 504 байта.

Но всё-таки нужен не «Hello World»… Во-первых, я выяснил, что излишни секции .data или .text и не требуется загрузка данных. Вдобавок, верхняя половина секции _start занимается выводом текста. В итоге, я попробовал следующий код:

global _start _start: mov ebx,0 mov eax,1 int 0x80

И он скомпилировался уже в 352 байта.

Но это ещё не искомый результат, потому что программа просто завершает свою работу, а нам нужно, чтобы она спала. В результате дополнительных исследований выяснилось, что команда mov eax заполняет регистр процессора соответствующим номером системного вызова Linux, а int 0x80 производит сам вызов. Подробнее это описано здесь.

А здесь я нашёл нужный список. Syscall 1 — это exit , а нужный нам — это syscall 29:pause . Получилась такая программа:

global _start _start: mov eax, 29 int 0x80

Мы сэкономили ещё 8 байтов: компиляция выдала результат в 344 байта, и теперь это подходящий нам бинарник, который ничего не делает и ожидает сигнала.

Копаясь в hex’ах

Настало время достать бензопилу и разобраться с бинарником… Для этого я использовал hexer, который по сути vim для бинарных файлов с возможностью прямого редактирования hex’ов. После продолжительных экспериментов я получил из такого:

Читайте также:  Fail to ban linux

Данный код делает то же самое, но обратите внимание, сколько строк и пробелов ушло. В процессе своей работы я руководствовался таким документом, но по большому счёту это был путь проб и ошибок.

Итак, размер уменьшился до 136 байт.

Меньше 100 байт?

Хотелось узнать, можно ли пойти дальше. Прочитав это, я предположил, что получится дойти до 45 байт, однако — увы! — нет. Описанные там фокусы рассчитаны только на 32-битные бинарники, а для 64-битных не проходили.

Лучшее же, что мне удалось, — взять эту 64-битную версию программы и встроить в свой системный вызов:

BITS 64 org 0x400000 ehdr: ; Elf64_Ehdr db 0x7f, "ELF", 2, 1, 1, 0 ; e_ident times 8 db 0 dw 2 ; e_type dw 0x3e ; e_machine dd 1 ; e_version dq _start ; e_entry dq phdr - $$ ; e_phoff dq 0 ; e_shoff dd 0 ; e_flags dw ehdrsize ; e_ehsize dw phdrsize ; e_phentsize dw 1 ; e_phnum dw 0 ; e_shentsize dw 0 ; e_shnum dw 0 ; e_shstrndx ehdrsize equ $ - ehdr phdr: ; Elf64_Phdr dd 1 ; p_type dd 5 ; p_flags dq 0 ; p_offset dq $$ ; p_vaddr dq $$ ; p_paddr dq filesize ; p_filesz dq filesize ; p_memsz dq 0x1000 ; p_align phdrsize equ $ - phdr _start: mov eax, 29 int 0x80 filesize equ $ - $$

Результирующий образ — 127 байт. На этом я прекратил попытки уменьшать размер, но принимаю предложения.

Крохотный Docker-образ

Теперь, когда есть бинарник, реализующий бесконечное ожидание, остаётся положить его в Docker-образ.

Чтобы сэкономить каждый возможный байт, я создал бинарник с файловым именем из одного байта — t — и поместил его в Dockerfile , создавая практически пустой образ:

Обратите внимание, что в Dockerfile нет CMD , поскольку это увеличило бы размер образа. Для запуска понадобится передавать команду через аргументы к docker run .

Далее командой docker save был создан tar-файл, а затем — сжат с максимальной компрессией gzip. Получился портируемый файл Docker-образа размером менее 1000 байт:

$ docker build -t t . $ docker save t | gzip -9 - | wc -c 976

Ещё я попытался уменьшить размер tar-файла, экспериментируя с manifest-файлом Docker, но тщетно: из-за специфики формата tar и алгоритма сжатия gzip такие изменения приводили только к росту финального gzip’а. Пробовал и другие алгоритмы компрессии, но gzip оказался лучшим для этого маленького файла.

P.S. от переводчика

Читайте также в нашем блоге:

Источник

Оцените статью
Adblock
detector