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

Архитектура

Высокоуровневый взгляд на то, как DWE и Docker устроены вместе: DWE — это CLI, который превращает YAML-дерево проекта в вызовы Docker Compose, журналирует результат локально и даёт разработчику способ работать с контейнерами. Эта страница — о границе между ними: что относится к DWE, что — к Docker, и где одно заканчивается, а другое начинается.

DWE стоит между разработчиком и Docker-стеком. Он читает проект с диска, пишет рядом небольшое количество сгенерированного состояния и управляет Docker Compose, который реально поднимает контейнеры. Разработчик никогда не набирает docker compose напрямую.

flowchart LR
  Dev["Разработчик<br/>(терминал + браузер)"]

  subgraph Project["Проект на диске"]
    Cfg["workspace.yml<br/>+ workspace/"]
    Comp["compose/<br/>оверлеи"]
    Gen[".dwe/<br/>+ .env<br/>(генерируется)"]
  end

  subgraph DWEBox["DWE CLI"]
    CLI["dwe"]
  end

  subgraph Engine["Docker engine"]
    Compose["docker compose"]
    Containers["контейнеры<br/>сети<br/>тома"]
  end

  Dev -->|"dwe run / deploy / stop"| CLI
  CLI -->|читает| Cfg
  CLI -->|читает| Comp
  CLI -->|пишет| Gen
  CLI -->|shell-вызов| Compose
  Compose --> Containers
  Dev -->|"http://*.localhost<br/>tcp-порты"| Containers

Сам CLI — это один статически слинкованный бинарник со встроенными документацией и пайплайнами. Нет компаньон-процесса, нет загрузчика плагинов — каждый запуск короткоживущий и не имеет состояния, кроме того, что пишется в .dwe/. Единственный резидентный компонент — опциональный демон хост-бриджа: stateless-форвардер, который запускается, пока стек поднят, чтобы dev-контейнеры могли обращаться к dwe на хосте, и сам останавливается вместе со стеком.

У DWE и Docker — каждый свой чёткий участок системы. Именно это разделение позволяет заменять DWE вокруг существующего Compose-стека и сохраняет работоспособность стека без установленного DWE.

ОбластьОтвечает DWEОтвечает Docker / Compose
Модель проектаworkspace.yml + дерево workspace/, включение/выключение сервисов
Список compose-файловУпорядоченный список -f (база + оверлеи), детерминированный порядок мержаСемантика мержа
Имя проектаРазрешается из ${project.prefix}-${project.name} и передаётся как -pИменование ресурсов (<project>_<svc>_<n>)
Команды жизненного циклаОркестрация dwe run / deploy / stop / restart / resetРеальные up / down / stop / rm / wait
Env контейнеровРендерит .env перед up, run, exec, restart, buildЧитает .env и environment: в контейнеры
СетиОбъявлены в compose-файлахСоздаются при up, удаляются при down
ТомаКонвенция именования (<project>_<vol>), политика shared/non-shared, sweep при resetРеальная персистентность данных
Health / готовностьОпрашивает через docker compose ps и docker inspectСообщает состояние health
Хуки / скриптыРендерит Git-хуки, запускает пайплайны deploy/reset/lifecycle
Журнал состояния.dwe/deploy/state.yml, решения о пропуске, блокировки
ЛогиЗеркалит вывод пайплайнов в .dwe/logs/Логи контейнеров (через docker compose logs)
Сборка / pull образовДрайвит docker compose build / pull с policy-аргументамиСлои образа, I/O с реестром

Между ними нет общего изменяемого состояния: DWE пишет YAML и .env, Docker пишет состояние контейнеров. Единственный контакт — это argv, который DWE передаёт в docker compose, и код возврата, который Docker возвращает.

Вызов dwe run — это канонический цикл: прочитать конфиг, отрендерить env, собрать argv, вызвать compose, дождаться health, напечатать info. Всё остальное (dwe deploy run, dwe stop, dwe reset run) повторяет ту же форму с другими пайплайнами и другими compose-подкомандами.

sequenceDiagram
  autonumber
  participant Dev as Разработчик
  participant CLI as dwe
  participant FS as ФС проекта
  participant Engine as Docker engine

  Dev->>CLI: dwe run
  CLI->>FS: читает workspace.yml + workspace/
  CLI->>FS: пишет .env (envfile.Regenerate)
  CLI->>FS: захватывает .dwe/deploy/deploy.lock
  CLI->>Engine: docker compose -p <proj> -f base -f svc1 -f svc2 up -d --wait
  Engine-->>CLI: контейнеры готовы
  CLI->>Engine: docker compose ps --services --status running
  Engine-->>CLI: имена запущенных сервисов
  CLI->>FS: освобождает lock, дописывает .dwe/logs/run.log
  CLI-->>Dev: info-панель (URL, хосты, порты, команды)
  Dev->>Engine: http://my-project.localhost:8080

