--- # Базовое состояние публичной 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