Dagger — открытый движок для сборки и запуска CI/CD-пайплайнов, в котором этапы описываются на обычном языке программирования, а не в YAML. Сборка, тесты и публикация артефактов превращаются в функции, и одну и ту же команду можно выполнить на ноутбуке и в конвейере. Ниже — установка, первый модуль на TypeScript, кэширование, интеграция с GitHub Actions и разбор ошибок.

Зачем описывать пайплайн кодом

YAML хорошо читается, пока конвейер простой. Как только появляются матрицы, условия, повторное использование шагов и передача файлов между заданиями, начинаются копипаста и «магия» переменных. Локально такой сценарий обычно не запустить: приходится коммитить, ждать раннер и смотреть логи.

Dagger предлагает другой подход. Операции с контейнерами — запуск команды, монтирование каталога, копирование файла, публикация образа — доступны как методы SDK. Пайплайн становится типизированной программой, которую можно проверить компилятором, разбить на функции и переиспользовать в разных проектах. Под капотом работает BuildKit: он строит граф операций и кэширует результаты по содержимому.

Ключевая единица в Dagger — функция. Модуль объединяет несколько функций, у каждой есть параметры и результат: каталог, контейнер, строка, файл.

Установка и проверка окружения

Dagger не требует отдельного сервера, но нужен контейнерный движок: Docker, Podman или установленный Dagger Engine. На Linux и macOS удобен официальный скрипт, на Windows практичнее работать внутри WSL2.

curl -fsSL https://dl.dagger.io/dagger/install.sh | BIN_DIR="$HOME/.local/bin" sh
export PATH="$HOME/.local/bin:$PATH"
dagger version
docker info

Команда dagger version показывает версию CLI и движка. Если движок ещё не запущен, он поднимется при первом вызове. Ошибка про сокет обычно означает, что пользователь не входит в группу docker или служба контейнеров не запущена.

Первый модуль

Модуль создаётся в каталоге проекта. Флаг --sdk выбирает язык: Go, Python, TypeScript, PHP. Для примера возьмём TypeScript — он хорошо подходит для небольших скриптов сборки.

dagger init --name=ci --sdk=typescript
dagger develop
dagger functions

Появятся файл dagger.json с описанием модуля и каталог dagger/src с исходником. Команда dagger develop подготавливает зависимости SDK, а dagger functions печатает список доступных функций и их аргументов — это основная справка во время работы.

Функция сборки на TypeScript

Опишем сборку Go-приложения. Функция принимает каталог с исходниками, создаёт контейнер на базе образа с Go, монтирует кэш сборки и выполняет сборку. Результат — контейнер, из которого потом можно забрать бинарник.

import { dag, Container, Directory, object, func } from "@dagger.io/dagger"

@object()
export class Ci {
  @func()
  build(source: Directory): Container {
    return dag
      .container()
      .from("golang:1.24-alpine")
      .withDirectory("/src", source)
      .withWorkdir("/src")
      .withMountedCache("/root/.cache/go-build", dag.cacheVolume("go-build"))
      .withExec(["go", "build", "-o", "/out/app", "./cmd/app"])
  }
}

Методы выстраиваются в цепочку и выполняются лениво: пока результат не запрошен, контейнер не запускается. Это позволяет Dagger оптимизировать граф и не выполнять лишние шаги. Каталог /out создаётся автоматически.

Вызов функций и экспорт артефактов

Локальный запуск почти не отличается от вызова в CI. Флаг --source передаёт текущий каталог как входной параметр, а export сохраняет результат на диск.

dagger call build --source=.
dagger call build --source=. export --path=./bin/app
dagger call build --source=. terminal

Команда terminal открывает оболочку внутри собранного контейнера, а глобальный флаг --debug печатает подробный журнал операций.

Если функция возвращает строку, её можно вывести через stdout. Например, отдельная функция тестов возвращает вывод go test:

@func()
async test(source: Directory): Promise<string> {
  return this.build(source)
    .withExec(["go", "test", "./..."])
    .stdout()
}

Вызов: dagger call test --source=. stdout. Такой подход удобен для проверок, где важен текст отчёта, а не файл.

Кэш и повторные запуски

Dagger кэширует каждый шаг по содержимому входа. Если исходники не менялись, повторный запуск занимает секунды. Если изменился один файл, пересобирается только затронутая часть графа. Кэш томов (cacheVolume) живёт между запусками и особенно полезен для пакетных менеджеров.

Порядок шагов влияет на скорость. Сначала копируйте файлы зависимостей и устанавливайте их, затем — остальные исходники. В примере выше кэш Go-сборки привязан к тому, поэтому переустановка образа не обнуляет компиляцию.

dagger call build --source=. export --path=./bin/app

Не монтируйте кэш в каталог, который меняется от запуска к запуску: это снижает эффективность. Имена томов лучше делать уникальными для проекта.

Интеграция с GitHub Actions

В конвейере достаточно установить CLI и вызвать ту же функцию. Отдельный YAML-раннер не нужен: Dagger сам поднимет движок внутри runner.

name: ci
on: [push]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: curl -fsSL https://dl.dagger.io/dagger/install.sh | BIN_DIR="$HOME/.local/bin" sh
      - run: echo "$HOME/.local/bin" >> "$GITHUB_PATH"
      - run: dagger call test --source=.
        env:
          DAGGER_CLOUD_TOKEN: ${{ secrets.DAGGER_CLOUD_TOKEN }}

Токен Dagger Cloud не обязателен: без него конвейер работает, но история запусков и общий кэш между машинами недоступны.

Отладка и типичные ошибки

Самая частая проблема — запуск команды вне каталога модуля. Dagger ищет dagger.json вверх по дереву и без него выдаёт ошибку о конфигурации. Вторая — отсутствие прав на запись при экспорте: проверьте владельца каталога и не запускайте экспорт в системные пути.

Если сборка падает на скачивании зависимостей, проверьте сеть внутри контейнера.

dagger --debug call build --source=.
dagger call build --source=. terminal

Интерактивная оболочка полезнее десятка логов: внутри можно повторить команду и проверить пути. Изменения в таком контейнере не сохраняются.

Ограничения и когда Dagger не нужен

Dagger добавляет слой абстракции и требует времени на изучение SDK. Для проекта из трёх шагов проще обойтись штатным CI: движок работает в контейнере, и на слабой машине сборка будет медленнее нативной.

  • Нужен командный стандарт сборки для нескольких языков — Dagger окупается.
  • Достаточно одного make test — не усложняйте.
  • Требуется полностью локальный конвейер без внешних сервисов — проверьте, какие функции Dagger Cloud вы используете.
  • Важна поддержка экзотической ОС — сначала проверьте доступность образа и движка.

Итог

Dagger переносит CI из мира копипасты в мир функций: пайплайн можно запустить локально, проверить типами и переиспользовать как библиотеку. Начните с одной функции сборки и одного теста, затем добавьте кэш томов и публикацию образа. Такой переход даёт предсказуемость и экономит время на отладке, но требует дисциплины в именовании функций и версиях базовых образов.

Насколько публикация полезна?

Нажмите на звезду, чтобы оценить!

Средняя оценка 0 / 5. Количество оценок: 0

Оценок пока нет. Поставьте оценку первым.

Поделиться: Telegram ВКонтакте