Сборка Docker-образов: гайд по Dockerfile и GitLab CI для начинающих
Быстрый гайд по сборке Docker-образов с примерами Dockerfile и GitLab CI для молодых разработчиков
Быстрый гайд по сборке Docker-образов
Если ты только начинаешь дружить с Docker и пока не шаришь, как собирать образы, — эта статья именно для тебя. Тут базовые штуки и пара лайфхаков, но не энциклопедия «для всех случаев жизни». За глубокими дебрями — к оф.документации.
Что понадобится
- Установленный
docker
— локально или в виртуалке, как удобнее. - Dockerfile — сценарий сборки.
- Доступ к registry (private или Docker Hub), чтобы скидывать собранный образ.
- Git-репа — будем автоматизировать процесс через CI/CD.
Must-do рекомендации
- Только официальные образы. Сторонние «сборки от Васи» = минус безопасность.
- Вес имеет значение. Чем меньше образ, тем быстрее билд, пуш и деплой.
- Воспроизводимость. Повторная сборка должна давать byte-в-byte тот же результат.
- Чистим хвосты. Никакого мусора и временных файлов в финальном слое.
- Уходим от root. Запускаем приложение под обычным пользователем.
- Одна задача — один контейнер. Нужно «всё и сразу»? Смотри в сторону LXC/LXD.
- HEALTHCHECK — мастхэв. Поможет быстро ловить фейлы в проде.
Разбираемся с Dockerfile
Dockerfile — это просто текстовый скрипт, где по шагам описано, как сварить твой образ.
Классический порядок директив
FROM
— базовый образ.LABEL
— метки (автор, версия и т.д.).ENV
— переменные окружения.USER
— под кем запускаем приложение.RUN
— устанавливаем пакеты, настраиваем софт.COPY/ADD
— закидываем файлы.VOLUME
— прописываем точки монтирования для данных.ENTRYPOINT
/CMD
— что запускаем при старте контейнера.
Основные команды
FROM
— бери Alpine, если нужна «пушинка».RUN
— группируй команды через&&
, чтобы не плодить слои.COPY
/ADD
— копируем файлы внутрь образа (уADD
ещё загрузка по URL).ENTRYPOINT
— единственная точка входа.CMD
— дефолтные аргументы, которые легко переопределить.HEALTHCHECK
— проверяем, что сервис жив.ARG
— переменные для этапа сборки.EXPOSE
— какие порты открываем миру.
Практический пример Dockerfile
Допустим, у нас есть простое Node.js приложение. Вот как будет выглядеть правильный Dockerfile:
# Используем официальный Node.js образ (безопасный)
FROM node:22-bullseye-slim
# Создаем юзера, устанавливаем curl и чистим кэш в рамках одного слоя
RUN useradd -ms /bin/bash appuser && \
apt update && \
apt install curl -y && \
rm -rf /var/lib/apt/lists/*
# Устанавливаем рабочую дирректорию - /app
WORKDIR /app
# Копируем файлы зависимостей
COPY --chown=appuser:appuser package.json .
COPY --chown=appuser:appuser package-lock.json .
# Меняем пользователя в контейнере
USER appuser
# Устанавливаем зависимости приложения
RUN npm install
# Копируем код приложения
COPY --chown=solana:solana . .
# Точка входа - запуск приложения
ENTRYPOINT ["/app/entrypoint.sh"]
# Healthcheck для проверки работоспособности
HEALTHCHECK --interval=3s --timeout=5s --retries=3 CMD curl -f 127.0.0.1:5555/status || exit 1
Разбор по полочкам
Многоэтапная сборка (Multi-stage build) Если у тебя есть этап компиляции, используй multi-stage:
ARG PYTHON_VERSION=3.11
# Этап сборки
FROM python:${PYTHON_VERSION}-slim-bookworm AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
# Финальный образ
FROM gcr.io/distroless/python3-debian12
ARG PYTHON_VERSION=3.11
WORKDIR /app
# Копируем только то, что нужно для запуска
COPY --from=builder /usr/local/lib/python${PYTHON_VERSION}/site-packages /usr/local/lib/python${PYTHON_VERSION}/site-packages
COPY . .
ENV LANG=C.UTF-8 \
LC_ALL=C.UTF-8 \
PYTHONDONTWRITEBYTECODE=1 \
PYTHONFAULTHANDLER=1 \
PYTHONPATH=/usr/local/lib/python${PYTHON_VERSION}/site-packages
CMD ["main.py"]
Зачем это нужно? Первый этап содержит всё для сборки (dev-зависимости, исходники), а во второй попадает только готовый продукт. Финальный образ получается в разы меньше.
GitLab CI для автоматической сборки
Теперь настроим автоматическую сборку через GitLab CI. Создай файл .gitlab-ci.yml
в корне проекта:
# Стадии пайплайна
stages:
- push
# Линтинг Dockerfile
build_and_push:
stage: push
script:
- docker build . -t $CI_REGISTRY_IMAGE:mytag # CI_REGISTRY_IMAGE - это переменная гитлаба, подробнее [тут](https://docs.gitlab.com/ci/variables/predefined_variables/)
- docker push $CI_REGISTRY_IMAGE:mytag
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- if: '$CI_COMMIT_BRANCH == "main"'
Пробуем на практике
Минимальный шаблон
# Базовый образ
FROM alpine
# Описание образа
LABEL description="My first image"
# Установка пакетов
RUN apk add --no-cache curl
# Переключение на пользователя nobody
USER nobody
# Установка точки входа
ENTRYPOINT ["/bin/date"]
Сборка
docker build . -t test
Дальше Docker покажет пошаговый лог: тянет базовый Alpine, ставит bash, добавляет метки, и т.д. В конце получаем лёгкий образ ~13 MB.
Проверка
docker image ls test # смотрим размер
docker run --rm test # если всё ок — увидишь дату/время
Что дальше?
Настраиваем CI/CD:
- Линтеры — hadolint и друзья.
- Сборка образа.
- Тесты — unit/integration внутри контейнера.
- Push в registry.
Почему пушить руками — плохая идея
Локальная сборка нужна только:
- для первичного прототипа;
- чтобы подебажить что-то перед коммитом.
Всё, что летит в registry, должно проходить через CI. Твоя задача — убедиться, что билд зелёный, тесты зелёные, и только потом пушишь код в Git.
FAQ
Сборка падает в CI
- Hadolint ругается. Чини Dockerfile, не отключай линтер.
- «У меня работает, в CI нет». Проверь своё окружение — CI стартует с нуля.
- Баг в плагине. Включи debug-режим, зафайль issue, попей кофе.
Как дебажить контейнер?
Запусти его с кастомным entrypoint
/command
, залезь внутрь и посмотри логи, конфиги, процессы. Но помни: в Swarm/K8s бегать по нодам «по месту» — плохая стратегия. Там нужны централизованные логи и трассировка.
Что пихать в HEALTHCHECK?
- Для веб-сервиса — реальный HTTP запрос, а не просто «порт открыт».
- Для MySQL —
SELECT 1;
. - Для Redis —
PING
. - Для CLI-утилиты — команду, проверяющую ключевой функционал.
Образ слишком толстый
Лайфхаки для похудения:
- Используй Alpine вместо Ubuntu/Debian
- Ставь только нужные пакеты
- Чисть кэш пакетного менеджера в том же RUN
- Используй multi-stage builds
- Проверяй слои через
docker history
Полезные советы напоследок
- Читай оф. документацию Docker.
- Применяй best practices контейнеризации.
- Используй multi-stage builds, чтобы похудеть образ.
- Следи за безопасностью: патчи и свежие базовые образы.
- Тестируй всё перед продом — дешевле поймать баг на этапе билда, чем ночью в бою.
Ссылки
Официальная документация
- Docker Docs — всё про Docker
- Dockerfile Reference — справочник по командам
- GitLab CI/CD — документация по GitLab CI/CD
- GitLab Container Registry — работа с registry
Best Practices и гайды
- Docker Best Practices — официальные рекомендации
- Multi-stage builds — экономим место
- Security Best Practices — безопасность контейнеров
Инструменты
- Hadolint — линтер для Dockerfile
- Dive — анализ слоёв образа
- Docker Bench Security — проверка безопасности
Полезные статьи и туториалы
- Distroless Images — минималистичные образы от Google
- Alpine Linux — лёгкий и безопасный базовый образ