Files
infra/ansible/roles/compose_service/README.md
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

6.8 KiB
Raw Blame History

compose_service

Раскладывает Docker Compose стек и systemd-юнит, который им управляет. Обобщение того, что делает playbooks/pve-grimmory.yml; такой же по форме код продублирован в roles/monitoring_server и roles/uptime_kuma (grep -rn "up -d --remove-orphans" — четыре копии одного юнита).

Что делает

  1. Создаёт корневой каталог сервиса и, при необходимости, каталоги данных с нужными owner/group/mode.
  2. Генерирует .env один раз под umask 077: статические пары из compose_service_env_static и случайные секреты (openssl rand -hex) для имён из compose_service_env_generated. Задача целиком под no_log: true; права форсируются в 0600. Повторный прогон существующий .env не перетирает — иначе поменялся бы пароль работающей БД.
  3. Кладёт compose.yml из inline-строки или из Jinja-шаблона.
  4. Ставит systemd-юнит Type=oneshot, RemainAfterExit=yes, ExecStart=docker compose up -d --remove-orphans, ExecStop=docker compose down.
  5. daemon-reload через handler + немедленный meta: flush_handlers (перечитать юнит надо ДО systemctl start, а не в конце play).
  6. Валидирует конфигурацию: docker compose config --quiet, no_log: true — при ошибке вывод подставляет значения из .env.
  7. Стартует сервис, перезапуская его только если изменился compose.yml, юнит или сработал внешний триггер (compose_service_restart_triggers).
  8. Ждёт health-endpoint через uri с retries/until.

Переменные

Полный список — в defaults/main.yml. Обязательные: compose_service_name и ровно один из compose_service_compose_content / compose_service_compose_template (проверяется assert в начале роли).

Переменная По умолчанию Назначение
compose_service_name имя сервиса и systemd-юнита
compose_service_root /opt/<name> корень стека
compose_service_compose_content "" inline compose.yml
compose_service_compose_template "" путь к Jinja-шаблону
compose_service_directories [] каталоги данных
compose_service_env_static {} пары для .env
compose_service_env_generated [] имена случайных секретов
compose_service_after / _requires [] доп. юниты в After=/Requires=
compose_service_restart_triggers [] внешние причины рестарта
compose_service_health_url "" URL health-check (пусто — пропустить)

Пример использования — как выглядел бы Gitea на новых ролях

playbooks/pve-gitea.yml сейчас 231 строка. На ролях pve_lxc + lxc_docker_host + compose_service содержательная часть сводится примерно к такому (сам стек Gitea пока НЕ мигрирован — это следующий этап):

---
- name: Create the Gitea LXC on cloud-pc
  hosts: localhost
  connection: local
  gather_facts: false
  vars:
    gitea: "{{ homelab_services.gitea }}"
  roles:
    - role: pve_lxc
      pve_lxc_vmid: "{{ gitea.vmid }}"
      pve_lxc_node: "{{ gitea.node }}"
      pve_lxc_hostname: "{{ gitea.hostname }}"
      pve_lxc_ip: "{{ gitea.ip }}/24"
      pve_lxc_disk: "{{ gitea.lxc.disk }}"
      pve_lxc_cores: "{{ gitea.lxc.cores }}"
      pve_lxc_memory: "{{ gitea.lxc.memory }}"
      pve_lxc_swap: "{{ gitea.lxc.swap }}"
      pve_lxc_startup: "{{ gitea.lxc.startup }}"
      pve_lxc_ostemplate: "{{ gitea.lxc.ostemplate }}"

- name: Configure the Gitea service
  hosts: gitea
  gather_facts: true
  vars:
    gitea: "{{ homelab_services.gitea }}"
  roles:
    - role: lxc_docker_host
      lxc_docker_host_extra_packages: [sqlite3, rsync]
      lxc_docker_host_ufw_service_rules:
        - port: "3000"
          sources: ["{{ homelab_lan_cidr }}", "{{ openvpn_network_cidr }}"]
        - port: "2222"
          sources: ["{{ homelab_lan_cidr }}", "{{ openvpn_network_cidr }}"]

    - role: compose_service
      compose_service_name: gitea
      compose_service_root: /opt/gitea
      compose_service_description: Gitea Compose stack
      compose_service_directories:
        - {path: /opt/gitea/data, owner: "1000", group: "1000", mode: "0750"}
      compose_service_env_static:
        USER_UID: "1000"
        USER_GID: "1000"
      compose_service_compose_content: |
        services:
          gitea:
            image: {{ gitea.images[0] }}
            container_name: gitea
            environment:
              USER_UID: "${USER_UID}"
              USER_GID: "${USER_GID}"
            ports:
              - "3000:3000"
              - "2222:22"
            volumes:
              - /opt/gitea/data:/data
            restart: unless-stopped
      compose_service_health_url: http://127.0.0.1:3000/api/healthz
      compose_service_health_retries: 24
      compose_service_health_delay: 5

Около 30 строк vars вместо 231 строки процедурного кода, и все факты (vmid, узел, адрес, ресурсы, digest образа) берутся из реестра homelab_services, а не дублируются в плейбуке.

Что при такой миграции меняется на живом хосте

Это не чистый рефакторинг, поэтому мигрировать нужно осознанно:

  • docker run в ExecStart= заменяется на compose-стек — контейнер пересоздаётся, юнит gitea.service меняет тип на oneshot.
  • появляется /opt/gitea/.env, которого раньше не было;
  • --pull never и явный docker pull по digest заменяются на image: в compose — политику закрепления образов надо перенести отдельно.

Поэтому перевод существующих сервисов вынесен в отдельный этап и делается по одному сервису, с бэкапом и --check --diff перед реальным прогоном.