Сборка Docker-образов: гайд по Dockerfile и GitLab CI для начинающих - docs.devboxops.com

Сборка Docker-образов: гайд по Dockerfile и GitLab CI для начинающих

Быстрый гайд по сборке Docker-образов с примерами Dockerfile и GitLab CI для молодых разработчиков

Быстрый гайд по сборке Docker-образов

Если ты только начинаешь дружить с Docker и пока не шаришь, как собирать образы, — эта статья именно для тебя. Тут базовые штуки и пара лайфхаков, но не энциклопедия «для всех случаев жизни». За глубокими дебрями — к оф.документации.

Что понадобится

  1. Установленный docker — локально или в виртуалке, как удобнее.
  2. Dockerfile — сценарий сборки.
  3. Доступ к registry (private или Docker Hub), чтобы скидывать собранный образ.
  4. Git-репа — будем автоматизировать процесс через CI/CD.

Must-do рекомендации

  1. Только официальные образы. Сторонние «сборки от Васи» = минус безопасность.
  2. Вес имеет значение. Чем меньше образ, тем быстрее билд, пуш и деплой.
  3. Воспроизводимость. Повторная сборка должна давать byte-в-byte тот же результат.
  4. Чистим хвосты. Никакого мусора и временных файлов в финальном слое.
  5. Уходим от root. Запускаем приложение под обычным пользователем.
  6. Одна задача — один контейнер. Нужно «всё и сразу»? Смотри в сторону LXC/LXD.
  7. HEALTHCHECK — мастхэв. Поможет быстро ловить фейлы в проде.

Разбираемся с Dockerfile

Dockerfile — это просто текстовый скрипт, где по шагам описано, как сварить твой образ.

Классический порядок директив

  1. FROM — базовый образ.
  2. LABEL — метки (автор, версия и т.д.).
  3. ENV — переменные окружения.
  4. USER — под кем запускаем приложение.
  5. RUN — устанавливаем пакеты, настраиваем софт.
  6. COPY/ADD — закидываем файлы.
  7. VOLUME — прописываем точки монтирования для данных.
  8. 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:

  1. Линтеры — hadolint и друзья.
  2. Сборка образа.
  3. Тесты — unit/integration внутри контейнера.
  4. 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, чтобы похудеть образ.
  • Следи за безопасностью: патчи и свежие базовые образы.
  • Тестируй всё перед продом — дешевле поймать баг на этапе билда, чем ночью в бою.

Ссылки

Официальная документация

Best Practices и гайды

Инструменты

Полезные статьи и туториалы

  • Distroless Images — минималистичные образы от Google
  • Alpine Linux — лёгкий и безопасный базовый образ
Top