Files
infra/servers/rpi.md
T

12 KiB
Raw Blame History

rpi — домашняя Raspberry Pi 4 (rpi.local, 192.168.10.25)

Поднята 2026-10-11. Домашний сервер за NAT: первая задача — бэкап личного OneDrive на внешний HDD. Мониторится с fr1 через pushgateway (скрейпить её снаружи нельзя — она за домашним роутером).

Железо и ОС

  • Raspberry Pi 4, SD 64 ГБ (mmcblk0), питание штатное — vcgencmd get_throttled = 0x0.
  • Raspberry Pi OS Lite 64-bit Trixie (Debian 13), образ 2026-10-06-raspios-trixie-arm64-lite.
  • Headless-настройка — cloud-init, а не старые ssh/userconf.txt: в Trixie Pi Imager пишет user-data/network-config на bootfs. Наш user-data создал пользователя, ключ, hostname, таймзону MSK.
  • Сеть: Ethernet, DHCP. MAC dc:a6:32:db:b0:f9, роутер 192.168.10.254. ⏳ Резерв адреса на роутере — на пользователе (см. хвосты).

Доступ

  • С Mac: ssh rpi (алиас → rpi.local, user rus, ключ ~/.ssh/id_ed25519). Резолвится через mDNS (avahi в образе по умолчанию).
  • Только по ключу: passwordauthentication no, у rus пароль заблокирован, sudo NOPASSWD (иначе sudo был бы недоступен вовсе).

Диски

Устройство Модель Роль
sda WD My Passport 4 ТБ (WD40NDZW, s/n WD-WX82D6228YCD) ext4 backup → /mnt/backup
— WD Elements 1 ТБ (WD10SDRW) не подключён, ждёт хаба (APFS, не отформатирован)
  • fstab по UUID с nofail,x-systemd.device-timeout=30s (stacks/rpi-backup/fstab.line): без диска малина грузится, бэкап просто пропускается (mountpoint -q).
  • ⚠️ Два 2.5" HDD от USB Pi 4 не тянут. Проверено 2026-10-11: при раскрутке обоих — over-current на всех USB3-портах; под нагрузкой 4-ТБ диск отваливался (USB disconnect) и залипал в Unit Not Ready. Один диск стабилен (20 ГБ чтения 103 МБ/с, mkfs ~60 ГБ записи без сбросов). Второй диск — только через USB3-хаб с собственным питанием.
  • Залипший после отвала диск лечится power-cycle всех USB (Pi 4 переключает питание портов только разом): sudo uhubctl -l 2 -a off; sudo uhubctl -l 1-1 -a off; sleep 8; …-a on.
  • SMART на момент ввода: 9 897 ч, 0 переназначенных/ожидающих, Start_Stop 21 946, Load_Cycle 42 530.

Бэкап OneDrive (код — stacks/rpi-backup/)

  • rclone (официальный .deb, v1.75.2 — в Debian 1.60, старый), remote onedrive: = личный OneDrive (drive_type personal). Токен — в ~rus/.config/rclone/rclone.conf (600), только на малине. Получен через rclone authorize onedrive на Mac.
  • onedrive-backup (таймер каждые 4 ч, 00/4:30): rclone sync в /mnt/backup/onedrive/current, всё удалённое/перезаписанное в облаке — в archive/<ts>/ навсегда (пользователь хочет иметь всё, что когда-либо было в OneDrive). Хэши архива — manifests/archive-<ts>.qxh, лог каждого прогона — logs/sync-<ts>.log (INFO: поштучно что скачано/убрано в архив).
  • Целостность: rclone сверяет QuickXorHash каждого скачанного файла с OneDrive; onedrive-verify (вс 05:00) перечитывает весь HDD: rclone check current↔облако + rclone checksum archive↔манифесты. Отчёт — logs/verify-<ts>*.
  • Оба юнита под общим flock /run/lock/onedrive-backup.lock — не идут параллельно и не убивают друг друга.
  • ⚠️ Файл, загруженный и удалённый в облаке между прогонами (<4 ч), в бэкап не попадёт.
  • ⚠️ Personal Vault исключён — rclone через API туда не ходит.
  • ⚠️ Архив без ротации: растёт вечно. Скрипт выходит с кодом 2 при заполнении >90 % (→ алерт BackupFailed), плюс HostDisk* если когда-то поставим node-exporter.
  • Первая полная выгрузка (368 ГБ) запущена 2026-10-11 ~13:13, ~27 МБ/с (упор в домашний канал).

Мониторинг → fr1

  • smartd (stacks/rpi-backup/smartd.conf): DEVICESCAN -d removable, -n standby,q (не будить диск), короткий самотест сб 03:00, длинный 1-го числа 05:00. ⚠️ -d sat с DEVICESCAN не работает («Not a SAT device») — тип моста пусть определяет сам.
  • smart-metrics (таймер 15 мин): smartctl -n standby → метрики smart_* → job smart. Спящий диск не будится: отдаются значения из кэша /var/lib/smart-metrics/ + smart_disk_standby=1.
  • backup-metrics <job> — ExecStopPost=+ у юнитов бэкапа: backup_last_exit_code, backup_last_run_timestamp, backup_last_success_timestamp (время успеха хранится локально в /var/lib/backup-metrics/, т.к. PUT заменяет группу целиком).
  • Доставка: metrics-push <job> = ssh с ключом /etc/metrics-push/id_ed25519 (root, 600) на rus@161.97.93.252; host key fr1 закреплён в /etc/metrics-push/known_hosts. На fr1 ключ ограничен forced command (servers/fr1.md → Мониторинг).
  • smart-metrics заодно отдаёт backup_disk_size_bytes/backup_disk_avail_bytes для /mnt/backup (statfs, диск не будит).
  • Дашборд: https://mon.ruzzy.dev/d/rpi-disk (SMART, износ за сутки, свежесть бэкапов, заполнение).
  • Алерты — группа rpi в stacks/monitoring/prometheus/rules/alerts.yml.

