0 3.4K ru

8 best practices по использованию Docker в продакшине

Categories: 💻 Programming

Зачем использовать лучшие практики? 🤷‍♀️

✅ улучшение безопасности;

✅ оптимизирование размера docker image;

✅ воспользоваться некоторыми полезными функциями Docker;

✅ более чистые и удобные в сопровождении Dockerfile;

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

Допустим, вы разрабатываете Node.js приложение и хотите собрать и запустить его как образ Docker.

Вместо того чтобы брать базовый образ операционной системы и устанавливать node.js, npm и любые другие инструменты, необходимые для вашего приложения, используйте официальный  node image для своего приложения.

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

Улучшения которые привнесет эта практика

  • Более чистый Dockerfile
  • Официальный и проверенный image, который уже создан с учетом лучших практик

2. Используйте конкретные версии Docker image

Итак, мы выбрали базовый image, но теперь, когда мы создаем image нашего приложения из этого Dockerfile, он всегда будет использовать latest тег node image.

Почему это может быть проблемой?

❌ - вы можете получить другой  image version, нежели  в предыдущей сборке

❌ - новая версия image может сломаться

❌ - latest тег непредсказуем, что может привести к неожиданному поведению приложения

Таким образом, вместо рандомного поведения latest тега image,  вы можете зафиксировать версию, и точно так же, как вы развертываете свое собственное приложение с определенной версией, вы хотите использовать официальный образ с определенной версией.

И здесь действует правило: чем конкретнее, тем лучше.

Используйте конкретные версии Docker image

Улучшения которые привнесет эта практика

  • Вы точно знаете какую версию вы используете, никакие breaking changes в базовом image не поломают вам билд.

3. Используйте официальные Image'ы небольшого размера

На примере с Node.js вы увидите, что на самом деле существует несколько официальных image. Не только с разными номерами версий, но и с разными дистрибутивами операционных систем:

. Используйте официальные Image'ы небольшого размера

Итак, вопрос: какой из них выбрать и почему это важно? 🤷🏻‍♂️

1) Размер Image

❌ Если image основан на полноценном дистрибутиве ОС, таком как Ubuntu или Centos, у вас будет набор инструментов, уже упакованных в образ. Таким образом, размер image будет больше, но вам не потребуется большинство этих инструментов в image приложений.

✅ Напротив, наличие image меньшего размера означает, что вам нужно меньше места для хранения в image repository, а также на сервере развертывания, и, конечно же, вы можете быстрее передавать image при извлечении или отправке их из хранилища.

2) Security Issue

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

Таким образом, вы с самого начала вносите ненужные проблемы с безопасностью в свой image! 🙉

✅ В сравнении с использованием образов меньшего размера с более компактными дистрибутивами ОС, которые комплектуют только необходимую систему инструментов и библиотек, вы также минимизируете поверхность атаки и создаете более безопасные images.

Таким образом, наилучшей практикой здесь будет выбор образа с определенной версией на основе более компактного дистрибутива ОС, такого как, например, alpine:

Security Issue docker

В Alpine есть все необходимое для запуска приложения в контейнере, но он намного легковеснее. И для большинства images, которые вы просматриваете на Docker Hub, вы увидите тег версии с alpine  дистрибутивом внутри.

Это один из самых распространенных и популярных базовых image для Docker контейнеров.

4. Оптимизация кэширования для image слоев при image build

Так что же такое image слои и что означает кэширование в этом контексте? 🤔

1) Что такое image слои?

Docker image создается на основе Dockerfile.

А в Dockerfile каждая команда или инструкция создает "слой":

Оптимизация кэширования для image слоев при image build

Поэтому, когда мы используем базовый image node alpine, как в приведенном выше примере, у него уже есть слои, потому что он уже был создан с использованием собственного Dockerfile. Кроме того, в нашем Dockerfile помимо этого у нас есть еще пара команд, каждая из которых добавит новый слой к этому imag'y.

2) А что с кешированием?

Каждый слой будет кэшироваться Docker'ом. 👍

Поэтому, когда вы ребилдите свой image, если ваш Dockerfile не изменился, Docker просто использует кешированные слои для создания image.

Преимущества cached image:

✅ - Более быстрое создание image

✅ - Более быстрый pulling и pushing новых версий image:

