Обновить

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

Оптимизация - это когда была программа размером N мегабайт, а стала 0.9N. Или даже 0.5N, если первая версия была ну очень неоптимальной. Ну пусть даже 0.1N, ладно.

Но когда образ одной и той же программы может весит 9 мегабайт, а может 2000 - это уже ни фига не "оптимизация", это уже из области "да вы охренели, что ли?".

это уже ни фига не "оптимизация", это уже из области "да вы охренели, что ли?"

Нет, скорее это из области "прочел документацию - узнал как надо было делать изначально", multi-stage build это буквально база того, как упаковывать в образ компилируемые приложения.

Как я уменьшил вес моего автомобиля с 2 тонн до 1 тонны
Начал с того, что убрал кузова мешки с цементом, которые я возил просто так

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

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

Это если сзади рессорная подвеска. Иначе "козлит" он сильно на скорости.
Ну или если хотят усилить сцепление задней оси с дорогой (зимой например).

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

И он тебе все это, минимум ДВП, а то и секретное, рассказал?

Да, рассказал. Причем мой родитель неуемной фантазией не страдал, так что не верить ему у меня оснований не было.

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

Во-вторых, рассказал он это не в 80-х, а уже в 90-х, когда и группы его давно перестали действовать, и волг этих не осталось, да и самого КГБ уже давно не существовало.

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

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

Ты же знаешь, что на порядок — это примерно в 10 раз?

Ты же знаешь, что "на порядок" - это на один знак? 1E+2 на порядок больше 9,9999E+1. Прикинь?

Один знак — это и есть порядок. У твоего жигуля мешок в багажнике реально увеличивает проходимость во много раз (в несколько раз, примерно раз в 10)?

Ты недостаточно хорошо понимаешь значение слова "порядок". 100 (1E+2) на порядок больше, чем 99 (9.8E+1). Разница в 1%. 999 (9.99E+2) тот же порядок, что 100. Хотя разница почти в 10 раз. В твоем определении из википедии (или откуда оно) в первом определении серьезная ошибка, на порядок - это не в 10 раз больше, а на один знак.

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

Знамо что — хабар. И присматривать за ним удобно.

Блиин, так не смешно же!

Хорошая заметка, которая четко показывает что правильным подходом является использование multi-stage с самого начала.

А вот UPX - мне кажется подход немного спорный. Замедление старта, лишние расходы по CPU (а процессор ресурс довольно дефицитный). Выигрыш по сети и хранении места образа в такой ситуации мне кажется не перевешивает негативные стороны.

Полностью согласен, UPX это спорный и редкий кейс, в статье я постарался отметить его как бонус, подчеркнув недостатки такого подхода.

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

Откройте для себе distroless - минимальные runtime образы для разных языковых стеков от Google. В частности для Go есть static, который - по сути то к чему вы пришли вручную (scratch + серты около 2Мб в сумме) - только бинарник скопировать и все.

Выигрыша от UPX примерно 0 т к слои образов в регистри хранятся и так в сжатом виде (gzip), соотвественно pull качает меньший объем данных. Если вы дополнительно сожмете бинарник, то сжатие этого слоя будет просто неэффективным - лишнее нагревание воздуха. После пула, образы хранятся локально уже в распакованном виде - это позволяет получить быстрый старт, кажется найти несколько лишних Мб для этого не проблема. Т.к. чудес не бывает и в памяти ваше приложение все равно займет ровно столько места сколько было до сжатия, но при этом опять нагрев воздух затратами на распаковку (впрочем тут CPU надо меньше чем на сжатие).

«Вы даете нереальные планы» люди не в курсе что в go, все модно в один бинарное компильнуть и скопировать куда надо. Чем дев расы заниматься будут?

Спасибо за развёрнутое объяснение!

Про distroless: я согласен, это отличное решение, показал его как компромисс между alpine и scratch. gcr.io/distroless/static-debian12 это по сути то, к чему я пришёл вручную (scratch + сертификаты), но с лучшей поддержкой и проверенными зависимостями.

Про UPX: его добавил больше с целью общего развития, чтобы показать как можно достичь максимально минимального образа.

Начиная с версии 23.0 поддерживается zstd сжатие.
https://docs.docker.com/engine/release-notes/23.0/
В том числе пересжатие базовых образов с gzip через force-compression=true
https://aws.amazon.com/blogs/containers/reducing-aws-fargate-startup-times-with-zstd-compressed-container-images/

И для Lazy Pulling отдельно по слоям можно использовать zstd:chunked вместо eStargz
https://www.redhat.com/en/blog/faster-container-image-pulls
https://www.slideshare.net/slideshow/starting-up-containers-super-fast-with-lazy-pulling-of-images/244154126

Согласен с автором выше. Посмотрите вот на эту статью.

В конце статьи показан пример (Multi-stage builds), который использует образы distroless от Google. Удобно

Вопрос на отвлеченную тему. Почему Gin? С Go 1.22 появился достаточно приятный роутинг в net/http + пакет сам по себе достаточно хорошо работает

Спасибо за вопрос!

