Files
dev-conventions/stack/ansible/app-directories.md
T
av 7701a28df1 db-identifiers и ansible/app-directories переписаны на формальный язык
- 7 и 9 правил соответственно, у каждого модальность и обоснование
- условие выбора первичного ключа стало правилом с таблицей веток, на
  которые можно ссылаться из отступлений и механизации
2026-07-25 18:56:30 +03:00

8.1 KiB

extends
extends
arch/app-directories.md

Категории директорий: реализация в Ansible

Как категории из arch/app-directories.md раскладываются на сервере плейбуком. Форма записи — common/language.md.

Область действия

Раскладка меняется вместе с миграцией данных, поэтому правила распространяются на новые приложения; существующие переезжают по мере касания, отдельной кампанией не переписываются.

Правила

R1. Каждая директория объявлена переменной *_dir

ДОЛЖЕН. Директория приложения объявляется переменной плейбука внутри base_dir, имя оканчивается на _dir. Для случая «одна директория на категорию» это config_dir, data_dir, cache_dir; когда категория состоит из нескольких директорий, имя даётся по содержимому (media_dir, uploads_dir, dumps_dir).

Почему. Переменная — единственная ссылка, которую разделяют задача создания директории и список бэкапа (R4). Литерал пути в одном из этих мест означает, что переименование директории молча разойдётся с бэкапом, и обнаружится это при восстановлении.

R2. Директории создаются одной задачей циклом по списку

СЛЕДУЕТ. Список директорий в единственной задаче создания.

Почему. Этот список — единственное место, где декларировано всё, что приложение пишет на диск. Разнесённое по нескольким задачам создание отвечает на вопрос «какие директории есть у приложения» только чтением всего плейбука, а именно этот вопрос задают при заведении бэкапа и при разборе места на диске.

R3. Владелец директорий — пользователь, от имени которого работает приложение

ДОЛЖЕН. Конкретная модель — выделенный пользователь на приложение (app_owner_uid == app_owner_gid) или общий primary_user — выбирается на репозиторий и фиксируется ниже.

Почему. Правило про соответствие владельца рантайму, а не про конкретную модель: приложение в контейнере пишет от определённого uid, и если директория принадлежит другому, отказ произойдёт не при деплое, а при первой записи — то есть после того, как плейбук отчитался об успехе. Выбор же модели — свойство репозитория: сервер с одним пользователем и сервер с изоляцией по приложениям решают разные задачи, и навязывать одну модель обоим значит гарантировать вечное отступление.

R4. Список бэкапа собирается из тех же переменных

ДОЛЖЕН. Плейбук кладёт в base_dir файл backup-targets, строки которого ссылаются на переменные *_dir из R1, а не на литеральные пути.

Почему. Правило вывода списка механическое (R5), но применяет его человек или шаблон — то есть ошибиться можно. Общая переменная делает целый класс ошибок невозможным: переименовал директорию — переименовалось в обоих местах. Независимо набранный список расходится тихо и проявляется в единственный момент, когда это уже неисправимо.

R5. В список бэкапа идут только данные

ДОЛЖЕН. Директории категории «данные», включая директорию дампов, — в списке; конфигурация и кеш — нет.

Почему. Реализация правила базовой конвенции. Кеш раздувает снапшот без пользы, а конфигурация содержит отрендеренные секреты — бэкап уезжает в облако, и источником истины для секретов остаётся vault, а не снапшот.

R6. Конфигурация монтируется только на чтение

СЛЕДУЕТ. В compose конфигурация подключается с :ro.

Почему. Плейбук — источник истины для конфигурации, и :ro превращает это из договорённости в свойство системы: приложение, которое втихую переписывает свой конфиг, падает сразу, а не расходится с репозиторием незаметно. Приложение, которому запись в конфиг нужна по устройству, монтируется на запись — это отступление, и оно записывается.

R7. docker-compose.yml лежит в корне base_dir

ДОЛЖЕН. Файл не переносится во вложенную директорию.

Почему. Туда смотрит project_src модуля docker_compose_v2. Правило внешнее по происхождению, но нарушается легко — при попытке «навести порядок» и убрать compose в config/, где ему по смыслу категорий было бы место.

R8. Секреты рендерятся в файл конфигурации

СЛЕДУЕТ. Значения приходят из vault-переменных и попадают в файл, принадлежащий пользователю приложения.

Почему. Файл под 0600 не наследуется дочерними процессами, не виден в docker inspect и не оседает в compose-файле на диске. Это те же три довода, по которым базовая конвенция конфигурации выбирает файл вместо окружения.

R9. Когда приложение не умеет файловые секреты — environment под no_log

ДОПУСКАЕТСЯ. Задача рендера идёт с no_log: true.

Почему. Явное разрешение нужно, чтобы R8 не читался как запрет на деплой такого приложения. Способ вынужденный: секрет попадает в метаданные контейнера и в compose-файл на диске. Приложение, научившееся читать секреты из файла, переводится на R8 при ближайшем касании.

Связано