Я участвовал в качестве сисадмина во множестве проектов, где моей основной обязанностью была поддержка процесса разработки, какое-то время сам был разработчиком. В последние 6-7 лет с интересом наблюдал за развитием docker как технологии, и, к сожалению, вынужден был отметить довольно характерную и для docker, и для контейнеризации в целом проблему, которую я для этой статьи могу назвать "Главным недостатком docker'а", не хейта ради, но конструктивной дискуссии для. Впрочем, "спешу огорчить", в данном случае речь идёт не о каком-то фатальном изъяне в самой технологии, речь - всего лишь о характерной проблеме, связанной с реальной практикой её [технологии] применения, то есть по сути в «человеческом факторе».  В чём же заключается эта проблема? Начать стоит с того, что когда разработчик задаётся вопросом о преимуществах упаковки в docker-контейнеры, ему в голову прежде всего приходят не какие-то прогрессивные фишки с динамическими перемещениями контейнеров между нодами k8s-кластера и связанными с этим HA/FT или эффективной (? на самом деле не очень эффективной, как мы уже знаем) изоляцией приложения от операционной системы и других приложений, работающих на том же хосте. Разработчику как правило это не особо интересно, он думает прежде всего об удобстве реализации CI/CD-конвейеров с использованием docker, а также конечно о неизменности программного окружения приложения, о фиксации его зависимостей.  И вот о последнем как раз хотелось бы поговорить подробнее, поскольку, на мой взгляд, это и является одной из главных фишек контейнеризации с т.з. многих разработчиков, особенно не слишком обременённых желанием вникать хотя бы в базовые концепции docker. Для чего разработчикам нужна фиксация зависимостей? Давайте попробуем заглянуть в историю и вспомнить о том, что в былинные времена являлось вполне мейнстримовой методикой упаковки любого софта (речь здесь и далее идёт исключительно о Linux). Такой методикой было «пакетирование», т. е. сборка пакетов под целевые дистрибутивы Linux, с непременным и набившим оскомину перечислением зависимостей пакета в его "спеке". И упомянутая методика с точки зрения разработчиков, безусловно, имела ряд объективных недостатков, как то: необходимость сборки пакетов  под каждый конкретный дистрибутив, который может быть установлен у заказчика, необходимость тестирования в разных средах, что требовало наличия отдельной тестовой среды для каждого целевого дистрибутива в отдельности и т.д.. Прежде всего именно наличие этих проблем в старом подходе к поставке ПО сделали docker столь популярным. Но при этом с началом широкого шествия docker по планете и как следствие - в результате смены главенствующей парадигмы поставки ПО, в радостной суматохе и эйфории было начисто забыто и одно из  главных преимуществ столь нелюбимой разработчиками пакетной системы зависимостей. А именно: если раньше можно было не беспокоиться о своевременном обновлении используемых софтом библиотек, поскольку это было обязанностью системных администраторов, делающих это вручную или автоматически, то теперь обязанность организации таких обновлений легла на хрупкие плечи разработчиков, собирающих свои контейнеры из «слоёв» и самостоятельно определяющих, что в этих слоях будет лежать. И вот тут-то и возник на горизонте тот самый «Главный недостаток docker». Дело в том, что разработчики в (абсолютном) большинстве своём  относятся к обновлению зависимостей как к некоему фееричному геморрою, приводящему потенциально к неработоспособности их софта или по меньшей мере к некоторому нервному напряжению и беспокойству в ожидании результатов тестирования с обновлёнными зависимостями. К чему это по идее должно было бы приводить? К тому, что базовые слои, содержащие зависимости, особенно такие глобальные как libssl и libcrypto, должны бы регулярно и принудительно обновляться, особенно после выхода соответствующих бюллетеней безопасности CVE — причём заниматься этим должны конечно же не разработчики, а сисадмины. После форсированного обновления зависимостей, разумеется, должны стартовать конвейеры CI/CD, тестирующие разработанный софт на совместимость с новыми версиями библиотек. И да, в каких-то случаях это неизбежно должно приводить к тому, что разработчикам придётся фиксить проблемы нарушенной совместимости, что категорически не может им понравится, поскольку время будет потрачено вроде бы на посторонние вещи, да и вообще «ну работало же, зачем вы всё сломали?!». Именно поэтому, в том числе и с учётом во многом привилегированного статуса разработчиков по отношению к другим IT-специалистам (ведь именно девелоперы удовлетворяют потребности бизнеса), - управление зависимостями для софта, упакованного в docker, часто происходит по сильно «упрощённому» регламенту. В лучшем случае пересобираются базовые слои, от которых созданный компанией софт зависит максимум на уровне использования 2-3х функций glibc. Реально же критичные зависимости, особенно близкие к предметной области софта, остаются при этом навечно лежать грудой дурно пахнущего кода внутри контейнеров непосредственно с целевым софтом или же в тех «кулуарных» для разработчиков слоях, до которых рука сисадмина никогда и не дотянется. Что такое docker-контейнер (да и в общем любой другой тоже) с точки зрения системного администратора и/или специалиста по IT-безопасности? Это маленький дистрибутив. По сути ведь не принципиально, дистрибутив это Linux или некоего Java-сервлета, запускающегося под JBoss. Суть в том, что это некий маленький программный комплекс, в котором код приложения или библиотеки X может использовать тот же библиотечный сервис Y, что и приложение или библиотека Z. В «больших» дистрибутивах Linux вся идеология поставки ПО построена на экосистеме разделяемого кода, обслуживаемого централизованно и обновляемого синхронно для всех приложений. В маленьких контейнерах docker всё примерно так же: там в большинстве случаев (в том числе и для golang-приложений с внешними C-dependencies) есть разделяемый код и есть код приложений, которые его используют.  Всё это упаковано в один файлик и выглядит как монолитное целое, но в действительноcти внутри это — маленький дистрибутив, в чём-то похожий на тот же Debian или Ubuntu. Но если у вас на виртуалке стоит Debian 7-й версии — у вас это непременно вызовет определённое беспокойство и даже может быть связано с рядом неудобств (пакетная система просто не даст установить что-либо новое). Но мало у кого вызывает беспокойство тот факт, что в контейнере docker как ни в чём ни бывало может лежать и дурно пахнуть Java-класс, разработанный 10 лет назад какими-то хорошо забытыми людьми, которые уже давным давно свой код не поддерживают, древние версии 100 раз опороченных во всех CVE библиотек шифрования и создания защищённых соединений или даже маленький и всем-очень-удобный web-сервер, находящийся в первозданном состоянии, характеризуемом как «лакомый кусочек для скриптикидиса». Повторюсь, проблема в данном случае не с докером как таковым — её, например, вполне успешно решают в крупных компаниях, где когда-то над всем этим крепко задумались и пришли к осознанию необходимости выработки разумной системы управления зависимостями внутри контейнеров. Проблема прежде всего в реальной практике многих компаний, особенно не полностью IT-специфичных. Корень её - в том, что если обязанности по системному администрированию и контролю безопасности используемого ПО отдаются на откуп тем, кто абсолютно не заинтересован ни в каком администрировании и безопасности, то есть разработчикам, то и результат получается аховым. Это такой замкнутый круг: разработчики используют docker, чтобы меньше возиться с проблемами глобального общесистемного масштаба (пакетная система, зависимости), но в итоге оказываются сами вовлечены в решение тех вопросов, которыми обычно занимались системные администраторы (обновления пакетов, установка патчей безопасности). Вот только в отличие от системных администраторов разработчики чаще попросту «забивают» на эти вопросы, если они вообще где-то существуют хотя бы на периферии их внимания. Также хотелось бы отметить, что порочная практика накопления внутри docker-контейнеров «протухающих» зависимостей может приводить и к множеству проблем, своими негативными последствиями затрагивающих рано или поздно самих разработчиков: При выявлении багов в сторонней библиотеке или болезненных просадок производительности в активно используемой приложением, но уже давным-давно не поддерживаемой библиотеке — придётся в лучшем случае дорабатывать чужой legacy-код, а в худшем — существенно переписывать собственное приложение; Желание использовать интересные фичи актуальной версии 5 лет не обновлявшейся внутри контейнера сторонней библиотеки — может также привести к массивной переработке кода основного приложения, чего бы не случилось, если бы приложение адаптировалось к изменениям в стороннем коде постепенно и своевременно, а не одним мучительным «дискретным переходом»; Сама по себе необходимость поддержания в компании сакральных знаний о каком-то давно умершем или устаревшем библиотечном софте — неизбежно приводит к появлению среди разработчиков незаменимых людей, которых невозможно ни уволить, ни разгрузить как-то по объёмам работы. Ведь в таком случае нанять новых сотрудников с актуальными для данной компании знаниями получится, лишь отправив за ними рекрутёра на машине времени в прошлое; В случае необходимости обновления рантайма языка или версии компилятора наличие в зависимостях неких не вполне свежих сущностей — может запросто вызвать лавину абсолютно несвязанных с разработкой в предметной области проблем и задач, вплоть до полной замены всех зависимостей на более современные аналоги… с консервацией этих аналогов ещё на 5 лет, до тех пор, пока уже они не «встанут поперёк прогресса».