// Engineering Log

Безопасная разработка: Часть 5 — Правильный Dockerfile

Опубликовано 22.09.2026

// Быстрый маршрут

Эта статья относится к теме Деплой и стабильная работа.

Dockerfile из трёх строк — FROM node, COPY . ., CMD npm start — работает, но в пайплайне такой образ собирается долго, каждый раз получается немного другим и плохо останавливается. Эта часть посвящена правильной сборке: воспроизводимости, кешу, секретам и процессу внутри контейнера. Минимальные базовые образы, сканирование и ограничения при запуске разобраны в части цикла о безопасности контейнеров.

Базовый образ: версия и digest

Тег latest и теги без версии меняются без предупреждения: сегодняшняя сборка и завтрашняя могут получить разные версии языка. Минимум — указывать версию. Надёжнее закрепить образ по digest, как рекомендует документация Docker: тогда сборка использует ровно тот образ, даже если издатель обновит тег.

dockerfile
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

Порядок инструкций и кеш

Каждая инструкция создаёт слой. Если изменился слой, все последующие собираются заново. Поэтому сначала копируют файлы зависимостей и устанавливают их, а код — после:

dockerfile
COPY package.json package-lock.json ./
RUN npm ci
COPY . .

Теперь правка кода не запускает установку зависимостей заново. npm ci ставит ровно то, что записано в lock-файле, — это и есть воспроизводимая сборка.

Системные пакеты устанавливают в одной инструкции с обновлением списка и очисткой, как рекомендует документация Docker:

dockerfile
RUN apt-get update && apt-get install -y --no-install-recommends \
    libpq5 \
 && rm -rf /var/lib/apt/lists/*

Многоэтапная сборка

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

dockerfile
# 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 — они подключаются к одной инструкции и в слои не попадают:

dockerfile
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc npm ci
bash
docker 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-форму команд, лишние слои — и легко встраивается в пайплайн:

bash
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-сервера, но в процессе консультации Михаил предложил гораздо более простое и экономичное решение. В итоге сэкономил бюджет и время. Михаил — настоящий эксперт, который работает …

kfhzasorin

Настройка vps, настройка сервера

12.05.2026 · ★ 5/5

Отличная работа! Очень быстро настроил сервер, установил панель, прописал IP. Однозначно могу порекомендовать!

Отличная работа ! Очень быстро настроил сервер, установил панель прописал IP Однозначно могу по рекомендовать !

fedinseo

Настройка vps, настройка сервера

19.04.2026 · ★ 5/5

Покупатель профи-эксперт

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

Было несколько проблем касаясь как технической части так и понимания в целом. Михаил быстро ответил на запрос, помог разобраться и решил проблеммы технические и помог разобраться в понимании, за что отдельное спасибо. …

abazawolf

Настройка vps, настройка сервера

18.02.2026 · ★ 5/5

// Contact

Нужна помощь?

Свяжись со мной и я помогу решить проблему

Написать в Telegram

Отвечаю в течение рабочего дня (03:00–13:00 GMT)

Или оставьте заявку здесь:

Подтвердите, что вы не бот.

Написать и получить быстрый ответ