Перейти к содержимому

Конфигурация сервиса (workspace/services/<name>/service.yml)

Объявления сервисов DWE-проекта.

Определения сервисов располагаются в workspace/services/, по одной папке на сервис. Каждый сервис объявляется в workspace/services/<name>/service.yml, где <name> становится ключом сервиса в итоговой карте DweConfig.Services. Каждая запись объявляет имя контейнера, порты, хосты, оверлей compose и необязательные структурные поля. Дискриминатор type: определяет, какие поля допустимы для записи.

При загрузке перечисляется каждая подпапка workspace/services/*/, и её service.yml парсится в строгом режиме: файл обязан декларировать type, каждое поле сверяется со списком разрешённых для этого типа, extends: отвергается у сервисов не-app, проверяются формы ports / hosts. Затем межсервисные цепочки extends: разрешаются в топологическом порядке, а поля родителей сливаются в потомков. Отсутствующий каталог workspace/services/ даёт пустой набор сервисов (не ошибка). Переключатели на стороне разработчика (enabled:, ports:, hosts:) лежат в трёхслойном оверлее; структурные поля принадлежат исключительно workspace/services/<name>/service.yml.

Каждая запись в services: требует ключа type:. Поддерживаются три значения:

type:СемантикаЖизненный цикл deployЦель depends_on:Поля только для app
appСервис с исходным кодом в dir:; рендерит шаблоны IDE/AI/git; запускается через dwe deploy.да (может существовать workspace/services/<name>/deploy.yml)дада (dir, dir_internal, work_dir_internal, configs, dirs, extends, cli, render, generated)
toolЭфемерный служебный контейнер (adminer, mailpit, redis-insight). Не может быть целью зависимости ни одного сервиса.нетнетнет
infraПоддерживающий сервис (БД, кеш, очередь, поиск). Может быть целью зависимости для app / других infra.нетданет

Фиксированные правила:

  • extends: только для app. Запись tool / infra с extends: отклоняется при загрузке.
  • depends_on: не может ссылаться на запись type: tool. Это проверяется при загрузке (ErrDependsOnTool), а не только при валидации.
  • workspace/services/<name>/deploy.yml поддерживается для любого типа сервиса (app, tool, infra). Полный deploy (dwe deploy run) перечисляет каждый включённый сервис, у которого есть deploy.yml; dwe deploy run --service <name> работает для любого типа сервиса с deploy-файлом независимо от состояния включения.
  • ports: всегда map[string]int, а hosts: всегда map[string]string. Скалярных сокращений port: / host: нет. Сервис с одним портом записывается как ports: { http: 8025 }.
  • Семантика типов разделяет порядок эмиссии файлов docker compose на tool → infra → app.

Сервисы type: infra могут быть необязательными (required: false) — они принимают оверлей compose: и переключаются через dwe services enable|disable <name> так же, как app и tool. Обязательная infra (required: true, типична для поддерживающих сервисов вроде баз данных, кешей и очередей) всегда включена и не переключается. Необязательная infra семантически подходит для компонентов на пути запроса или данных, не строго обязательных каждому разработчику (например, Varnish-кеш перед nginx или MinIO как S3-хранилище, используемое только когда внешний S3 не настроен).

Разрешённые поля по типам отражают разные роли сервисов в dev-окружении:

  • app: отвечает за исходный код и запущенный контейнер. Поддерживает монтирования, шаблоны и оркестрацию деплоя (dir, extends, configs, depends_on).
  • tool: автономный служебный контейнер (UI базы данных, фронтенд наблюдаемости и т.д.). Не отвечает за исходный код; не может зависеть от других сервисов и быть целью depends_on.
  • infra: поддерживающий контейнер (база данных, кеш, брокер, обратный прокси). Может быть целью depends_on, но не отвечает за исходный код; более лёгкий футпринт, чем app.

Это разделение гарантирует, что логика сборки и деплоя явная (определена только в type: app), а инфраструктурные сервисы остаются независимо тестируемыми без зависимости от кода приложения.

Полеapptoolinfra
type
container
required
compose
ports
hosts
icon
info
depends_on
status
dir
dir_internal
work_dir_internal
configs
dirs
extends
cli
render
generated
on_enable
on_disable
notes
bridge

Запрещённое поле — фатальная ошибка загрузки (ErrServiceFieldNotAllowed). Валидация агрегирует нарушения каждого файла через errors.Join, поэтому один проход разбора выявляет сразу все проблемы.

  • Каждый workspace/services/<name>/service.yml декодируется строго — неизвестные и не разрешённые для данного типа поля являются фатальными ошибками. Ошибки со всех папок сообщаются вместе, поэтому сразу выявляются все сломанные папки, а не только первая.
  • Наследование сервисов через extends: разрешается в топологическом порядке (родители раньше потомков), поэтому многоуровневые цепочки (C → B → A) сливаются корректно независимо от порядка обхода карты. Циклы и неизвестные родители сообщаются как ошибки загрузки. extends: только для app.
  • Для каждого потомка от родителя наследуются только поля с нулевым значением; при конфликтах поля потомка имеют приоритет. Наследуемые слайсы и карты копируются защитно, поэтому мутация потомка никогда не повреждает родителя.
  • Поле dirs дедуплицируется между родителем и потомком (сначала родитель, потомок дописывается). cli.env сливается рекурсивно: родитель даёт значения по умолчанию, потомок выигрывает при конфликтах ключей.
  • После загрузки enabled вычисляется из трёхслойного слияния (services.<name>.enabled); обязательные сервисы принудительно получают enabled: true.
  • Оверлеи под services.<name> могут задавать только enabled:, ports: и hosts:. Любое другое поле там — ошибка оверлея с учётом слоя; структурные поля (container, dir, configs, compose, extends, …) принадлежат workspace/services/<name>/service.yml. Валидатор оверлея также проверяет форму: ports: должна быть картой имя → целое число в 1..65535; hosts: — картой имя → строка.
  • ports: и hosts: глубоко сливаются по имени записи поверх объявленной карты: пер-разработческое переопределение в workspace/local.yml затрагивает только перечисленные ключи; объявленные записи, не упомянутые в оверлее, сохраняются. Через оверлей можно также вводить новые записи. Это первоклассная возможность DWE: разработчикам регулярно нужно переназначить порт, конфликтующий с уже занятым на их хосте, или сменить *.local имя хоста, не редактируя общий workspace/services/<name>/service.yml.
  • Каждый разрешённый сервис (включая вложенные карты ports / hosts после оверлея) внедряется в DweConfig.Raw["services"], так что dot-пути вроде services.main.ports.http и services.adminer.hosts.web разрешаются в правилах экспорта, шаблонах docker.yml, default_from: команд и ссылках info.yml.
  • Значения портов ограничены 1..65535 во время загрузки (как в service.yml, так и в слоях оверлея).

Пример: разработчик, у которого хост уже занимает 8027, локально переназначает adminer без правки общей конфигурации —

# workspace/local.yml (не отслеживается git)
services:
adminer:
ports:
http: 9027 # переопределяет объявленный 8027
main:
hosts:
api: api.dev.local # добавляет новую запись; web остаётся как объявлено

Каждый сервис лежит в собственной папке в workspace/services/. Имя папки становится ключом сервиса.

workspace/services/
main/
service.yml
deploy.yml # необязательно; включает жизненный цикл deploy для этого сервиса
db/
service.yml
varnish/
service.yml
adminer/
service.yml
workspace/services/main/service.yml
# type: app — владеет исходным кодом в dir:, имеет жизненный цикл deploy, рендерит шаблоны
type: app
container: app-main
required: true
dir: ./services/main
dir_internal: /workspace
work_dir_internal: /workspace/src
extends: <parent-app-key> # только для app
depends_on: [db, redis] # может указывать на app или infra (никогда tool)
icon: "📦"
hosts:
web: app.localhost
ports:
http: 80
info:
title: "Main Application"
primary_host: web
primary_port: http
paths:
- name: "API Documentation"
path: /api/docs
icon: "📖"
compose:
- compose/services/main/overlay.yml
configs:
- file: .env
mountpoint: src/.env
dirs: [logs, home, runtime]
cli:
mode: auto|exec|run
shell: bash
user: www-data
workdir: /workspace/src
env:
- KEY=value
render:
ide: { enabled: true, template: <pack> }
ai: { enabled: true, template: <pack> }
git: { enabled: true, template: <pack> }
workspace/services/db/service.yml
# type: infra — поддерживающий сервис, может быть целью depends_on
type: infra
container: db
required: true # всегда включённый поддерживающий сервис
ports:
mysql: 13306
workspace/services/varnish/service.yml
# type: infra (необязательный) — переключается через `dwe services enable varnish`
# Примечание: поле container здесь опущено — по умолчанию используется имя папки "varnish"
type: infra
compose:
- compose/services/varnish/overlay.yml
ports:
http: 6081
workspace/services/adminer/service.yml
# type: tool — эфемерный служебный контейнер, никогда не цель depends_on
type: tool
container: adminer
icon: "🔧"
compose:
- compose/tools/adminer.yml
ports:
http: 8027
hosts:
web: db.localhost
info:
title: Adminer
  • dwe shell [service] — открыть shell в контейнере любого включённого сервиса (блок умолчаний cli: действует только для type: app; tool/infra используют встроенные умолчания bash/auto).
  • dwe status — составное представление только для чтения: разделы apps + tools + infra, у каждого свои status: колонки.
  • dwe status apps / dwe status tools / dwe status infra — таблицы по типам.
  • dwe services — интерактивный мультивыбор для переключения каждого необязательного сервиса всех типов.
  • dwe services list — read-only список всех настроенных сервисов (app, tool, infra, включая обязательную infra) с их состоянием enabled/running; то же представление, на которое голый dwe services откатывается при неинтерактивном stdin или под --output json. Никогда не пишет в local.yml и не запускает lifecycle-хуки.
  • dwe services enable <name> / dwe services disable <name> — переключение по имени (тип определяется внутри).
  • dwe deploy run — запускает полный пайплайн deploy; перечисляет все включённые сервисы, у которых есть workspace/services/<name>/deploy.yml (любой тип сервиса).
  • dwe reset run --service <name> — сбрасывает один сервис: останавливает и удаляет контейнер, удаляет dir: сервиса, если он объявлен и существует, запускает per-service reset.yml, если он есть, помечает сервис требующим последующего deploy. Тома автоматически не удаляются (opt-in через docker_remove_project_volumes).