📱 Управление приложениями (демон)
📌 Описание
Ядро демона: единый интерфейс управления приложениями трёх типов — 🐳 Docker-контейнеры, 📦 нативные приложения (systemd-юниты) и 🔧 обычные процессы (PID). Ключевое отличие от Portainer/Coolify: не только Docker.
🎯 Сценарии использования
asc app install helloworld— установка из реестра (Docker или нативное — решает манифест).asc app start|stop|restart|status|logs <name>— управление жизненным циклом.startсразу подключается к консоли приложения (Docker-приложения, интерактивный терминал — какdocker runбез-d);asc app start -d <name>запускает в фоне, без подключения. Если хосту не хватает ресурсов, заявленных вrequirementsманифеста,startпредупреждает с цифрами и спрашивает, продолжать ли на свой страх и риск (DMN-029).- Пользовательские названия (DMN-024):
asc installспрашивает название приложения (Enter — по умолчанию; неинтерактивно —--name).asc app listпоказывает и оригинальный ID, и NAME, а все команды принимают и то и другое:asc app start "My Server"=asc app start cs2-server. При совпадении названия с чужим id приоритет у id; неоднозначное название (несколько одинаковых) — ошибка с подсказкой использовать id. - Несколько инстансов одного приложения (DMN-033): повторная установка уже установленного пакета больше не завершается ошибкой — создаётся новый инстанс
<пакет>-2,<пакет>-3, … (он же становится названием по умолчанию), поэтомуasc install nginxдважды даёт два независимых приложенияnginx/nginx-2. Повторная установка целого стека называет новые инстансы так же;--name <префикс>при установке целого стека называет по этому же принципу каждое приложение-участник (DMN-034), так как у стека нет собственной сущности. asc app disk <имя>(DMN-035): дисковое пространство — прогресс-бар против квоты, если заданquota.max_disk, затем разбивка по Docker-образу, репозиторию, приватным данным и кастомным томам (именованные тома Docker помечены как общие и не входят в сумму по каталогу приложения).asc app clone <имя> [--name <имя-клона>](DMN-019): полная копия инстанса приложения — репозиторий, config и data — под новым id (та же нумерация<id>-N, что и при повторномasc install, DMN-033), с живым прогресс-баром копирования в терминале. Сам рантайм не копируется (Docker-контейнер/systemd-юнит/процесс скопировать нельзя) — он пересоздаётся заново из скопированного манифеста и настроек, точно как при установке. Клон всегда стартует остановленным. Клонирование на другую ноду (перенос копии на другой сервер через платформу) — отдельный, более поздний инкремент, см. 🧬 app-cloning.asc app list— пользователь видит только свои приложения;sudo asc app list— приложения всех пользователей. Короткие алиасы (DMN-025):asc lsиasc ps— тот же вывод и те же права.asc ports [<имя>]/asc app ports [<имя>](DMN-049): опубликованные порты host==container приложения с транспортом (27015/tcp,27015/udpили27015/tcp+udp, когда оба на одном порту), взятые из настроекtype: ports— поэтому остановленное приложение всё равно показывает порты, которые оно займёт при следующем старте. Без имени — таблица всех видимых приложений с их портами (root видит приложения всех пользователей).asc stats [--live] [--sort cpu|mem]— потребление CPU, памяти, диска и сети по приложениям (аналогdocker stats, см. 📊 monitoring).- Подкоманды списка (DMN-049):
asc ls ports,asc ls diskиasc ls statsпереключают тот же список приложений на вид портов, использования диска или живой статистики — зеркалаasc ports/asc disk/asc stats. asc stacks(DMN-051): установленные приложения, сгруппированные по стеку (asc.stack.yaml), из которого они пришли, деревом — имя стека с пометкой, сколько его приложений сейчас запущено ([running/total]), затем его приложения ветками├──/└──(id, name, kind, state, version, uuid — те же колонки, что и вasc ls), root видит приложения всех пользователей. Стек определяется поmeta.package("<стек>/<приложение>", записывается при установке — см. 📦 package-manager); у приложения, установленного самого по себе, вpackageнет/, и оно сюда не попадает.- Платформа выполняет те же операции через API демона.
- После ребута сервера демон восстанавливает состояние приложений (running/stopped).
🏗️ Техническое решение
👥 Группы приложений по пользователям
- Каждое приложение принадлежит Linux-пользователю, который его установил (владелец фиксируется в
meta.jsonи индексе). - Обычный пользователь видит и управляет только своей группой приложений.
- Через
sudo(или отroot) видны и доступны приложения всех пользователей — вывод группируется по владельцам. - API демона применяет то же правило: контекст запроса определяет видимую группу.
- При работающем системном демоне (DMN-042) команды жизненного цикла (
ls/status/install/app start|stop|restart|logs|remove|info) идут через его unix-сокет/run/asc/asc.sock: демон берёт uid вызывающего у ядра (SO_PEERCRED) и применяет это правило к общему системному стору — обычный пользователь управляет своими приложениями в/asc/appsбез sudo и без группы docker, аasc lsиsudo asc lsнаконец показывают одно и то же. Подробнее — 📡 api.
📂 Хранилище приложений: /asc/apps/
Каждое приложение живёт в каталоге по своему ID:
/asc/apps/<id>/
├── config/ # ⚙️ настройки приложения (см. asc.settings.yaml в package-manager.md)
├── repository/ # 📦 клонированный репозиторий приложения (версии — git-теги)
├── data/ # 💾 volumes — если приложение в Docker
└── meta.json # 📇 информация о приложении: id, uuid, имя, пользовательское название, владелец, версия (тег), источник, состояние- Установка = клонирование репозитория пакета в
repository/; переключение версии — checkout нужного git-тега (подробнее — 📦 package-manager). meta.json— источник правды для восстановления индекса после сбоя/ребута.- Разделение путей по пользователям:
/asc/apps/(вместе с/etc/asc/config.tomlи/var/lib/asc) — дерево root-установки: системный демон иsudo asc. Запускascот обычного пользователя без работающего системного демона работает со своим приватным деревом в~/.asc/:~/.asc/apps,~/.asc/data,~/.asc/config.toml— так пользователь редактирует настройки своих приложений и конфиг без sudo. При наличии демона команды жизненного цикла работают с общим системным деревом через сокет демона (DMN-042, см. выше). Root-секция[policy]по-прежнему читается из системного конфига и не переопределяется на уровне пользователя.
🆔 UUID инстанса (DMN-044)
Кроме id, каждый инстанс получает UUID, который генерируется при asc install и хранится в meta.json в поле uuid. Он переживает upgrade и выводится последней колонкой asc ls:
ID NAME KIND STATE VERSION UUID
pingpong Ping Pong docker stopped 0.1.0 6f8a1c2e-3b4d-4e5f-8a9b-0c1d2e3f4a5b
legacy-app Legacy App docker stopped 1.2.0 -Зачем второй идентификатор: id переиспользуется. После удаления helloworld-2 этот id освобождается для следующей установки (DMN-033) — и всё, что живёт дольше приложения (сохранённые учётные данные (DMN-045), записи платформы, история аудита), молча привязалось бы к другому приложению. UUID уходит вместе с инстансом и никогда не выдаётся повторно.
- Генерируется как случайный UUIDv4 (RFC 4122) из
/dev/urandom— ради шестнадцати байт не тянем отдельную зависимость. - Необязателен в
meta.json: у приложений, установленных до DMN-044, ключаuuidнет — они читаются как обычно и показывают-. Ничего не дописывается втихую. asc app cloneвыдаёт клону свой UUID — это отдельный инстанс, а не копия идентичности.- В API отдаётся как
App.uuid(поле proto 10, RESTuuid), отсутствует, если не задан.
⚙️ Ядро
- Драйверы: трейт
AppDriver { start, stop, restart, state, logs, remove }с реализациямиDockerDriver(через Docker Engine API поверх unix-сокета — не черезdockerCLI; путь сокета настраивается —[docker] socket, по умолчанию/var/run/docker.sock),SystemdDriver(юнитыasc-app-<id>.service),ProcessDriver(supervised PID: pid-файл и лог-файл в каталоге приложения). Установка приложения (создание контейнера/юнита из манифеста) — зона пакетного менеджера (DMN-003). - Docker Engine API: демон общается с Docker через Engine API (клиент
bollard, unix-сокет), а не через CLI — это работает и для rootless-инсталляций, и при нестандартном расположении сокета (достаточно указать путь в конфиге). Управляющие операции (start/stop/inspect/create/remove) — синхронные; стрим логов и attach для консоли — асинхронные по тому же сокету. Создание контейнера само скачивает образ, если его нет на хосте (pull через тот же Engine API); ответ Engine с любым HTTP-статусом не считается недоступностью Docker — пользователю показывается сообщение самого Engine. - Индекс приложений:
meta.json— источник правды; в MVP индекс строится сканированием/asc/apps/*/meta.jsonпри обращении, при старте демон сверяет желаемое состояние (desired_state) с реальностью (контейнеры, юниты, процессы) и дозапускает упавшее. Локальная БД (SQLite) появится, когда добавится состояние сверх meta.json (метрики, история операций). - Логи: единый интерфейс — docker logs / journald / файл; стрим наружу через 🖥️ console.
- Cluster-мод (пост-MVP): несколько узлов, работающих вместе как платформа. Несколько инстансов одного приложения на одном узле уже работают (DMN-033/034, выше).
- CLI-команды MVP:
asc status,asc stats,asc ports,asc stacks,asc app list|install|remove|start|stop|restart|logs|info|disk|ports|clone|settings(+ алиасыasc ls/asc psдля списка иasc ls ports|disk|statsдля видов портов/диска/статистики),asc service(управление самим демоном).
🔗 Связанные задачи
DMN-002, DMN-004, DMN-019, DMN-044, DMN-051, FE-005 в ROADMAP.md.