# Итоги: контейнер для приложения План: `docs/plan-app-container.md`. ## Сделано - **`Dockerfile`**: `python:3.11-slim`, зависимости отдельным слоем, копируются только рабочие файлы, пользователь `ripe` (uid 10001), `VOLUME /data`, `CMD` в exec-форме (uvicorn). - **`docker-compose.yml`**: сервисы `api` (порт `${API_PORT:-8000}`, токен из `.env`) и `collector` (`collector_daemon.py`, `stop_grace_period: 60s`), общий том `ripe_data`, `read_only`, `tmpfs /tmp`, `cap_drop: ALL`, `no-new-privileges`, ротация логов, healthcheck обоих сервисов, `TZ` для расписания. - **`healthcheck.py`**: `api` (GET `/health`, 200) и `collector` (heartbeat в `status.json` моложе `STATUS_STALE_AFTER`). - **Код**: `RIPE_DATA_DIR` (`cidr_collector.DATA_DIR`) для путей данных и `LOCK_FILE` демона; без переменной поведение прежнее. - **`.env.example`**, `.env` в `.gitignore` и `.dockerignore`; README, раздел 8 (запуск, настройки, миграция данных, эксплуатация, риски). ## Проверка (стенд в scratchpad, реальные данные не менялись) - Регрессия: 13 тестов в `Dockerfile.test` проходят. - `docker compose up -d`: оба сервиса `healthy` через ~45 с. - Миграция копий данных в том по инструкции: API видит те же адреса (24 CIDR), после `down`/`up -d` данные на месте. - `/health`: `collector_alive: true`; POST без ключа 401, с ключом 201; `format=nftables` работает. - Пользователь в контейнере `uid=10001`, запись вне `/data` даёт `Read-only file system`; `TZ=Europe/Moscow` применился. - Демон в контейнере выполнил плановый сбор (`CIDR data saved to /data/data.json`). - `docker compose stop collector`: штатное завершение за 1 с, через ~2 минуты `/health` = `degraded`, `collector_alive: false`. - После проверки удалены тестовые контейнеры, том и образы. ## Замечания - Порт публикуется наружу без TLS (решение пользователя): токен идёт открытым текстом. - Файлы `config.json` и `status.json`, записанные сервисами, получают режим 600 (создаются через `mkstemp`); оба сервиса работают под одним пользователем, но сторонние читатели тома этих файлов не прочитают. - Зависимости в образе не закреплены (риск воспроизводимости сборки). - Строка `google.com` в текущих данных не входит в конфигурацию и будет стареть по TTL (актуально и при миграции в том). - Для миграции нужно знать имя тома `<проект>_ripe_data` (`docker volume ls`), в README это описано.