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

Наблюдение за контейнерами 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.shCMD контейнера. Маршрутизирует аргументы пользователя, переключается на 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.pyS6ServiceManager: register_profile_gateway, unregister_profile_gateway, start/stop/restart/is_running, list_profile_gateways.
vibeos_cli/container_boot.pyreconcile_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 помешали этому:

  1. Скрипты cont-init.d не получают аргументов CMD — поэтому хук stage2 не может разобрать docker run &lt;image&gt; chat -q "hi", чтобы установить VIBEOS_ARGS для использования скриптом run сервиса.
  2. /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 &lt;image&gt; --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

Добавление нового статического сервиса​

  1. Создайте docker/s6-rc.d/&lt;name&gt;/type с содержимым longrun\n и docker/s6-rc.d/&lt;name&gt;/run (используйте #!/command/with-contenv sh + # shellcheck shell=sh).
  2. Переключитесь на vibeos через s6-setuidgid vibeos в начале run (если вам не нужен root).
  3. Создайте пустой docker/s6-rc.d/&lt;name&gt;/dependencies.d/base, чтобы он ожидал базовый набор.
  4. Создайте пустой docker/s6-rc.d/user/contents.d/&lt;name&gt;, чтобы он присоединился к пользовательскому набору.
  5. 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 &lt;c&gt; 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 &lt;c&gt; 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/&lt;name&gt;/ вручную от root — следующий проход согласования очистит их, но текущие операции могут столкнуться с ошибками прав доступа.

Слот сервиса существует, но s6-svstat говорит «s6-supervise not running»​

Каталог сервиса находится на tmpfs и был очищен при перезапуске контейнера. Либо согласующий процесс cont-init еще не запустился (подождите немного после docker restart), либо он завершился ошибкой. Проверьте docker logs &lt;c&gt; | grep '02-reconcile'.

Шлюз запускается и сразу завершается (down (exitcode 1) в svstat)​

Скорее всего, у профиля нет модели или аутентификации. Слот сервиса корректен — сам шлюз не настроен. Сначала запустите vibeos -p &lt;profile&gt; 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-agent
  • vibeos-tool-quirks: Специфические обходные пути для инструментов VibeOS (sed/grep/и т.д.) — загружайте при отладке взаимодействия стека s6 со встроенными инструментами vibeos.