Files
infra/stacks/imgbot/Dockerfile
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

41 lines
2.6 KiB
Docker
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. Сам код — не наш: проект живёт у агента
# в /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()"]