Поиск по сайту:

7 анти-шаблонов Docker, которых нужно избегать


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

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

1. Применение обновлений внутри контейнеров

Возможно, самый распространенный антишаблон Docker — это попытка обновить контейнеры с использованием методов, перенесенных с традиционных виртуальных машин. Файловые системы контейнеров эфемерны, поэтому все изменения теряются при остановке контейнера. Их состояние должно быть воспроизведено из Dockerfile, используемого для создания образа.

Это означает, что вам не следует запускать apt upgrade внутри ваших контейнеров. Тогда они будут отличаться от образа, из которого они были созданы. Контейнеры предназначены для свободной взаимозаменяемости; отделение ваших данных от вашего кода и зависимостей позволяет вам заменять экземпляры контейнера в любое время.

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

2. Запуск нескольких сервисов внутри одного контейнера

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

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

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

3. Сборки изображений с побочными эффектами

Сборки образа Docker должны быть идемпотентными операциями, которые всегда дают один и тот же результат. Запуск docker build не должен ни в малейшей степени влиять на вашу более широкую среду, поскольку его единственная цель — создать образ контейнера.

Тем не менее, многие команды создают файлы Dockerfile, которые манипулируют внешними ресурсами. Dockerfile может трансформироваться в форму всеобъемлющего CI-скрипта, который публикует релизы, создает Git-коммиты и записывает во внешние API или базы данных.

Эти действия не принадлежат Dockerfile. Создание образа Docker — это независимая операция, которая должна быть отдельным этапом конвейера CI. Затем подготовка к выпуску происходит как отдельный этап, поэтому вы всегда можете собрать docker без неожиданной публикации нового тега.

4. Чрезмерное усложнение вашего Dockerfile

Точно так же Dockerfiles может делать слишком много. Ограничение вашего Dockerfile минимальным набором необходимых вам инструкций минимизирует размер вашего образа и повышает удобочитаемость и удобство сопровождения.

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

Ищите логические разделы в вашем Dockerfile, которые служат определенным целям. Попробуйте разбить их на отдельные файлы Docker, создав автономные служебные образы, которые могут работать независимо, чтобы выполнять части вашего более широкого конвейера.

Вы можете создать образ «сборщика» с зависимостями, необходимыми для компиляции исходного кода. Используйте это изображение в качестве одного из этапов конвейера непрерывной интеграции, а затем передавайте его выходные данные в виде артефактов на следующий этап. Теперь вы можете скопировать скомпилированные двоичные файлы в окончательный образ Docker, который вы используете в производстве.

5. Жестко закодированная конфигурация

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

Используйте переменные среды и объявленные секреты Docker для внедрения конфигурации в момент запуска контейнера. При этом изображения сохраняются как активы многократного использования, а доступ к конфиденциальным данным ограничивается только во время выполнения.

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

6. Отдельные образы для разработки и развертывания

Вы должны создавать только один образ контейнера для каждого изменения в вашем приложении. Поддержание нескольких похожих образов для отдельных сред предполагает, что вы не получаете выгоду от менталитета Docker «работает где угодно».

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

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

7. Хранение данных внутри контейнеров

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

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

Запись файловой системы контейнера также может привести к значительному снижению производительности при изменении существующих файлов. Использование Docker стратегии многоуровневого копирования «копирование при записи» означает, что файлы, существующие на нижних уровнях файловой системы, считываются с этого уровня, а не с последнего слоя вашего образа. Если в файл внесено изменение, Docker должен сначала скопировать его на самый верхний уровень, а затем применить изменение. Этот процесс может занять несколько секунд для больших файлов.

Заключение

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

Контейнеры должны быть эфемерными, автономными единицами функциональности, созданными в результате воспроизводимого процесса сборки. Они сопоставляются с этапами вашего процесса разработки, а не со средами развертывания, но сами по себе напрямую не способствуют этому процессу. Изображения должны быть артефактами, создаваемыми конвейером CI, а не механизмом, определяющим этот конвейер.

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