# Эксплуатация: контейнер, миграции, TLS, зависимости (изменение 022) Находка ревью № 12, серьёзность — низкая. ## Context - `Dockerfile`: процесс работает от root, нет `HEALTHCHECK`; у сервиса `app` в compose нет `healthcheck`, хотя `/healthz` есть. - `alembic upgrade head` в `CMD` выполняется при старте каждой реплики — при масштабировании миграции гоняются параллельно. - Приложение отдаётся по HTTP на `0.0.0.0:8088`: JWT и пароли идут открытым текстом по LAN. - Зависимости заданы диапазонами без lock-файла — сборки невоспроизводимы. ## Решение 1. **Dockerfile:** пользователь `app` (uid 10001), `USER app`; `HEALTHCHECK` через `python -c` запрос к `/healthz` (curl в slim-образе отсутствует). 2. **Compose:** `healthcheck` у `app`; `restart: unless-stopped` у обоих сервисов; порт приложения по умолчанию — `127.0.0.1:${APP_PORT}` с переменной `APP_BIND` (по умолчанию 127.0.0.1) для явного открытия в LAN. 3. **Миграции:** в `alembic/env.py` — `pg_advisory_lock` на время `run_migrations` (одна реплика мигрирует, остальные ждут и видят актуальную схему). 4. **TLS:** раздел README «Публикация» — пример блока Caddy (на хосте уже есть контейнер `caddy`) с reverse-proxy на `127.0.0.1:${APP_PORT}` и `TRUSTED_PROXIES` = адрес сети Docker прокси. Конфигурацию Caddy на хосте не меняем — только документация. 5. **Зависимости:** `requirements.lock` через `pip-compile` (pip-tools в `requirements-dev.txt`); `Dockerfile` ставит из lock-файла; обновление — командой из README. ## Файлы `Dockerfile`, `docker-compose.yml`, `alembic/env.py`, `requirements.lock`, `requirements-dev.txt`, `.env.example`, `README.md`. ## Проверка - Пересборка стенда `ipam_control_006`: контейнер `healthy`, процесс не root (`docker exec … id`), тесты `pytest -q` проходят. - Два одновременных `docker compose run app alembic upgrade head` на пустой БД — без ошибок. - Изменение `APP_BIND` по умолчанию меняет доступность с LAN — **согласовать с пользователем перед внедрением** (сейчас UI открывается по 192.168.5.9:8088).