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

63 lines
7.7 KiB
Markdown
Raw Permalink 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.
# План: контейнер для приложения (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. По окончании убрать тестовые контейнеры, тома и образы, созданные при проверке.