Files
infra/ansible/inventory/group_vars/all/services.yml
T
DmitryandClaude Opus 5 9725d3ea7c Add service registry, shared roles and unified reverse proxy
Collect the facts about all 14 services -- VMID, node, address, ports,
domain, pinned images, resources, backup and monitoring participation --
into group_vars/all/services.yml. Values are taken from the existing
playbooks; gaps are marked null rather than invented.

Replace reverse-proxy-{gitea,vaultwarden,grimmory}.yml with a single
playbook iterating over registry entries that declare a domain. It keeps
every check the three had, preserves grimmory's richer Caddy block
byte-for-byte, and restarts Caddy once when any site changed instead of
up to three times. Verified with --check --diff against ru-vps: ok=6
changed=0, so it reproduces the current Caddyfile exactly.

Add two roles factoring out the skeleton duplicated across the pve-*
playbooks: lxc_docker_host (packages, /dev/fuse assertion, fuse-overlayfs
storage driver, UFW baseline) and compose_service (compose file, systemd
unit, config validation, health check). They are not wired into any
playbook yet -- migrating a live service is a separate, per-service step;
compose_service/README.md shows the Gitea example and spells out what
actually changes on the host.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GTocXkGUUazHdKKd3r9k71
2026-08-26 22:10:16 +03:00

