Capture current Ansible control plane state
Commit the accumulated infrastructure work that was living only in the working tree: monitoring stack, emergency access/bot, gyro allocator, grimmory, adguard, backup audit and the OpenCode agent definitions. Also ignore Python bytecode, local archives and Nix/direnv artifacts. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GTocXkGUUazHdKKd3r9k71
This commit is contained in:
@@ -0,0 +1,551 @@
|
||||
# План развертывания Grimmory
|
||||
|
||||
## Целевая архитектура
|
||||
|
||||
- Отдельный unprivileged LXC `grimmory` на `cloud-pc`.
|
||||
- Итоговые значения: CTID `149`, IP `192.168.1.34`. Первоначальный `.33` оказался занят после создания LXC.
|
||||
- RootFS на storage `data`, первоначально `64 GB` с возможностью расширения.
|
||||
- Ресурсы: `2 vCPU`, `4 GB RAM`, `1 GB swap`.
|
||||
- Docker с `fuse-overlayfs` внутри LXC.
|
||||
- Docker Compose: Grimmory и MariaDB.
|
||||
- Публичный адрес: `https://books.ada-dev.ru`.
|
||||
- Книги и данные находятся внутри диска LXC.
|
||||
- Daily PBS snapshot всего CT.
|
||||
- Offsite restic: дамп MariaDB, конфигурация и `/app/data`, но не книги.
|
||||
- Образы фиксируются на версиях:
|
||||
- `grimmory/grimmory:v3.2.4`;
|
||||
- `lscr.io/linuxserver/mariadb:11.4.8`.
|
||||
|
||||
Размер диска нужно скорректировать до развертывания, если текущая библиотека уже приближается к 40-50 GB.
|
||||
|
||||
## 1. Предварительные проверки
|
||||
|
||||
1. Проверить, что CTID `149` свободен:
|
||||
- в Proxmox API;
|
||||
- в `pct list` на обоих узлах;
|
||||
- в PBS среди исторических VMID.
|
||||
2. Проверить IP `192.168.1.34`:
|
||||
- отсутствие в inventory;
|
||||
- отсутствие DHCP reservation;
|
||||
- отсутствие ответа на ARP/ping;
|
||||
- отсутствие DNS-записи с другим назначением.
|
||||
3. Проверить емкость `cloud-pc:data`.
|
||||
4. Проверить работоспособность PBS datastore и последнего backup job `cloud-pc`.
|
||||
5. Проверить возможность создать DNS-запись `books.ada-dev.ru`.
|
||||
6. Зафиксировать ожидаемый объем существующей коллекции и темп роста.
|
||||
7. Получить digest образов после выбора версий. В первой итерации допустим pinned tag; digest предпочтительнее для полностью воспроизводимого состояния.
|
||||
|
||||
Проверка должна завершиться без создания LXC. При конфликте CTID/IP выбрать следующие свободные значения и использовать их согласованно во всех файлах.
|
||||
|
||||
## 2. Inventory
|
||||
|
||||
Изменить `ansible/inventory/hosts.yml`:
|
||||
|
||||
- добавить `grimmory` в `lxc_infra`;
|
||||
- указать `ansible_host: 192.168.1.34`;
|
||||
- указать `expected_lan_ip`;
|
||||
- указать `ansible_user: root`;
|
||||
- использовать существующий ProxyJump через `ru-vps`;
|
||||
- добавить в `monitoring_exporters`, если Node Exporter разворачивается сразу.
|
||||
|
||||
После изменения проверить:
|
||||
|
||||
```bash
|
||||
ansible-inventory --graph
|
||||
ansible-inventory --host grimmory
|
||||
```
|
||||
|
||||
Edge cases:
|
||||
|
||||
- не использовать IP за пределами маршрутизируемого service range `.5-.40`;
|
||||
- убедиться, что имя `grimmory` разрешается Ansible именно в новый IP;
|
||||
- не добавлять host в inventory до выбора окончательных CTID/IP во всех playbooks.
|
||||
|
||||
## 3. Provisioning LXC
|
||||
|
||||
Создать `ansible/playbooks/pve-grimmory.yml`, используя роль `pve_lxc`.
|
||||
|
||||
Параметры:
|
||||
|
||||
```yaml
|
||||
pve_lxc_vmid: 149
|
||||
pve_lxc_node: cloud-pc
|
||||
pve_lxc_hostname: grimmory
|
||||
pve_lxc_ip: 192.168.1.34/24
|
||||
pve_lxc_gateway: 192.168.1.1
|
||||
pve_lxc_storage: data
|
||||
pve_lxc_disk: 64
|
||||
pve_lxc_cores: 2
|
||||
pve_lxc_memory: 4096
|
||||
pve_lxc_swap: 1024
|
||||
pve_lxc_unprivileged: true
|
||||
pve_lxc_features:
|
||||
- nesting=1
|
||||
- keyctl=1
|
||||
```
|
||||
|
||||
Дополнительно настроить Docker-in-LXC по существующему паттерну:
|
||||
|
||||
- разрешить `/dev/fuse`;
|
||||
- добавить bind устройства `/dev/fuse`;
|
||||
- перезапустить LXC только при изменении его PVE-конфигурации;
|
||||
- дождаться SSH;
|
||||
- проверить наличие пользовательского SSH-ключа.
|
||||
|
||||
Текущая роль `pve_lxc` не управляет `/dev/fuse`, поэтому эти задачи остаются в playbook, аналогично существующим Docker LXC.
|
||||
|
||||
Проверки:
|
||||
|
||||
```bash
|
||||
ansible-playbook --syntax-check playbooks/pve-grimmory.yml
|
||||
ansible-playbook playbooks/pve-grimmory.yml --check
|
||||
```
|
||||
|
||||
`--check` для Proxmox API не гарантирует полноценной симуляции, поэтому его результат использовать только как дополнительную проверку.
|
||||
|
||||
После создания проверить:
|
||||
|
||||
- `pct status 149`;
|
||||
- unprivileged mode включен;
|
||||
- rootfs действительно находится на `data`;
|
||||
- `onboot=1`;
|
||||
- SSH работает через ProxyJump;
|
||||
- `/dev/fuse` существует внутри LXC;
|
||||
- повторный запуск playbook не создает изменений.
|
||||
|
||||
## 4. Docker Runtime
|
||||
|
||||
Внутри LXC установить:
|
||||
|
||||
- `docker.io`;
|
||||
- Docker Compose v2, если доступен как `docker-compose-plugin`;
|
||||
- `fuse-overlayfs`;
|
||||
- `ca-certificates`;
|
||||
- `curl`;
|
||||
- `mariadb-client` для backup/restore-проверок;
|
||||
- `prometheus-node-exporter`.
|
||||
|
||||
Настроить `/etc/docker/daemon.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"storage-driver": "fuse-overlayfs"
|
||||
}
|
||||
```
|
||||
|
||||
Проверить:
|
||||
|
||||
```bash
|
||||
docker info
|
||||
docker compose version
|
||||
docker run --rm hello-world
|
||||
```
|
||||
|
||||
Критерий успеха: Docker сообщает `fuse-overlayfs` как storage driver и переживает перезапуск LXC.
|
||||
|
||||
## 5. Persistent Data
|
||||
|
||||
Создать структуру:
|
||||
|
||||
```text
|
||||
/opt/grimmory/
|
||||
├── compose.yml
|
||||
├── .env
|
||||
├── data/
|
||||
├── books/
|
||||
├── bookdrop/
|
||||
├── mariadb/
|
||||
└── backup-staging/
|
||||
```
|
||||
|
||||
Назначение:
|
||||
|
||||
- `data` -> `/app/data`;
|
||||
- `books` -> `/books`;
|
||||
- `bookdrop` -> `/bookdrop`;
|
||||
- `mariadb` -> `/config`;
|
||||
- `backup-staging` -> временные согласованные дампы.
|
||||
|
||||
Права каталогов должны соответствовать `APP_USER_ID`, `APP_GROUP_ID`, MariaDB `PUID/PGID`.
|
||||
|
||||
Edge cases:
|
||||
|
||||
- `bookdrop` не является библиотекой: после успешного импорта исходники удаляются оттуда;
|
||||
- не размещать важные необработанные оригиналы только в `bookdrop`;
|
||||
- не вкладывать bind mounts друг в друга;
|
||||
- не использовать `DISK_TYPE=NETWORK`, поскольку файлы находятся на локальном rootfs;
|
||||
- оставлять минимум 15-20% диска свободным: импорт и обработка могут временно удваивать размер книги;
|
||||
- учитывать крупные PDF/CBR и возможные heap dump при OOM.
|
||||
|
||||
## 6. Compose
|
||||
|
||||
Управлять двумя контейнерами через Compose, поскольку Grimmory зависит от MariaDB. Для этого сервиса Compose логичнее отдельных systemd `docker run`.
|
||||
|
||||
Основные параметры:
|
||||
|
||||
- `DISK_TYPE=LOCAL`;
|
||||
- `TZ` соответствует HomeLab;
|
||||
- `ALLOWED_ORIGINS=https://books.ada-dev.ru`;
|
||||
- MariaDB доступна только во внутренней Docker network;
|
||||
- наружу публикуется только Grimmory `6060`;
|
||||
- порт привязывается к LAN IP LXC, а не `0.0.0.0`, если Compose это позволяет;
|
||||
- healthcheck использует `/api/v1/healthcheck`;
|
||||
- `restart: unless-stopped`.
|
||||
|
||||
Создать systemd unit-обертку для:
|
||||
|
||||
```bash
|
||||
docker compose -f /opt/grimmory/compose.yml up -d --remove-orphans
|
||||
```
|
||||
|
||||
Не использовать `--pull always`: обновление должно быть отдельной контролируемой операцией.
|
||||
|
||||
Секреты:
|
||||
|
||||
- случайные отдельные пароли `DB_PASSWORD` и `MYSQL_ROOT_PASSWORD`;
|
||||
- `.env` с mode `0600`;
|
||||
- реальные значения не добавляются в Git;
|
||||
- `.env.example` содержит только имена переменных и фиктивные значения;
|
||||
- пароль первого администратора создается вручную через UI.
|
||||
|
||||
Проверки:
|
||||
|
||||
```bash
|
||||
docker compose config
|
||||
docker compose pull
|
||||
systemctl start grimmory
|
||||
docker compose ps
|
||||
docker compose logs
|
||||
curl -fsS http://127.0.0.1:6060/api/v1/healthcheck
|
||||
```
|
||||
|
||||
Критерии:
|
||||
|
||||
- оба контейнера healthy;
|
||||
- MariaDB не публикует порт на LAN;
|
||||
- после рестарта LXC оба контейнера возвращаются в healthy;
|
||||
- логи не содержат Flyway errors, authentication failures и циклических рестартов.
|
||||
|
||||
## 7. Первый запуск
|
||||
|
||||
1. Открыть сервис сначала по LAN.
|
||||
2. Создать локального администратора.
|
||||
3. Сохранить пароль в password manager.
|
||||
4. Отключить неиспользуемую публичную регистрацию.
|
||||
5. Создать отдельного обычного пользователя для чтения, если административная учетная запись не должна использоваться на BOOX.
|
||||
6. Включить OPDS.
|
||||
7. Создать отдельные OPDS credentials.
|
||||
8. Выдать пользователю разрешение `Access OPDS`.
|
||||
9. Создать тестовую библиотеку в `/books`.
|
||||
10. Импортировать одну тестовую EPUB через BookDrop.
|
||||
|
||||
Важно: OPDS credentials отделены от обычного логина Grimmory. Пароль OPDS после создания нельзя получить повторно.
|
||||
|
||||
## 8. Reverse Proxy
|
||||
|
||||
Создать `ansible/playbooks/reverse-proxy-grimmory.yml` по паттерну Gitea/Vaultwarden.
|
||||
|
||||
Caddy site:
|
||||
|
||||
```caddy
|
||||
books.ada-dev.ru {
|
||||
reverse_proxy 192.168.1.34:6060
|
||||
}
|
||||
```
|
||||
|
||||
Caddy автоматически поддерживает WebSocket upgrade, но это нужно проверить для `/ws`.
|
||||
|
||||
Не добавлять дополнительный Caddy Basic Auth: он конфликтует с Basic Auth OPDS.
|
||||
|
||||
Проверки до переключения DNS:
|
||||
|
||||
- Caddy может достучаться до `192.168.1.34:6060` через OpenVPN;
|
||||
- upstream healthcheck возвращает `2xx`;
|
||||
- Caddyfile проходит `caddy validate`;
|
||||
- конфигурация действительно смонтирована в контейнер Caddy.
|
||||
|
||||
После DNS:
|
||||
|
||||
```bash
|
||||
curl -fsS https://books.ada-dev.ru/api/v1/healthcheck
|
||||
```
|
||||
|
||||
Дополнительно проверить:
|
||||
|
||||
- валидную цепочку TLS;
|
||||
- WebSocket `/ws`;
|
||||
- правильные внешние `Host`, scheme и port;
|
||||
- отсутствие redirect на внутренний `http://192.168.1.34:6060`;
|
||||
- загрузку файла размером больше стандартного proxy limit;
|
||||
- доступ с мобильной сети, а не только из домашнего LAN.
|
||||
|
||||
## 9. OPDS и BOOX Go 7
|
||||
|
||||
Каталог:
|
||||
|
||||
```text
|
||||
https://books.ada-dev.ru/api/v1/opds
|
||||
```
|
||||
|
||||
Настроить в NeoReader:
|
||||
|
||||
- URL корневого каталога;
|
||||
- отдельный OPDS username/password;
|
||||
- HTTPS;
|
||||
- без второго слоя proxy-аутентификации.
|
||||
|
||||
Тестовый сценарий:
|
||||
|
||||
1. Каталог открывается.
|
||||
2. Отображаются разделы и обложки.
|
||||
3. Работает поиск.
|
||||
4. EPUB скачивается.
|
||||
5. Книга открывается в NeoReader.
|
||||
6. Кириллица в названии, авторе и описании отображается корректно.
|
||||
7. Повторная загрузка не создает поврежденный файл.
|
||||
8. Проверить EPUB, PDF и FB2 отдельно.
|
||||
9. Проверить работу из домашнего Wi-Fi и внешней сети.
|
||||
10. Проверить поведение после смены OPDS-пароля.
|
||||
|
||||
Позиция чтения NeoReader обратно в Grimmory через OPDS не синхронизируется. Это ожидаемое поведение, а не дефект сервера.
|
||||
|
||||
## 10. Firewall
|
||||
|
||||
Минимальная политика:
|
||||
|
||||
- SSH `22` только из LAN и OpenVPN;
|
||||
- `6060` только из LAN/OpenVPN и от нужного reverse-proxy маршрута;
|
||||
- MariaDB не публикуется;
|
||||
- остальные входящие соединения запрещены.
|
||||
|
||||
Edge case: Docker published ports могут обходить UFW. Поэтому проверить фактические nftables/iptables rules и доступ к `6060` с нежелательных сегментов, а не полагаться только на вывод `ufw status`.
|
||||
|
||||
Публично сервис должен быть доступен только через Caddy на `ru-vps`, не через router port-forward.
|
||||
|
||||
## 11. Monitoring
|
||||
|
||||
Добавить:
|
||||
|
||||
- Node Exporter target `192.168.1.34:9100`;
|
||||
- LAN blackbox HTTP probe `/api/v1/healthcheck`;
|
||||
- публичный HTTPS probe `https://books.ada-dev.ru/api/v1/healthcheck`;
|
||||
- TLS expiry check;
|
||||
- базовые алерты по availability, CPU, RAM, disk и backup freshness.
|
||||
|
||||
Не считать один health endpoint достаточным. Он может отвечать при частично неисправной функции OPDS.
|
||||
|
||||
Добавить синтетическую OPDS-проверку:
|
||||
|
||||
- Basic Auth тестового monitoring-пользователя;
|
||||
- ответ `200`;
|
||||
- ожидаемый OPDS content type;
|
||||
- корректный XML/Atom;
|
||||
- секреты только из runtime env, не из шаблона в Git.
|
||||
|
||||
Если хранить OPDS credential в monitoring нежелательно, ограничиться публичным healthcheck и ручным OPDS smoke test после обновлений.
|
||||
|
||||
## 12. PBS Backup
|
||||
|
||||
Добавить CTID в:
|
||||
|
||||
- `ansible/playbooks/pve-backup-jobs.yml`, job `homelab-pbs-daily-cloud`;
|
||||
- `ansible/roles/backup_audit/defaults/main.yml`.
|
||||
|
||||
Сохранить исключение Docker merged layers:
|
||||
|
||||
```text
|
||||
/var/lib/docker/fuse-overlayfs/*/merged
|
||||
```
|
||||
|
||||
Проверки:
|
||||
|
||||
1. Применить backup job.
|
||||
2. Запустить первый backup вручную.
|
||||
3. Убедиться, что snapshot завершен без warnings.
|
||||
4. Проверить наличие VMID `149` в PBS.
|
||||
5. Проверить прохождение backup audit.
|
||||
6. Проверить размер backup: слишком маленький может означать ошибочное исключение данных.
|
||||
7. Выполнить тестовое file-level восстановление нескольких файлов.
|
||||
8. Запланировать полный restore drill в отдельный временный CTID.
|
||||
|
||||
PBS snapshot MariaDB считается crash-consistent, но не заменяет логический dump.
|
||||
|
||||
## 13. Offsite Restic
|
||||
|
||||
Offsite должен содержать:
|
||||
|
||||
- согласованный `mariadb-dump`;
|
||||
- `/opt/grimmory/data`;
|
||||
- `compose.yml`;
|
||||
- зашифрованную или отдельно защищенную конфигурацию;
|
||||
- manifest с версиями Grimmory и MariaDB.
|
||||
|
||||
Не включать:
|
||||
|
||||
- `/opt/grimmory/books`;
|
||||
- `/opt/grimmory/bookdrop`;
|
||||
- `/opt/grimmory/mariadb` как raw-copy работающей БД;
|
||||
- Docker layers;
|
||||
- временные heap dump и cache.
|
||||
|
||||
Для дампа:
|
||||
|
||||
- использовать `mariadb-dump --single-transaction`;
|
||||
- сначала писать во временный файл;
|
||||
- проверять exit code;
|
||||
- атомарно переносить завершенный dump в staging;
|
||||
- только после этого запускать restic;
|
||||
- удалять старый staging после успешного snapshot.
|
||||
|
||||
Расширить существующий restic-паттерн MariaDB-aware логикой, а не использовать SQLite backup helper.
|
||||
|
||||
Проверки:
|
||||
|
||||
- `mariadb` может прочитать dump;
|
||||
- restic snapshot содержит dump и `/app/data`;
|
||||
- backup audit отслеживает профиль Grimmory;
|
||||
- выполнить пробное восстановление в отдельную временную MariaDB;
|
||||
- убедиться, что восстановленная схема проходит Flyway startup на той же версии Grimmory.
|
||||
|
||||
## 14. Обновления
|
||||
|
||||
Обновлять только явно, например `v3.2.4 -> v3.2.5`.
|
||||
|
||||
Процесс:
|
||||
|
||||
1. Прочитать release notes и migration warnings.
|
||||
2. Проверить свободное место.
|
||||
3. Сделать MariaDB dump.
|
||||
4. Сделать offsite restic snapshot.
|
||||
5. Убедиться в наличии свежего PBS backup.
|
||||
6. Изменить pinned image tag/digest.
|
||||
7. Pull и restart.
|
||||
8. Проверить Flyway logs.
|
||||
9. Проверить health, UI, BookDrop и OPDS с BOOX.
|
||||
10. Зафиксировать версию в документации.
|
||||
|
||||
Не выполнять code-only downgrade после применения Flyway migration. Для отката возвращать одновременно старый image и pre-upgrade backup базы.
|
||||
|
||||
## 15. Edge Cases
|
||||
|
||||
Обязательно проверить:
|
||||
|
||||
- заполнение rootfs во время массового импорта;
|
||||
- дубликаты и одинаковые ISBN;
|
||||
- книги без ISBN;
|
||||
- кириллические имена и длинные пути;
|
||||
- два файла одной книги в разных форматах;
|
||||
- поврежденный EPUB/CBR;
|
||||
- файлы больше 1 GB;
|
||||
- cross-filesystem BookDrop move;
|
||||
- перезапуск во время импорта;
|
||||
- падение MariaDB во время обработки;
|
||||
- недоступность Google Books/Open Library/Amazon;
|
||||
- истечение TLS;
|
||||
- смена внешнего домена;
|
||||
- потеря OPDS-пароля;
|
||||
- потеря admin-пароля;
|
||||
- OOM и появление большого heap dump;
|
||||
- несовместимая Flyway migration;
|
||||
- неожиданное изменение floating Docker tag;
|
||||
- частичный backup: БД без `/app/data` либо наоборот;
|
||||
- восстановление БД с файлами библиотеки другого состояния.
|
||||
|
||||
## 16. Финальная приемка
|
||||
|
||||
Развертывание считается завершенным, когда:
|
||||
|
||||
- Ansible запускается повторно без изменений;
|
||||
- LXC автоматически стартует;
|
||||
- контейнеры healthy после reboot;
|
||||
- MariaDB не доступна из LAN;
|
||||
- HTTPS и WebSocket работают;
|
||||
- NeoReader открывает OPDS и скачивает тестовые книги;
|
||||
- импорт через BookDrop проходит полностью;
|
||||
- Node Exporter и blackbox targets находятся в состоянии `UP`;
|
||||
- создан и проверен PBS backup;
|
||||
- создан и тестово восстановлен restic snapshot;
|
||||
- секреты отсутствуют в Git diff;
|
||||
- Obsidian-документация обновлена.
|
||||
|
||||
## 17. Ожидаемые файлы
|
||||
|
||||
Основные изменения:
|
||||
|
||||
- `ansible/inventory/hosts.yml`;
|
||||
- `ansible/playbooks/pve-grimmory.yml`;
|
||||
- `ansible/playbooks/reverse-proxy-grimmory.yml`;
|
||||
- `ansible/playbooks/pve-backup-jobs.yml`;
|
||||
- `ansible/roles/backup_audit/defaults/main.yml`;
|
||||
- `ansible/playbooks/offsite-restic-yadisk.yml`;
|
||||
- `ansible/playbooks/backup-audit.yml`;
|
||||
- `ansible/roles/monitoring_server/templates/prometheus.yml.j2`;
|
||||
- возможно `ansible/roles/monitoring_blackbox/templates/push-metrics.sh.j2`;
|
||||
- `ansible/.env.example`, только если потребуются новые runtime secrets.
|
||||
|
||||
Документация:
|
||||
|
||||
- `Notes/Текущее состояние HomeLab после миграции на Proxmox.md`;
|
||||
- `Log.md`;
|
||||
- `Context.md`;
|
||||
- при необходимости `HomeLab.md`.
|
||||
|
||||
## 18. Открытый параметр
|
||||
|
||||
Перед реализацией подтвердить `64 GB` как начальный размер диска или выбрать его по текущему объему коллекции.
|
||||
|
||||
Решение: текущая коллекция меньше `20 GB`, начальный rootfs оставлен `64 GB`.
|
||||
|
||||
## Журнал выполнения
|
||||
|
||||
### 2026-07-28 — preflight и проектные решения
|
||||
|
||||
- Прочитаны актуальные HomeLab notes и существующие Ansible-паттерны LXC, Caddy, monitoring, PBS и restic.
|
||||
- CTID `149` отсутствует в `pct list` на `cloud-pc` и `mini-pc`, а также среди исторических snapshot в PBS.
|
||||
- IP `192.168.1.33` отсутствует в inventory и AdGuard config, не отвечает на ping, ARP-состояние с `cloud-pc` — `INCOMPLETE`; пользователь подтвердил отсутствие DHCP reservation.
|
||||
- Storage `cloud-pc:data` активен: доступно около `836 GB`, занято `3.67%`.
|
||||
- PBS storage и datastore `pbs` активны; последние snapshots текущих cloud-pc CT созданы `2026-07-27`.
|
||||
- `books.ada-dev.ru` уже разрешается в `157.22.231.198` (`ru-vps`); случайное имя в зоне не разрешается, поэтому это не wildcard. До добавления Caddy site TLS handshake не завершается.
|
||||
- Подтверждены `grimmory/grimmory:v3.2.4` и `lscr.io/linuxserver/mariadb:11.4.8`; выбраны OCI index digests `sha256:dfa7afdfcf25d649fd664497a62385dd00cd9678c37546e182c172e41c8e80cb` и `sha256:91de7f701bc7fc3a424b81beafca7a7c6c4c5b7c8be6afd2ae148698695c0b0c`.
|
||||
- Rootfs подтвержден как `data:64`, timezone — `Europe/Moscow`.
|
||||
- Пароли MariaDB будут случайно созданы один раз внутри LXC и сохранены только в `/opt/grimmory/.env` с mode `0600`.
|
||||
- В первой итерации автоматизируются health/TLS probes; OPDS smoke test остается ручным после создания credentials.
|
||||
- Offsite audit первой итерации: атомарный MariaDB dump, проверка наличия dump в snapshot и `restic check`; автоматический restore в MariaDB отложен.
|
||||
- Первый apply остановился до создания из-за quoting условия VMID guard; второй — из-за отсутствия прямого маршрута к Proxmox API. Для API использован временный SSH local-forward через `ru-vps`.
|
||||
- После создания CT выяснился IP conflict: с `mini-pc` адрес `.33` резолвился как `YandexStationMini_1DD2.loc` и отклонял TCP/22, тогда как локальный bridge `cloud-pc` видел LXC. Выбран следующий адрес `192.168.1.34`; он не отвечает с обоих PVE-узлов, ARP — `INCOMPLETE`, DNS отсутствует.
|
||||
|
||||
### 2026-07-28 — реализация и автоматическая приёмка
|
||||
|
||||
- Создан unprivileged LXC `grimmory` CT `149` на `cloud-pc`: `192.168.1.34/24`, `data:64`, 2 vCPU, 4096 MB RAM, 1024 MB swap, `onboot=1`, `nesting=1`, `keyctl=1`, `/dev/fuse`.
|
||||
- Из-за ограничений API token роль `pve_lxc` используется в create-only mode для Grimmory; VMID guard запрещает менять чужой CT, а network/keyctl/FUSE идемпотентно управляются через `pct` на PVE node.
|
||||
- Установлены Docker Compose v2 `2.26.1`, Docker `26.1.5`, `fuse-overlayfs`, MariaDB client, Node Exporter и UFW. Docker подтвердил storage driver `fuse-overlayfs`.
|
||||
- Случайные DB passwords созданы один раз внутри LXC; `/opt/grimmory/.env` имеет mode `0600`, значения не выводились и не добавлялись в Git.
|
||||
- Compose запустил pinned Grimmory `v3.2.4` и MariaDB `11.4.8`; первый startup применил 142 Flyway migrations и занял около 6 минут. Health wait увеличен до 10 минут.
|
||||
- После reboot LXC оба контейнера вернулись в `healthy`; MariaDB не слушает host/LAN port, Grimmory привязан к `192.168.1.34:6060`.
|
||||
- UFW ограничивает SSH/`6060` LAN и OpenVPN, `9100` разрешён только от monitoring LXC. Отдельная `GRIMMORY-FILTER` chain в `DOCKER-USER` закрывает обход UFW через Docker published port.
|
||||
- Caddy site применён. Проверены public health `200`, сертификат Let's Encrypt до `2026-10-26` и WebSocket/STOMP Upgrade `101` на `/ws`.
|
||||
- Prometheus targets: Node Exporter `up=1`, LAN health `probe_success=1`, public Grimmory probe `probe_success=1`; TLS expiry покрыт существующим alert.
|
||||
- CT `149` добавлен в `homelab-pbs-daily-cloud` и PBS audit. Первый PBS backup `ct/149/2026-07-28T14:47:32Z` успешен, размер `2583749785` bytes; audit success, file-level restore трёх объектов проверен.
|
||||
- Ограничение PBS: storage `data` не поддерживает native LXC snapshots, поэтому job с mode `snapshot` автоматически перешёл в `suspend`; фактическая пауза гостя составила около 1 секунды.
|
||||
- Создан restic snapshot `a3b012ab` в Yandex Disk. Включены atomic MariaDB dump, `/opt/grimmory/data`, Compose, manifest и зашифрованная restore-конфигурация; books/BookDrop/raw MariaDB исключены. `restic check` и restore непустого `grimmory.sql` прошли.
|
||||
- Повторные runs `pve-grimmory.yml`, `reverse-proxy-grimmory.yml`, `pve-backup-jobs.yml`, Grimmory offsite/audit и limited monitoring завершились с `changed=0`.
|
||||
- Обновлены Obsidian current state, `Log.md` и `Context.md`.
|
||||
- Reverse DNS для `.34` содержит stale имя телефона, но ARP на обоих PVE nodes показывает MAC LXC `bc:24:11:43:75:31`. Нужно исключить `.34` из DHCP pool или закрепить reservation, чтобы предотвратить будущий конфликт.
|
||||
|
||||
### Требует участия пользователя
|
||||
|
||||
- Создать первого локального администратора и сохранить пароль в password manager.
|
||||
- Отключить public registration, создать обычного reader-пользователя.
|
||||
- Создать библиотеку `/books`, выполнить тестовый BookDrop import.
|
||||
- Включить OPDS, создать отдельные OPDS credentials и выдать `Access OPDS`.
|
||||
- Проверить EPUB, PDF и FB2 на BOOX Go 7 через Wi-Fi и внешнюю сеть.
|
||||
- После создания OPDS пользователя решить, добавлять ли credential в synthetic monitoring; сейчас OPDS smoke test ручной.
|
||||
- Создать DHCP reservation/exclusion для `192.168.1.34`.
|
||||
- Полный restore drill CT и восстановление dump во временную MariaDB остаются отдельной плановой операцией; file-level restore и dump restore-from-restic уже проверены.
|
||||
|
||||
### 2026-07-28 — Caddy KOReader и OPDS compatibility
|
||||
|
||||
- `reverse-proxy-grimmory.yml` разделяет device endpoints `/api/koreader/**` и `/api/v1/opds/**` в отдельный Caddy handler.
|
||||
- Для этих endpoints upstream compression отключён и установлен `Accept-Encoding: identity`; Caddy не добавляет proxy Basic Auth и не изменяет `Authorization`, `X-Auth-*`, `Range`, `WWW-Authenticate` или download headers.
|
||||
- Запросы с `Accept-Encoding: gzip, br` подтверждённо возвращают `401` без `Content-Encoding` для KOReader и OPDS. Caddy validate, Grimmory health и idempotent rerun успешны.
|
||||
- Дополнительно исправлен Grimmory OPDS `500` из-за `No acceptable representation`: Caddy нормализует `Accept` только для Atom/OpenSearch catalog routes, оставляя original `Accept` для book download/cover routes.
|
||||
Reference in New Issue
Block a user