%

начни
бесплатно

01:42:24

2 дня

%

Все статьи

Многоэтапная сборка Docker: от bloated-образов к безопасным production-ready контейнерам. Почему root убивает вашу безопасность

Рассказываем, как сократить вес Docker-образов, ускорить CI/CD и повысить безопасность: multi-stage сборка, отказ от root, кейсы для разных языков

Архитектура надежных образов
Архитектура надежных образов

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

Материал будет полезен backend-разработчикам, DevOps-инженерам, тимлидам, системным администраторам и junior-специалистам, изучающим Docker и CI/CD.

Проблема «тяжелых» Docker-образов: почему это важно

Однажды вы запускаете сборку и с удивлением обнаруживаете, что образ вашего скромного приложения весит больше одного гигабайта. Ситуация не редка, особенно если речь идет о Node.js или Python, где в образ могут попасть десятки лишних зависимостей, исходники и даже инструменты для сборки, которые в продакшене не нужны.

Большие Docker-образы — это не просто неудобство. Это прямой удар по скорости CI/CD, по затратам на трафик, по времени деплоя и, самое главное, по безопасности.

Каждый лишний слой, каждая утилита или библиотека — потенциальная точка атаки. А если вы еще запускаете такой контейнер от root-пользователя — это уже не просто ошибка, а приглашение для злоумышленника.

Пример. Приложение на Node.js после стандартной сборки весит 1.2 ГБ. Почему?

В образ попали:

  • node_modules, включая dev-зависимости;

  • исходный код, тесты, логи;

  • сборочные инструменты — webpack и eslint;

  • base image на основе ubuntu, а не alpine.

Результат — медленные деплои, переполненный кеш на серверах, проблемы при прохождении аудитов. А теперь представьте, что вы работаете в команде и таких проектов — десятки. Умножьте это на стоимость трафика и скорость работы пайплайна — и станет ясно, что размер Docker-образа имеет значение.

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

Нет времени читать статью?

Получите ответы от практикующих специалистов на бесплатном занятии в вашем городе

Нажимая на кнопку, я соглашаюсь на обработку персональных данных

Что такое multi-stage build в Docker и зачем он нужен

Большинство разработчиков знают, что Dockerfile можно написать просто: указать базовый образ, установить зависимости, скопировать код, запустить. Но именно такой подход и приводит к появлению «монстров» по 2–3 гигабайта. К счастью, у Docker есть мощный инструмент, который позволяет делить сборку на этапы — multi-stage build.

Принцип прост: на первом этапе вы собираете приложение, на втором — упаковываете только нужные файлы. Это как сначала сшить костюм, а потом — выбросить все лекала, нитки и обрезки ткани.

Как это выглядит на примере оптимизации Go приложения:

FROM golang:1.20 AS builder
WORKDIR /app
COPY . .
RUN go build -o app


FROM alpine:latest
WORKDIR /root/
COPY --from=builder /app/app .
CMD ["./app"]

Multi-stage build в Docker — фундамент для продакшен-образов. Он внедряется за один день, а пользу приносит на месяцы вперед.

Освоить все тонкости работы с Docker вы можете на курсе Академии ТОП «DevOps-инженер». Кроме того, вы научитесь писать скрипты и приложения, которые помогут разработчикам, тестировщикам и системным администраторам автоматизировать рутинные процессы и получите востребованную и высокодоходную специальность.

Безопасность Docker: почему запуск контейнера от root — это ошибка

Запускать контейнер от root-пользователя — почти то же самое, что и доверять всем внешним зависимостям и библиотекам как себе. Это риск, который редко оправдан. Если хакер найдет уязвимость в приложении, работающем от root, он получит права суперпользователя в контейнере. А выход за его пределы — вопрос времени.

Сценарий под названием container escape — не миф, а зафиксированная проблема безопасности. Особенно опасны образы, в которых установлены ненужные системные утилиты — curl, wget, bash, nano. Именно они становятся инструментом для атак.

Правильный путь — использовать USER в Dockerfile и создавать отдельного пользователя с минимальными правами. Пример:

RUN addgroup app && adduser -S -G app app

Теперь даже если злоумышленник проберется внутрь, его действия будут ограничены. В связке с multi-stage билдом это превращает ваш образ в почти неприступную крепость.

Запуск контейнера без root — это не опция, а стандарт, к которому нужно стремиться по умолчанию.

