Как стать автором
Обновить

Docker Bake: современный подход к сборке контейнеров

Уровень сложностиПростой
Время на прочтение10 мин
Количество просмотров12K

Традиционный способ сборки Docker-образов с помощью команды docker build прост и понятен, но при работе с комплексными приложениями, состоящими из множества компонентов, этот процесс может стать утомительным и подверженным ошибкам. Именно здесь на помощь приходит Docker Bake — мощный и гибкий инструмент для организации многоступенчатой и параллельной сборки образов.

В этой статье мы рассмотрим возможности Docker Bake, его преимущества перед стандартным подходом, а также разберем практические примеры использования для различных сценариев разработки.

Что такое Docker Bake

Docker Bake — это функция BuildKit, которая позволяет организовать и автоматизировать процесс сборки Docker-образов с использованием конфигурационных файлов.

Основные преимущества Docker Bake:

  1. Декларативный синтаксис: Вместо множества команд в скриптах вы описываете желаемый результат в HCL (HashiCorp Configuration Language), JSON или YAML(Docker Compose файлы).

  2. Параллельная сборка: BuildKit автоматически выполняет сборку образов параллельно, где это возможно.

  3. Переиспользование кэша: Эффективное использование кэша между различными сборками.

  4. Группировка и целевые сборки: Возможность определять группы образов и собирать только нужные в данный момент цели.

  5. Переменные и наследование: Мощная система переменных и наследования свойств между целями сборки.

  6. Интеграция с CI/CD: Легко встраивается в пайплайны непрерывной интеграции и доставки.

Самый просто пример для понимания, что такое bake, возьмем команду для сборки образа:

 $ docker build -f Dockerfile -t myapp:latest .

И перепишем эту команду в виде bake файла:

target "myapp" {
  context = "."
  dockerfile = "Dockerfile"
  tags = ["myapp:latest"]
}

Теперь нам не нужно снова и снова писать длинную команду, достаточно один раз описать в файле и вызвать команду:

$ docker buildx bake myapp

Но это лишь примитивный пример, на деле же bake способен на многое о чем написано ниже в статье.

Анатомия Bake-файла

Рассмотрим основные компоненты bake файла:

1. Переменные (Variables)

Переменные позволяют определить значения, которые могут использоваться в разных местах конфигурации и легко переопределяться при запуске:

variable "TAG" {
  default = "latest"
}

variable "DEBUG" {
  default = "false"
}

Переменные можно использовать в других частях конфигурации через интерполяцию строк: ${TAG}.

2. Группы (Groups)

Группы позволяют объединять несколько целей для одновременной сборки:

group "default" {
  targets = ["app", "api"]
}

group "backend" {
  targets = ["api", "database"]
}

3. Цели (Targets)

Цели — это основные единицы сборки, каждая из которых определяет один Docker-образ:

target "app" {
  dockerfile = "Dockerfile.app"
  context = "./app"
  tags = ["myorg/app:${TAG}"]
  args = {
    DEBUG = "${DEBUG}"
  }
  platforms = ["linux/amd64", "linux/arm64"]
}

Основные параметры целей:

  • dockerfile — путь к Dockerfile

  • context — контекст сборки

  • tags — теги для образа

  • args — аргументы для передачи в Dockerfile

  • platforms — платформы для мультиплатформенной сборки

  • target — цель многоступенчатой сборки в Dockerfile

  • output — куда выводить результат сборки

  • cache-from и cache-to — настройки кэширования

4. Наследование (Inheritance)

Одно из самых мощных свойств Bake — возможность наследования параметров:

target "base" {
  context = "."
  args = {
    BASE_IMAGE = "node:16-alpine"
  }
}

target "app" {
  inherits = ["base"]
  dockerfile = "app/Dockerfile"
  tags = ["myapp/app:latest"]
}

Цель app унаследует все параметры из base и перезапишет или дополнит их своими.

5. Функции (Functions)

В HCL можно определять функции для более гибкой конфигурации:

function "tag" {
  params = [name, version]
  result = ["${name}:${version}"]
}

target "app" {
  tags = tag("myapp/app", "v1.0.0")
}

Установка и настройка

Docker Bake является частью BuildKit — современного движка для сборки Docker-образов. Начиная с Docker 23.0, BuildKit включен по умолчанию, поэтому для большинства пользователей дополнительная настройка не требуется. Однако, если вы используете более старую версию Docker или хотите убедиться, что BuildKit активирован, следуйте инструкциям ниже.

Проверка версии Docker

Убедитесь, что у вас установлена актуальная версия Docker (23.0 или выше). Проверить версию можно командой:

docker --version

Если версия Docker устарела, обновите её, следуя официальной документации.

Активация BuildKit (для старых версий Docker)

