// Engineering Log
Безопасная разработка: Часть 5 — Правильный Dockerfile
Опубликовано 22.09.2026
// Быстрый маршрут
Эта статья относится к теме Деплой и стабильная работа.
Dockerfile из трёх строк — FROM node, COPY . ., CMD npm start — работает, но в пайплайне такой образ собирается долго, каждый раз получается немного другим и плохо останавливается. Эта часть посвящена правильной сборке: воспроизводимости, кешу, секретам и процессу внутри контейнера. Минимальные базовые образы, сканирование и ограничения при запуске разобраны в части цикла о безопасности контейнеров.
Базовый образ: версия и digest
Тег latest и теги без версии меняются без предупреждения: сегодняшняя сборка и завтрашняя могут получить разные версии языка. Минимум — указывать версию. Надёжнее закрепить образ по digest, как рекомендует документация Docker: тогда сборка использует ровно тот образ, даже если издатель обновит тег.
FROM python:3.13-slim@sha256:<digest>Узнать digest можно командой docker buildx imagetools inspect python:3.13-slim. Чтобы закреплённые образы не устаревали, их обновляют через Dependabot (package-ecosystem: "docker") — он создаёт запросы на слияние с новыми тегами и digest по расписанию.
Версия должна быть поддерживаемой. На сентябрь 2026 года:
- Node.js 18 не получает обновлений безопасности с 30 апреля 2025 года, Node.js 20 — с 30 апреля 2026-го; для новых образов подходят LTS-ветки 22 (исправления безопасности до 30 апреля 2027 года) и 24.
- Python 3.9 не поддерживается с 31 октября 2025 года, у 3.10 поддержка заканчивается 31 октября 2026-го; для новых образов — 3.12 или 3.13.
Контекст сборки и .dockerignore
Всё содержимое каталога сборки передаётся сборщику, если его не исключить. Файл .dockerignore ускоряет сборку и не даёт лишнему — в том числе файлам с паролями — попасть в образ:
.git
node_modules
.env
*.log
distПорядок инструкций и кеш
Каждая инструкция создаёт слой. Если изменился слой, все последующие собираются заново. Поэтому сначала копируют файлы зависимостей и устанавливают их, а код — после:
COPY package.json package-lock.json ./
RUN npm ci
COPY . .Теперь правка кода не запускает установку зависимостей заново. npm ci ставит ровно то, что записано в lock-файле, — это и есть воспроизводимая сборка.
Системные пакеты устанавливают в одной инструкции с обновлением списка и очисткой, как рекомендует документация Docker:
RUN apt-get update && apt-get install -y --no-install-recommends \
libpq5 \
&& rm -rf /var/lib/apt/lists/*Многоэтапная сборка
Компиляторы, dev-зависимости и исходники нужны для сборки, но не для работы. Многоэтапная сборка оставляет в итоговом образе только необходимое:
# syntax=docker/dockerfile:1
FROM node:24-slim AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:24-slim
WORKDIR /app
ENV NODE_ENV=production
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
COPY --from=build /app/dist ./dist
USER node
CMD ["node", "dist/server.js"]Первый этап собирает приложение, второй получает только готовый результат и зависимости для работы. Инструкция USER node запускает приложение от непривилегированного пользователя, который уже есть в официальных образах Node.js.
Секреты при сборке
Токен для приватного реестра пакетов нельзя передавать через ARG или ENV: значения сохраняются в образе и видны в его истории. Для этого есть секреты BuildKit — они подключаются к одной инструкции и в слои не попадают:
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc npm cidocker build --secret id=npmrc,src=$HOME/.npmrc .По умолчанию секрет доступен как файл /run/secrets/<id>; параметр env= позволяет передать его в переменную окружения только для этой команды.
Процесс в контейнере: PID 1 и сигналы
Команда остановки контейнера отправляет главному процессу сигнал SIGTERM. Если CMD записан в shell-форме (CMD node server.js), главным процессом становится оболочка, и приложение сигнала может не получить — через таймаут контейнер завершается принудительно, незавершённые запросы теряются.
- Используйте exec-форму:
CMD ["node", "dist/server.js"]. - Если нужен скрипт запуска, он должен заканчиваться
exec "$@", чтобы приложение стало PID 1 и получало сигналы, — такой шаблон приводит документация Docker. - Если приложение порождает дочерние процессы, запускайте контейнер с
docker run --init(в Compose —init: true): Docker добавит маленький init-процесс на основе tini, который передаёт сигналы и убирает завершившиеся процессы.
Проверка Dockerfile
Статический анализатор hadolint находит типичные ошибки в Dockerfile — незакреплённые версии, shell-форму команд, лишние слои — и легко встраивается в пайплайн:
docker run --rm -i hadolint/hadolint < DockerfileТипичные ошибки
COPY . .в начале файла. Кеш зависимостей сбрасывается при любой правке кода.- Нет
.dockerignore. В образ попадают.git, локальныеnode_modulesи.env. npm installвместоnpm ci. Сборка может получить другие версии пакетов, чем записаны в lock-файле.- Токены в
ARGиENV. Их можно извлечь из истории образа. - Shell-форма
CMD. Приложение не получает SIGTERM и не успевает корректно завершиться. - Устаревшие базовые образы. Node.js 18 и Python 3.9 больше не получают исправлений безопасности.
// Похожая задача
Если у вас похожая ситуация
Эта статья относится к одной из рабочих тем. Можно продолжить чтение по теме, перейти на главную, чтобы понять, чем я занимаюсь, или сразу открыть услуги.
Тема статьи
Деплой и стабильная работа
Docker, CI/CD, релизы, мониторинг, observability и разбор инцидентов.
Часто с этим приходят
- Настроить деплой без ручных действий и хаоса
- Подключить мониторинг, алерты и базовую observability
- Разобрать инциденты и стабилизировать production
// Следующий шаг
Если вам нужна не только статья, а помощь по этой теме, удобнее сразу перейти в услугу. Главная и подборка материалов остаются рядом.
Открыть услуги// Reviews
Отзывы по теме
Пришел с дорогим запросом по настройке VPS-сервера, но в процессе консультации Михаил предложил гораздо более простое и экономичное решение. В итоге сэкономил бюджет и время. Михаил — настоящий эксперт, который работает на результат клиента, а не на чек. Рекомендую!
Пришел с дорогим запросом по настройке VPS-сервера, но в процессе консультации Михаил предложил гораздо более простое и экономичное решение. В итоге сэкономил бюджет и время. Михаил — настоящий эксперт, который работает …
Настройка vps, настройка сервера
12.05.2026 · ★ 5/5
Отличная работа! Очень быстро настроил сервер, установил панель, прописал IP. Однозначно могу порекомендовать!
Отличная работа ! Очень быстро настроил сервер, установил панель прописал IP Однозначно могу по рекомендовать !
Всё отлично, помог оперативно и профессионально, спасибо, рекомендую сообществу
Всё отлично, помог оперативно и профессионально, спасибо, рекомендую сообществу
Настройка vps, настройка сервера
16.04.2026 · ★ 5/5
Было несколько проблем касаясь как технической части так и понимания в целом. Михаил быстро ответил на запрос, помог разобраться и решил проблеммы технические и помог разобраться в понимании, за что отдельное спасибо. Результатом доволен.
Было несколько проблем касаясь как технической части так и понимания в целом. Михаил быстро ответил на запрос, помог разобраться и решил проблеммы технические и помог разобраться в понимании, за что отдельное спасибо. …
Настройка vps, настройка сервера
18.02.2026 · ★ 5/5
Все было сделано быстро и четко. Рекомендую
Все было сделано быстро и четко. Рекомендую
Настройка vps, настройка сервера
17.01.2026 · ★ 5/5
Всё прошло хорошо, исполнитель быстро реагировал на вопросы и помог решить проблему. Спасибо!
Всё прошло хорошо, исполнитель быстро реагировал на вопросы и помог решить проблему. Спасибо!
Настройка vps, настройка сервера
16.12.2025 · ★ 5/5
// Contact
Нужна помощь?
Свяжись со мной и я помогу решить проблему
Написать в TelegramОтвечаю в течение рабочего дня (03:00–13:00 GMT)
Или оставьте заявку здесь:
// Related