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
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
# 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` перед реальным прогоном.
|
||||
Reference in New Issue
Block a user