Skip to content

📱 Управление приложениями (демон)

📌 Описание

Ядро демона: единый интерфейс управления приложениями трёх типов — 🐳 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, REST uuid), отсутствует, если не задан.

⚙️ Ядро

  • Драйверы: трейт AppDriver { start, stop, restart, state, logs, remove } с реализациями DockerDriver (через Docker Engine API поверх unix-сокета — не через docker CLI; путь сокета настраивается — [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.

Распространяется по лицензии MIT.