# ============================================================================ # Сервис №7 по плану (docs/ai/migration-tofu.md, раздел 5): grimmory. # Самый болезненный шаг: MariaDB, Flyway-миграции, контракт OPDS/KOReader. # # OLD vmid 149, узел cloud-pc, боевой адрес 192.168.1.34. # Эталон снят 2026-09-02: ssh cloud-pc sudo pct config 149 (и совпадающий # дословно /etc/pve/lxc/149.conf): # # arch: amd64 # cmode: shell # cores: 2 # features: nesting=1,keyctl=1 # hostname: grimmory # memory: 4096 # nameserver: 1.1.1.1 # net0: name=eth0,bridge=vmbr0,firewall=1,gw=192.168.1.1,ip=192.168.1.34/24,type=veth # onboot: 1 # ostype: debian # rootfs: data:149/vm-149-disk-0.raw,size=64G # startup: order=100 # swap: 1024 # unprivileged: 1 # lxc.cgroup2.devices.allow: c 10:229 rwm # lxc.mount.entry: /dev/fuse dev/fuse none bind,create=file # # Ни mpN, ни dev0 в эталоне нет — только /dev/fuse через сырые lxc.* строки. # Значит правило про bind mount -> mount_point (как у gitea) здесь не # применяется: mount_point в этом ресурсе не добавлен. # # vm_id 157 — новый VMID для blue-green переезда (назначен, не менять). # TMPIP 192.168.1.16 ниже используется только на шаге 4.3 (проверка перед # cutover; запись grimmory-new уже есть в hosts.yml/ssh_config — заведена # координатором). После шага 4.5.15 адрес меняется на боевой 192.168.1.34, # OLD (149) останавливается и остаётся откатом минимум неделю. # # --- /dev/fuse: features.fuse, а не device_passthrough ---------------------- # Эталонные lxc.cgroup2.devices.allow + lxc.mount.entry для /dev/fuse — это # ручной обход (lineinfile по /etc/pve/lxc/149.conf в pve-grimmory.yml), # потому что Proxmox API сырые lxc.* ключи не принимает. # # Заменяется штатным флагом PVE features.fuse, а НЕ device_passthrough. # device_passthrough (dev0:) предназначен для сырых character-устройств вида # /dev/net/tun; для /dev/fuse у PVE есть флаг, и именно он проверен на пилоте # VMID 199 (`features: fuse=1,keyctl=1,nesting=1`), см. tofu/pilot.tf.example # строки 17-21 и tofu/README.md. Формулировка «device_passthrough для каждого # устройства из lxc.mount.entry» в migration-tofu.md п.4.2 была слишком # широкой и уточнена там же 2026-09-02; так же поправлены svc-docker-test.tf # и svc-vaultwarden.tf, так что все ресурсы tofu/ теперь единообразны. # # Следствие: pct config нового контейнера покажет features: fuse=1,keyctl=1, # nesting=1 — текстуально иначе, чем эталон, при том же эффекте. # # --- MariaDB: только dump/restore, не копирование файлов -------------------- # /opt/grimmory/mariadb — это datadir MariaDB (grimmory-mariadb, БД grimmory). # Копировать эти файлы между старым и новым контейнером НЕЛЬЗЯ: нужен # mariadb-dump на старом контейнере (сервис приложения остановлен, MariaDB # работает) и restore на новом. Рецепт dump уже есть и проверен в рабочем # offsite-профиле restic (playbooks/offsite-restic-yadisk.yml, задача # ../tasks/offsite-restic-profile.yml): docker exec -e MYSQL_PWD=... # grimmory-mariadb mariadb-dump --single-transaction --routines --events # --databases grimmory. legacy-warning.md и docs/ai/edge-cases.md запрещают # code-only downgrade после Flyway-миграции — на новом контейнере обязан # развернуться РОВНО ТОТ ЖЕ pinned образ v3.2.4 (проверено 2026-09-02: # запущенный в CT 149 образ grimmory/grimmory:v3.2.4 имеет digest # sha256:dfa7afdfcf25d649fd664497a62385dd00cd9678c37546e182c172e41c8e80cb — # совпадает с реестром services.yml байт-в-байт, расхождения нет). # ============================================================================ resource "proxmox_virtual_environment_container" "grimmory" { node_name = "cloud-pc" vm_id = 157 unprivileged = true start_on_boot = true started = true tags = ["tofu"] initialization { hostname = "grimmory" ip_config { ipv4 { address = "192.168.1.34/24" gateway = "192.168.1.1" } } dns { servers = ["1.1.1.1"] } user_account { keys = [trimspace(file(pathexpand("~/.ssh/id_ed25519_homelab.pub")))] } } operating_system { template_file_id = "local:vztmpl/debian-13-standard_13.1-2_amd64.tar.zst" type = "debian" } cpu { cores = 2 } memory { dedicated = 4096 swap = 1024 } disk { datastore_id = "data" size = 64 } # Эталон: features: nesting=1,keyctl=1 плюс /dev/fuse через сырые lxc.* # строки. fuse=1 — штатная замена этого обхода, см. комментарий файла выше. features { nesting = true keyctl = true fuse = true } # roles/pve_lxc всем контейнерам ставит cmode=shell (pve_lxc_cmode), провайдер # это отслеживает через блок console.type — без него на следующем apply # откатит на дефолт Proxmox tty. Обнаружено 2026-09-02 на emergency-bot, # см. tofu/README.md. console { type = "shell" } network_interface { name = "eth0" bridge = "vmbr0" firewall = true } startup { order = 100 } } output "grimmory" { description = "Что проверять на узле после apply" value = { vmid = proxmox_virtual_environment_container.grimmory.vm_id node = proxmox_virtual_environment_container.grimmory.node_name verify = "ssh cloud-pc sudo pct config ${proxmox_virtual_environment_container.grimmory.vm_id}" } }