One ru-vps housekeeping sweep (2026-09-02/03): - playbooks/ru-vps-base.yml (new): adopt the Caddy compose stack into Ansible (pinned image by digest, homelab-caddy.service), and manage the corosync-qnetd UFW rule - allow 5403/tcp from homelab_pve_egress_ip, drop the stale rule for the retired ZeroTier 10.122.62.0/24. The qdevice had gone silent because its only allowed path was the decommissioned ZeroTier network. - group_vars/all/main.yml: homelab_pve_egress_ip (the NATed home egress the PVE nodes reach corosync-qnetd from - a direct path that does not depend on the OpenVPN tunnel). Marked dynamic: a change silently re-breaks the qdevice. - playbooks/status.yml: CLUSTER QUORUM section (pvecm status per PVE node) so a repeat failure is visible. Also drops the memoir-bot unit list and moves the gitea offsite-restic unit to the gitea host (see the OpenTofu-migration commit). - playbooks/ru-vps-zerotier-decommission.yml (new): stop the zerotier container, disable ssh-zt22.service, remove the interface/9993/9001/10.122.62.0/24 UFW rules. Node identity and data are kept; removal is a separate step. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012uoq5AVK8mkBgg83Mq6o5V
138 lines
7.6 KiB
YAML
138 lines
7.6 KiB
YAML
---
|
||
# Базовое состояние публичной VPS и стек Caddy.
|
||
#
|
||
# ОБЛАСТЬ ОТВЕТСТВЕННОСТИ
|
||
# Этот плейбук ПРИНИМАЕТ существующий ru-vps под управление Ansible, а не
|
||
# разворачивает его с нуля. Docker и compose-плагин уже установлены вручную
|
||
# (Docker CE 29.x из upstream-репозитория, не Ubuntu'шный docker.io), поэтому
|
||
# плейбук их проверяет, но не ставит: `apt install docker.io` поверх Docker CE
|
||
# на боевой VPS сломал бы все стеки сразу.
|
||
#
|
||
# Управляется здесь ТОЛЬКО стек Caddy. Остальные каталоги в
|
||
# /opt/services/ru-vps (gitea-runner, mihomo, resticprofile, squid, zerotier)
|
||
# развёрнуты вручную и осознанно оставлены вне управления — решение по каждому
|
||
# принимается отдельно, см. docs/ai/plan.md.
|
||
#
|
||
# ГРАНИЦА С reverse-proxy.yml
|
||
# Содержимое Caddyfile принадлежит playbooks/reverse-proxy.yml. Здесь файл
|
||
# только проверяется на наличие: стартовать Caddy без конфига бессмысленно,
|
||
# а перезаписать его отсюда — значит потерять все маршруты.
|
||
#
|
||
# ЧТО МЕНЯЕТСЯ НА ЖИВОМ ХОСТЕ ПРИ ПЕРВОМ ПРОГОНЕ
|
||
# 1. Образ caddy:2-alpine закрепляется по digest -> контейнер пересоздаётся.
|
||
# 2. Появляется systemd-юнит homelab-caddy.service: до сих пор жизненным
|
||
# циклом стека управляла только политика restart=unless-stopped, то есть
|
||
# после `docker compose down` его никто не поднимал.
|
||
# Это кратковременный перерыв в публичном HTTPS. Прогонять осознанно.
|
||
#
|
||
# ЗАМЕЧАНИЕ ПРО --check
|
||
# До первого реального прогона `--check` заканчивается ошибкой
|
||
# "Could not find the requested service homelab-caddy": в check-режиме юнит
|
||
# на диск не пишется, поэтому systemd его не видит. Это артефакт проверки,
|
||
# а не дефект плейбука. Diff'ы задач выше при этом достоверны.
|
||
|
||
- name: Adopt the ru-vps Caddy stack into Ansible
|
||
hosts: ru-vps
|
||
gather_facts: true
|
||
|
||
# roles: выполняются РАНЬШЕ tasks:, поэтому все проверки идут в pre_tasks —
|
||
# иначе стек разложился бы до проверки Caddyfile.
|
||
pre_tasks:
|
||
- name: Verify the container runtime is present
|
||
vars:
|
||
ru_vps_docker_binary: /usr/bin/docker
|
||
block:
|
||
- name: Check the Docker binary
|
||
ansible.builtin.command: "{{ ru_vps_docker_binary }} version --format '{{ '{{' }}.Server.Version{{ '}}' }}'"
|
||
register: ru_vps_docker_version
|
||
changed_when: false
|
||
# Read-only проверка, поэтому выполняется и в --check.
|
||
check_mode: false
|
||
|
||
- name: Check the Compose plugin
|
||
ansible.builtin.command: "{{ ru_vps_docker_binary }} compose version --short"
|
||
register: ru_vps_compose_version
|
||
changed_when: false
|
||
# Read-only проверка, поэтому выполняется и в --check.
|
||
check_mode: false
|
||
|
||
- name: Report the detected runtime
|
||
ansible.builtin.debug:
|
||
msg: >-
|
||
docker={{ ru_vps_docker_version.stdout | trim }}
|
||
compose={{ ru_vps_compose_version.stdout | trim }}
|
||
|
||
# --- Арбитр кластера ---------------------------------------------------
|
||
# corosync-qnetd на ru-vps даёт двухнодовому кластеру третий голос.
|
||
# Ноды подключаются к нему по публичному адресу (см. corosync.conf,
|
||
# host: 157.22.231.198), а старое правило пускало 5403 только из
|
||
# 10.122.62.0/24 — сети ZeroTier, выведенной в июле. После этого qdevice
|
||
# молча перестал голосовать.
|
||
- name: Allow corosync-qnetd from the PVE nodes
|
||
community.general.ufw:
|
||
rule: allow
|
||
port: "5403"
|
||
proto: tcp
|
||
src: "{{ homelab_pve_egress_ip }}"
|
||
comment: corosync-qnetd for the HomeLab PVE cluster
|
||
|
||
- name: Drop the qnetd rule for the retired ZeroTier network
|
||
community.general.ufw:
|
||
rule: allow
|
||
port: "5403"
|
||
proto: tcp
|
||
src: 10.122.62.0/24
|
||
delete: true
|
||
|
||
# Caddyfile принадлежит reverse-proxy.yml. Без него стек стартует пустым,
|
||
# поэтому сначала проверяем наличие и только потом раскладываем стек.
|
||
- name: Check the Caddyfile managed by reverse-proxy.yml
|
||
ansible.builtin.stat:
|
||
path: "{{ homelab_reverse_proxy_caddyfile }}"
|
||
register: ru_vps_caddyfile
|
||
|
||
- name: Require the Caddyfile before touching the stack
|
||
ansible.builtin.assert:
|
||
that:
|
||
- ru_vps_caddyfile.stat.exists
|
||
- ru_vps_caddyfile.stat.size > 0
|
||
fail_msg: >-
|
||
{{ homelab_reverse_proxy_caddyfile }} отсутствует или пуст.
|
||
Сначала прогони playbooks/reverse-proxy.yml, иначе Caddy стартует
|
||
без маршрутов и публичные сервисы лягут.
|
||
|
||
roles:
|
||
- role: compose_service
|
||
compose_service_name: "{{ homelab_reverse_proxy_unit }}"
|
||
compose_service_description: Caddy reverse proxy for public HomeLab services
|
||
compose_service_root: "{{ homelab_reverse_proxy_dir }}"
|
||
# Каталог создан вручную с 0755 — сохраняем, чтобы прогон не давал
|
||
# косметического diff'а на боевом хосте.
|
||
compose_service_root_mode: "0755"
|
||
# Имя файла сохраняем: имя compose-проекта выводится из каталога, а сам
|
||
# файл должен остаться тем же, иначе стек станет orphan и поднимется
|
||
# второй контейнер на тех же 80/443.
|
||
compose_service_compose_file: docker-compose.yml
|
||
# /opt/data/caddy и /opt/configs/caddy принадлежат ada:ada и содержат
|
||
# выпущенные сертификаты. Роль их владельца не трогает намеренно.
|
||
compose_service_directories: []
|
||
compose_service_compose_content: |
|
||
services:
|
||
caddy:
|
||
image: {{ homelab_reverse_proxy_image | trim }}
|
||
container_name: {{ homelab_reverse_proxy_container }}
|
||
restart: unless-stopped
|
||
ports:
|
||
- "80:80"
|
||
- "443:443"
|
||
- "443:443/udp"
|
||
volumes:
|
||
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
||
- /opt/data/caddy:/data
|
||
- /opt/configs/caddy:/config
|
||
networks: {}
|
||
# Caddy на голый IP отвечает 308 (редирект http -> https), а не 200.
|
||
compose_service_health_url: http://127.0.0.1:80/
|
||
compose_service_health_status: [308]
|
||
compose_service_health_follow_redirects: none
|