Три свойства этого цикла критичны:

  • Детерминированный argv. Список compose-файлов отсортирован (tools → infra → apps, по алфавиту внутри каждой группы). Имя проекта шаблонизируется один раз и переиспользуется. Два вызова dwe run на одинаковом конфиге дают побайтово идентичные команды docker compose.
  • Env свежий перед каждым релевантным вызовом. DWE перегенерирует .env непосредственно перед up / run / exec / restart / build. Переменные, видимые контейнеру, всегда синхронизированы с разрешённым конфигом.
  • Никакого долгоживущего процесса. CLI завершается, как только Docker принял команду (или после того, как --wait отработал). Docker engine держит контейнеры живыми; DWE за ними не следит.

Полный пайплайн деплоя разворачивает этот цикл в фазы — preflight, шаги деплоя по сервисам, создание томов, up --wait, info — но форма каждого листового вызова в Docker та же.

Полезная ментальная модель: есть три концентрических хранилища, у каждого свой владелец.

ХранилищеЖивёт вВладелецПереживает dwe reset?
Исходники проектаworkspace.yml, workspace/, compose/, ваш кодВы / gitДа
Сгенерированные артефакты.env, .dwe/, логи, журнал состоянияDWE.dwe/ пересобирается; .env ре-рендерится при следующем run
Runtime-состояниеКонтейнеры, именованные тома, сети, образыDocker engineNon-shared тома сметаются; shared тома выживают

Два следствия, которые стоит знать:

  • Удаление DWE не ломает ваш стек. Файлы в compose/ остаются валидным входом для docker compose. Можно поднять руками: docker compose -f compose/base.yml -f ... up. Ценность DWE — в автоматизации, а не в привязке к инструменту.
  • Клонирование проекта не требует состояния Docker. .dwe/ и .env генерируются. Свежий клон проходит путь от нуля до запущенного состояния через dwe deploy run — копировать какие-либо снапшоты engine не нужно.

Несколько жёстких линий, которые держат архитектуру предсказуемой:

  • DWE не заменяет docker или docker compose. Любая операция с контейнерами — это вызов через shell. Никакого встроенного compose-движка.
  • DWE не устанавливает Docker. Ожидается, что на хосте есть docker и docker compose в $PATH. DWE вызывает их через настраиваемые переопределения (binary_docker в ~/.config/dwe/config), но не разворачивает сам engine.
  • DWE не работает как демон. Никакого фонового процесса, сокета, tray-приложения. Каждая команда начинается заново, читает конфиг, делает свою работу, выходит. Единственное исключение — хост-бридж: пока стек поднят, per-project демон-форвардер обслуживает вызовы dwe из dev-контейнеров и автоматически останавливается вместе с последним контейнером.
  • DWE не делает сетевых вызовов на штатном пути. Никаких обращений к внешним серверам, проверок обновлений, скачиваний шаблонов. Сетевой трафик возникает только из того, что пользователь сам положил в шаг пайплайна или пользовательскую команду (curl в шаге type: shell, docker pull из реестра, git push в хуке).
  • DWE не управляет /etc/hosts или прокси. Имена вроде my-project.localhost резолвятся через системный резолвер (*.localhost — это loopback по RFC) или через локальный DNS / reverse proxy, который запускает разработчик. DWE рендерит имена в конфиг и в info-панель; маршрутизация — вне его зоны ответственности.

Эта узкая граница — то, что делает DWE полезным в CI, на изолированных машинах и рядом с существующими Compose-воркфлоу.

  • Интеграция с Docker — глубокое погружение в сборку compose-файлов, именование проекта, проброс env, конвенции для томов и несколько случаев, когда DWE обходит compose и вызывает docker stop / docker rm напрямую.
  • Раскладка проекта — для чего каждая папка под workspace/ и что генерируется под .dwe/.
  • Пайплайны — модель выполнения phase / step / condition, которую разделяют deploy, reset и lifecycle.
  • Состояние и блокировки — что записывает state.yml и как deploy.lock / snapshot.lock сериализуют мутации.
  • Интеграция с Git — что DWE рендерит в .git/hooks/ проекта и как собирается обзор workspace.
  • Для контрибьюторов: docs/internals/architecture.md — внутреннее разделение cli/core/shared/ внутри бинарника, и docs/internals/packages.md для ответственностей пакетов.