Честно говоря, Gin выбрал просто как удобный для меня инструмент. В Go-разработке я не силён, поэтому на коде не зацикливался. Фокус был именно на оптимизации Docker образа.

Спасибо за ответ! Рекомендую присмотреться к net/http, он правда хорош сейчас)

статья в общем-то неплохая, но можно поменьше ушей от ИИ? раздражают эти подведения итогов на каждом шаге

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

Мне вот gemini тоже так отвечает на мои замечания: позитивный ответ, обещает учесть эти замечания ..
Какая-то ИИ-паранойя 🤖

вы абсолютно правы. кожаным мешкам стоит изменить промпт.

На Хабре уже есть несколько хороших статей и про distroless (не только от гугла), и про image layers в целом.
В данном экземпляре даже dive не упомянут, не говоря уже о strip/objcopy/билдфлагах go и тп.

Про image layers видимо не достаточно, раз постоянно все в один слой собирают, а потом запускают штук 50 сборок на этом 5гб образе одновременно. А у меня канал в сотку всего....

У меня нет особо опыта в этом, но не очень понял - для чего вообще делать в контейнере, если собирается бинарник без зависимостей? Почему бы его напрямую не запускать?

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

Контейнеры это просто изоляция. Мы с докера ушли и ее реализуем стандартными инструментами linux типа cgroups. Кому-то эта реализация покажется не по феншую, но тут каждый выбирает свой способ построения своей инфраструктуры. Но в общем и целом я с вами согласен.

Почему не podman?

Мы не используем докер после перехода на Golang. В том числе мы перестали дробить на мелкие куски и нет задачи в наших реалиях а-ля: «ой там endpoint не справляется, добавим еще пару микросервисов для распределения нагрузки..»

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

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

Поэтому какая-то прокладка podman лишняя, как и сам докер

А сборка в dev среде и деплой на прод - всегда удачные? Я думал над аналогичной вещью, была мысль собирать/компилировать именно на прод среде. Но пока в сыром виде эта идея так и висит.

Выкладывать на прод бинарь который не тестировался и даже не запускался так себе идея.

Любой бинарь собирает CI. Где-то в своем окружении. И дальше он едет через все тесты и все среды без изменений.

Всегда успешные, даже с учетом того что есть разные процессоры (amd/intel), но безусловно придется продумать немного. У нас UI вызывает bash скрипты, где зашиты конкретные действия которые надо выполнить перед и после деплоя на сервере (например стопануть приложение и тд)

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

для чего вообще делать в контейнере, если собирается бинарник без зависимостей?

Это единица изоляции.

И самое главное - единица масштабирования.

сегодня нужно 2, завтра 10 а послезавтра Н

Если все это в контейнерах и соот. требованиям CNA операция масштабирования - рутинная операция

Это единица изоляции.

Нынче изоляция легко делается через systemd

Пока масштабирование не нужно, запускаем голые бинарники. Все же 70Мб - это 70Мб, быстро просто и памяти больше остается. Если же начинаются истории - нам нужно 10..20..30 инстансов - то тут да, надо что-то уже применять, чтобы хотя бы администрировать проще было.

У меня нет особо опыта в этом, но не очень понял - для чего вообще делать в контейнере, если собирается бинарник без зависимостей? Почему бы его напрямую не запускать?

На моем компьютере работает. И значит на любом заработает.

На моем компьютере работает. И значит на любом заработает.

А вот нифига, образ с elasticsearch 8.2 не запускается на современных линуксах например

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

Эх... Кликбейт... Я когда увидел, что бинарник весит 2гб, то аж сильно удивился что там могло быть, что автор говорит аж о целых 200 раз сжатия... а оказывается речь зашла о том, что автор изначально в образ весь SDK тянул, что как бы не удивительно, что размер образа очень сильно разбух :/

Странная статья. Я такие могу хоть каждый день писать: написал приложение на С++, сделал образ на 100 Гб, потом выкинул студию и игры - получил 50 Гб, выкинул винду - осталось 10 Мб, ... вау в 10000 раз уменьшил.

В общем-то статья же полезна. В особенности для тех кто не знаком с multistage сборкой. Кликбейт это да, конечно. Но в целом не умаляет трудов автора.

Я когда иду в поход, беру с собой гантелю. И вот когда прошёл 2/3 пути, и ноги уже не идут, и натёр, и задолбало, и всё до фонаря уже - тогда выкинешь гантелю нахрен, и остаток пути идёшь легко, вприпрыжку, счастливый!

Этак гантелей не напасёсси на вас...

А зачем все эти приседания с adduser и ручным копированием /etc/passwd и /etc/group, когда можно сделать USER 1000? В директивах COPY --chown аналогично, именованные пользователи и группы не требуются.

Безусловно, USER 1000 - более простой вариант, и создание явного пользователя и группы необязательно, но я предпочитаю подход, который использовал в статье. Считаю оба подхода верными - каждый выбирает тот, который ему по душе.

По тексту поста:

Проблема: в отличие от прошлого образа мы не можем создать пользователя в финальной стадии.

Решение:

Вы решаете проблему, которой нет.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации