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
6.8 KiB
compose_service
Раскладывает Docker Compose стек и systemd-юнит, который им управляет.
Обобщение того, что делает playbooks/pve-grimmory.yml; такой же по форме
код продублирован в roles/monitoring_server и roles/uptime_kuma
(grep -rn "up -d --remove-orphans" — четыре копии одного юнита).
Что делает
- Создаёт корневой каталог сервиса и, при необходимости, каталоги данных с нужными owner/group/mode.
- Генерирует
.envодин раз подumask 077: статические пары изcompose_service_env_staticи случайные секреты (openssl rand -hex) для имён изcompose_service_env_generated. Задача целиком подno_log: true; права форсируются в0600. Повторный прогон существующий.envне перетирает — иначе поменялся бы пароль работающей БД. - Кладёт
compose.ymlиз inline-строки или из Jinja-шаблона. - Ставит systemd-юнит
Type=oneshot,RemainAfterExit=yes,ExecStart=docker compose up -d --remove-orphans,ExecStop=docker compose down. daemon-reloadчерез handler + немедленныйmeta: flush_handlers(перечитать юнит надо ДОsystemctl start, а не в конце play).- Валидирует конфигурацию:
docker compose config --quiet,no_log: true— при ошибке вывод подставляет значения из.env. - Стартует сервис, перезапуская его только если изменился
compose.yml, юнит или сработал внешний триггер (compose_service_restart_triggers). - Ждёт 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 перед реальным прогоном.