Как стать автором
Обновить

Комментарии 44

Не совсем понятно. Решение все-таки рабочее или нет?
Решение не идеальное ;)
Нет, работает только в частных случаях (когда приложение за время профилирования, как минимум обратилось ко всем зависимостям, которые будут использоваться в production).
А есть ли какая-нибудь утилита для очистки неиспользуемых мной программ в Linux? Например, я знаю, что буду использовать в docker-контейнере. Утилита тянет информацию с кэша пакетных менеджеров (секции «зависимости»), а все остальные приложения удаляет.
Неужели только «apt-get autoremove» единым?
deborphan --guess-all -n
В основном это «catch-all» root возможность

Охладите уже углепластик
Честно, сам перечитывая думал может оставить как capability, но все таки решил перевести, тем более что в тут как раз «возможности» в русском man'е.
Универсальный capability, присущий пользователю root.
Не зная заранее о чем идет речь, распарсить "«catch-all» root возможность" нормальному человеку не представляется возможным.
Интересный способ себя занять, это даже более геморно, чем страдания с Alpine образом (5МБ базовый образ — busybox + apk manager).

Для интересующихся: в Alpine используется musl libc, которому просто не хватает поддержки и некоторые вещи (компиляторы и другие крупные/старые проекты) очень сложно собирать, но пакетов уже достаточно много, так что не всё уж так плохо.
Отличный подход, сначала накидаем неизвестно чего, потом будем «сжимать», потом в продакт. Мне нравится.
Это новый путь. Чем меньше думаешь о безопасности и продакшене, тем выше скорость разработки.
Для того чтобы найти все все файлы который открывает процесс можно было использовать старый как мир strace.

strace -f -e trace=open /bin/ls
Просветите, открывает ли он реально ВСЕ файлы, или таки только то, что вот надо прям сейчас, и может, если понадобится, через часик откроет еще вон ту пачку библиотек?
strace будет мониторить файлы открытые дочерними процессами? простите, в устройства *nix не силен и вопрос может звучать глупо.
с ключиком -f будет мониторить и дочерние процессы.
НЛО прилетело и опубликовало эту надпись здесь
Да, это самый простой способ. А если atime отсутствует, то есть strace и ftrace/trace-cmd, DTrace, SystemTap. Но тут автор не ищет легких путей :)

Хотя главная ошибка, что он ловит только open, а нужено еще и отслеживать stat, т.к. часто бывает проверки наличие файлов/каталогов, без того, чтобы открывать их.
Это не убирает принципиального недостатка такого подхода. Приложение могло ни разу не обращаться к файлу, к которому обратится потом по внешнему запросу в ходе работы.
Для докера с большими приложениями — да, не подходит принципиально. Но очень хорошо подходит для собирания initrd, практикой проверено.
Для initrd подходит с аналогичными ограничениями.

Вы получите минимальный образ, который будет работать в той же конфигурации. Например, если initrd выполняет определенные действия только, если выполняется специальное условие. Например, выполняет btrfs scrub только если есть точки монтирования btrfs в /etc/fstab. Или условное выполнения действия при наличии определенного устройства.

В общем, сводится к тому, есть ли реакция на «внешний» мир. Если его зафиксировать (например, собирая initrd под определенную конфигурацию), то этот подход нормален. Если не фиксировать — рано или поздно будет ошибка.
Проблема та же. Приложение могло за время работы не обратиться к файлу, соответственно atime останется прежним (в предположении, что он вообще включен).

Представьте, что у вас nginx, например, который раздаёт статику из /var/www. За время профилирования вы запросили не все файлы, которые были в /var/www, удалили те, к которым не было обращений. В итоге получается явное удаления части необходимых файлов.
файлы из в /var/www вероятно должен быть скопированы в образ с хост системы перед упаковкой…
Это пример. С тем же успехом это может быть какая-нибудь библиотека из /usr/lib.
Это плохой пример. Прогоните системные тесты в приложении, если не все библиотеки были использованы — плохие тесты у вас.
Не ведаю, что за «системные тесты». Если вы имели ввиду unit/func/integration для приложения, то они обычно не включаются в итоговое приложение. Если же профилировать сборку с запуском тестов, то вы получите в списке «необходимых» для работы ещё систему сборки и всю тестовую машинерию.
Тесты которые гоняют весь стэй без всяких mock'ов. Учитывая, что вероятно 90% докер контейнеров это уеб приложения. В чем сложность их запускать вне контейнера?

Что это за приложение такое которое не все подгружает при запуске? Я думал в продакшене мы загружаем все в память. Это раз.
Два. Что мешает включить мозг свой и сделать strace или просто знать, что libzaebis используется в приложении? Или Вы один из тех разработчиков которые не понимают их приложение работает?
Учитывая, что вероятно 90% докер контейнеров это уеб приложения.
Откуда вы почерпнули 90%, а не, скажем, 40%? Мне неизвестна реальная статистика. Для них, конечно, стоит прогонять интеграционные + smoke тесты. Вопрос, как всегда, полноты покрытия.

