Урок 05

Dockerfile: основы

Что такое Dockerfile

Dockerfile — текстовый файл с инструкциями, как собрать образ. Читается сверху вниз. Каждая инструкция создаёт слой.

Структура

FROM ubuntu:22.04
RUN apt update && apt install -y nginx
COPY nginx.conf /etc/nginx/
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Основные инструкции

FROM — базовый образ

Первая строка любого Dockerfile.

FROM ubuntu:22.04
FROM php:8.5-fpm-alpine
FROM node:22-alpine
FROM nginx:1.25-alpine

Правило: выбирай минимальный образ. Alpine — 5-50 МБ, slim — 100-200 МБ, полный — 300+ МБ.

RUN — выполнить команду

При сборке.

RUN apt update
RUN apt install -y git curl

Склеивай в одну строку для меньшего числа слоёв:

RUN apt update && \
    apt install -y git curl unzip && \
    apt clean && rm -rf /var/lib/apt/lists/*

rm -rf /var/lib/apt/lists/* — чистит кеш apt. Экономит десятки МБ.

COPY — скопировать файлы

Из контекста сборки в образ.

COPY composer.json composer.lock ./
COPY . .
COPY ./config /app/config

Первый аргумент — откуда (относительно контекста), второй — куда.

ADD — как COPY, но с фишками

Умеет распаковывать архивы и качать по URL:

ADD https://example.com/file.tar.gz /tmp/
ADD archive.tar.gz /app/

Правило: используй COPY по умолчанию. ADD — только если нужны распаковка или URL.

WORKDIR — рабочая папка

WORKDIR /var/www/html

Все последующие RUN/CMD/COPY работают относительно неё. Создаётся, если нет.

ENV — переменные окружения

ENV APP_ENV=production
ENV NODE_ENV=production

Доступны во время сборки и в контейнере.

ARG — аргументы сборки

ARG PHP_VERSION=8.5
FROM php:${PHP_VERSION}-fpm

Отличие от ENV: ARG — только во время сборки, ENV — и в контейнере.

Передаётся:

docker build --build-arg PHP_VERSION=8.4 .

EXPOSE — документировать порт

EXPOSE 80
EXPOSE 3000

Не открывает порт! Только документирует. Порт открывается через -p при docker run.

CMD — команда по умолчанию

Запускается при старте контейнера. Может быть переопределена при docker run.

CMD ["php-fpm"]
CMD ["nginx", "-g", "daemon off;"]
CMD ["node", "server.js"]

Exec-форма (массив): без shell. Shell-форма (строка): через /bin/sh -c.

Правильно: exec-форма.

ENTRYPOINT — точка входа

Как CMD, но не переопределяется при docker run (без --entrypoint).

ENTRYPOINT ["docker-php-entrypoint"]
CMD ["php-fpm"]

Паттерн: ENTRYPOINT — программа, CMD — аргументы.

USER — от какого пользователя

USER www-data

Для безопасности: не работай от root. Создай пользователя:

RUN adduser --disabled-password --gecos '' app
USER app

HEALTHCHECK — проверка здоровья

HEALTHCHECK --interval=30s --timeout=3s \
    CMD curl -f http://localhost/health || exit 1

Docker будет периодически проверять. Полезно для оркестраторов.

LABEL — метаданные

LABEL maintainer="you@example.com"
LABEL version="1.0.0"

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

Docker кеширует каждый слой. Если слой не изменился — берётся из кеша.

Правило: сначала то, что меняется редко.

Плохо:

COPY . /app                 # копируем ВСЁ
RUN composer install        # при любом изменении — заново

Хорошо:

COPY composer.json composer.lock /app/   # только composer
RUN composer install                     # кешируется
COPY . /app                              # остальное в конце

Теперь при изменении кода composer install не запустится — возьмётся из кеша.

.dockerignore

Файл в корне, как .gitignore. Указывает, что не отправлять в образ.

.git
.gitignore
node_modules
vendor
storage/logs
storage/framework/cache
.env
.env.*
*.md
.DS_Store
.idea
.vscode
docker-compose.yml
Dockerfile

Плюс: меньше контекст → быстрее сборка. Меньше размер образа.

Пример: минимальный Dockerfile

FROM nginx:1.25-alpine

COPY nginx.conf /etc/nginx/nginx.conf

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]

Собери:

docker build -t my-nginx .

-t — имя и тег. . — текущая папка (контекст).

Запусти:

docker run -d -p 8080:80 my-nginx

Сборка с тегом

docker build -t my-app:1.0.0 .
docker build -t my-app:latest .
docker build -t my-app:1.0.0 -t my-app:latest .

Просмотр истории образа

docker history my-app

Показывает все слои и их размер. Видно, где раздуто.

Многоступенчатая сборка (multi-stage)

Идея: собираем в одном образе, запускаем в другом, минимальном.

# Этап сборки
FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Финальный этап — только готовое
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80

Плюс: финальный образ в разы меньше. Без node, без исходников, без dev-зависимостей.

Для: Node.js, React, Next.js, Go, Rust.

Проверка синтаксиса

Линтер — hadolint:

docker run --rm -i hadolint/hadolint < Dockerfile

Показывает ошибки и антипаттерны.

Управление кешем

docker build --no-cache -t my-app .       # без кеша
docker builder prune                       # очистить кеш
docker buildx prune                        # для buildx

Частые ошибки

1. COPY . . до RUN composer install — кеш ломается.

2. Нет .dockerignore — в образ попадают .git, node_modules.

3. Много слоёв RUN — размер растёт. Склеивай через &&.

4. Не чистят apt-кеш — лишние 50-100 МБ.

5. Запуск от root — небезопасно. Используй USER.

6. Полный базовый образ — 1 ГБ вместо 50 МБ. Используй alpine/slim.

Итог

Минимум:

  • FROM — базовый образ
  • WORKDIR — рабочая папка
  • COPY — файлы
  • RUN — команды сборки
  • CMD — что запустить

Правила:

  • Копируй composer.json до COPY . .
  • Всегда .dockerignore
  • Multi-stage для фронта
  • Alpine для экономии

Дальше — Dockerfile для Laravel.