# ============================================================================ # ОБРАЗЕЦ, НЕ ЗАГРУЖАЕТСЯ. Расширение .example выбрано намеренно: сам пилот # (VMID 199) снесён 2026-09-02, и будь этот файл активным, каждый `tofu-plan` # предлагал бы создать его заново. # # Держим как рабочий пример формы ресурса: по нему описываются реальные # сервисы при переезде. См. docs/ai/migration-tofu.md, шаг 4.2. # ============================================================================ # # Пилот: одноразовый LXC, созданный OpenTofu. # ============================================================================ # # ЗАЧЕМ # Проверить на живом кластере ровно те места, из-за которых переезд на Tofu # выглядел рискованным, не трогая ни один боевой контейнер: # # 1. Проброс /dev/fuse. В pve-*.yml он сделан правкой /etc/pve/lxc/.conf # (lxc.cgroup2.devices.allow + lxc.mount.entry), потому что Proxmox API # сырые lxc.* ключи не принимает. У провайдера для этого есть # features.fuse — штатный флаг PVE, а не обход. # 2. Проброс /dev/net/tun — блок device_passthrough (dev0: в PVE 8.2+). # 3. Mount point на storage вместо bind mount каталога хоста. Именно bind # mount у gitea (mp0: /opt/data/gitea) требует прав root@pam и не # создаётся API-токеном; volume на datastore — создаётся. # # Контейнер намеренно не входит в inventory и не автозапускается. # Снести после проверки: make tofu-destroy CONFIRM=1 # # VMID 199 и 192.168.1.39 выбраны свободными на 2026-09-02 и лежат вне # диапазона сервисов, но внутри маршрутов ru-vps (192.168.1.5-40). locals { # Привилегированный режим = аутентификация root@pam по паролю. Makefile # выбирает его, когда в корневом .env задан PROXMOX_ROOT_PASSWORD. privileged = var.pve_password != "" } resource "proxmox_virtual_environment_container" "pilot" { node_name = "cloud-pc" vm_id = 199 unprivileged = true start_on_boot = false started = true tags = ["tofu", "pilot"] initialization { hostname = "tofu-pilot" ip_config { ipv4 { address = "192.168.1.39/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 = 1 } memory { dedicated = 512 swap = 512 } disk { datastore_id = "data" size = 8 } # Проверка №1 и №2: то, что в pve-*.yml делается правкой конфига на узле. # Токену Proxmox разрешает только nesting; keyctl и fuse требуют root@pam. # null означает «не задавать поле», а не «выключить». features { nesting = true keyctl = local.privileged ? true : null fuse = local.privileged ? true : null } # Проброс устройства Proxmox разрешает только root@pam (403 для токена, # проверено на пилоте 2026-09-02), поэтому блок появляется лишь в # привилегированном режиме. dynamic "device_passthrough" { for_each = local.privileged ? [1] : [] content { path = "/dev/net/tun" } } # Проверка №3: volume на datastore, а не bind mount каталога хоста. mount_point { volume = "data" size = "4G" path = "/opt/pilot-data" } network_interface { name = "eth0" bridge = "vmbr0" firewall = true } startup { order = 999 } # ignore_changes = [features] здесь НЕТ намеренно. Он нужен только в # гибридной схеме, где features выставляет Ansible через `pct set`: иначе # Tofu на следующем apply откатывает их и ломает Docker в контейнере # (проверено на пилоте). В привилегированном режиме features описаны выше # декларативно, и обход не требуется. } output "pilot" { description = "Что проверять на узле после apply" value = { vmid = proxmox_virtual_environment_container.pilot.vm_id node = proxmox_virtual_environment_container.pilot.node_name verify = "ssh cloud-pc sudo pct config ${proxmox_virtual_environment_container.pilot.vm_id}" } }