606 lines
24 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
# ============================================================================
# homelab_services — декларативный реестр сервисов HomeLab
# ============================================================================
#
# ЧТО ЭТО
# Единственное место, где собраны факты о каждом сервисе домашней лаборатории:
# VMID, узел Proxmox, адрес, порты, публичный домен, закреплённые образы
# с digest, ресурсы LXC, схема резервного копирования, мониторинг и порядок
# автозапуска.
#
# ВСЕ значения взяты из существующего кода (ansible/playbooks/*.yml,
# ansible/roles/*, ansible/inventory/hosts.yml). Ничего не выдумано.
# Там, где факта в коде нет, стоит комментарий "# нет в коде", а не догадка.
#
# КТО ЭТО ПОТРЕБЛЯЕТ (на текущем этапе)
# * ansible/playbooks/reverse-proxy.yml — итерируется по сервисам,
# у которых задан блок `proxy`, и собирает Caddyfile на ru-vps.
# * Человек — как справочник вместо чтения семи playbook'ов по 200-400 строк.
#
# Существующие pve-*.yml пока НЕ читают этот реестр: их перевод на
# homelab_services + роли lxc_docker_host/compose_service — отдельный этап.
# До тех пор реестр и pve-*.yml нужно править согласованно.
#
# ЧТО ПРАВИТЬ ПРИ ДОБАВЛЕНИИ НОВОГО СЕРВИСА
# 1. Добавить запись в homelab_services ниже (все обязательные поля).
# 2. Если сервис публикуется наружу — заполнить блок `proxy`
# (domain, upstream, caddy_marker, caddy_container_check, health).
# Больше ничего для reverse-proxy делать не нужно.
# 3. Хост в ansible/inventory/hosts.yml (+ группы monitoring_exporters и др.).
# 4. Задание бэкапа в ansible/playbooks/pve-backup-jobs.yml
# и VMID в roles/backup_audit/defaults/main.yml.
# 5. Плейбук развёртывания (pve-<name>.yml).
#
# СОГЛАШЕНИЯ ПО ПОЛЯМ
# vmid int, уникальный в кластере
# node cloud-pc | mini-pc — узел Proxmox, где живёт контейнер
# ip адрес в LAN без маски
# hostname имя LXC и имя хоста в inventory
# role короткое человеческое описание
# provisioner чем создаётся контейнер:
# pct_ssh — прямой `pct create` по SSH на узле PVE
# pve_lxc — роль roles/pve_lxc через Proxmox API
# unmanaged — создан вручную, в репозитории нет плейбука
# lxc ресурсы и параметры контейнера (as-created)
# ports опубликованные порты сервиса
# images закреплённые образы с digest (пустой список — образ не
# используется или собирается локально)
# backup pbs / restic / none — фактическая схема из pve-backup-jobs.yml
# и offsite-restic-yadisk.yml
# monitoring node_exporter — состоит ли хост в группе monitoring_exporters;
# blackbox — пробится ли снаружи из roles/monitoring_blackbox
# proxy присутствует только у сервисов с публичным доменом
# ============================================================================
# Хост, на котором стоит Caddy и терминируется публичный HTTPS.
homelab_reverse_proxy_host: ru-vps
homelab_reverse_proxy_dir: /opt/services/ru-vps/caddy
homelab_reverse_proxy_caddyfile: /opt/services/ru-vps/caddy/Caddyfile
# ВНИМАНИЕ: сам контейнер Caddy на ru-vps ansible'ом НЕ управляется —
# в репозитории нет плейбука его установки. Управляется только Caddyfile.
homelab_reverse_proxy_container: caddy
# Адрес центрального хоста мониторинга (источник scrape для node-exporter).
homelab_monitoring_host_ip: 192.168.1.30
homelab_services:
# --------------------------------------------------------------------------
pbs:
vmid: 120
node: cloud-pc
ip: 192.168.1.20
hostname: pbs
role: Proxmox Backup Server
provisioner: unmanaged # плейбука создания в репозитории нет
lxc:
cores: null # нет в коде
memory: null # нет в коде
swap: null # нет в коде
disk: null # нет в коде
startup: null # нет в коде
features: null # нет в коде
unprivileged: null # нет в коде
ports:
- {name: pbs-api, port: 8007, proto: tcp} # стандартный порт PBS
images: []
backup:
kind: pbs-local
# pve-backup-jobs.yml: job homelab-local-weekly-pbs, хранилище "backup"
job: homelab-local-weekly-pbs
schedule: "Sun 03:30"
storage: backup
prune: keep-last=2
monitoring:
node_exporter: true
blackbox: false
backup_audit_vmid: false # 120 отсутствует в backup_audit_pbs_vmids
# --------------------------------------------------------------------------
ovpn-mini:
vmid: 132
node: mini-pc
ip: 192.168.1.23
hostname: ovpn-mini
role: OpenVPN-шлюз в LAN (клиент ru-vps)
provisioner: pve_lxc # playbooks/pve-ovpn-mini.yml
lxc:
cores: 1 # из roles/pve_lxc/defaults
memory: 256 # из roles/pve_lxc/defaults
swap: 128 # из roles/pve_lxc/defaults
disk: local-lvm:8
startup: order=30 # из roles/pve_lxc/defaults
features: "nesting=1"
unprivileged: true
devices: ["/dev/net/tun"]
ports:
- {name: openvpn, port: 8443, proto: tcp, note: "туннель к ru-vps 10.78.0.1"}
images: []
backup:
kind: pbs
job: homelab-pbs-daily-mini
schedule: "02:40"
storage: pbs
monitoring:
node_exporter: true
blackbox: false
backup_audit_vmid: true
# --------------------------------------------------------------------------
vaultwarden:
vmid: 140
node: mini-pc
ip: 192.168.1.24
hostname: vaultwarden
role: Менеджер паролей Vaultwarden
provisioner: pct_ssh # playbooks/pve-vaultwarden.yml
lxc:
cores: 2
memory: 1024
swap: 512
disk: local-lvm:16
startup: order=40
features: "nesting=1,keyctl=1"
unprivileged: true
ostemplate: local:vztmpl/debian-13-standard_13.1-2_amd64.tar.zst
devices: ["/dev/fuse"]
data_dir: /opt/vaultwarden/data
ports:
- {name: http, port: 80, proto: tcp}
images:
- vaultwarden/server:1.37.1@sha256:e9efdf001bf0d68c21f2cbfb8e1d9b5961a7ca9c85e0a7e58bf51a13b997d744
runtime: docker-run-systemd # /etc/systemd/system/vaultwarden.service
backup:
kind: pbs+restic
job: homelab-pbs-daily-mini
schedule: "02:40"
storage: pbs
restic:
profile: vaultwarden
repository: rclone:yadisk:System/Backups/HomeLab/restic/vaultwarden
source_path: /opt/vaultwarden/data
schedule: "*-*-* 04:45:00"
sqlite_db: /opt/vaultwarden/data/db.sqlite3
monitoring:
node_exporter: true
blackbox: true
backup_audit_vmid: true
proxy:
domain: pass.ada-dev.ru
upstream: 192.168.1.24:80
caddy_marker: "# {mark} ANSIBLE MANAGED VAULTWARDEN SITE"
# caddy_body не задан -> используется простой `reverse_proxy <upstream>`
caddy_container_check: "reverse_proxy 192.168.1.24:80"
# Устаревшая секция от миграции с 10.122.62.95. Удалить это поле
# можно после подтверждённого прогона reverse-proxy.yml на ru-vps.
caddy_legacy_regexp: '(?ms)^pass\.ada-dev\.ru \{\n\s*reverse_proxy 10\.122\.62\.95:10380\n\}\n+'
health:
path: /
status_code: [200]
follow_redirects: none # соответствует `curl -fsS` без -L в старом плейбуке
# --------------------------------------------------------------------------
gitea:
vmid: 141
node: cloud-pc
ip: 192.168.1.25
hostname: gitea
role: Git-хостинг Gitea
provisioner: pct_ssh # playbooks/pve-gitea.yml
lxc:
cores: 2
memory: 2048
swap: 1024
disk: data:32
startup: order=50
features: "nesting=1,keyctl=1"
unprivileged: true
ostemplate: local:vztmpl/debian-13-standard_13.1-2_amd64.tar.zst
devices: ["/dev/fuse"]
mounts:
# mp0 на cloud-pc; каталог на хосте принадлежит uid/gid 101000
- {host_path: /opt/data/gitea, container_path: /opt/gitea/data, host_uid: 101000, host_gid: 101000}
data_dir: /opt/gitea/data
ports:
- {name: http, port: 3000, proto: tcp}
- {name: ssh, port: 2222, proto: tcp, container_port: 22}
images:
- gitea/gitea:1.27.1@sha256:b64126cf5c3f4e5f0f231b510bb13715f6cb8e508188b44de90bdb9a04f3055d
runtime: docker-run-systemd # /etc/systemd/system/gitea.service
backup:
kind: pbs+restic
job: homelab-pbs-daily-cloud
schedule: "02:10"
storage: pbs
restic:
profile: gitea
# restic-профиль выполняется на cloud-pc, а не внутри LXC
run_on: cloud-pc
repository: rclone:yadisk:System/Backups/HomeLab/restic/gitea
source_path: /opt/data/gitea
schedule: "*-*-* 04:15:00"
sqlite_db: /opt/data/gitea/gitea/gitea.db
monitoring:
node_exporter: true
blackbox: true
backup_audit_vmid: true
proxy:
domain: git.ada-dev.ru
upstream: 192.168.1.25:3000
caddy_marker: "# {mark} ANSIBLE MANAGED GITEA SITE"
caddy_container_check: "reverse_proxy 192.168.1.25:3000"
# Устаревшая секция от миграции с 10.122.62.51. Удалить это поле
# можно после подтверждённого прогона reverse-proxy.yml на ru-vps.
caddy_legacy_regexp: '(?ms)^git\.ada-dev\.ru \{\n\s*reverse_proxy 10\.122\.62\.51:3002\n\}\n+'
health:
path: /
status_code: [200]
follow_redirects: none # соответствует `curl -fsS` без -L в старом плейбуке
# --------------------------------------------------------------------------
memoir-bot:
vmid: 142
node: mini-pc
ip: 192.168.1.26
hostname: memoir-bot
role: Telegram-бот дневника в Obsidian-хранилище
provisioner: pct_ssh # playbooks/pve-memoir-bot.yml
lxc:
cores: 1
memory: 512
swap: 512
disk: local-lvm:16
startup: order=60
features: "nesting=1,keyctl=1"
unprivileged: true
ostemplate: local:vztmpl/debian-13-standard_13.1-2_amd64.tar.zst
devices: ["/dev/fuse"]
data_dir: /srv/memoir-bot
ports: [] # портов не публикует, только исходящие подключения к Telegram
images:
- memoir-bot:local # собирается на месте `docker build`, digest отсутствует
runtime: docker-run-systemd # /etc/systemd/system/memoir-bot.service
backup:
kind: pbs
job: homelab-pbs-daily-mini
schedule: "02:40"
storage: pbs
monitoring:
node_exporter: true
blackbox: false
backup_audit_vmid: true
# --------------------------------------------------------------------------
mihomo:
vmid: 143
node: mini-pc
ip: 192.168.1.27
hostname: mihomo
role: Локальный прокси Mihomo + веб-интерфейс MetaCubeXD
provisioner: pct_ssh # playbooks/pve-mihomo.yml
lxc:
cores: 1
memory: 512
swap: 512
disk: local-lvm:8
startup: order=70
features: "nesting=1,keyctl=1"
unprivileged: true
ostemplate: local:vztmpl/debian-13-standard_13.1-2_amd64.tar.zst
devices: ["/dev/fuse", "/dev/net/tun"]
data_dir: /opt/mihomo
ports:
- {name: mixed, port: 7890, proto: tcp}
- {name: socks, port: 7891, proto: tcp}
- {name: controller, port: 9090, proto: tcp}
- {name: ui, port: 8080, proto: tcp, container_port: 80}
images:
- metacubex/mihomo:v1.19.29@sha256:5e7bcc5e7a866afcc8b007ef827c9ba773f2f34b6d7311b6d39ed1751f37cfd5
- ghcr.io/metacubex/metacubexd:v1.270.6@sha256:156d55be885d4ba6254d840bd781b715c20c00afee6e6c24c76be4cfe5eb89d4
runtime: docker-run-systemd # mihomo.service + mihomo-ui.service
backup:
kind: pbs
job: homelab-pbs-daily-mini
schedule: "02:40"
storage: pbs
monitoring:
node_exporter: true
blackbox: false
backup_audit_vmid: true
# --------------------------------------------------------------------------
adguard:
vmid: 144
node: mini-pc
ip: 192.168.1.28
hostname: adguard
role: AdGuard Home — DNS с фильтрацией
provisioner: pct_ssh # playbooks/pve-adguard.yml
lxc:
cores: 1
memory: 512
swap: 512
disk: local-lvm:8
startup: order=40
features: "nesting=1,keyctl=1"
unprivileged: true
ostemplate: local:vztmpl/debian-13-standard_13.1-2_amd64.tar.zst
devices: ["/dev/fuse"]
data_dir: /opt/adguard
ports:
- {name: dns-tcp, port: 53, proto: tcp}
- {name: dns-udp, port: 53, proto: udp}
- {name: http, port: 80, proto: tcp}
- {name: setup, port: 3000, proto: tcp}
images:
- adguard/adguardhome:v0.107.78@sha256:2c127294fa5f96151d9d3a433fb9d66c17e4d18cf698c2b04372a80e26fdd26f
runtime: docker-run-systemd # /etc/systemd/system/adguard.service
backup:
kind: pbs
job: homelab-pbs-daily-mini
schedule: "02:40"
storage: pbs
monitoring:
node_exporter: true
blackbox: false
backup_audit_vmid: true
# --------------------------------------------------------------------------
docker-test:
vmid: 145
node: cloud-pc
ip: 192.168.1.29
hostname: docker-test
role: Песочница для проверки Docker в непривилегированном LXC
provisioner: pct_ssh # playbooks/pve-docker-test.yml
lxc:
cores: 1
memory: 512
swap: 512
disk: data:8
startup: order=50
features: "nesting=1,keyctl=1"
unprivileged: true
ostemplate: local:vztmpl/debian-13-standard_13.1-2_amd64.tar.zst
devices: ["/dev/fuse"]
ports: []
images: [] # используется только hello-world для smoke-теста
backup:
kind: pbs
job: homelab-pbs-daily-cloud
schedule: "02:10"
storage: pbs
monitoring:
node_exporter: false # хоста нет в группе monitoring_exporters
blackbox: false
backup_audit_vmid: true
# --------------------------------------------------------------------------
monitoring:
vmid: 146
node: cloud-pc
ip: 192.168.1.30
hostname: monitoring
role: Prometheus + Alertmanager + Grafana + blackbox + pve-exporter + Uptime Kuma
provisioner: pve_lxc # playbooks/pve-monitoring.yml
lxc:
cores: 2
memory: 4096
swap: 512
disk: data:24
startup: order=80
# создаётся с nesting=1, keyctl=1 добавляется отдельной задачей `pct set`
features: "nesting=1,keyctl=1"
unprivileged: true
ostemplate: local:vztmpl/debian-13-standard_13.1-2_amd64.tar.zst
data_dir: /opt/monitoring
ports:
- {name: grafana, port: 3000, proto: tcp, bind: 192.168.1.30}
- {name: pushgateway, port: 9091, proto: tcp, bind: 192.168.1.30}
- {name: uptime-kuma, port: 3001, proto: tcp, bind: 192.168.1.30}
images:
# без digest — так закреплено в roles/monitoring_server/templates/compose.yml.j2
- prom/prometheus:v3.2.1
- prom/alertmanager:v0.28.0
- grafana/grafana:11.5.1
- prom/blackbox-exporter:v0.25.0
- prompve/prometheus-pve-exporter:3.5.5
- prom/pushgateway:v1.11.0
- louislam/uptime-kuma:1.23.16@sha256:431fee3be822b04861cf0e35daf4beef6b7cb37391c5f26c3ad6e12ce280fe18
runtime: docker-compose-systemd
backup:
kind: pbs
job: homelab-pbs-daily-cloud
schedule: "02:10"
storage: pbs
monitoring:
node_exporter: true
blackbox: false
backup_audit_vmid: true
# --------------------------------------------------------------------------
hermes-ai:
vmid: 147
node: cloud-pc
ip: 192.168.1.31
hostname: hermes-ai
role: Хост под приложение Hermes + прозрачный TUN-прокси через mihomo
provisioner: pct_ssh # playbooks/pve-hermes-ai.yml
lxc:
cores: 2
memory: 4096
swap: 512
disk: data:24
startup: order=90
features: "nesting=1,keyctl=1"
unprivileged: true
ostemplate: local:vztmpl/debian-13-standard_13.1-2_amd64.tar.zst
devices: ["/dev/fuse", "/dev/net/tun"]
data_dir: /srv/hermes-ai
ports: [] # публикация портов запрещена: Docker обходит UFW
images:
- metacubex/mihomo@sha256:e6acd921addecfd59a8e2d38203f88356d635b54de6c0673db0e015139989312
runtime: docker-run-systemd # hermes-ai-tun-proxy.service
backup:
kind: pbs
job: homelab-pbs-daily-cloud
schedule: "02:10"
storage: pbs
monitoring:
node_exporter: false # хоста нет в группе monitoring_exporters
blackbox: false
backup_audit_vmid: true
# --------------------------------------------------------------------------
emergency-bot:
vmid: 148
node: mini-pc
ip: 192.168.1.32
hostname: emergency-bot
role: Telegram-бот аварийного доступа
provisioner: pve_lxc # playbooks/pve-emergency-bot.yml
lxc:
cores: 1
memory: 512
swap: 256
disk: local-lvm:4
startup: order=70
features: "nesting=1" # из roles/pve_lxc/defaults
unprivileged: true
ostemplate: local:vztmpl/debian-13-standard_13.1-2_amd64.tar.zst
ports: []
images: [] # роль emergency_bot образы не закрепляет
backup:
kind: none # VMID 148 отсутствует в pve-backup-jobs.yml — бэкапа нет
monitoring:
node_exporter: false # хоста нет в группе monitoring_exporters
blackbox: false
backup_audit_vmid: false
# --------------------------------------------------------------------------
grimmory:
vmid: 149
node: cloud-pc
ip: 192.168.1.34
hostname: grimmory
role: Библиотека книг Grimmory (+ MariaDB), OPDS/KOReader
provisioner: pve_lxc # playbooks/pve-grimmory.yml
lxc:
cores: 2
memory: 4096
swap: 1024
disk: data:64
startup: order=100
# создаётся с nesting=1, keyctl=1 добавляется отдельной задачей `pct set`
features: "nesting=1,keyctl=1"
unprivileged: true
ostemplate: local:vztmpl/debian-13-standard_13.1-2_amd64.tar.zst
devices: ["/dev/fuse"]
data_dir: /opt/grimmory
ports:
- {name: http, port: 6060, proto: tcp, bind: 192.168.1.34}
images:
- grimmory/grimmory:v3.2.4@sha256:dfa7afdfcf25d649fd664497a62385dd00cd9678c37546e182c172e41c8e80cb
- lscr.io/linuxserver/mariadb:11.4.8@sha256:91de7f701bc7fc3a424b81beafca7a7c6c4c5b7c8be6afd2ae148698695c0b0c
runtime: docker-compose-systemd # grimmory.service + grimmory-docker-firewall.service
backup:
kind: pbs+restic
job: homelab-pbs-daily-cloud
schedule: "02:10"
storage: pbs
restic:
profile: grimmory
repository: rclone:yadisk:System/Backups/HomeLab/restic/grimmory
source_path: /opt/grimmory
schedule: "*-*-* 05:15:00"
mariadb_container: grimmory-mariadb
mariadb_database: grimmory
monitoring:
node_exporter: true
blackbox: true
backup_audit_vmid: true
proxy:
domain: books.ada-dev.ru
upstream: 192.168.1.34:6060
caddy_marker: "# {mark} ANSIBLE MANAGED GRIMMORY SITE"
# Тело секции Caddy целиком: OPDS/KOReader требуют отключить сжатие
# и принудительно выставить Accept, иначе читалки не понимают ответ.
# Апстрим в теле обязан совпадать с полем `upstream` выше —
# reverse-proxy.yml это проверяет assert'ом.
caddy_body: |
@grimmory_opds_atom path /api/v1/opds /api/v1/opds/libraries /api/v1/opds/shelves /api/v1/opds/magic-shelves /api/v1/opds/authors /api/v1/opds/series /api/v1/opds/catalog /api/v1/opds/recent /api/v1/opds/surprise
handle @grimmory_opds_atom {
reverse_proxy 192.168.1.34:6060 {
header_up Accept "application/atom+xml"
header_up Accept-Encoding identity
transport http {
compression off
}
}
}
@grimmory_opds_search path /api/v1/opds/search.opds
handle @grimmory_opds_search {
reverse_proxy 192.168.1.34:6060 {
header_up Accept "application/opensearchdescription+xml"
header_up Accept-Encoding identity
transport http {
compression off
}
}
}
@grimmory_device_api path /api/koreader /api/koreader/* /api/v1/opds /api/v1/opds/*
handle @grimmory_device_api {
reverse_proxy 192.168.1.34:6060 {
header_up Accept-Encoding identity
transport http {
compression off
}
}
}
handle {
reverse_proxy 192.168.1.34:6060
}
# Проверка бинд-маунта внутри контейнера caddy: у grimmory ищем маркер
# маршрутизации, а не строку reverse_proxy — она встречается многократно.
caddy_container_check: "@grimmory_device_api path /api/koreader"
# caddy_legacy_regexp отсутствует: в старом reverse-proxy-grimmory.yml
# секции удаления legacy-конфига не было.
health:
path: /api/v1/healthcheck
status_code: [200]
follow_redirects: safe # поведение ansible.builtin.uri по умолчанию
# --------------------------------------------------------------------------
gyro:
vmid: 150
node: mini-pc
ip: 192.168.1.35
hostname: gyro
role: Изолированный контейнер под задачу gyro (доступ в сеть только через mihomo)
provisioner: pve_lxc # playbooks/pve-gyro.yml
lxc:
cores: 1
memory: 512
swap: 256
disk: local-lvm:2
startup: order=80
features: "" # pve_lxc_features: [] — nesting отключён намеренно
unprivileged: true
ostemplate: local:vztmpl/debian-13-standard_13.1-2_amd64.tar.zst
firewall: proxmox # /etc/pve/firewall/150.fw, policy_in DROP
ports: []
images: [] # роль gyro образы не закрепляет
backup:
kind: pbs
job: homelab-pbs-daily-mini
schedule: "02:40"
storage: pbs
monitoring:
node_exporter: false # хоста нет в группе monitoring_exporters
blackbox: false
backup_audit_vmid: true