Практические кейсы оптимизации Docker-образов

Ниже примеры оптимизации образов на разных технологиях.

Node.js: исходный образ весил 1.2 ГБ, содержал все зависимости, dev-инструменты, тесты. После применения multi-stage build и перехода на node:slim — 85 МБ. Разница более чем в 10 раз.

Python: вместо установки всех зависимостей из requirements.txt использовали разделение на requirements-dev.txt и requirements-prod.txt. Кроме того, в финальный образ включали только runtime-библиотеки и очищали кеш pip. Без оптимизации вес образа — 700 МБ, с — 400 МБ.

Spring Boot (Java): изначально использовался OpenJDK full с Maven внутри. Образ весил 1.1 ГБ. После перехода на сборку через Gradle и копирования только jar-файла во второй stage, а также смены базового образа на eclipse-temurin:17-jre-alpine — минус 600 МБ.

Оптимизация Docker-образов для разных языков — это не про тонкие настройки, а про эффективный подход. Каждый лишний файл — это потенциальная уязвимость и пустая трата ресурсов.

Мы собрали подборку курсов для людей с разным уровнем подготовки

Хотите стать программистом?

Мы собрали подборку курсов для людей с разным уровнем подготовкиПерейти

Продвинутые техники Docker-сборки

Когда базовые принципы освоены, время сделать следующий шаг и вывести образы на новый уровень. Для этого воспользуемся .dockerignore. Этот файл —как .gitignore, но для Docker. Если вы не исключите файлы node_modules, .git, tests и логи, они попадут внутрь образа. И увеличат его размер на сотни мегабайт.

Пример содержимого файла .dockerignore:

node_modules
.git
tests
*.log

Кеширование слоев — еще один важный момент. Не секрет, что Docker строит образы поэтапно. Если сначала вы устанавливаете зависимости, а потом копируете код, при каждом изменении кода зависимости не пересобираются. Это сокращает время сборки с минут до секунд.

И, наконец, базовые образы. Вместо Ubuntu или Debian используйте Alpine или Distroless. Первый — суперлегкий (около 5 МБ), но работает на musl, а не glibc, поэтому некоторые библиотеки могут быть несовместимы. Второй — вообще без shell и package manager, что идеально для продакшена, но несколько усложняет отладку и требует тщательной подготовки окружения.

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

Курс Академии ТОП «Python разработчик с нуля до PRO» идеально подходит тем, кто хочет расширить свои навыки от разработки backend на Python до контейнеризации и развертывания приложений.

Чек-лист: как собрать production-ready Docker-образ

Оптимизированный production-ready Docker-образ — всегда результат строгого соблюдения инженерной дисциплины. Если вы хотите, чтобы он не просто работал, а был надежным, быстрым и безопасным — проверьте его по списку:

  • Размер финального образа — не превышает 500 МБ.

  • Используется multi-stage build для отделения build-time зависимостей.

  • В образе только runtime-библиотеки — никаких дев-утилит.

  • Контейнер запускается не от root.

  • Используется .dockerignore.

  • Базовый образ — slim, alpine или distroless.

  • Все зависимости зафиксированы (версии, хеши).

  • Применены обновления безопасности.

  • Пройден анализ уязвимостей (например, docker scan, trivy, snyk).

Docker — мощный инструмент, но только в умелых руках он превращается в безопасный и производительный механизм доставки приложений. Не надо собирать образы по наитию — внедряйте стандарты и подходы, которые работают. Multi-stage, отказ от root, правильные базовые образы — минимум, без которого контейнеризация теряет смысл.

Источники и полезные ссылки:

https://docs.docker.com/build/building/multi-stage

https://github.com/GoogleContainerTools/distroless

https://snyk.io/blog/10-docker-image-security-best-practices

https://docs.docker.com/engine/scan

Об эксперте

Михаил Шмаров — преподаватель Python и DevOps в Академии ТОП. Автор учебных программ и методик обучения современным языкам программирования и инструментам разработки. Автор учебника The Ultimate Docker Mastery Guide for DevOps Engineers. 

Хотите лучше разобраться в вопросе?

Приходите на бесплатное занятие в вашем городе и получите ответы от практикующих экспертов

Нажимая на кнопку, я соглашаюсь на обработку персональных данных

Мы свяжемся с вами в течение дня

💫

Перезвоним и поможем подобрать курс

👍

Запишем на бесплатные пробные занятия

💯

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