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>
This commit is contained in:
co-authored by
Claude Opus 5
parent
bf4708b775
commit
a65d6a249e
@@ -0,0 +1,40 @@
|
||||
# Среда запуска для imgbot-daemon. Сам код — не наш: проект живёт у агента
|
||||
# в /home/tgclaude/imgbot и правится им же. Здесь только окружение.
|
||||
#
|
||||
# База — официальный образ playwright с тегом ровно под версию из uv.lock
|
||||
# проекта (playwright 1.62.0): в нём уже есть chromium и его системные
|
||||
# библиотеки. Именно поэтому в контейнере не нужен костыль
|
||||
# ~tgclaude/chrome-libs/extract/usr/lib/... , которым это лечилось на хосте.
|
||||
FROM mcr.microsoft.com/playwright/python:v1.62.0-noble
|
||||
|
||||
# В этом образе лежат только браузеры (/ms-playwright, PLAYWRIGHT_BROWSERS_PATH
|
||||
# уже выставлен), а python-пакета playwright нет — его ставим сами, и версию
|
||||
# фиксируем ровно 1.62.0 = версия из uv.lock проекта = ревизия chromium-1234,
|
||||
# вкомпилированная в этот тег. Разъезд версии пакета и образа даёт
|
||||
# "Executable doesn't exist" при launch(), поэтому теги менять только парой.
|
||||
#
|
||||
# Остальное — по фактическим импортам обоих сервисов из этого образа:
|
||||
# collector/fetcher → playwright
|
||||
# storage/analyze → pillow
|
||||
# daemon → apscheduler (tzlocal подтянётся зависимостью)
|
||||
# app/mcp_server → mcp (с ним приходят starlette, uvicorn, httpx)
|
||||
# Версии — из uv.lock проекта, чтобы образ и хостовый venv не разъезжались.
|
||||
RUN pip install --no-cache-dir \
|
||||
playwright==1.62.0 \
|
||||
pillow==12.3.0 \
|
||||
apscheduler==3.11.3 \
|
||||
mcp==1.29.1
|
||||
|
||||
# Исходники НЕ копируются в образ — они монтируются снаружи (см. compose),
|
||||
# чтобы правки в проекте применялись рестартом контейнера, без пересборки.
|
||||
#
|
||||
# HOME задан не случайно: collector.py безусловно вызывает Path.home(), а uid
|
||||
# 1001 внутри образа не имеет записи в /etc/passwd — без HOME expanduser
|
||||
# упадёт с RuntimeError.
|
||||
ENV PYTHONPATH=/app/src \
|
||||
PYTHONUNBUFFERED=1 \
|
||||
IMGBOT_DATA_DIR=/home/tgclaude/imgbot-data \
|
||||
HOME=/tmp
|
||||
|
||||
WORKDIR /app
|
||||
CMD ["python3", "-c", "from imgbot.daemon import main; main()"]
|
||||
Reference in New Issue
Block a user