Files
ayurishchevandClaude Sonnet 5 bcf8156085 Initial commit: RIPE CIDR/FQDN collector
Collector daemon, FastAPI server (addresses, diff, collect, sources),
SQLite storage with change journal, Docker Compose deployment,
tests, documentation and project rules.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 07:29:38 +03:00

7.7 KiB
Raw Permalink Blame History

План: контейнер для приложения (Dockerfile и compose)

Context

Сейчас приложение ставится вручную: venv, два systemd/OpenRC-сервиса, ручное управление пользователем и правами. Контейнеры используются только для тестов (Dockerfile.test). Цель: запускать API и демон-сборщик одной командой docker compose up -d, с данными на томе, без root и с проверками состояния. Формат данных и API не меняются.

Решение пользователя: порт 8000 публикуется наружу без TLS (как сейчас). Риск: токен X-API-Key идёт по HTTP открытым текстом, защита только файрволом; это фиксируется в README, TLS - отдельная доработка.

Артефакты по правилам проекта (создаются при реализации)

  • docs/plan-app-container.md - копия этого плана (первым шагом)
  • docs/summary-app-container.md - итоги (в конце)
  • обновить README.md (раздел про Docker Compose, миграция существующих данных)

Архитектура

Один образ, два сервиса compose (api, collector) с общим томом ripe_data, смонтированным в /data. Сервисы по-прежнему общаются только файлами (config.json, data.json, fqdn_data.json, status.json, *.lock).

Изменения

1. Код: настраиваемый каталог данных (минимальная правка)

  • cidr_collector.py: добавить DATA_DIR = os.environ.get("RIPE_DATA_DIR", BASE_DIR); пути CONFIG_FILE, DATA_FILE, FQDN_DATA_FILE, STATUS_FILE строятся от DATA_DIR. Без переменной поведение прежнее (systemd/venv-установки не ломаются).
  • collector_daemon.py: LOCK_FILE строится от cc.DATA_DIR (сейчас cc.BASE_DIR).
  • Тесты не меняются (они подменяют атрибуты модуля).

2. Dockerfile (новый)

  • База python:3.11-slim; зависимости отдельным слоем из requirements.txt (pip install --no-cache-dir).
  • Копируются только рабочие файлы: api_server.py, cidr_collector.py, collector_daemon.py, formatters.py, storage.py, healthcheck.py.
  • Пользователь ripe (uid 10001), /data создаётся и принадлежит ему (именованный том при первом создании наследует владельца).
  • ENV RIPE_DATA_DIR=/data PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1, VOLUME /data.
  • CMD в exec-форме (сигналы доходят до процесса): по умолчанию uvicorn api_server:app --host 0.0.0.0 --port 8000; сборщик переопределяет команду в compose.

3. healthcheck.py (новый, маленький)

  • python healthcheck.py api - GET http://127.0.0.1:8000/health, успех при HTTP 200.
  • python healthcheck.py collector - читает status.json (cc.STATUS_FILE), успех, если updated_at моложе cc.STATUS_STALE_AFTER (переиспользуем константы и логику heartbeat).

4. docker-compose.yml (новый)

  • api: build: ., ports: "8000:8000", env_file: .env (RIPE_API_TOKEN), healthcheck (api), restart: unless-stopped.
  • collector: тот же образ, command: ["python", "collector_daemon.py"], healthcheck (collector), stop_grace_period: 60s (идущий сбор успевает завершиться по SIGTERM), токен не передаётся.
  • Оба: volumes: ripe_data:/data, environment: TZ=${TZ:-UTC} (расписание cron считается в этом поясе; в README отметить), усиление изоляции: read_only: true, tmpfs: /tmp, cap_drop: [ALL], security_opt: [no-new-privileges:true], ротация логов json-file (max-size: 10m, max-file: 3).
  • volumes: ripe_data:.
  • .env.example с RIPE_API_TOKEN=change-me и TZ=UTC; .env в .gitignore и .dockerignore. Если токен не задан, POST остаётся отключённым (503) - поведение fail closed сохраняется.

5. Документация и миграция (README.md)

  • Раздел «Docker Compose»: cp .env.example .env, генерация токена, docker compose up -d, docker compose ps, логи, обновление (docker compose build && docker compose up -d).
  • Миграция существующих данных в том: копирование config.json, data.json, fqdn_data.json во временный контейнер с chown 10001.
  • Предупреждения: порт 8000 без TLS - токен виден в сети, ограничить файрволом или поставить proxy; часовой пояс TZ; google.com в текущих данных не входит в конфигурацию и будет стареть по TTL.
  • Уточнить: systemd/OpenRC-установка остаётся рабочей альтернативой; Dockerfile.test используется только для тестов.
  • .dockerignore: добавить .env.

6. Тесты

Новых автотестов не добавляем (минимум по правилам проекта): healthcheck.py и сборка проверяются в контейнерном сценарии ниже; 13 существующих тестов продолжают проходить (и в Dockerfile.test).

Критичные файлы

Новые: Dockerfile, docker-compose.yml, healthcheck.py, .env.example. Правки: cidr_collector.py (пути), collector_daemon.py (LOCK_FILE), README.md, .gitignore, .dockerignore.

Проверка

  1. docker build -f Dockerfile.test ... и запуск: 13 passed (регрессия после правки путей).
  2. docker compose build && docker compose up -d с тестовым .env на копии данных (проект в каталоге scratchpad, реальные данные не трогаем); порт можно временно сменить переменной, чтобы не конфликтовать.
  3. docker compose ps: оба сервиса healthy не позднее чем через ~1 минуту.
  4. curl /health: collector_alive: true; POST /asns с токеном (201), без токена (401); GET /addresses?format=nftables отдаёт конфиг.
  5. Данные переживают docker compose down && up -d (том), файлы в томе принадлежат uid 10001; docker compose exec api id - не root; запись вне /data невозможна (read-only rootfs).
  6. docker compose stop collector завершается штатно (меньше stop_grace_period), остановка демона делает /health degraded через ~2 минуты.
  7. Миграция: скопировать реальные config.json/data.json/fqdn_data.json (копии) в том по инструкции README, убедиться, что API видит те же адреса.
  8. По окончании убрать тестовые контейнеры, тома и образы, созданные при проверке.