Наблюдение за контейнерами VibeOS S6
Изменение, отладка или расширение дерева наблюдения s6-overlay внутри Docker-образа VibeOS — добавление новых сервисов, отладка шлюзов профилей, понимание шаблона основной программы Архитектуры B.
Метаданные навыка
| Источник | Опционально — установка с помощью vibeos skills install official/devops/vibeos-s6-container-supervision |
| Путь | optional-skills/devops/vibeos-s6-container-supervision |
| Версия | 1.0.0 |
| Автор | VibeOS |
| Лицензия | MIT |
| Платформы | linux |
| Теги | docker, s6, supervision, gateway, profiles |
| Связанные навыки | vibeos-agent, vibeos-agent-dev |
Справочник: полный SKILL.md
Ниже приведено полное определение навыка, которое VibeOS загружает при активации этого навыка. Это те инструкции, которые видит агент, когда навык активен.
Наблюдение за контейнерами VibeOS s6-overlay
Когда использовать этот навык
Загружайте этот навык, когда работаете над:
- Добавлением или удалением статического сервиса в Docker-образе VibeOS (что-то, что должно наблюдаться при каждом запуске контейнера, например, панель управления)
- Диагностикой причин, по которым шлюз для конкретного профиля не запускается, не перезапускается или не выживает после
docker restart - Пониманием, почему CMD контейнера —
/opt/vibeos/docker/main-wrapper.shи как аргументы с ведущим дефисом достигают программы пользователя - Изменением загрузочных скриптов
cont-init.d(переназначение UID, заполнение томов, согласование профилей) - Изменением сгенерированного скрипта запуска для шлюзов для конкретных профилей (Фаза 4)
Если вы просто запускаете VibeOS и хотите использовать Docker, обратитесь к website/docs/user-guide/docker.md.
Архитектура с первого взгляда
/init ← PID 1 (s6-overlay v3.2.3.0)
├── cont-init.d ← одноразовая настройка, выполняется от root
│ ├── 01-vibeos-setup ← docker/stage2-hook.sh
│ │ ├── Переназначение UID/GID
│ │ ├── chown /opt/data
│ │ ├── chown /opt/data/profiles (при каждой загрузке)
│ │ ├── Заполнение .env / config.yaml / SOUL.md
│ │ └── skills_sync.py
│ └── 02-reconcile-profiles ← vibeos_cli.container_boot
│ ├── chown /run/service (доступно для записи vibeos для регистрации во время выполнения)
│ └── Обход $VIBEOS_HOME/profiles/<name>/gateway_state.json
│ → воссоздание /run/service/gateway-<name>/
│ → автоматический запуск только тех, у кого prior_state == "running"
│
├── s6-rc.d (статические сервисы, в /etc/s6-overlay/s6-rc.d/)
│ ├── main-vibeos/run ← exec sleep infinity (заглушка без операции)
│ └── dashboard/run ← если VIBEOS_DASHBOARD=1, запускает `vibeos dashboard`
│
├── /run/service (наблюдается s6-svscan; tmpfs)
│ ├── gateway-coder/ ← зарегистрированные во время выполнения для каждого профиля
│ │ ├── type ("longrun")
│ │ ├── run ("#!/command/with-contenv sh ... exec s6-setuidgid vibeos vibeos -p coder gateway run")
│ │ ├── down (маркер — наличие означает "зарегистрирован, но не запускать автоматически")
│ │ └── log/run (s6-log → $VIBEOS_HOME/logs/gateways/coder/current)
│ └── ...
│
└── CMD ("основная программа") ← /opt/vibeos/docker/main-wrapper.sh
└── Маршрутизация аргументов пользователя: прямой exec | подкоманда vibeos | vibeos (без аргументов)
— выполняется /init с наследованием stdin/stdout/stderr (TTY для --tui)
Ключевые файлы
| Путь | Роль |
|---|---|
Dockerfile | Установка s6-overlay + подключение cont-init.d + ENTRYPOINT ["/init", "/opt/vibeos/docker/main-wrapper.sh"] |
docker/stage2-hook.sh | «Старая логика точки входа» — переназначение UID, chown, заполнение, синхронизация навыков. Выполняется как cont-init.d/01-vibeos-setup. |
docker/cont-init.d/02-reconcile-profiles | Вызывает vibeos_cli.container_boot при каждой загрузке для восстановления слотов шлюзов профилей из постоянного тома. |
docker/main-wrapper.sh | CMD контейнера. Маршрутизирует аргументы пользователя, переключается на vibeos через s6-setuidgid, выполняет выбранную программу. |
docker/s6-rc.d/main-vibeos/run | Заглушка sleep infinity — слот существует, чтобы набор пользователя s6-rc был валидным; основной vibeos запускается как CMD, а не как наблюдаемый сервис. |
docker/s6-rc.d/dashboard/run | Условный сервис — exec sleep infinity, если только VIBEOS_DASHBOARD не является истинным. |
docker/entrypoint.sh | Обратно-совместимая заглушка, которая выполняет хук stage2. Внешние скрипты, жестко прописавшие старый путь точки входа, все еще работают. |
vibeos_cli/service_manager.py | S6ServiceManager: register_profile_gateway, unregister_profile_gateway, start/stop/restart/is_running, list_profile_gateways. |
vibeos_cli/container_boot.py | reconcile_profile_gateways() — обходит постоянные профили, перегенерирует слоты s6, записывает container-boot.log. |
vibeos_cli/gateway.py::_dispatch_via_service_manager_if_s6 | Перехватывает vibeos gateway start/stop/restart и направляет в s6 при работе в контейнере. |
Почему Архитектура B (CMD как основная программа, а не под наблюдением s6)
Первоначальный план (v1–v3) предполагал запуск основного vibeos как наблюдаемого сервиса s6-rc. Два реальных механизма s6-overlay v3 помешали этому:
- Скрипты cont-init.d не получают аргументов CMD — поэтому хук stage2 не может разобрать
docker run <image> chat -q "hi", чтобы установитьVIBEOS_ARGSдля использования скриптомrunсервиса. /run/s6/basedir/bin/haltНЕ передает код возврата, записанный в/run/s6-linux-init-container-results/exitcode. Контейнеры всегда завершаются с кодом 143 (SIGTERM) независимо от этого. Подтверждено skarnet (автором s6) в issue #477: «если вы хотите корректного завершения контейнера, вам нужно либо дождаться завершения вашего CMD, либо, если у вас нет CMD, записать желаемый код возврата контейнера, а затем вызвать halt».
Поэтому мы используем нативный для s6-overlay шаблон CMD: ENTRYPOINT ["/init", "/opt/vibeos/docker/main-wrapper.sh"]. /init автоматически добавляет обертку к аргументам пользователя — так что docker run <image> --version превращается в /init main-wrapper.sh --version, и --version не перехватывается POSIX-шеллом /init. Обертка переключается на vibeos через s6-setuidgid, затем выполняет выбранную программу. Код возврата программы становится кодом возврата контейнера, что полностью соответствует контракту tini (до-s6).
Компромисс: основной vibeos не наблюдается под s6. Это в точности соответствует его поведению под tini (образ до-s6). Наблюдение за панелью управления — это единственная новая гарантия — а шлюзы для конкретных профилей в /run/service/ получают полное наблюдение.
Быстрые рецепты
Проверка, что s6 является PID 1 в работающем контейнере
docker exec <c> sh -c 'cat /proc/1/comm; readlink /proc/1/exe'
# Ожидается: s6-svscan или init / /package/admin/s6/.../s6-svscan
Проверка сервиса шлюза профиля
# /command/ нет в PATH для docker-exec — используйте абсолютный путь
docker exec <c> /command/s6-svstat /run/service/gateway-<name>
# "up (pid …) … seconds" → работает
# "down (exitcode N) … seconds, normally up, want up, …" → s6 хочет его запустить, но процесс постоянно завершается (цикл падений)
# "down … normally up, ready …" → пользователь остановил его
Ручной запуск/остановка сервиса
docker exec <c> /command/s6-svc -u /run/service/gateway-<name> # вверх
docker exec <c> /command/s6-svc -d /run/service/gateway-<name> # вниз
docker exec <c> /command/s6-svc -t /run/service/gateway-<name> # SIGTERM (перезапуск)
Просмотр журнала согласования cont-init
docker exec <c> tail -n 50 /opt/data/logs/container-boot.log
# 2026-05-21T06:18:05+0000 profile=coder prior_state=running action=started
# 2026-05-21T06:18:05+0000 profile=writer prior_state=stopped action=registered
Добавление нового статического сервиса
- Создайте
docker/s6-rc.d/<name>/typeс содержимымlongrun\nиdocker/s6-rc.d/<name>/run(используйте#!/command/with-contenv sh+# shellcheck shell=sh). - Переключитесь на vibeos через
s6-setuidgid vibeosв начале run (если вам не нужен root). - Создайте пустой
docker/s6-rc.d/<name>/dependencies.d/base, чтобы он ожидал базовый набор. - Создайте пустой
docker/s6-rc.d/user/contents.d/<name>, чтобы он присоединился к пользовательскому набору. COPY docker/s6-rc.d/в Dockerfile подхватит это автоматически — никаких других изменений.
Изменение команды запуска шлюза для конкретного профиля
Отредактируйте S6ServiceManager._render_run_script в vibeos_cli/service_manager.py. Эта функция также вызывается из vibeos_cli/container_boot.py::_register_service во время согласования при загрузке, так что это единый источник истины. Обновите соответствующее утверждение в tests/vibeos_cli/test_service_manager.py::test_s6_register_creates_service_dir_and_triggers_scan.
Запуск тестового набора Docker
docker build -t vibeos-agent-harness:latest .
VIBEOS_TEST_IMAGE=vibeos-agent-harness:latest scripts/run_tests.sh tests/docker/ -v
# Ожидается: 19 пройдено, 0 xfailed для образа s6
Тестовый набор находится в tests/docker/ и пропускается, если Docker недоступен. Таймаут для каждого теста увеличен до 180 с (см. tests/docker/conftest.py).
Типичные ошибки
«command not found» через docker exec
/command/ (где s6-overlay размещает свои бинарники) находится в PATH только для процессов, порожденных деревом наблюдения — сервисов, cont-init.d, main-wrapper.sh. docker exec <c> s6-svstat … завершится ошибкой «command not found»; всегда используйте абсолютный путь /command/s6-svstat. Бинарник vibeos работает, потому что Dockerfile добавляет /opt/vibeos/.venv/bin в ENV PATH во время выполнения.
Владение каталогом профиля
Согласующий процесс cont-init работает от имени vibeos (s6-setuidgid vibeos в 02-reconcile-profiles). Если каталог профиля оказывается принадлежащим root (например, потому что docker exec <c> vibeos profile create … по умолчанию выполняется от root), согласующий процесс не сможет прочитать SOUL.md и завершится ошибкой PermissionError. Смягчение: stage2-hook.sh выполняет chown для $VIBEOS_HOME/profiles на vibeos при каждой загрузке, идемпотентно. Не удаляйте этот блок.
Файлы, записанные через docker exec, принадлежат root
docker exec по умолчанию выполняется от root. Либо передавайте --user vibeos, либо полагайтесь на очистку chown на этапе stage2 при следующей перезагрузке. Не записывайте файлы в $VIBEOS_HOME/profiles/<name>/ вручную от root — следующий проход согласования очистит их, но текущие операции могут столкнуться с ошибками прав доступа.
Слот сервиса существует, но s6-svstat говорит «s6-supervise not running»
Каталог сервиса находится на tmpfs и был очищен при перезапуске контейнера. Либо согласующий процесс cont-init еще не запустился (подождите немного после docker restart), либо он завершился ошибкой. Проверьте docker logs <c> | grep '02-reconcile'.
Шлюз запускается и сразу завершается (down (exitcode 1) в svstat)
Скорее всего, у профиля нет модели или аутентификации. Слот сервиса корректен — сам шлюз не настроен. Сначала запустите vibeos -p <profile> setup. Наблюдатель s6 будет продолжать перезапускать его; это желаемое поведение (когда вы исправите конфигурацию, следующая попытка будет успешной и останется активной).
Согласующий процесс пропустил профиль
Согласующий процесс ориентируется на наличие SOUL.md как маркер «настоящего профиля». vibeos profile create всегда создает его. Если в каталоге профиля отсутствует SOUL.md (посторонний каталог, частичное восстановление, выполняющееся резервное копирование), согласующий процесс намеренно пропускает его. Добавьте SOUL.md (даже пустой), чтобы снова включить профиль.
«Помогите, контейнер завершается с кодом 143!»
Проверьте, не вызывает ли что-то s6-svscanctl -t или /run/s6/basedir/bin/halt — оба заставляют /init начать завершение этапа 3, но возвращают 143 (SIGTERM) вместо желаемого кода возврата. Это был поворот архитектуры Фазы 2 от A к B. Для корректного завершения контейнера с реальным кодом возврата вы должны позволить CMD (main-wrapper.sh) завершиться нормально; не пытайтесь управлять завершением из скрипта finish.
Связанные навыки
vibeos-agent-dev: Общая навигация по кодовой базе vibeos-agentvibeos-tool-quirks: Специфические обходные пути для инструментов VibeOS (sed/grep/и т.д.) — загружайте при отладке взаимодействия стека s6 со встроенными инструментами vibeos.