Files
infra/stacks/imgbot/README.md
T
gilfanovrandClaude Opus 5 a65d6a249e imgbot: стек из двух сервисов вместо процессов на хосте
Проект агента (~tgclaude/imgbot) работал тремя голыми python-процессами,
запущенными руками из SSH-сессии: без юнита, без restart-политики, при
Linger=no. Держались только на KillUserProcesses=no и не пережили бы
перезагрузку, причём молча.

Один образ на базе playwright/python:v1.62.0-noble (версия пакета
playwright обязана совпадать с тегом, иначе разъезжается ревизия
chromium), два сервиса:
  - imgbot-daemon — APScheduler: refresh 30 мин, analyze 5 мин
  - imgbot-http   — объединил прежние mcp-http (8765) и file-server
    (8766): MCP как под-приложение Starlette плюс маршрут /photo/<id>

Неочевидное, из-за чего это ломается молча (в compose есть комментарии):
  - данные монтируются в тот же путь, что на хосте: images.file_path в
    SQLite абсолютный, и его читают обе стороны
  - корней хранения может быть больше одного — проверять все префиксы
    file_path, а не только data_dir
  - HOME обязателен: collector.py вызывает Path.home(), а uid 1001 не
    имеет записи в /etc/passwd образа
  - у MCP своя защита от DNS-rebinding: без IMGBOT_ALLOWED_HOSTS отдаёт
    421 на любой Host кроме localhost

Порт не публикуется на хост, сервис доступен из общей docker-сети с
claude-channels — агент наконец ходит по MCP, а не по SSH.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 15:26:16 +03:00

69 lines
4.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# imgbot-daemon
Контейнер для фонового демона проекта **imgbot** — headless-поиска и скачивания
картинок. Проект принадлежит агенту, живёт в `/home/tgclaude/imgbot` и правится
им же; здесь только среда запуска и присмотр.
## Зачем
До этого демон работал как голый процесс на хосте: запущен руками из
SSH-сессии, `PPID=1`, без юнита и restart-политики, `Linger=no` у пользователя.
Держался только потому, что в Ubuntu `KillUserProcesses=no` — **после
перезагрузки не поднялся бы**, и сигнала об этом не было бы. Плюс его 15%
ядра не были ничем ограничены и не были видны мониторингу.
Контейнер даёт: автозапуск и `restart: unless-stopped`, потолки CPU/памяти,
видимость в cAdvisor (а значит и в Grafana), и снимает костыль с руками
распакованными библиотеками chromium.
## Что тут НЕ запускается и почему
Два других процесса проекта — `imgbot-mcp-http` (порт 8765) и
`imgbot-file-server` (8766) — оставлены вне контейнеров сознательно:
- оба **жёстко забиндены в коде на `172.22.0.1`** — адрес шлюза docker-сети
`openclaw_default`. В контейнере такой адрес не поднять, а сама привязка
ломается при пересоздании сети;
- до них всё равно никто не дотягивается: единственный живой потребитель —
агент в контейнере `claude-channels` — находится в другой docker-сети и
ходит через SSH, а не по HTTP (проверено: `curl` из контейнера даёт `000`);
- `imgbot-mcp-http` вообще без авторизации, в отличие от file-server с его
Bearer-токеном.
Их имеет смысл контейнеризовать только вместе с правкой бинда на `0.0.0.0`
(две строки: `HOST` в `file_server.py`, `host=` в `mcp_server.py`) и уже в
общей docker-сети с `claude-channels` — тогда агент получит MCP по токену
вместо SSH. Это решение владельца кода, не инфраструктуры.
## Запуск
Собрать и поднять (на сервере каталог стека — `~/imgbot-daemon/`, чтобы не путался с самим проектом в `~tgclaude/imgbot`):
docker compose build
# погасить запущенный руками демон, иначе будет два и двойной analyze:
pkill -u tgclaude -f imgbot-daemon
docker compose up -d
docker logs -f imgbot-daemon
Проверка, что расписание пошло: в `~tgclaude/imgbot-data/daemon.log` должны
появляться строки `_job_analyze ... executed successfully` каждые 5 минут и
`_job_refresh` каждые 30.
## Откат
docker compose down
# и вернуть как было, из-под tgclaude:
cd ~/imgbot && .venv/bin/imgbot-daemon >> ~/imgbot-data/daemon.log 2>&1 &
## Важно для владельца кода
Перезапуск демона теперь `docker restart imgbot-daemon`, а не `pkill` плюс
ручной старт. Если запустить процесс руками при живом контейнере — получатся
два демона, которые будут конкурировать за одни и те же строки SQLite
(в проекте это уже случалось с cron без `flock` и выглядело как зависание).
Правки кода пересборки не требуют: исходники монтируются read-only, достаточно
`docker restart imgbot-daemon`. Пересобирать нужно только при изменении
зависимостей в `pyproject.toml` — и тогда версию playwright в `Dockerfile`
надо держать совпадающей с `uv.lock`, иначе разойдётся ревизия браузера.