Для версий Docker ниже 23.0 BuildKit необходимо активировать вручную. Это можно сделать одним из следующих способов:

  1. Через переменную окружения:

    export DOCKER_BUILDKIT=1
  2. В конфигурационном файле Docker:
    Отредактируйте файл ~/.docker/config.json и добавьте следующие параметры:

    {
      "features": {
        "buildkit": true
      }
    }
    
  3. Через командную строку:
    При использовании команды docker build или docker buildx bake можно явно указать использование BuildKit:

    DOCKER_BUILDKIT=1 docker buildx bake

Установка Docker Buildx

Docker Buildx — это расширение Docker CLI, которое предоставляет дополнительные возможности для сборки образов, включая поддержку многоплатформенной сборки. Начиная с Docker 20.10, Buildx входит в состав Docker, но для полной функциональности рекомендуется убедиться, что он установлен и активирован.

  1. Проверка установки Buildx:

    docker buildx version

    Если Buildx не установлен, следуйте инструкциям ниже(или официальной инструкции https://github.com/docker/buildx?tab=readme-ov-file#installing).

  2. Установка Buildx:

    • Для Linux:

      mkdir -p ~/.docker/cli-plugins
      curl -sSL https://github.com/docker/buildx/releases/latest/download/buildx-linux-amd64 -o ~/.docker/cli-plugins/docker-buildx
      chmod +x ~/.docker/cli-plugins/docker-buildx
      
    • Для macOS (с использованием Homebrew):

      brew install docker-buildx
  3. Создание и использование Buildx-билдера:
    По умолчанию Docker использует встроенный билдер, но для полной функциональности рекомендуется создать новый билдер:

    docker buildx create --use --name my-builder

    Проверьте, что билдер активен:

    docker buildx ls

Основы использования Docker Bake

Конфигурационные файлы

Docker Bake использует конфигурационные файлы, которые могут быть написаны в форматах HCL (по умолчанию), JSON или YAML. Стандартные имена для этих файлов:

  • docker-bake.hcl

  • docker-bake.json

Также можно использовать docker-compose.yml с некоторыми расширениями.

Структура HCL-файла

Типичный файл конфигурации Docker Bake имеет следующую структуру:

// Определение переменных
variable "TAG" {
  default = "latest"
}

// Определение групп
group "default" {
  targets = ["app", "api"]
}

// Определение общих настроек
target "docker-metadata-action" {
  tags = ["user/app:${TAG}"]
}

// Определение целей сборки
target "app" {
  inherits = ["docker-metadata-action"]
  dockerfile = "Dockerfile.app"
  context = "./app"
}

target "api" {
  inherits = ["docker-metadata-action"]
  dockerfile = "Dockerfile.api"
  context = "./api"
}

Выполнение сборки

Собрать все цели из группы по умолчанию:

docker buildx bake

Собрать конкретную цель или группу:

docker buildx bake app

Передать переменные:

TAG=v1.0.0 docker buildx bake

Практические примеры

Пример 1: Простое многокомпонентное приложение

Предположим, у нас есть приложение, состоящее из веб-фронтенда, API и сервиса баз данных. Вот как может выглядеть файл docker-bake.hcl:

variable "TAG" {
  default = "latest"
}

group "default" {
  targets = ["frontend", "api", "db"]
}

group "services" {
  targets = ["api", "db"]
}

target "base" {
  context = "."
  args = {
    BASE_IMAGE = "node:16-alpine"
  }
}

target "frontend" {
  inherits = ["base"]
  dockerfile = "frontend/Dockerfile"
  tags = ["myapp/frontend:${TAG}"]
  args = {
    API_URL = "http://api:3000"
  }
}

target "api" {
  inherits = ["base"]
  dockerfile = "api/Dockerfile"
  tags = ["myapp/api:${TAG}"]
  args = {
    DB_HOST = "db"
    DB_PORT = "5432"
  }
}

target "db" {
  context = "./db"
  dockerfile = "Dockerfile"
  tags = ["myapp/db:${TAG}"]
}

Пример 2: Многоплатформенная сборка

Один из мощных аспектов Docker Bake — это простота настройки многоплатформенной сборки:

variable "TAG" {
  default = "latest"
}

group "default" {
  targets = ["app-all"]
}

target "app" {
  dockerfile = "Dockerfile"
  tags = ["myapp/app:${TAG}"]
}

target "app-linux-amd64" {
  inherits = ["app"]
  platforms = ["linux/amd64"]
}

target "app-linux-arm64" {
  inherits = ["app"]
  platforms = ["linux/arm64"]
}

target "app-all" {
  inherits = ["app"]
  platforms = ["linux/amd64", "linux/arm64"]
}

Пример 3: Разные среды разработки

Docker Bake позволяет легко управлять сборками для разных сред (например, разработки, тестирования и production). Для этого можно использовать переменные, которые переопределяются через командную строку:

variable "ENV" {
  default = "dev"
}

group "default" {
  targets = ["app-${ENV}"]
}

target "app-base" {
  dockerfile = "Dockerfile"
  args = {
    BASE_IMAGE = "node:16-alpine"
  }
}

target "app-dev" {
  inherits = ["app-base"]
  tags = ["myapp/app:dev"]
  args = {
    NODE_ENV = "development"
    DEBUG = "true"
  }
}

target "app-stage" {
  inherits = ["app-base"]
  tags = ["myapp/app:stage"]
  args = {
    NODE_ENV = "production"
    API_URL = "https://api.stage.example.com"
  }
}

target "app-prod" {
  inherits = ["app-base"]
  tags = ["myapp/app:prod", "myapp/app:latest"]
  args = {
    NODE_ENV = "production"
    API_URL = "https://api.example.com"
  }
}

Для сборки образа для конкретной среды используйте команду:

ENV=prod docker buildx bake

Продвинутые возможности Docker Bake

Матричные сборки

Docker Bake позволяет определять матрицы для создания нескольких вариантов сборки на основе комбинаций параметров:

variable "REGISTRY" {
  default = "docker.io/myorg"
}

target "app" {
  matrix = {
    platform = ["linux/amd64", "linux/arm64"]
    version = ["1.0", "2.0"]
  }
  name = "app-${version}-${platform}"
  tags = ["${REGISTRY}/app:${version}-${platform}"]
  platforms = [platform]
  args = {
    VERSION = version
  }
}

Этот код создаст четыре варианта образа: для каждой комбинации платформы и версии. Вы можете собрать их все одной командой:

docker buildx bake app

Использование внешних файлов и функций

Docker Bake позволяет использовать внешние файлы и функции для более гибкой настройки:

// Импорт переменных из JSON-файла
variable "settings" {
  default = {}
}

function "tag" {
  params = [name, tag]
  result = ["${name}:${tag}"]
}

target "app" {
  dockerfile = "Dockerfile"
  tags = tag("myapp/app", "v1.0.0")
  args = {
    CONFIG = "${settings.app_config}"
  }
}

Затем можно передать файл с настройками:

docker buildx bake --file settings.json

Интеграция с Docker Compose

Docker Bake можно интегрировать с Docker Compose, что особенно удобно для существующих проектов:

# docker-compose.yml
services:
  app:
    build:
      context: ./app
      dockerfile: Dockerfile
      args:
        VERSION: "1.0"
    image: myapp/app:latest

  api:
    build:
      context: ./api
      dockerfile: Dockerfile
    image: myapp/api:latest
# docker-bake.hcl
target "default" {
  context = "."
  dockerfile-inline = <<EOT
    FROM docker/compose:1.29.2
    WORKDIR /app
    COPY docker-compose.yml .
    RUN docker-compose build
  EOT
}

Условная логика

Для более сложных сценариев можно использовать условную логику:

variable "DEBUG" {
  default = "false"
}

target "app" {
  dockerfile = "Dockerfile"
  tags = ["myapp/app:latest"]
  args = {
    DEBUG = "${DEBUG}"
    EXTRA_PACKAGES = DEBUG == "true" ? "vim curl htop" : ""
  }
}

Применение Docker Bake в CI/CD

Docker Bake отлично подходит для использования в CI/CD-пайплайнах. Вот пример интеграции с GitHub Actions, где используются секреты для безопасной аутентификации в Docker Hub:

# .github/workflows/build.yml
name: Build and Publish

on:
  push:
    branches: [main]
    tags: ['v*']

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2

      - name: Login to DockerHub
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Docker Metadata
        id: meta
        uses: docker/metadata-action@v4
        with:
          images: myapp/app
          tags: |
            type=ref,event=branch
            type=ref,event=pr
            type=semver,pattern={{version}}

      - name: Build and push
        uses: docker/bake-action@v2
        with:
          files: |
            ./docker-bake.hcl
          targets: app
          push: true
          set: |
            *.tags=${{ steps.meta.outputs.tags }}

Отладка и мониторинг сборок

Docker Bake предоставляет несколько полезных опций для отладки процесса сборки:

  • Просмотр конфигурации без сборки:

docker buildx bake --print
  • Подробные логи:

docker buildx bake --progress=plain
  • Экспорт в JSON для анализа:

docker buildx bake --print | jq

Сравнение с другими инструментами

Docker Bake vs. Docker Compose

Функция

Docker Bake

Docker Compose

Основное назначение

Сборка образов

Управление контейнерами

Параллельная сборка

Да, автоматически

Ограничено

Матричные сборки

Да

Нет

Наследование

Да, мощная система

Ограничено (extends)

Многоплатформенность

Да, интегрировано

Нет

Формат конфигурации

HCL, JSON

YAML

Docker Bake vs. Скрипты сборки

Аспект

Docker Bake

Bash/скрипты

Декларативность

Высокая

Низкая

Сложность поддержки

Низкая

Высокая

Переиспользование

Простое

Сложное

Параллелизм

Автоматический

Ручной

Интеграция с CI/CD

Простая

Требует усилий

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

  • Организуйте таргеты в логические группы:

group "all" {
  targets = ["app", "api", "worker"]
}

group "backend" {
  targets = ["api", "worker"]
}
  • Используйте наследование для общих настроек:

target "common" {
  context = "."
  args = {
    BASE_IMAGE = "node:16-alpine"
  }
}

target "app" {
  inherits = ["common"]
  dockerfile = "app/Dockerfile"
}
  • Организуйте сложные конфигурации в несколько файлов:

docker buildx bake \
  -f ./common.hcl \
  -f ./development.hcl \
  app-dev
  • Используйте переменные для гибкости:

variable "REGISTRY" {
  default = "docker.io/myorg"
}

target "app" {
  tags = ["${REGISTRY}/app:latest"]
}
  • Применяйте матрицы для сложных сценариев сборки:

target "matrix" {
  matrix = {
    env = ["dev", "prod"]
    platform = ["linux/amd64", "linux/arm64"]
  }
  name = "app-${env}-${platform}"
  tags = ["myapp/app:${env}-${platform}"]
}

Типичные проблемы и их решения

Проблема 1: Кэш не используется эффективно

Решение: Правильно структурируйте Dockerfile, размещая слои, которые меняются реже, в начале файла:

FROM node:16-alpine

# Сначала копируем только файлы для зависимостей
COPY package.json package-lock.json ./
RUN npm install

# Затем копируем исходный код
COPY . .

Проблема 2: Конфликт переменных окружения

Решение: Используйте явные значения в Docker Bake:

target "app" {
  args = {
    NODE_ENV = "production"
  }
}

Проблема 3: Сложно отлаживать сборки

Решение: Используйте подробные логи и инспекцию:

docker buildx bake --progress=plain --print app

Docker Bake vs Docker Compose

Docker Bake упрощает и ускоряет процесс сборки, особенно если нужно собирать несколько образов одновременно. Вместо того, чтобы писать несколько команд docker build, всё можно описать в одном конфигурационном файле и запускать одной командой. Это также позволяет легко переопределять параметры (например, теги) и использовать параллельную сборку для ускорения процесса. 

  • Docker Compose используется для управления контейнерами и их взаимосвязями (например, запуск нескольких сервисов в одном проекте).

  • Docker Bake используется для сборки Docker образов, причем он позволяет собирать несколько образов одновременно, ускоряя процесс.

Таким образом, docker-compose — для запуска контейнеров, а docker bake — для сборки образов.

Заключение

Docker Bake предоставляет мощный, гибкий и декларативный подход к организации сборки Docker-образов. Он решает многие проблемы, с которыми сталкиваются команды при использовании традиционных подходов к сборке, особенно в сложных многокомпонентных проектах.

Основные преимущества Docker Bake:

  • Декларативный подход

  • Эффективное использование кэша

  • Параллельная и многоплатформенная сборка

  • Мощная система переменных и наследования

  • Отличная интеграция с CI/CD-пайплайнами

Внедрение Docker Bake в рабочий процесс может значительно упростить и ускорить процессы сборки образов, особенно для команд, работающих с микросервисной архитектурой или сложными многокомпонентными приложениями.

Полезные ресурсы

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Пользуетесь ли вы docker bake?
5.66% Да, мне нравится3
0% Да, но не нравится0
45.28% Нет, но планирую24
49.06% Нет и не планирую26
Проголосовали 53 пользователя. Воздержались 6 пользователей.
Теги:
Хабы:
Всего голосов 13: ↑11 и ↓2+10
Комментарии28

Публикации

Истории

Работа

DevOps инженер
32 вакансии

Ближайшие события

19 марта – 28 апреля
Экспедиция «Рэйдикс»
Нижний НовгородЕкатеринбургНовосибирскВладивостокИжевскКазаньТюменьУфаИркутскЧелябинскСамараХабаровскКрасноярскОмск
24 апреля
VK Go Meetup 2025
Санкт-ПетербургОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань
14 мая
LinkMeetup
Москва
5 июня
Конференция TechRec AI&HR 2025
МоскваОнлайн
20 – 22 июня
Летняя айти-тусовка Summer Merge
Ульяновская область