Dockerfile — создаём собственный образ Docker

Dockerfile — текстовый файл с инструкциями для сборки Docker образа. Каждая инструкция создаёт слой образа. Разбираем все инструкции с примерами для Python и Node.js.

Вся рубрика Docker: уроки, команды и практические сценарии

Структура Dockerfile

# Комментарии начинаются с #

# Базовый образ — ОБЯЗАТЕЛЬНО первым
FROM ubuntu:22.04

# Метаданные образа
LABEL maintainer="dev@example.com"
LABEL version="1.0"

# Переменные окружения
ENV APP_PORT=8080
ENV NODE_ENV=production

# Выполнить команду при СБОРКЕ образа
RUN apt update && apt install -y python3

# Рабочая директория (создаётся если нет)
WORKDIR /app

# Скопировать файлы из контекста сборки в образ
COPY requirements.txt .
COPY . .

# Открытый порт (документация — не открывает реально)
EXPOSE 8080

# Команда при ЗАПУСКЕ контейнера
CMD ["python3", "app.py"]

Таблица инструкций Dockerfile

ИнструкцияОписание
FROMБазовый образ. Обязательно первым
RUNВыполнить команду при сборке образа
COPYСкопировать файлы из контекста в образ
ADDКак COPY, но умеет URL и распаковывает .tar.gz
WORKDIRУстановить рабочую директорию
ENVПеременная окружения (доступна при сборке и в контейнере)
ARGАргумент сборки — доступен только при build
EXPOSEДокументирует порт (не открывает реально)
CMDКоманда по умолчанию при запуске контейнера
ENTRYPOINTТочка входа — не перезаписывается при docker run
VOLUMEОбъявить точку монтирования
USERПользователь для последующих команд
LABELМетаданные образа (ключ=значение)
HEALTHCHECKКоманда проверки здоровья контейнера

Dockerfile для Python приложения

# Dockerfile
FROM python:3.12-slim

# Не буферизовать вывод Python — логи видны сразу
ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1

WORKDIR /app

# Сначала копируем зависимости (кэш слоёв!)
# Если requirements.txt не изменился — pip install не запустится
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Потом остальной код
COPY . .

EXPOSE 5000

CMD ["python", "app.py"]
# requirements.txt
flask==3.0.0
gunicorn==21.2.0
requests==2.31.0

Dockerfile для Node.js

FROM node:20-alpine

WORKDIR /app

# Сначала package.json (кэш npm install)
COPY package*.json ./
RUN npm ci --only=production

# Потом код
COPY . .

EXPOSE 3000

# Запуск от непривилегированного пользователя (безопасность)
USER node

CMD ["node", "server.js"]

Многоэтапная сборка (multi-stage build)

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

# Этап 1: сборка (большой образ с Node.js)
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build        # создаёт dist/

# Этап 2: production образ (только Nginx + статика)
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Финальный образ содержит только Nginx + статику, без Node.js и node_modules (~150 МБ вместо ~500 МБ).

.dockerignore — исключить из контекста

# .dockerignore — работает как .gitignore
node_modules/
dist/
.git/
*.log
.env
.env.local
__pycache__/
*.pyc
*.pyo
.pytest_cache/
venv/
.venv/
*.md

Без .dockerignore весь node_modules (100+ МБ) отправляется в Docker daemon при каждой сборке — значительно замедляет docker build.

CMD vs ENTRYPOINT

# CMD — команда по умолчанию, ПЕРЕЗАПИСЫВАЕТСЯ при docker run
CMD ["python", "app.py"]
docker run myimage               # запустит: python app.py
docker run myimage python other.py  # запустит: python other.py

# ENTRYPOINT — точка входа, НЕ перезаписывается
ENTRYPOINT ["python"]
CMD ["app.py"]
docker run myimage               # запустит: python app.py
docker run myimage other.py      # запустит: python other.py
docker run --entrypoint bash myimage  # перезаписать только через --entrypoint

# Типичный паттерн: ENTRYPOINT + CMD вместе
ENTRYPOINT ["gunicorn"]
CMD ["--bind", "0.0.0.0:5000", "app:app"]
# docker run myimage → gunicorn --bind 0.0.0.0:5000 app:app
# docker run myimage --workers 4 app:app → заменит CMD

Лучшие практики

# ✅ Объединять RUN команды (меньше слоёв, меньше размер)
RUN apt update && \
    apt install -y curl vim && \
    rm -rf /var/lib/apt/lists/*

# ❌ Много отдельных RUN — много слоёв
RUN apt update
RUN apt install -y curl
RUN apt install -y vim

# ✅ Копировать зависимости отдельно от кода
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .   # изменения кода не сбрасывают кэш pip install

# ✅ Использовать slim/alpine образы
FROM python:3.12-slim   # ~130 МБ вместо ~1 ГБ
FROM node:20-alpine     # ~40 МБ вместо ~350 МБ

# ✅ Не запускать от root
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

# ✅ Фиксировать версии
FROM python:3.12-slim   # не FROM python:latest

Часто задаваемые вопросы

CMD vs ENTRYPOINT — в чём разница?

CMD задаёт команду по умолчанию при запуске контейнера, но её можно легко перезаписать через аргументы docker run. ENTRYPOINT задаёт «исполнитель» — его нельзя перезаписать через обычные аргументы (только через --entrypoint). Распространённый паттерн: ENTRYPOINT = исполняемый файл, CMD = аргументы по умолчанию. Например: ENTRYPOINT ["python"] + CMD ["app.py"] — запускает python app.py, но docker run img other.py запустит python other.py.

Почему нельзя запускать контейнер от root?

Если приложение в контейнере скомпрометировано и контейнер запущен от root — атакующий может эксплуатировать уязвимости ядра для получения прав root на хосте. Запуск от непривилегированного пользователя (USER node, USER 1000) — базовый принцип безопасности. Большинство официальных образов (Node.js, Python) уже содержат непривилегированного пользователя.

Что такое .dockerignore?

Файл .dockerignore работает как .gitignore — исключает файлы и папки из контекста сборки (то что отправляется в Docker daemon при docker build). Без него node_modules, .git, логи и другой мусор включаются в контекст — сборка замедляется, размер образа растёт. Файл .dockerignore должен находиться в том же каталоге что и Dockerfile.

Оцените статью
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x