Сборка 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 — лёгкий и безопасный базовый образ