--- topic: app-directories prefix: ANSD extends: arch/app-directories.md --- # Категории директорий: реализация в Ansible Как категории из базового слоя раскладываются на сервере плейбуком. Ключевые слова ДОЛЖЕН, НЕ ДОЛЖЕН, СЛЕДУЕТ, НЕ СЛЕДУЕТ, ДОПУСКАЕТСЯ и метки ПОЧЕМУ, ПРИМЕРЫ, МЕХАНИЗИРОВАНО и СНЯТО толкуются как описано в языке конвенций версии 1 — тогда и только тогда, когда написаны заглавными. ## Область действия Раскладка меняется вместе с миграцией данных, поэтому правила распространяются на **новые приложения**; существующие переезжают по мере касания, отдельной кампанией не переписываются. ## Правила ### ANSD-1. Каждая директория объявлена переменной `*_dir` **ДОЛЖЕН.** Директория приложения объявляется переменной плейбука внутри `base_dir`, имя оканчивается на `_dir`. Для случая «одна директория на категорию» это `config_dir`, `data_dir`, `cache_dir`; когда категория состоит из нескольких директорий, имя даётся по содержимому (`media_dir`, `uploads_dir`, `dumps_dir`). **ПОЧЕМУ.** Переменная — единственная ссылка, которую разделяют задача создания директории и список бэкапа (ANSD-4). Литерал пути в одном из этих мест означает, что переименование директории молча разойдётся с бэкапом, и обнаружится это при восстановлении. ### ANSD-2. Директории создаются одной задачей циклом по списку **СЛЕДУЕТ.** Список директорий в единственной задаче создания. **ПОЧЕМУ.** Этот список — единственное место, где декларировано всё, что приложение пишет на диск. Разнесённое по нескольким задачам создание отвечает на вопрос «какие директории есть у приложения» только чтением всего плейбука, а именно этот вопрос задают при заведении бэкапа и при разборе места на диске. ### ANSD-3. Владелец директорий — пользователь, от имени которого работает приложение **ДОЛЖЕН.** Конкретная модель — выделенный пользователь на приложение (`app_owner_uid == app_owner_gid`) или общий `primary_user` — выбирается на репозиторий и фиксируется ниже. **ПОЧЕМУ.** Правило про соответствие владельца рантайму, а не про конкретную модель: приложение в контейнере пишет от определённого uid, и если директория принадлежит другому, отказ произойдёт не при деплое, а при первой записи — то есть после того, как плейбук отчитался об успехе. Выбор же модели — свойство репозитория: сервер с одним пользователем и сервер с изоляцией по приложениям решают разные задачи, и навязывать одну модель обоим значит гарантировать вечное отступление. ### ANSD-4. Список бэкапа собирается из тех же переменных **ДОЛЖЕН.** Плейбук кладёт в `base_dir` файл `backup-targets`, строки которого ссылаются на переменные `*_dir` из ANSD-1, а не на литеральные пути. **ПОЧЕМУ.** Правило вывода списка механическое (ANSD-5), но применяет его человек или шаблон — то есть ошибиться можно. Общая переменная делает целый класс ошибок невозможным: переименовал директорию — переименовалось в обоих местах. Независимо набранный список расходится тихо и проявляется в единственный момент, когда это уже неисправимо. ### ANSD-5. В список бэкапа идут только данные **ДОЛЖЕН.** Директории категории «данные», включая директорию дампов, — в списке; конфигурация и кеш — нет. **ПОЧЕМУ.** Реализация правила базовой конвенции. Кеш раздувает снапшот без пользы, а конфигурация содержит отрендеренные секреты — бэкап уезжает в облако, и источником истины для секретов остаётся vault, а не снапшот. ### ANSD-6. Конфигурация монтируется только на чтение **СЛЕДУЕТ.** В compose конфигурация подключается с `:ro`. **ПОЧЕМУ.** Плейбук — источник истины для конфигурации, и `:ro` превращает это из договорённости в свойство системы: приложение, которое втихую переписывает свой конфиг, падает сразу, а не расходится с репозиторием незаметно. Приложение, которому запись в конфиг нужна по устройству, монтируется на запись — это отступление, и оно записывается. ### ANSD-7. `docker-compose.yml` лежит в корне `base_dir` **ДОЛЖЕН.** Файл не переносится во вложенную директорию. **ПОЧЕМУ.** Туда смотрит `project_src` модуля `docker_compose_v2`. Правило внешнее по происхождению, но нарушается легко — при попытке «навести порядок» и убрать compose в `config/`, где ему по смыслу категорий было бы место. ### ANSD-8. Секреты рендерятся в файл конфигурации **СЛЕДУЕТ.** Значения приходят из vault-переменных и попадают в файл, принадлежащий пользователю приложения. **ПОЧЕМУ.** Файл под `0600` не наследуется дочерними процессами, не виден в `docker inspect` и не оседает в compose-файле на диске. Это те же три довода, по которым базовая конвенция конфигурации выбирает файл вместо окружения. ### ANSD-9. Когда приложение не умеет файловые секреты — `environment` под `no_log` **ДОПУСКАЕТСЯ.** Задача рендера идёт с `no_log: true`. **ПОЧЕМУ.** Явное разрешение нужно, чтобы ANSD-8 не читался как запрет на деплой такого приложения. Способ вынужденный: секрет попадает в метаданные контейнера и в compose-файл на диске. Приложение, научившееся читать секреты из файла, переводится на ANSD-8 при ближайшем касании.