Что это за приложение такое которое не все подгружает при запуске? Я думал в продакшене мы загружаем все в память.
Бывают модульные приложения. Бывает необходимость включить подсистему только тогда, когда загрузили определенный плагин/таск. Простейший пример из столь любимого вами «уеба» — appserver'а. Какой-нибудь JDBC-драйвер базы не будет загружен пока не будет настроен соответствующий jndi-ресурс (и иногда может требоваться, чтобы было задеплоено приложение, использующее этот ресурс).

Что мешает включить мозг свой и сделать strace или просто знать, что libzaebis используется в приложении? Или Вы один из тех разработчиков которые не понимают их приложение работает?
Не хамите.

Несколькими сообщениями выше я уже пытался объяснить, что имеется ввиду. Эта проблема сродни проблеме останова, для произвольного приложения невозможно заранее сказать к каким зависимостям обратится данное приложение в процессе работы, и strace/ptrace/atime может помочь только в частных случаях. Рабочий вариант — ориентироваться на то, что разработчики корректно описали зависимости.
Почти то же самое на примере nginx и в исполнении разработчиков докера — типа best practice.
А можете рассказать, зачем вообще пытаться делать минимальный размер образа? Ведь фича образов докера в том, что они состоят из слоёв вплоть до запуска контейнера. Если использовать aufs/overlayfs, то тот самый жирный базовый слой будет смонтирован только один раз, и будет пошарен между всеми контейнерами. При этом он будет один раз в page cache, что тоже положительно скажется на скорости работы и потреблении памяти.

В вашем же случае каждый контейнер уникальный, профита от переиспользования базового образа никакого. То есть, если запустить 100 контейнеров, собранных вашим способом, и 100 контейнеров, не ужатых, но с overlayfs, то второй вариант победит по потреблению page cache, а места на диске потребует незначительно больше.
Позвольте мне ответить. В таком извращении, которым занимался автор (не переводчик), смысла я тоже не вижу, а вот в минимизации образа я вижу смысл. Только для минимизации образа лучше просто брать маленький базовый образ, например, сейчас мне очень нравится Alpine (5МБ, которые включают busybox и apk пакетный менеджер). То есть /bin/ls вы получаете ценой в 5МБ и тут уже не получится кричать, что образ ужался на 98% (он бы ещё Haskell образ (1ГБ, официальный образ между прочим...) взял ради /bin/ls).

Маленький образ — это меньшее количество уязвимостей. Я уже писал об этом здесь. Судя по всему, нужно дописать уже статью, а то по комментариям хожу проповедую :)

Кроме того, есть две большие разницы — базовый образ внутри компании, который все используют и ничего внешнего не приносят, и образы, которые выложены на Docker Hub, где каждый кто во что горазд (ubuntu, debian, google/debian, fedora, centos — собрать такой зоопарк, если не следить за тем что там авторы наваяли, не составит труда, а это уже 1ГБ, а потом они ещё обновляются кто во что горазд — наследуемые образы НЕ перестраиваются после обновления базового если этого явно не указать в настройках (мало кто указывает)). Таким образом 1ГБ базовых образов можно собрать за первый день и потом в течение месяца из-за обновлений одних образов и не обновлений других — можно новый 1ГБ собрать.

Главное заблуждение: официальные образы на Docker Hub — хороши. Да ничего подобного! Большинство образов на троечку собраны. Самые наглядные примеры:

  • Python: мои образы основанные на Alpine (50МБ) VS официальный slim (216МБ, а «обычный» — 750МБ) — тыц
  • Golang: мой образ (126МБ) VS официальный (517МБ) — тыц

(у меня ещё 7 образов есть с аналогичными сравнениями)
А я вас полностью поддерживаю :) Единственное что, 5МБ в качестве первого слоя не всегда круто, всё же нужен баланс между количеством слоёв и их объёмом. Конечно, тут уже нужно смотреть на свои образы и по ним изучать, что же можно назвать общей базой для бОльшей части из них.
В том-то и дело, что, например, официальный Python образ страдает во многом из-за убогости выбранного базового образа — первая команда у них в Dockerfile: apt-get purge python.*, но это уже не вернёт утраченное место.

Да, найти баланс действительно сложно, Debian всё-таки «привычнее», все знают apt-get и слова поперёк не скажут если в репах не будет какого-то пакета или он будет старый, а вот в Alpine хоть и достаточно много пакетов, но если что-то экзотическое (например из последнего, haskell, которому для сборки себя нужен haskell), то musl libc станет поперёк горла, или просто проприетарные бинарники, которые нужно заталкивать через установку glibc. Но я пока держусь на Alpine и только для особо замороченных случаев (Hadoop, FreeIPA) приходится использовать тяжёлую артиллерию.
Да, конечно, минимизация образов абсолютно необходима, большинство из них необоснованно огромны как по мне, но использовать для этого busybox или alpine — допустим вам необходим образ ИМЕННО с федорой или дебианом определенной версии, под которой должно запускаться приложение в контейнере, и любой другой базовый образ системы вам не подходит? Конечно, можно попытаться привести alpine/busybox к необходимым зависимостям, но время, которое на это потратится скомпрометирует саму идею смысла перехода на контейнеры для dev-ops тим.

