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

134 lines
6.8 KiB
Markdown
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.
# 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 пока НЕ мигрирован — это следующий этап):
```yaml
---
- 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` перед реальным прогоном.