Агент Astra (с 2026-10-11)

Агент на базе Codex (OpenAI), работает на самой малине, управление — из Telegram-бота @rasp_contol_bot. Код бота — отдельный репо ruzzy/astra-bot (там же схема работы).

  • Пользователь astra (группы sudo, adm, systemd-journal, users), пароль заблокирован, sudo NOPASSWD (/etc/sudoers.d/90-astra) — по решению пользователя. SSH-вход под ним — ключом ~/.ssh/id_ed25519 с Mac (алиас ssh rpi-astra).
  • ⚠️ sudo = агент видит всё: токен OneDrive (~rus/.config/rclone/rclone.conf), пуш-ключ метрик (/etc/metrics-push/), deploy-ключ с записью в infra, токен бота. Утечка контекста агента = утечка этих секретов.
  • Codex CLI 0.162.1 — полный пакет в /opt/codex, симлинк /usr/local/bin/codex. ⚠️ Голый бинарник из релиза (codex-aarch64-…tar.gz) не годится: remote-control/app-server требуют пакет (codex-package-aarch64-unknown-linux-musl.tar.gz), иначе «this CLI has no complete local package». Логин — подпиской ChatGPT (codex login --device-auth, аккаунт ruzzyrullezz@gmail.com, Plus), токен ~astra/.codex/auth.json (600). Ключ API не используется.
  • Агенту разрешено пушить в main ruzzy/infra напрямую (решение пользователя 2026-10-11, перекрывает правило из CLAUDE.md только для него): любые изменения сервера он обязан внести в servers/rpi.md/stacks/ и запушить в том же прогоне, конфликты — pull --rebase без --force, неразрешимые — отдать пользователю. Источник инструкций — codex/AGENTS.md в ruzzy/astra-bot.
  • Конфиг ~astra/.codex/config.toml: approval_policy=on-request, sandbox_mode=workspace-write; инструкции агенту — ~astra/.codex/AGENTS.md (паспорт, правила infra, что не трогать).
  • astra-bot (astra-bot.service, user astra): /opt/astra-bot/bot.py ⇄ codex app-server (JSON-RPC stdio) ⇄ Telegram long polling. Подтверждения команд/правок — кнопками в чате. Конфиг /etc/astra-bot.env (root 600): TG_TOKEN, TG_ALLOWED_IDS=607015. Тред Codex на чат — ~astra/.local/state/astra-bot.json. Бот лежит в /opt под root, а не в ~/infra, чтобы агент не правил свой код без sudo.
  • ⚠️ Почему не штатный Codex Remote из ChatGPT: телефон к безголовому Linux не сопрягается — официально хостом может быть только macOS/Windows; codex remote-control pair выдаёт код, но и QR, и ручной ввод дают «Pair Failed», до демона попытка даже не доходит (проверено с debug-логом 2026-10-11). Рабочая альтернатива — ChatGPT Desktop на Mac → SSH-хост rpi-astra, но тогда Mac должен быть включён.
  • /boot/firmware/cmdline.txt: добавлен cfg80211.ieee80211_regdom=RU (бэкап .bak-20261011) — только чтобы /etc/profile.d/wifi-check.sh не печатал в stdout login-shell предупреждение про rfkill: оно ломало бы stdio-протоколы (Codex app-server по SSH). Wi-Fi не используется.
  • Рабочая копия ~astra/infra (клон ruzzy/infra), git identity Astra (rpi) <astra@rpi.ruzzy.dev>, deploy-ключ ~astra/.ssh/gitea_infra (запись, Gitea id 10), host key Gitea (RSA) закреплён и сверен с Mac. Автопулла нет — агент делает git pull сам.

Открытые хвосты (на 2026-10-11)

  • ⏳ Закрепить 192.168.10.25 за MAC dc:a6:32:db:b0:f9 на роутере.
  • ⏳ USB3-хаб с питанием → подключить и отформатировать WD Elements 1 ТБ (вторая копия архива — сейчас копия одна).
  • Нет node-exporter (CPU/RAM/диск самой малины в Prometheus не видны) — пуш через pushgateway для node-метрик неудобен; решить, нужен ли.
  • Первый прогон бэкапа стартовал до появления ExecStopPost — его итог может не дойти в метрики; backup_last_success_timestamp появится с первого прогона по таймеру. До этого BackupStale молчит (серии нет).
  • ⏳ codex-remote.service (демон codex remote-control с RUST_LOG=debug) всё ещё включён и работает, хотя сопряжение не взлетело — убрать или оставить ждать поддержки Linux от OpenAI (решение за пользователем).
  • Токен бота передавался через чат с ассистентом — при желании перевыпустить в @BotFather (/revoke) и вписать в /etc/astra-bot.env.
  • Через неделю снять SMART и посчитать реальный прирост Start_Stop/Load_Cycle — прогноз ресурса пока оценочный.