Если я запулю новую версию image того же приложения и, скажем, в новой версии будут добавлены 2 новых слоя: будут загружены только вновь добавленные слои, остальные уже локально кэшируются докером.

3) Оптимизация кэширования

Итак, чтобы оптимизировать кеширование, вам нужно знать, что:

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

Итак, правило здесь и лучшая практика:

Отсортируйте свои команды в Dockerfile от наименее до наиболее часто меняющихся команд, чтобы воспользоваться преимуществами кэширования и таким образом оптимизировать скорость создания образа. 🚀

5. Используйте .dockerignore file

Обычно, когда мы создаем образ, нам не нужно все, что у нас есть в проекте, для запуска приложения. Нам не нужны автоматически сгенерированные папки, такие как папки target или build, нам не нужен файл readme и т. д.

Так как же исключить попадание такого контента в образ нашего приложения? 🤔

👉 Юзать .dockerignore.

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

Используйте .dockerignore file

Улучшения которые привнесет эта практика

  • Уменьшает размер imag'a

6. Используйте Multi-Stage билды

Но теперь предположим, что в вашем проекте есть некоторый контент (например, инструменты для разработки, тестирования и библиотеки), который вам НЕОБХОДИМ для создания image, но вам НЕ НУЖНЫ они в самом финальном image для запуска приложения.

Если вы сохраните эти артефакты в окончательном образе, даже если они абсолютно не нужны для запуска приложения, это снова приведет к увеличению размера image и увеличению площади атаки. 🧐

Так как же нам отделить стадию сборки от стадии выполнения? Другими словами, как исключить зависимости сборки из образа, оставив их доступными при сборке образа? 🤷‍♀️

Для этого вы можете использовать так называемые multi-stage builds 💡

multi-stage builds позволяет использовать несколько временных образов в процессе сборки, но оставить только последний image в качестве финального артефакта:

Используйте Multi-Stage билды

Таким образом, эти предыдущие шаги (отмеченные как «1 st» на рисунке выше) будут задискаржены.

Улучшения которые привнесет эта практика

  • Отделение инструментов сборки и зависимостей от того, что необходимо для runtime
  • Меньше зависимостей и уменьшенный размер imag'a

7. Используйте наименее привилегированного пользователя

Создав image и в конечном итоге запустим его как контейнер, какой пользователь операционной системы будет использоваться для запуска приложения внутри? 🤔

По умолчанию, когда в файле Dockerfile не указан пользователь, используется root user 🙉 Но на самом деле запускать контейнеры с правами суперпользователя чаще всего незачем.

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

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

✅ Чтобы избежать этого, лучше всего просто создать выделенного пользователя и выделенную группу в образе Docker для запуска приложения, а также запускать приложение внутри контейнера с этим пользователем:

Используйте наименее привилегированного пользователя

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

Совет. В некоторые образы уже встроен generic пользователь, который вы можете использовать. Таким образом, вам не нужно создавать нового. Например, образ node.js уже содержит общего пользователя с именем node, которого вы можете просто использовать для запуска приложения внутри контейнера. 👍

8. Сканируйте свои Docker image на наличие уязвимостей в системе безопасности

И, наконец, как убедиться в том, что созданный вами образ имеет несколько уязвимостей или вообще не содержит их? 🧐

Итак, моя последняя рекомендация: после создания образа сканируйте его на наличие уязвимостей безопасности с помощью команды docker scan. 🔍

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

Пример output'a команды docker scan:

Сканируйте свои изображения на наличие уязвимостей в системе безопасности

На экране мы видим следующее:

  1. тип уязвимости,
  2. URL-адрес для получения дополнительной информации
  3. но также, что самое главное, видно, какая версия соответствующей библиотеки действительно исправляет эту уязвимость. Таким образом, вы можете обновить свои библиотеки, чтобы избавиться от этих проблем. 👍

Автоматизируйте сканирование 🚀

Помимо сканирования образов вручную с помощью команды docker scan в интерфейсе командной строки, вы также можете настроить Docker Hub для автоматического сканирования образов, когда они пушаться в репозиторий. И, конечно же, вы можете интегрировать эту проверку в CI/CD pipeline при создании Docker образов.

Источник: dev.to

Comments:

Please log in to be able add comments.