В этом смысле мне пока нравиться подход Kelsey Hightower из coreos — изначально используется не busybox, а debian, и используется он внутри компании, так что остальные члены комманды могут добавлять в него свои слои. Но на этапе деплоя, контейнер передается в ci, который каким то образом его ужимает — в примере, так как ничего другого не нужно было, просто копировалась конечная папка с go бинарниками в новый, «продакшн» образ с дебианом как основной системой.

То есть ясно, когда можнo использовать busybox/scratch/alpine то использовать стоит их, но когда это уж очень сложно и тянет кучу зависимостей — думаю больший смысл имеет использовать большой образ внутри, но ужать его при деплое. И я думаю такие механизмы «ужатия» будут появляться в будущем.

Поэтому я не вижу будущего за отдельными образами (busybox/scratch/alpine), а скорее за самим докером или скорее за rkt, как унифицированной спецификацией контейнеров, где этот механизм можно будет реализовать.

Просто пока, такой единственный механизм я увидел только в этой статье, при помощи профилирования, которая ясно подходит далеко не для всего, но на сколько я знаю пока единственная, которую можно автоматизировать переведя в функцию ci сервера.
Внутри компании можно хоть 1ГБ образ использовать, лишь бы все от него наследовались и обновлялись вместе с базовым. Вот на Docker Hub заливать стоит, как мне кажется, только с наследованием от debian (официальный, а не один из 100500 кастомных) или минимальных бразов (scratch, busybox, progrium/busybox, alpine, cirros). К другим базовым образам нужно подходить с полным осознанием, что на debian это не взлетит (FreeIPA, например, имеет только rpm пакеты и все скрипты рассчитаны на Red Hat/CentOS/Fedora и проект достаточно большой) или вам нужна конкретная версия Python, например, тогда целесообразно использовать python:3.2.1-slim.

Да, иногда трудно с Alpine, но я уже спокойно с ним уживаюсь и мне интересно развитие musl libc. У меня есть целый проект, в котором я использую только образы наследованные от Alpine и проект работает замечательно. Всё-таки Alpine помогает иногда понять что на самом деле у моего приложения за зависимости, нужен ли мне bash и тд. Опять же, бывают случаи, например, в Python такое часто, когда модули имеют опциональные зависимости, но в приложении эти функции не используются, но модули будут пробовать подключать эти опциональные зависимости и они будут включены в финальную сборку если пользоваться методом описанным в статье, а в случае минимальных базовых образов такого не случится.
Ещё один нюанс — Over 30% of Official Images in Docker Hub Contain High Priority Security Vulnerabilities. То есть базовые образы таки надо обновлять и обновлять регулярно! Долгое время в Ubuntu образе был shellock, который каждый исправлял уже в своём образе через apt-get dist-upgrade, что приводило к ещё большему разбуханию образа.
Забавный эксперимент, хоть и неудачный. Было интересно почитать.
По fanotify обещал написать статью Michael Kerrisk, да что-то всё никак не возьмётся. У него на LWN была серия статей по механизмам уведомлений файловой системы: dnotify, inotify. Хорошо, что хотя бы в manpage есть пример использования.
Спасибо за ссылки, открыл для себя нового автора). Я вот как то наоборот — слышал про inotify и fanotify, но не слышал про dnotify. Вообще есть еще забавная, правда давно заброшенная loggedfs, а если уж совсем мониторить по полной — то можно и auditd в ядре включить.
Но только почему эксперимент неудачный? То есть понятно, что это вариант далеко не для всех задач — об этом и выше люди писали, а скорее для полностью детерминированного выполнения, и перевел я его просто потому что он показался мне остроумным, но в чем конкретно неудача?
Черт, это должен был быть ответ пользователю JIghtuse, я до этого не сталкивался с комментариями на хабре — ни удалить, ни отредактировать после истечения какого то времени.
Автор хороший, мэйнтэйнер manpages =) есть у него книга отличная — The Linux Programming Interface. Многие бывалые рекомендуют.

Неудачный — я к тому, что с натяжкой работает даже для ls. То есть польза от эксперимента определённо есть — приобретённые знания и опыт, но едва ли его можно применять где-то. Сомневаюсь в существовании детерминированного выполнения, посмотрите хотя бы на количество флагов ls. Кто знает, к каким файлам он обращается при вызове каждого. Что уж говорить о более сложных инструментах.

Только сейчас заметил, что это перевод — обычно метка висит. Хорошая работа, корявостей в языке не заметил.
Метки нет потому что статья из песочницы, насколько я понял там такую метку установить нельзя. Спасибо, старался)
Присмотритесь в сторону Rocket Container!
А как rkt решает эту проблему?
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории